File indexing completed on 2024-04-14 14:53:21

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2019 Jarosław Staniek <staniek@kde.org>
0003 
0004    Portions of kstandarddirs.cpp:
0005    Copyright (C) 1999 Sirtaj Singh Kang <taj@kde.org>
0006    Copyright (C) 1999,2007 Stephan Kulow <coolo@kde.org>
0007    Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
0008    Copyright (C) 2009 David Faure <faure@kde.org>
0009 
0010    Portions of kshell.cpp:
0011    Copyright (c) 2003,2007 Oswald Buddenhagen <ossi@kde.org>
0012 
0013    This program is free software; you can redistribute it and/or
0014    modify it under the terms of the GNU Library General Public
0015    License as published by the Free Software Foundation; either
0016    version 2 of the License, or (at your option) any later version.
0017 
0018    This program is distributed in the hope that it will be useful,
0019    but WITHOUT ANY WARRANTY; without even the implied warranty of
0020    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0021    Library General Public License for more details.
0022 
0023    You should have received a copy of the GNU Library General Public License
0024    along with this program; see the file COPYING.  If not, write to
0025    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0026  * Boston, MA 02110-1301, USA.
0027 */
0028 
0029 #include "KDbUtils.h"
0030 #include "KDb.h"
0031 #include "KDbConnection.h"
0032 #include "KDbDriverManager.h"
0033 #include "KDbUtils_p.h"
0034 #include "config-kdb.h"
0035 #include "kdb_debug.h"
0036 
0037 #include <QRegularExpression>
0038 #include <QDataStream>
0039 #include <QDir>
0040 #include <QFile>
0041 #include <QFileInfo>
0042 
0043 static const int SQUEEZED_TEXT_LIMIT = 1024;
0044 static const int SQUEEZED_TEXT_SUFFIX = 24;
0045 
0046 #ifdef Q_OS_WIN
0047 #include <windows.h>
0048 #ifdef _WIN32_WCE
0049 #include <basetyps.h>
0050 #endif
0051 #ifdef Q_OS_WIN64
0052 //! @todo did not find a reliable way to fix with kdewin mingw header
0053 #define interface struct
0054 #endif
0055 #endif
0056 
0057 using namespace KDbUtils;
0058 
0059 class Q_DECL_HIDDEN Property::Private
0060 {
0061 public:
0062     Private() : isNull(true) {}
0063     Private(const QVariant &aValue, const QString &aCaption)
0064         : value(aValue), caption(aCaption), isNull(false)
0065     {
0066     }
0067     bool operator==(const Private &other) const {
0068         return std::tie(value, caption, isNull)  == std::tie(other.value, other.caption, other.isNull);
0069     }
0070     QVariant value;  //!< Property value
0071     QString caption; //!< User visible property caption
0072     bool isNull;
0073 };
0074 
0075 Property::Property()
0076     : d(new Private)
0077 {
0078 }
0079 
0080 Property::Property(const QVariant &value, const QString &caption)
0081     : d(new Private(value, caption))
0082 {
0083 }
0084 
0085 Property::Property(const Property &other)
0086 : d(new Private(*other.d))
0087 {
0088 }
0089 
0090 Property::~Property()
0091 {
0092     delete d;
0093 }
0094 
0095 bool Property::operator==(const Property &other) const
0096 {
0097     return *d == *other.d;
0098 }
0099 
0100 bool Property::isNull() const
0101 {
0102     return d->isNull;
0103 }
0104 
0105 QVariant Property::value() const
0106 {
0107     return d->value;
0108 }
0109 
0110 void Property::setValue(const QVariant &value)
0111 {
0112     d->value = value;
0113     d->isNull = false;
0114 }
0115 
0116 QString Property::caption() const
0117 {
0118     return d->caption;
0119 }
0120 
0121 void Property::setCaption(const QString &caption)
0122 {
0123     d->caption = caption;
0124     d->isNull = false;
0125 }
0126 
0127 //---------
0128 
0129 bool KDbUtils::hasParent(QObject *par, QObject *o)
0130 {
0131     if (!o || !par) {
0132         return false;
0133     }
0134     while (o && o != par) {
0135         o = o->parent();
0136     }
0137     return o == par;
0138 }
0139 
0140 QString KDbUtils::toISODateStringWithMs(const QTime& time)
0141 {
0142 #ifdef HAVE_QT_ISODATEWITHMS
0143     return time.toString(Qt::ISODateWithMs);
0144 #else
0145     QString result;
0146     if (time.isValid()) {
0147         result = QString::asprintf("%02d:%02d:%02d.%03d", time.hour(), time.minute(), time.second(),
0148                                    time.msec());
0149     }
0150     return result;
0151 #endif
0152 }
0153 
0154 QString KDbUtils::toISODateStringWithMs(const QDateTime& dateTime)
0155 {
0156 #ifdef HAVE_QT_ISODATEWITHMS
0157     return dateTime.toString(Qt::ISODateWithMs);
0158 #else
0159     QString result;
0160     if (!dateTime.isValid()) {
0161         return result;
0162     }
0163     result = dateTime.toString(Qt::ISODate);
0164     if (result.isEmpty()) { // failure
0165         return result;
0166     }
0167     QString timeString = KDbUtils::toISODateStringWithMs(dateTime.time());
0168     if (timeString.isEmpty()) { // failure
0169         return QString();
0170     }
0171     const int offset = strlen("0000-00-00T");
0172     const int timeLen = strlen("00:00:00");
0173     result.replace(offset, timeLen, timeString); // replace time with time+ms
0174     return result;
0175 #endif
0176 }
0177 
0178 QTime KDbUtils::timeFromISODateStringWithMs(const QString &string)
0179 {
0180 #ifdef HAVE_QT_ISODATEWITHMS
0181     return QTime::fromString(string, Qt::ISODateWithMs);
0182 #else
0183     return QTime::fromString(string, Qt::ISODate); // supports HH:mm:ss.zzzzz already
0184 #endif
0185 }
0186 
0187 QDateTime KDbUtils::dateTimeFromISODateStringWithMs(const QString &string)
0188 {
0189 #ifdef HAVE_QT_ISODATEWITHMS
0190     return QDateTime::fromString(string, Qt::ISODateWithMs);
0191 #else
0192     return QDateTime::fromString(string, Qt::ISODate); // supports HH:mm:ss.zzzzz already
0193 #endif
0194 }
0195 
0196 QDateTime KDbUtils::stringToHackedQTime(const QString &s)
0197 {
0198     if (s.isEmpty()) {
0199         return QDateTime();
0200     }
0201     return QDateTime(QDate(0, 1, 2), KDbUtils::timeFromISODateStringWithMs(s));
0202 }
0203 
0204 void KDbUtils::serializeMap(const QMap<QString, QString>& map, QByteArray *array)
0205 {
0206     if (!array) {
0207         return;
0208     }
0209     QDataStream ds(array, QIODevice::WriteOnly);
0210     ds.setVersion(QDataStream::Qt_3_1);
0211     ds << map;
0212 }
0213 
0214 void KDbUtils::serializeMap(const QMap<QString, QString>& map, QString *string)
0215 {
0216     if (!string) {
0217         return;
0218     }
0219     QByteArray array;
0220     QDataStream ds(&array, QIODevice::WriteOnly);
0221     ds.setVersion(QDataStream::Qt_3_1);
0222     ds << map;
0223     kdbDebug() << array[3] << array[4] << array[5];
0224     const int size = array.size();
0225     string->clear();
0226     string->reserve(size);
0227     for (int i = 0; i < size; i++) {
0228         (*string)[i] = QChar(ushort(array[i]) + 1);
0229     }
0230 }
0231 
0232 QMap<QString, QString> KDbUtils::deserializeMap(const QByteArray& array)
0233 {
0234     QMap<QString, QString> map;
0235     QByteArray ba(array);
0236     QDataStream ds(&ba, QIODevice::ReadOnly);
0237     ds.setVersion(QDataStream::Qt_3_1);
0238     ds >> map;
0239     return map;
0240 }
0241 
0242 QMap<QString, QString> KDbUtils::deserializeMap(const QString& string)
0243 {
0244     QByteArray array;
0245     const int size = string.length();
0246     array.resize(size);
0247     for (int i = 0; i < size; i++) {
0248         array[i] = char(string[i].unicode() - 1);
0249     }
0250     QMap<QString, QString> map;
0251     QDataStream ds(&array, QIODevice::ReadOnly);
0252     ds.setVersion(QDataStream::Qt_3_1);
0253     ds >> map;
0254     return map;
0255 }
0256 
0257 QString KDbUtils::stringToFileName(const QString& string)
0258 {
0259     QString _string(string);
0260     static const QRegularExpression re(QLatin1String("[\\\\/:\\*?\"<>|]"));
0261     _string.replace(re, QLatin1String(" "));
0262     if (_string.startsWith(QLatin1Char('.'))) {
0263         _string.prepend(QLatin1Char('_'));
0264     }
0265     return _string.simplified();
0266 }
0267 
0268 void KDbUtils::simpleCrypt(QString *string)
0269 {
0270     if (!string) {
0271         return;
0272     }
0273     for (int i = 0; i < string->length(); i++) {
0274         ushort& unicode = (*string)[i].unicode();
0275         unicode += (47 + i);
0276     }
0277 }
0278 
0279 bool KDbUtils::simpleDecrypt(QString *string)
0280 {
0281     if (!string) {
0282         return false;
0283     }
0284     QString result(*string);
0285     for (int i = 0; i < result.length(); i++) {
0286         ushort& unicode = result[i].unicode();
0287         if (unicode <= (47 + i)) {
0288             return false;
0289         }
0290         unicode -= (47 + i);
0291     }
0292     *string = result;
0293     return true;
0294 }
0295 
0296 QString KDbUtils::pointerToStringInternal(void* pointer, int size)
0297 {
0298     QString string;
0299     unsigned char* cstr_pointer = (unsigned char*) & pointer;
0300     for (int i = 0; i < size; i++) {
0301         QString s;
0302         s.asprintf("%2.2x", cstr_pointer[i]);
0303         string.append(s);
0304     }
0305     return string;
0306 }
0307 
0308 void* KDbUtils::stringToPointerInternal(const QString& string, int size)
0309 {
0310     if ((string.length() / 2) < size)
0311         return nullptr;
0312     QByteArray array;
0313     array.resize(size);
0314     bool ok;
0315     for (int i = 0; i < size; i++) {
0316         array[i] = (unsigned char)(string.midRef(i * 2, 2).toUInt(&ok, 16));
0317         if (!ok)
0318             return nullptr;
0319     }
0320     return static_cast<void*>(array.data());
0321 }
0322 
0323 //---------
0324 
0325 //! @internal
0326 class Q_DECL_HIDDEN StaticSetOfStrings::Private
0327 {
0328 public:
0329     Private() : array(nullptr), set(nullptr) {}
0330     ~Private() {
0331         delete set;
0332     }
0333     const char* const * array;
0334     QSet<QByteArray> *set;
0335 };
0336 
0337 StaticSetOfStrings::StaticSetOfStrings()
0338         : d(new Private)
0339 {
0340 }
0341 
0342 StaticSetOfStrings::StaticSetOfStrings(const char* const array[])
0343         : d(new Private)
0344 {
0345     setStrings(array);
0346 }
0347 
0348 StaticSetOfStrings::~StaticSetOfStrings()
0349 {
0350     delete d;
0351 }
0352 
0353 void StaticSetOfStrings::setStrings(const char* const array[])
0354 {
0355     delete d->set;
0356     d->set = nullptr;
0357     d->array = array;
0358 }
0359 
0360 bool StaticSetOfStrings::isEmpty() const
0361 {
0362     return d->array == nullptr;
0363 }
0364 
0365 bool StaticSetOfStrings::contains(const QByteArray& string) const
0366 {
0367     if (!d->set) {
0368         d->set = new QSet<QByteArray>();
0369         for (const char * const * p = d->array;*p;p++)
0370             d->set->insert(QByteArray::fromRawData(*p, qstrlen(*p)));
0371     }
0372     return d->set->contains(string);
0373 }
0374 
0375 //---------
0376 
0377 #ifdef Q_OS_MACOS
0378 //! Internal, from kdelibs' kstandarddirs.cpp
0379 static QString getBundle(const QString& path, bool ignore)
0380 {
0381     QFileInfo info;
0382     QString bundle = path;
0383     bundle += QLatin1String(".app/Contents/MacOS/") + bundle.section(QLatin1Char('/'), -1);
0384     info.setFile( bundle );
0385     FILE *file;
0386     if (file = fopen(info.absoluteFilePath().toUtf8().constData(), "r")) {
0387         fclose(file);
0388         struct stat _stat;
0389         if ((stat(info.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) {
0390             return QString();
0391         }
0392         if ( ignore || (_stat.st_mode & S_IXUSR) ) {
0393             if ( ((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK) ) {
0394                 return bundle;
0395             }
0396         }
0397     }
0398     return QString();
0399 }
0400 #endif
0401 
0402 //! Internal, from kdelibs' kstandarddirs.cpp
0403 static QString checkExecutable(const QString& path, bool ignoreExecBit)
0404 {
0405 #ifdef Q_OS_MACOS
0406     QString bundle = getBundle(path, ignoreExecBit);
0407     if (!bundle.isEmpty()) {
0408         return bundle;
0409     }
0410 #endif
0411     QFileInfo info(path);
0412     QFileInfo orig = info;
0413 #ifdef Q_OS_MACOS
0414     FILE *file;
0415     if (file = fopen(orig.absoluteFilePath().toUtf8().constData(), "r")) {
0416         fclose(file);
0417         struct stat _stat;
0418         if ((stat(orig.absoluteFilePath().toUtf8().constData(), &_stat)) < 0) {
0419             return QString();
0420         }
0421         if ( ignoreExecBit || (_stat.st_mode & S_IXUSR) ) {
0422             if ( ((_stat.st_mode & S_IFMT) == S_IFREG) || ((_stat.st_mode & S_IFMT) == S_IFLNK) ) {
0423                 orig.makeAbsolute();
0424                 return orig.filePath();
0425             }
0426         }
0427     }
0428     return QString();
0429 #else
0430     if (info.exists() && info.isSymLink())
0431         info = QFileInfo(info.canonicalFilePath());
0432     if (info.exists() && ( ignoreExecBit || info.isExecutable() ) && info.isFile()) {
0433         // return absolute path, but without symlinks resolved in order to prevent
0434         // problems with executables that work differently depending on name they are
0435         // run as (for example gunzip)
0436         orig.makeAbsolute();
0437         return orig.filePath();
0438     }
0439     return QString();
0440 #endif
0441 }
0442 
0443 //! Internal, from kdelibs' kstandarddirs.cpp
0444 #if defined _WIN32 || defined _WIN64
0445 # define KPATH_SEPARATOR ';'
0446 # define ESCAPE '^'
0447 #else
0448 # define KPATH_SEPARATOR ':'
0449 # define ESCAPE '\\'
0450 #endif
0451 
0452 //! Internal, from kdelibs' kstandarddirs.cpp
0453 static inline QString equalizePath(QString &str)
0454 {
0455 #ifdef Q_OS_WIN
0456     // filter pathes through QFileInfo to have always
0457     // the same case for drive letters
0458     QFileInfo f(str);
0459     if (f.isAbsolute())
0460         return f.absoluteFilePath();
0461     else
0462 #endif
0463         return str;
0464 }
0465 
0466 //! Internal, from kdelibs' kstandarddirs.cpp
0467 static void tokenize(QStringList& tokens, const QString& str,
0468                      const QString& delim)
0469 {
0470     const int len = str.length();
0471     QString token;
0472 
0473     for(int index = 0; index < len; index++) {
0474         if (delim.contains(str[index])) {
0475             tokens.append(equalizePath(token));
0476             token.clear();
0477         } else {
0478             token += str[index];
0479         }
0480     }
0481     if (!token.isEmpty()) {
0482         tokens.append(equalizePath(token));
0483     }
0484 }
0485 
0486 //! Internal, based on kdelibs' kshell.cpp
0487 static QString tildeExpand(const QString &fname)
0488 {
0489     if (!fname.isEmpty() && fname[0] == QLatin1Char('~')) {
0490         int pos = fname.indexOf( QLatin1Char('/') );
0491         QString ret = QDir::homePath(); // simplified
0492         if (pos > 0) {
0493             ret += fname.midRef(pos);
0494         }
0495         return ret;
0496     } else if (fname.length() > 1 && fname[0] == QLatin1Char(ESCAPE) && fname[1] == QLatin1Char('~')) {
0497         return fname.mid(1);
0498     }
0499     return fname;
0500 }
0501 
0502 //! Internal, from kdelibs' kstandarddirs.cpp
0503 static QStringList systemPaths(const QString& pstr)
0504 {
0505     QStringList tokens;
0506     QString p = pstr;
0507 
0508     if (p.isEmpty()) {
0509         p = QString::fromLocal8Bit( qgetenv( "PATH" ) );
0510     }
0511 
0512     QString delimiters(QLatin1Char(KPATH_SEPARATOR));
0513     delimiters += QLatin1Char('\b');
0514     tokenize(tokens, p, delimiters);
0515 
0516     QStringList exePaths;
0517 
0518     // split path using : or \b as delimiters
0519     for(int i = 0; i < tokens.count(); i++) {
0520         exePaths << tildeExpand(tokens[ i ]);
0521     }
0522     return exePaths;
0523 }
0524 
0525 //! Internal, from kdelibs' kstandarddirs.cpp
0526 #ifdef Q_OS_WIN
0527 static QStringList executableExtensions()
0528 {
0529     QStringList ret = QString::fromLocal8Bit(qgetenv("PATHEXT")).split(QLatin1Char(';'));
0530     if (!ret.contains(QLatin1String(".exe"), Qt::CaseInsensitive)) {
0531         // If %PATHEXT% does not contain .exe, it is either empty, malformed, or distorted in ways that we cannot support, anyway.
0532         ret.clear();
0533         ret << QLatin1String(".exe")
0534             << QLatin1String(".com")
0535             << QLatin1String(".bat")
0536             << QLatin1String(".cmd");
0537     }
0538     return ret;
0539 }
0540 #endif
0541 
0542 //! Based on kdelibs' kstandarddirs.cpp
0543 QString KDbUtils::findExe(const QString& appname,
0544                                   const QString& path,
0545                                   FindExeOptions options)
0546 {
0547 #ifdef Q_OS_WIN
0548     QStringList executable_extensions = executableExtensions();
0549     if (!executable_extensions.contains(
0550             appname.section(QLatin1Char('.'), -1, -1, QString::SectionIncludeLeadingSep),
0551             Qt::CaseInsensitive))
0552     {
0553         QString found_exe;
0554         foreach (const QString& extension, executable_extensions) {
0555             found_exe = findExe(appname + extension, path, options);
0556             if (!found_exe.isEmpty()) {
0557                 return found_exe;
0558             }
0559         }
0560         return QString();
0561     }
0562 #endif
0563 
0564     // absolute or relative path?
0565     if (appname.contains(QDir::separator())) {
0566         return checkExecutable(appname, options & FindExeOption::IgnoreExecBit);
0567     }
0568 
0569     QString p;
0570     QString result;
0571 
0572     const QStringList exePaths = systemPaths(path);
0573     for (QStringList::ConstIterator it = exePaths.begin(); it != exePaths.end(); ++it)
0574     {
0575         p = (*it) + QLatin1Char('/');
0576         p += appname;
0577 
0578         // Check for executable in this tokenized path
0579         result = checkExecutable(p, options & FindExeOption::IgnoreExecBit);
0580         if (!result.isEmpty()) {
0581             return result;
0582         }
0583     }
0584 
0585     // Not found in PATH, look into a bin dir
0586     p = QFile::decodeName(BIN_INSTALL_DIR "/");
0587     p += appname;
0588     result = checkExecutable(p, options & FindExeOption::IgnoreExecBit);
0589     if (!result.isEmpty()) {
0590         return result;
0591     }
0592 
0593     // If we reach here, the executable wasn't found.
0594     // So return empty string.
0595     return QString();
0596 }
0597 
0598 // ---
0599 
0600 class Q_DECL_HIDDEN PropertySet::Private
0601 {
0602 public:
0603     Private() {}
0604     Private(const Private &other) {
0605         copy(other);
0606     }
0607     void copy(const Private &other) {
0608         for (AutodeletedHash<QByteArray, Property*>::ConstIterator it(other.data.constBegin());
0609              it != other.data.constEnd(); ++it)
0610         {
0611             data.insert(it.key(), new Property(*it.value()));
0612         }
0613     }
0614     bool operator==(const Private &other) const {
0615         if (data.count() != other.data.count()) {
0616             return false;
0617         }
0618         for (AutodeletedHash<QByteArray, Property*>::ConstIterator it(other.data.constBegin());
0619              it != other.data.constEnd(); ++it)
0620         {
0621             AutodeletedHash<QByteArray, Property*>::ConstIterator findHere(data.constFind(it.key()));
0622             if (*findHere.value() != *it.value()) {
0623                 return false;
0624             }
0625         }
0626         return true;
0627     }
0628     AutodeletedHash<QByteArray, Property*> data;
0629 };
0630 
0631 PropertySet::PropertySet()
0632  : d(new Private)
0633 {
0634 }
0635 
0636 PropertySet::PropertySet(const PropertySet &other)
0637     : d(new Private(*other.d))
0638 {
0639 }
0640 
0641 PropertySet::~PropertySet()
0642 {
0643     delete d;
0644 }
0645 
0646 PropertySet& PropertySet::operator=(const PropertySet &other)
0647 {
0648     if (this != &other) {
0649         d->data.clear();
0650         d->copy(*other.d);
0651     }
0652     return *this;
0653 }
0654 
0655 bool PropertySet::operator==(const PropertySet &other) const
0656 {
0657     return *d == *other.d;
0658 }
0659 
0660 void PropertySet::insert(const QByteArray &name, const QVariant &value, const QString &caption)
0661 {
0662     QString realCaption = caption;
0663     Property *existing = d->data.value(name);
0664     if (existing) {
0665         existing->setValue(value);
0666         if (!caption.isEmpty()) { // if not, reuse
0667             existing->setCaption(caption);
0668         }
0669     } else {
0670         if (KDb::isIdentifier(name)) {
0671             d->data.insert(name, new Property(value, realCaption));
0672         } else {
0673             kdbWarning() << name << "cannot be used as property name";
0674         }
0675     }
0676 }
0677 
0678 void PropertySet::setCaption(const QByteArray &name, const QString &caption)
0679 {
0680     Property *existing = d->data.value(name);
0681     if (existing) {
0682         existing->setCaption(caption);
0683     }
0684 }
0685 
0686 void PropertySet::setValue(const QByteArray &name, const QVariant &value)
0687 {
0688     Property *existing = d->data.value(name);
0689     if (existing) {
0690         existing->setValue(value);
0691     }
0692 }
0693 
0694 void PropertySet::remove(const QByteArray &name)
0695 {
0696     d->data.remove(name);
0697 }
0698 
0699 Property PropertySet::property(const QByteArray &name) const
0700 {
0701     Property *result = d->data.value(name);
0702     return result ? *result : Property();
0703 }
0704 
0705 QList<QByteArray> PropertySet::names() const
0706 {
0707     return d->data.keys();
0708 }
0709 
0710 QVariant KDbUtils::squeezedValue(const QVariant &value)
0711 {
0712     switch(value.type()) {
0713     case QVariant::String:
0714         if (value.toString().length() > SQUEEZED_TEXT_LIMIT) {
0715             return QVariant(value.toString().left(SQUEEZED_TEXT_LIMIT - SQUEEZED_TEXT_SUFFIX)
0716                     + QString::fromLatin1("...")
0717                     + value.toString().right(SQUEEZED_TEXT_SUFFIX)
0718                     + QString::fromLatin1("[%1 characters]").arg(value.toString().length()));
0719         }
0720         break;
0721     case QVariant::ByteArray:
0722         if (value.toByteArray().length() > SQUEEZED_TEXT_LIMIT) {
0723             return QVariant(value.toByteArray().left(SQUEEZED_TEXT_LIMIT - SQUEEZED_TEXT_SUFFIX)
0724                     + "..."
0725                     + value.toByteArray().right(SQUEEZED_TEXT_SUFFIX)
0726                     + '[' + QByteArray::number(value.toByteArray().length())
0727                     + " bytes]");
0728         }
0729         break;
0730     default:
0731         break;
0732     }
0733 //! @todo add BitArray, Url, Hash, Map, Pixmap, Image?
0734     return value;
0735 }