File indexing completed on 2024-06-02 05:43:56

0001 /*
0002     SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "Misc.h"
0007 #include "config-paths.h"
0008 #include <QByteArray>
0009 #include <QDir>
0010 #include <QList>
0011 #include <QMap>
0012 #include <QProcess>
0013 #include <QSet>
0014 #include <QStandardPaths>
0015 #include <QTemporaryFile>
0016 #include <QTextStream>
0017 #include <QUrlQuery>
0018 #include <ctype.h>
0019 
0020 namespace KFI
0021 {
0022 namespace Misc
0023 {
0024 QString prettyUrl(const QUrl &url)
0025 {
0026     QString u(url.url());
0027 
0028     u.replace("%20", " ");
0029     return u;
0030 }
0031 
0032 QString dirSyntax(const QString &d)
0033 {
0034     if (!d.isEmpty()) {
0035         QString ds(d);
0036 
0037         ds.replace("//", "/");
0038 
0039         int slashPos(ds.lastIndexOf('/'));
0040 
0041         if (slashPos != (((int)ds.length()) - 1)) {
0042             ds.append('/');
0043         }
0044 
0045         return ds;
0046     }
0047 
0048     return d;
0049 }
0050 
0051 QString fileSyntax(const QString &d)
0052 {
0053     if (!d.isEmpty()) {
0054         QString ds(d);
0055 
0056         ds.replace("//", "/");
0057 
0058         int slashPos(ds.lastIndexOf('/'));
0059 
0060         if (slashPos == (((int)ds.length()) - 1)) {
0061             ds.remove(slashPos, 1);
0062         }
0063         return ds;
0064     }
0065 
0066     return d;
0067 }
0068 
0069 QString getDir(const QString &f)
0070 {
0071     QString d(f);
0072 
0073     int slashPos(d.lastIndexOf('/'));
0074 
0075     if (slashPos != -1) {
0076         d.remove(slashPos + 1, d.length());
0077     }
0078 
0079     return dirSyntax(d);
0080 }
0081 
0082 QString getFile(const QString &f)
0083 {
0084     QString d(f);
0085 
0086     int slashPos = d.lastIndexOf('/');
0087 
0088     if (slashPos != -1) {
0089         d.remove(0, slashPos + 1);
0090     }
0091 
0092     return d;
0093 }
0094 
0095 bool createDir(const QString &dir)
0096 {
0097     if (!QDir().mkpath(dir)) {
0098         return false;
0099     }
0100     //
0101     // Clear any umask before setting dir perms
0102     mode_t oldMask(umask(0000));
0103     const QByteArray d = QFile::encodeName(dir);
0104     ::chmod(d.constData(), DIR_PERMS);
0105     // Reset umask
0106     ::umask(oldMask);
0107     return true;
0108 }
0109 
0110 void setFilePerms(const QByteArray &f)
0111 {
0112     //
0113     // Clear any umask before setting file perms
0114     mode_t oldMask(umask(0000));
0115     ::chmod(f.constData(), FILE_PERMS);
0116     // Reset umask
0117     ::umask(oldMask);
0118 }
0119 
0120 bool doCmd(const QString &cmd, const QString &p1, const QString &p2, const QString &p3)
0121 {
0122     QStringList args;
0123 
0124     if (!p1.isEmpty()) {
0125         args << p1;
0126     }
0127     if (!p2.isEmpty()) {
0128         args << p2;
0129     }
0130     if (!p3.isEmpty()) {
0131         args << p3;
0132     }
0133 
0134     return 0 == QProcess::execute(cmd, args);
0135 }
0136 
0137 QString changeExt(const QString &f, const QString &newExt)
0138 {
0139     QString newStr(f);
0140     int dotPos(newStr.lastIndexOf('.'));
0141 
0142     if (-1 == dotPos) {
0143         newStr += QChar('.') + newExt;
0144     } else {
0145         newStr.remove(dotPos + 1, newStr.length());
0146         newStr += newExt;
0147     }
0148     return newStr;
0149 }
0150 
0151 //
0152 // Get a list of files associated with a file, e.g.:
0153 //
0154 //    File: /home/a/courier.pfa
0155 //
0156 //    Associated: /home/a/courier.afm /home/a/courier.pfm
0157 //
0158 void getAssociatedFiles(const QString &path, QStringList &files, bool afmAndPfm)
0159 {
0160     QString ext(path);
0161     int dotPos(ext.lastIndexOf('.'));
0162     bool check(false);
0163 
0164     if (-1 == dotPos) { // Hmm, no extension - check anyway...
0165         check = true;
0166     } else // Cool, got an extension - see if it is a Type1 font...
0167     {
0168         ext = ext.mid(dotPos + 1);
0169         check = 0 == ext.compare("pfa", Qt::CaseInsensitive) || 0 == ext.compare("pfb", Qt::CaseInsensitive);
0170     }
0171 
0172     if (check) {
0173         const char *afm[] = {"afm", "AFM", "Afm", nullptr}, *pfm[] = {"pfm", "PFM", "Pfm", nullptr};
0174         bool gotAfm(false);
0175         int e;
0176 
0177         for (e = 0; afm[e]; ++e) {
0178             QString statFile(changeExt(path, afm[e]));
0179 
0180             if (fExists(statFile)) {
0181                 files.append(statFile);
0182                 gotAfm = true;
0183                 break;
0184             }
0185         }
0186 
0187         if (afmAndPfm || !gotAfm) {
0188             for (e = 0; pfm[e]; ++e) {
0189                 QString statFile(changeExt(path, pfm[e]));
0190 
0191                 if (fExists(statFile)) {
0192                     files.append(statFile);
0193                     break;
0194                 }
0195             }
0196         }
0197     }
0198 }
0199 
0200 time_t getTimeStamp(const QString &item)
0201 {
0202     QT_STATBUF info;
0203 
0204     return !item.isEmpty() && 0 == QT_LSTAT(QFile::encodeName(item), &info) ? info.st_mtime : 0;
0205 }
0206 
0207 bool check(const QString &path, bool file, bool checkW)
0208 {
0209     QT_STATBUF info;
0210     QByteArray pathC(QFile::encodeName(path));
0211 
0212     return 0 == QT_LSTAT(pathC, &info) && (file ? (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode)) : S_ISDIR(info.st_mode))
0213         && (!checkW || 0 == ::access(pathC, W_OK));
0214 }
0215 
0216 QString getFolder(const QString &defaultDir, const QString &root, QStringList &dirs)
0217 {
0218     if (dirs.contains(defaultDir)) {
0219         return defaultDir;
0220     } else {
0221         QStringList::const_iterator it, end = dirs.constEnd();
0222         bool found = false;
0223 
0224         for (it = dirs.constBegin(); it != end && !found; ++it) {
0225             if (0 == (*it).indexOf(root)) {
0226                 return *it;
0227             }
0228         }
0229     }
0230 
0231     return defaultDir;
0232 }
0233 
0234 bool checkExt(const QString &fname, const QString &ext)
0235 {
0236     QString extension('.' + ext);
0237 
0238     return fname.length() > extension.length() ? 0 == fname.mid(fname.length() - extension.length()).compare(extension, Qt::CaseInsensitive) : false;
0239 }
0240 
0241 bool isBitmap(const QString &str)
0242 {
0243     return checkExt(str, "pcf") || checkExt(str, "bdf") || checkExt(str, "pcf.gz") || checkExt(str, "bdf.gz");
0244 }
0245 
0246 bool isMetrics(const QString &str)
0247 {
0248     return checkExt(str, "afm") || checkExt(str, "pfm");
0249 }
0250 
0251 int getIntQueryVal(const QUrl &url, const char *key, int defVal)
0252 {
0253     QUrlQuery query(url);
0254     QString item(query.queryItemValue(key));
0255     int val(defVal);
0256 
0257     if (!item.isNull()) {
0258         val = item.toInt();
0259     }
0260 
0261     return val;
0262 }
0263 
0264 bool printable(const QString &mime)
0265 {
0266     return mime == "font/otf" || mime == "font/ttf" || mime == "application/x-font-ttf" || mime == "application/x-font-otf"
0267         || mime == "application/x-font-type1";
0268 }
0269 
0270 uint qHash(const KFI::Misc::TFont &key)
0271 {
0272     // return qHash(QString(key.family+'%'+QString().setNum(key.styleInfo, 16)));
0273     const QChar *p = key.family.unicode();
0274     int n = key.family.size();
0275     uint h = 0, g;
0276 
0277     h = (h << 4) + key.styleInfo;
0278     if ((g = (h & 0xf0000000)) != 0) {
0279         h ^= g >> 23;
0280     }
0281     h &= ~g;
0282 
0283     while (n--) {
0284         h = (h << 4) + (*p++).unicode();
0285         if ((g = (h & 0xf0000000)) != 0) {
0286             h ^= g >> 23;
0287         }
0288         h &= ~g;
0289     }
0290     return h;
0291 }
0292 
0293 // Taken from qdom.cpp
0294 QString encodeText(const QString &str)
0295 {
0296     QString retval(str);
0297     int len = retval.length(), i = 0;
0298 
0299     while (i < len) {
0300         const QChar ati(retval.at(i));
0301 
0302         if (ati == QLatin1Char('<')) {
0303             retval.replace(i, 1, QLatin1String("&lt;"));
0304             len += 3;
0305             i += 4;
0306         } else if (ati == QLatin1Char('"')) {
0307             retval.replace(i, 1, QLatin1String("&quot;"));
0308             len += 5;
0309             i += 6;
0310         } else if (ati == QLatin1Char('&')) {
0311             retval.replace(i, 1, QLatin1String("&amp;"));
0312             len += 4;
0313             i += 5;
0314         } else if (ati == QLatin1Char('>') && i >= 2 && retval[i - 1] == QLatin1Char(']') && retval[i - 2] == QLatin1Char(']')) {
0315             retval.replace(i, 1, QLatin1String("&gt;"));
0316             len += 3;
0317             i += 4;
0318         } else {
0319             if (ati.isPrint()) {
0320                 ++i;
0321             } else {
0322                 // We have to use a character reference to get it through.
0323                 const ushort codepoint(ati.unicode());
0324                 const QString replacement(QLatin1String("&#x") + QString::number(codepoint, 16) + QLatin1Char(';'));
0325                 retval.replace(i, 1, replacement);
0326                 i += replacement.length();
0327                 len += replacement.length() - 1;
0328             }
0329         }
0330     }
0331 
0332     return retval;
0333 }
0334 
0335 QString contractHome(QString path)
0336 {
0337     if (!path.isEmpty() && '/' == path[0]) {
0338         QString home(QDir::homePath());
0339 
0340         if (path.startsWith(home)) {
0341             int len = home.length();
0342 
0343             if (len > 1 && (path.length() == len || path[len] == '/')) {
0344                 return path.replace(0, len, QString::fromLatin1("~"));
0345             }
0346         }
0347     }
0348 
0349     return path;
0350 }
0351 
0352 QString expandHome(QString path)
0353 {
0354     if (!path.isEmpty() && '~' == path[0]) {
0355         return 1 == path.length() ? QDir::homePath() : path.replace(0, 1, QDir::homePath());
0356     }
0357 
0358     return path;
0359 }
0360 
0361 QMap<QString, QString> getFontFileMap(const QSet<QString> &files)
0362 {
0363     QMap<QString, QString> map;
0364     QSet<QString>::ConstIterator it = files.constBegin(), end = files.constEnd();
0365     QMap<QString, QSet<QString>> fontsFiles;
0366 
0367     for (; it != end; ++it) {
0368         fontsFiles[unhide(getFile(*it))].insert(getDir(*it));
0369     }
0370 
0371     QMap<QString, QSet<QString>>::ConstIterator fIt(fontsFiles.constBegin()), fEnd(fontsFiles.constEnd());
0372 
0373     for (; fIt != fEnd; ++fIt) {
0374         if (fIt.value().count() > 1) {
0375             QList<QString> orig(fIt.value().count()), modified(fIt.value().count());
0376             QSet<QString>::ConstIterator oIt(fIt.value().constBegin());
0377             bool good = true;
0378             int count = fIt.value().count();
0379 
0380             for (int i = 0; i < count && good; ++i, ++oIt) {
0381                 orig[i] = modified[i] = *oIt;
0382             }
0383 
0384             while (good) {
0385                 int end = modified[0].indexOf('/', 1);
0386 
0387                 if (-1 != end) {
0388                     QString dir = modified[0].left(end);
0389 
0390                     for (int i = 1; i < count && good; ++i) {
0391                         if (0 != modified[i].indexOf(dir)) {
0392                             good = false;
0393                         }
0394                     }
0395                     if (good) {
0396                         for (int i = 0; i < count && good; ++i) {
0397                             modified[i].remove(0, dir.length());
0398                         }
0399                     }
0400                 } else {
0401                     good = false;
0402                 }
0403             }
0404             for (int i = 0; i < count; ++i) {
0405                 map[getDir(modified[i]).mid(1) + fIt.key()] = fExists(orig[i] + fIt.key()) ? orig[i] + fIt.key() : orig[i] + hide(fIt.key());
0406             }
0407         } else { // Only 1 entry! :-)
0408             map[unhide(fIt.key())] =
0409                 fExists((*fIt.value().begin()) + fIt.key()) ? (*fIt.value().begin()) + fIt.key() : (*fIt.value().begin()) + hide(fIt.key());
0410         }
0411     }
0412 
0413     return map;
0414 }
0415 
0416 QString modifyName(const QString &fname)
0417 {
0418     static const char constSymbols[] = {'-', ' ', ':', ';', '/', '~', 0};
0419 
0420     QString rv(fname);
0421 
0422     for (int s = 0; constSymbols[s]; ++s) {
0423         rv.replace(constSymbols[s], '_');
0424     }
0425 
0426     int dotPos(rv.lastIndexOf('.'));
0427 
0428     return -1 == dotPos ? rv : rv.left(dotPos + 1) + rv.mid(dotPos + 1).toLower();
0429 }
0430 
0431 QString app(const QString &name, const char *path)
0432 {
0433     static QMap<QString, QString> apps;
0434 
0435     if (!apps.contains(name)) {
0436         QStringList installPaths;
0437         if (qstrcmp(path, "libexec") == 0) {
0438             installPaths.append(KFONTINST_LIBEXEC_DIR);
0439         }
0440         apps[name] = QStandardPaths::findExecutable(name, installPaths);
0441     }
0442     return apps[name];
0443 }
0444 
0445 } // Misc::
0446 
0447 } // KFI::