• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

kconfigini.cpp
Go to the documentation of this file.
00001 /*
00002    This file is part of the KDE libraries
00003    Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
00004    Copyright (c) 1999 Preston Brown <pbrown@kde.org>
00005    Copyright (C) 1997-1999 Matthias Kalle Dalheimer (kalle@kde.org)
00006 
00007    This library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Library General Public
00009    License as published by the Free Software Foundation; either
00010    version 2 of the License, or (at your option) any later version.
00011 
00012    This library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Library General Public License for more details.
00016 
00017    You should have received a copy of the GNU Library General Public License
00018    along with this library; see the file COPYING.LIB.  If not, write to
00019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020    Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include "kconfigini_p.h"
00024 
00025 #include "kconfig.h"
00026 #include "kconfigbackend.h"
00027 #include "bufferfragment_p.h"
00028 #include "kconfigdata.h"
00029 #include <ksavefile.h>
00030 #include <kde_file.h>
00031 #include "kstandarddirs.h"
00032 
00033 #include <qdatetime.h>
00034 #include <qdir.h>
00035 #include <qfile.h>
00036 #include <qfileinfo.h>
00037 #include <qdebug.h>
00038 #include <qmetaobject.h>
00039 #include <qregexp.h>
00040 
00041 extern bool kde_kiosk_exception;
00042 
00043 QString KConfigIniBackend::warningProlog(const QFile &file, int line)
00044 {
00045     return QString::fromLatin1("KConfigIni: In file %2, line %1: ")
00046             .arg(line).arg(file.fileName());
00047 }
00048 
00049 KConfigIniBackend::KConfigIniBackend()
00050  : KConfigBackend()
00051 {
00052 }
00053 
00054 KConfigIniBackend::~KConfigIniBackend()
00055 {
00056 }
00057 
00058 KConfigBackend::ParseInfo
00059         KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap,
00060                                        ParseOptions options)
00061 {
00062     return parseConfig(currentLocale, entryMap, options, false);
00063 }
00064 
00065 KConfigBackend::ParseInfo
00066 KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap,
00067                                ParseOptions options, bool merging)
00068 {
00069     if (filePath().isEmpty() || !QFile::exists(filePath()))
00070         return ParseOk;
00071 
00072     bool bDefault = options&ParseDefaults;
00073     bool allowExecutableValues = options&ParseExpansions;
00074 
00075     QByteArray currentGroup("<default>");
00076 
00077     QFile file(filePath());
00078     if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
00079         return ParseOpenError;
00080 
00081     QList<QByteArray> immutableGroups;
00082 
00083     bool fileOptionImmutable = false;
00084     bool groupOptionImmutable = false;
00085     bool groupSkip = false;
00086 
00087     int lineNo = 0;
00088     // on systems using \r\n as end of line, \r will be taken care of by
00089     // trim() below
00090     QByteArray buffer = file.readAll();
00091     BufferFragment contents(buffer.data(), buffer.size());
00092     unsigned int len = contents.length();
00093     unsigned int startOfLine = 0;
00094 
00095     while (startOfLine < len) {
00096         BufferFragment line = contents.split('\n', &startOfLine);
00097         line.trim();
00098         lineNo++;
00099 
00100         // skip empty lines and lines beginning with '#'
00101         if (line.isEmpty() || line.at(0) == '#')
00102             continue;
00103 
00104         if (line.at(0) == '[') { // found a group
00105             groupOptionImmutable = fileOptionImmutable;
00106 
00107             QByteArray newGroup;
00108             int start = 1, end;
00109             do {
00110                 end = start;
00111                 for (;;) {
00112                     if (end == line.length()) {
00113                         qWarning() << warningProlog(file, lineNo) << "Invalid group header.";
00114                         // XXX maybe reset the current group here?
00115                         goto next_line;
00116                     }
00117                     if (line.at(end) == ']')
00118                         break;
00119                     end++;
00120                 }
00121                 if (end + 1 == line.length() && start + 2 == end &&
00122                     line.at(start) == '$' && line.at(start + 1) == 'i')
00123                 {
00124                     if (newGroup.isEmpty())
00125                         fileOptionImmutable = !kde_kiosk_exception;
00126                     else
00127                         groupOptionImmutable = !kde_kiosk_exception;
00128                 }
00129                 else {
00130                     if (!newGroup.isEmpty())
00131                         newGroup += '\x1d';
00132                     BufferFragment namePart=line.mid(start, end - start);
00133                     printableToString(&namePart, file, lineNo);
00134                     newGroup += namePart.toByteArray();
00135                 }
00136             } while ((start = end + 2) <= line.length() && line.at(end + 1) == '[');
00137             currentGroup = newGroup;
00138 
00139             groupSkip = entryMap.getEntryOption(currentGroup, 0, 0, KEntryMap::EntryImmutable);
00140 
00141             if (groupSkip && !bDefault)
00142                 continue;
00143 
00144             if (groupOptionImmutable)
00145                 // Do not make the groups immutable until the entries from
00146                 // this file have been added.
00147                 immutableGroups.append(currentGroup);
00148         } else {
00149             if (groupSkip && !bDefault)
00150                 continue; // skip entry
00151 
00152             BufferFragment aKey;
00153             int eqpos = line.indexOf('=');
00154             if (eqpos < 0) {
00155                 aKey = line;
00156                 line.clear();
00157             } else {
00158                 BufferFragment temp = line.left(eqpos);
00159                 temp.trim();
00160                 aKey = temp;
00161                 line.truncateLeft(eqpos + 1);
00162             }
00163             if (aKey.isEmpty()) {
00164                 qWarning() << warningProlog(file, lineNo) << "Invalid entry (empty key)";
00165                 continue;
00166             }
00167 
00168             KEntryMap::EntryOptions entryOptions=0;
00169             if (groupOptionImmutable)
00170                 entryOptions |= KEntryMap::EntryImmutable;
00171 
00172             BufferFragment locale;
00173             int start;
00174             while ((start = aKey.lastIndexOf('[')) >= 0) {
00175                 int end = aKey.indexOf(']', start);
00176                 if (end < 0) {
00177                     qWarning() << warningProlog(file, lineNo)
00178                             << "Invalid entry (missing ']')";
00179                     goto next_line;
00180                 } else if (end > start + 1 && aKey.at(start + 1) == '$') { // found option(s)
00181                     int i = start + 2;
00182                     while (i < end) {
00183                         switch (aKey.at(i)) {
00184                             case 'i':
00185                                 if (!kde_kiosk_exception)
00186                                     entryOptions |= KEntryMap::EntryImmutable;
00187                                 break;
00188                             case 'e':
00189                                 if (allowExecutableValues)
00190                                     entryOptions |= KEntryMap::EntryExpansion;
00191                                 break;
00192                             case 'd':
00193                                 entryOptions |= KEntryMap::EntryDeleted;
00194                                 aKey = aKey.left(start);
00195                                 printableToString(&aKey, file, lineNo);
00196                                 entryMap.setEntry(currentGroup, aKey.toByteArray(), QByteArray(), entryOptions);
00197                                 goto next_line;
00198                             default:
00199                                 break;
00200                         }
00201                         i++;
00202                     }
00203                 } else { // found a locale
00204                     if (!locale.isNull()) {
00205                         qWarning() << warningProlog(file, lineNo)
00206                                 << "Invalid entry (second locale!?)";
00207                         goto next_line;
00208                     }
00209 
00210                     locale = aKey.mid(start + 1,end - start - 1);
00211                 }
00212                 aKey.truncate(start);
00213             }
00214             if (eqpos < 0) { // Do this here after [$d] was checked
00215                 qWarning() << warningProlog(file, lineNo) << "Invalid entry (missing '=')";
00216                 continue;
00217             }
00218             printableToString(&aKey, file, lineNo);
00219             if (!locale.isEmpty()) {
00220                 if (locale != currentLocale) {
00221                     // backward compatibility. C == en_US
00222                     if (locale.at(0) != 'C' || currentLocale != "en_US") {
00223                         if (merging)
00224                             entryOptions |= KEntryMap::EntryRawKey;
00225                         else
00226                             goto next_line; // skip this entry if we're not merging
00227                     }
00228                 }
00229             }
00230 
00231             if (!(entryOptions & KEntryMap::EntryRawKey))
00232                 printableToString(&aKey, file, lineNo);
00233 
00234             if (options&ParseGlobal)
00235                 entryOptions |= KEntryMap::EntryGlobal;
00236             if (bDefault)
00237                 entryOptions |= KEntryMap::EntryDefault;
00238             if (!locale.isNull())
00239                 entryOptions |= KEntryMap::EntryLocalized;
00240             printableToString(&line, file, lineNo);
00241             if (entryOptions & KEntryMap::EntryRawKey) {
00242                 QByteArray rawKey;
00243                 rawKey.reserve(aKey.length() + locale.length() + 2);
00244                 rawKey.append(aKey.toVolatileByteArray());
00245                 rawKey.append('[').append(locale.toVolatileByteArray()).append(']');
00246                 entryMap.setEntry(currentGroup, rawKey, line.toByteArray(), entryOptions);
00247             } else {
00248                 entryMap.setEntry(currentGroup, aKey.toByteArray(), line.toByteArray(), entryOptions);
00249             }
00250         }
00251 next_line:
00252         continue;
00253     }
00254 
00255     // now make sure immutable groups are marked immutable
00256     foreach(const QByteArray& group, immutableGroups) {
00257         entryMap.setEntry(group, QByteArray(), QByteArray(), KEntryMap::EntryImmutable);
00258     }
00259 
00260     return fileOptionImmutable ? ParseImmutable : ParseOk;
00261 }
00262 
00263 void KConfigIniBackend::writeEntries(const QByteArray& locale, QFile& file,
00264                                      const KEntryMap& map, bool defaultGroup, bool &firstEntry)
00265 {
00266     QByteArray currentGroup;
00267     bool groupIsImmutable = false;
00268     const KEntryMapConstIterator end = map.constEnd();
00269     for (KEntryMapConstIterator it = map.constBegin(); it != end; ++it) {
00270         const KEntryKey& key = it.key();
00271 
00272         // Either process the default group or all others
00273         if ((key.mGroup != "<default>") == defaultGroup)
00274             continue; // skip
00275 
00276         // the only thing we care about groups is, is it immutable?
00277         if (key.mKey.isNull()) {
00278             groupIsImmutable = it->bImmutable;
00279             continue; // skip
00280         }
00281 
00282         const KEntry& currentEntry = *it;
00283         if (!defaultGroup && currentGroup != key.mGroup) {
00284             if (!firstEntry)
00285                 file.putChar('\n');
00286             currentGroup = key.mGroup;
00287             for (int start = 0, end;; start = end + 1) {
00288                 file.putChar('[');
00289                 end = currentGroup.indexOf('\x1d', start);
00290                 if (end < 0) {
00291                     int cgl = currentGroup.length();
00292                     if (currentGroup.at(start) == '$' && cgl - start <= 10) {
00293                         for (int i = start + 1; i < cgl; i++) {
00294                             char c = currentGroup.at(i);
00295                             if (c < 'a' || c > 'z')
00296                                 goto nope;
00297                         }
00298                         file.write("\\x24");
00299                         start++;
00300                     }
00301                   nope:
00302                     file.write(stringToPrintable(currentGroup.mid(start), GroupString));
00303                     file.putChar(']');
00304                     if (groupIsImmutable) {
00305                         file.write("[$i]", 4);
00306                     }
00307                     file.putChar('\n');
00308                     break;
00309                 } else {
00310                     file.write(stringToPrintable(currentGroup.mid(start, end - start), GroupString));
00311                     file.putChar(']');
00312                 }
00313             }
00314         }
00315 
00316         firstEntry = false;
00317         // it is data for a group
00318 
00319         if (key.bRaw) // unprocessed key with attached locale from merge
00320             file.write(key.mKey);
00321         else {
00322             file.write(stringToPrintable(key.mKey, KeyString)); // Key
00323             if (key.bLocal && locale != "C") { // 'C' locale == untranslated
00324                 file.putChar('[');
00325                 file.write(locale); // locale tag
00326                 file.putChar(']');
00327             }
00328         }
00329         if (currentEntry.bDeleted) {
00330             if (currentEntry.bImmutable)
00331                 file.write("[$di]", 5); // Deleted + immutable
00332             else
00333                 file.write("[$d]", 4); // Deleted
00334         } else {
00335             if (currentEntry.bImmutable || currentEntry.bExpand) {
00336                 file.write("[$", 2);
00337                 if (currentEntry.bImmutable)
00338                     file.putChar('i');
00339                 if (currentEntry.bExpand)
00340                     file.putChar('e');
00341                 file.putChar(']');
00342             }
00343             file.putChar('=');
00344             file.write(stringToPrintable(currentEntry.mValue, ValueString));
00345         }
00346         file.putChar('\n');
00347     }
00348 }
00349 
00350 void KConfigIniBackend::writeEntries(const QByteArray& locale, QFile& file, const KEntryMap& map)
00351 {
00352     bool firstEntry = true;
00353 
00354     // write default group
00355     writeEntries(locale, file, map, true, firstEntry);
00356 
00357     // write all other groups
00358     writeEntries(locale, file, map, false, firstEntry);
00359 }
00360 
00361 bool KConfigIniBackend::writeConfig(const QByteArray& locale, KEntryMap& entryMap,
00362                                     WriteOptions options, const KComponentData &data)
00363 {
00364     Q_ASSERT(!filePath().isEmpty());
00365 
00366     KEntryMap writeMap;
00367     const bool bGlobal = options & WriteGlobal;
00368 
00369     // First, reparse the file on disk, to merge our changes with the ones done by other apps
00370     {
00371         ParseOptions opts = ParseExpansions;
00372         if (bGlobal)
00373             opts |= ParseGlobal;
00374         ParseInfo info = parseConfig(locale, writeMap, opts, true);
00375         if (info != ParseOk) // either there was an error or the file became immutable
00376             return false;
00377     }
00378 
00379     const KEntryMapIterator end = entryMap.end();
00380     for (KEntryMapIterator it=entryMap.begin(); it != end; ++it) {
00381         if (!it.key().mKey.isEmpty() && !it->bDirty) // not dirty, doesn't overwrite entry in writeMap. skips default entries, too.
00382             continue;
00383 
00384         const KEntryKey& key = it.key();
00385 
00386         // only write entries that have the same "globality" as the file
00387         if (it->bGlobal == bGlobal) {
00388             if (!it->bDeleted) {
00389                 writeMap[key] = *it;
00390             } else {
00391                 KEntryKey defaultKey = key;
00392                 defaultKey.bDefault = true;
00393                 if (!entryMap.contains(defaultKey)) {
00394                     writeMap.remove(key); // remove the deleted entry if there is no default
00395                     //qDebug() << "Detected as deleted=>removed:" << key.mGroup << key.mKey << "global=" << bGlobal;
00396                 } else {
00397                     writeMap[key] = *it; // otherwise write an explicitly deleted entry
00398                     //qDebug() << "Detected as deleted=>[$d]:" << key.mGroup << key.mKey << "global=" << bGlobal;
00399                 }
00400             }
00401             it->bDirty = false;
00402         }
00403     }
00404 
00405     // now writeMap should contain only entries to be written
00406     // so write it out to disk
00407 
00408     // check if file exists
00409     QFile::Permissions fileMode = QFile::ReadUser | QFile::WriteUser;
00410     bool createNew = true;
00411 
00412     QFileInfo fi(filePath());
00413     if (fi.exists())
00414     {
00415         if (fi.ownerId() == ::getuid())
00416         {
00417             // Preserve file mode if file exists and is owned by user.
00418             fileMode = fi.permissions();
00419         }
00420         else
00421         {
00422             // File is not owned by user:
00423             // Don't create new file but write to existing file instead.
00424             createNew = false;
00425         }
00426     }
00427 
00428     if (createNew) {
00429         KSaveFile file( filePath(), data );
00430         if (!file.open()) {
00431             return false;
00432         }
00433 
00434         file.setPermissions(fileMode);
00435 
00436         file.setTextModeEnabled(true); // to get eol translation
00437         writeEntries(locale, file, writeMap);
00438 
00439         if (!file.flush()) {
00440             // Couldn't write. Disk full?
00441             kWarning() << "Couldn't write" << filePath() << ". Disk full?";
00442             file.abort();
00443             return false;
00444         }
00445 
00446         if (!file.size() && (fileMode == (QFile::ReadUser | QFile::WriteUser))) {
00447             // File is empty and doesn't have special permissions: delete it.
00448             file.abort();
00449 
00450             if (fi.exists()) {
00451                 // also remove the old file in case it existed. this can happen
00452                 // when we delete all the entries in an existing config file.
00453                 // if we don't do this, then deletions and revertToDefault's
00454                 // will mysteriously fail
00455                 QFile::remove(filePath());
00456             }
00457         } else {
00458             // Normal case: Close the file
00459             return file.finalize();
00460         }
00461     } else {
00462         // Open existing file. *DON'T* create it if it suddenly does not exist!
00463 #ifdef Q_OS_UNIX
00464         int fd = KDE_open(QFile::encodeName(filePath()), O_WRONLY | O_TRUNC);
00465         if (fd < 0) {
00466             return false;
00467         }
00468         FILE *fp = KDE_fdopen(fd, "w");
00469         if (!fp) {
00470             close(fd);
00471             return false;
00472         }
00473         QFile f;
00474         if (!f.open(fp, QIODevice::WriteOnly)) {
00475             fclose(fp);
00476             return false;
00477         }
00478         writeEntries(locale, f, writeMap);
00479         f.close();
00480         fclose(fp);
00481 #else
00482         QFile f( filePath() );
00483         // XXX This is broken - it DOES create the file if it is suddenly gone.
00484         if (!f.open( QIODevice::WriteOnly | QIODevice::Truncate )) {
00485             return false;
00486         }
00487         f.setTextModeEnabled(true);
00488         writeEntries(locale, f, writeMap);
00489 #endif
00490     }
00491     return true;
00492 }
00493 
00494 bool KConfigIniBackend::isWritable() const
00495 {
00496     if (!filePath().isEmpty()) {
00497         if (KStandardDirs::checkAccess(filePath(), W_OK)) {
00498             return true;
00499         }
00500         // The check might have failed because any of the containing dirs
00501         // did not exist. If the file does not exist, check if the deepest
00502         // existing dir is writable.
00503         if (!QFileInfo(filePath()).exists()) {
00504             QDir dir = QFileInfo(filePath()).absolutePath();
00505             while (!dir.exists()) {
00506                 if (!dir.cdUp()) {
00507                     return false;
00508                 }
00509             }
00510             return QFileInfo(dir.absolutePath()).isWritable();
00511         }
00512     }
00513 
00514     return false;
00515 }
00516 
00517 QString KConfigIniBackend::nonWritableErrorMessage() const
00518 {
00519     return i18n("Configuration file \"%1\" not writable.\n", filePath());
00520 }
00521 
00522 void KConfigIniBackend::createEnclosing()
00523 {
00524     const QString file = filePath();
00525     if (file.isEmpty())
00526         return; // nothing to do
00527 
00528     // Create the containing dir, maybe it wasn't there
00529     QDir dir;
00530     dir.mkpath(QFileInfo(file).absolutePath());
00531 }
00532 
00533 void KConfigIniBackend::setFilePath(const QString& file)
00534 {
00535     if (file.isEmpty())
00536         return;
00537 
00538     Q_ASSERT(QDir::isAbsolutePath(file));
00539 
00540     const QFileInfo info(file);
00541     if (info.exists()) {
00542         setLocalFilePath(info.canonicalFilePath());
00543         setLastModified(info.lastModified());
00544         setSize(info.size());
00545     } else {
00546         setLocalFilePath(file);
00547         setSize(0);
00548         QDateTime dummy;
00549         dummy.setTime_t(0);
00550         setLastModified(dummy);
00551     }
00552 }
00553 
00554 KConfigBase::AccessMode KConfigIniBackend::accessMode() const
00555 {
00556     if (filePath().isEmpty())
00557         return KConfigBase::NoAccess;
00558 
00559     if (isWritable())
00560         return KConfigBase::ReadWrite;
00561 
00562     return KConfigBase::ReadOnly;
00563 }
00564 
00565 bool KConfigIniBackend::lock(const KComponentData& componentData)
00566 {
00567     Q_ASSERT(!filePath().isEmpty());
00568 
00569     if (!lockFile) {
00570         lockFile = new KLockFile(filePath() + QLatin1String(".lock"), componentData);
00571     }
00572 
00573     if (lockFile->lock() == KLockFile::LockStale) // attempt to break the lock
00574         lockFile->lock(KLockFile::ForceFlag);
00575     return lockFile->isLocked();
00576 }
00577 
00578 void KConfigIniBackend::unlock()
00579 {
00580     lockFile->unlock();
00581     lockFile.clear();
00582 }
00583 
00584 bool KConfigIniBackend::isLocked() const
00585 {
00586     return lockFile && lockFile->isLocked();
00587 }
00588 
00589 QByteArray KConfigIniBackend::stringToPrintable(const QByteArray& aString, StringType type)
00590 {
00591     static const char nibbleLookup[] = {
00592         '0', '1', '2', '3', '4', '5', '6', '7',
00593         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
00594     };
00595 
00596     if (aString.isEmpty())
00597         return aString;
00598     const int l = aString.length();
00599 
00600     QByteArray result; // Guesstimated that it's good to avoid data() initialization for a length of l*4
00601     result.resize(l * 4); // Maximum 4x as long as source string due to \x<ab> escape sequences
00602     register const char *s = aString.constData();
00603     int i = 0;
00604     char *data = result.data();
00605     char *start = data;
00606 
00607     // Protect leading space
00608     if (s[0] == ' ' && type != GroupString) {
00609         *data++ = '\\';
00610         *data++ = 's';
00611         i++;
00612     }
00613 
00614     for (; i < l; ++i/*, r++*/) {
00615         switch (s[i]) {
00616             default:
00617             // The \n, \t, \r cases (all < 32) are handled below; we can ignore them here
00618                 if (((unsigned char)s[i]) < 32)
00619                     goto doEscape;
00620                 *data++ = s[i];
00621                 break;
00622             case '\n':
00623                 *data++ = '\\';
00624                 *data++ = 'n';
00625                 break;
00626             case '\t':
00627                 *data++ = '\\';
00628                 *data++ = 't';
00629                 break;
00630             case '\r':
00631                 *data++ = '\\';
00632                 *data++ = 'r';
00633                 break;
00634             case '\\':
00635                 *data++ = '\\';
00636                 *data++ = '\\';
00637                 break;
00638             case '=':
00639                 if (type != KeyString) {
00640                     *data++ = s[i];
00641                     break;
00642                 }
00643                 goto doEscape;
00644             case '[':
00645             case ']':
00646             // Above chars are OK to put in *value* strings as plaintext
00647                 if (type == ValueString) {
00648                     *data++ = s[i];
00649                     break;
00650                 }
00651         doEscape:
00652                 *data++ = '\\';
00653                 *data++ = 'x';
00654                 *data++ = nibbleLookup[((unsigned char)s[i]) >> 4];
00655                 *data++ = nibbleLookup[((unsigned char)s[i]) & 0x0f];
00656                 break;
00657         }
00658     }
00659     *data = 0;
00660     result.resize(data - start);
00661 
00662     // Protect trailing space
00663     if (result.endsWith(' ') && type != GroupString) {
00664         result.replace(result.length() - 1, 1, "\\s");
00665     }
00666     result.squeeze();
00667 
00668     return result;
00669 }
00670 
00671 char KConfigIniBackend::charFromHex(const char *str, const QFile& file, int line)
00672 {
00673     unsigned char ret = 0;
00674     for (int i = 0; i < 2; i++) {
00675         ret <<= 4;
00676         quint8 c = quint8(str[i]);
00677 
00678         if (c >= '0' && c <= '9') {
00679             ret |= c - '0';
00680         } else if (c >= 'a' && c <= 'f') {
00681             ret |= c - 'a' + 0x0a;
00682         } else if (c >= 'A' && c <= 'F') {
00683             ret |= c - 'A' + 0x0a;
00684         } else {
00685             QByteArray e(str, 2);
00686             e.prepend("\\x");
00687             qWarning() << warningProlog(file, line) << "Invalid hex character " << c
00688                     << " in \\x<nn>-type escape sequence \"" << e.constData() << "\".";
00689             return 'x';
00690         }
00691     }
00692     return char(ret);
00693 }
00694 
00695 void KConfigIniBackend::printableToString(BufferFragment* aString, const QFile& file, int line)
00696 {
00697     if (aString->isEmpty() || aString->indexOf('\\')==-1)
00698         return;
00699     aString->trim();
00700     int l = aString->length();
00701     char *r = aString->data();
00702     char *str=r;
00703 
00704     for(int i = 0; i < l; i++, r++) {
00705         if (str[i]!= '\\') {
00706             *r=str[i];
00707         } else {
00708             // Probable escape sequence
00709             i++;
00710             if (i >= l) { // Line ends after backslash - stop.
00711                 *r = '\\';
00712                 break;
00713             }
00714 
00715             switch(str[i]) {
00716                 case 's':
00717                     *r = ' ';
00718                     break;
00719                 case 't':
00720                     *r = '\t';
00721                     break;
00722                 case 'n':
00723                     *r = '\n';
00724                     break;
00725                 case 'r':
00726                     *r = '\r';
00727                     break;
00728                 case '\\':
00729                     *r = '\\';
00730                     break;
00731                 case 'x':
00732                     if (i + 2 < l) {
00733                         *r = charFromHex(str + i + 1, file, line);
00734                         i += 2;
00735                     } else {
00736                         *r = 'x';
00737                         i = l - 1;
00738                     }
00739                     break;
00740                 default:
00741                     *r = '\\';
00742                     qWarning() << warningProlog(file, line)
00743                                << QString::fromLatin1("Invalid escape sequence \"\\%1\".").arg(str[i]);
00744             }
00745         }
00746     }
00747     aString->truncate(r - aString->constData());
00748 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal