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

KDECore

  • kdecore
  • config
kconfigini.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the KDE libraries
3  Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
4  Copyright (c) 1999 Preston Brown <pbrown@kde.org>
5  Copyright (C) 1997-1999 Matthias Kalle Dalheimer (kalle@kde.org)
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Library General Public
9  License as published by the Free Software Foundation; either
10  version 2 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 */
22 
23 #include "kconfigini_p.h"
24 
25 #include "kconfig.h"
26 #include "kconfigbackend.h"
27 #include "bufferfragment_p.h"
28 #include "kconfigdata.h"
29 #include <ksavefile.h>
30 #include <kde_file.h>
31 #include "kstandarddirs.h"
32 
33 #include <qdatetime.h>
34 #include <qdir.h>
35 #include <qfile.h>
36 #include <qfileinfo.h>
37 #include <qdebug.h>
38 #include <qmetaobject.h>
39 #include <qregexp.h>
40 
41 extern bool kde_kiosk_exception;
42 
43 QString KConfigIniBackend::warningProlog(const QFile &file, int line)
44 {
45  return QString::fromLatin1("KConfigIni: In file %2, line %1: ")
46  .arg(line).arg(file.fileName());
47 }
48 
49 KConfigIniBackend::KConfigIniBackend()
50  : KConfigBackend()
51 {
52 }
53 
54 KConfigIniBackend::~KConfigIniBackend()
55 {
56 }
57 
58 KConfigBackend::ParseInfo
59  KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap,
60  ParseOptions options)
61 {
62  return parseConfig(currentLocale, entryMap, options, false);
63 }
64 
65 // merging==true is the merging that happens at the beginning of writeConfig:
66 // merge changes in the on-disk file with the changes in the KConfig object.
67 KConfigBackend::ParseInfo
68 KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap,
69  ParseOptions options, bool merging)
70 {
71  if (filePath().isEmpty() || !QFile::exists(filePath()))
72  return ParseOk;
73 
74  bool bDefault = options&ParseDefaults;
75  bool allowExecutableValues = options&ParseExpansions;
76 
77  QByteArray currentGroup("<default>");
78 
79  QFile file(filePath());
80  if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
81  return ParseOpenError;
82 
83  QList<QByteArray> immutableGroups;
84 
85  bool fileOptionImmutable = false;
86  bool groupOptionImmutable = false;
87  bool groupSkip = false;
88 
89  int lineNo = 0;
90  // on systems using \r\n as end of line, \r will be taken care of by
91  // trim() below
92  QByteArray buffer = file.readAll();
93  BufferFragment contents(buffer.data(), buffer.size());
94  unsigned int len = contents.length();
95  unsigned int startOfLine = 0;
96 
97  while (startOfLine < len) {
98  BufferFragment line = contents.split('\n', &startOfLine);
99  line.trim();
100  lineNo++;
101 
102  // skip empty lines and lines beginning with '#'
103  if (line.isEmpty() || line.at(0) == '#')
104  continue;
105 
106  if (line.at(0) == '[') { // found a group
107  groupOptionImmutable = fileOptionImmutable;
108 
109  QByteArray newGroup;
110  int start = 1, end;
111  do {
112  end = start;
113  for (;;) {
114  if (end == line.length()) {
115  qWarning() << warningProlog(file, lineNo) << "Invalid group header.";
116  // XXX maybe reset the current group here?
117  goto next_line;
118  }
119  if (line.at(end) == ']')
120  break;
121  end++;
122  }
123  if (end + 1 == line.length() && start + 2 == end &&
124  line.at(start) == '$' && line.at(start + 1) == 'i')
125  {
126  if (newGroup.isEmpty())
127  fileOptionImmutable = !kde_kiosk_exception;
128  else
129  groupOptionImmutable = !kde_kiosk_exception;
130  }
131  else {
132  if (!newGroup.isEmpty())
133  newGroup += '\x1d';
134  BufferFragment namePart=line.mid(start, end - start);
135  printableToString(&namePart, file, lineNo);
136  newGroup += namePart.toByteArray();
137  }
138  } while ((start = end + 2) <= line.length() && line.at(end + 1) == '[');
139  currentGroup = newGroup;
140 
141  groupSkip = entryMap.getEntryOption(currentGroup, 0, 0, KEntryMap::EntryImmutable);
142 
143  if (groupSkip && !bDefault)
144  continue;
145 
146  if (groupOptionImmutable)
147  // Do not make the groups immutable until the entries from
148  // this file have been added.
149  immutableGroups.append(currentGroup);
150  } else {
151  if (groupSkip && !bDefault)
152  continue; // skip entry
153 
154  BufferFragment aKey;
155  int eqpos = line.indexOf('=');
156  if (eqpos < 0) {
157  aKey = line;
158  line.clear();
159  } else {
160  BufferFragment temp = line.left(eqpos);
161  temp.trim();
162  aKey = temp;
163  line.truncateLeft(eqpos + 1);
164  }
165  if (aKey.isEmpty()) {
166  qWarning() << warningProlog(file, lineNo) << "Invalid entry (empty key)";
167  continue;
168  }
169 
170  KEntryMap::EntryOptions entryOptions=0;
171  if (groupOptionImmutable)
172  entryOptions |= KEntryMap::EntryImmutable;
173 
174  BufferFragment locale;
175  int start;
176  while ((start = aKey.lastIndexOf('[')) >= 0) {
177  int end = aKey.indexOf(']', start);
178  if (end < 0) {
179  qWarning() << warningProlog(file, lineNo)
180  << "Invalid entry (missing ']')";
181  goto next_line;
182  } else if (end > start + 1 && aKey.at(start + 1) == '$') { // found option(s)
183  int i = start + 2;
184  while (i < end) {
185  switch (aKey.at(i)) {
186  case 'i':
187  if (!kde_kiosk_exception)
188  entryOptions |= KEntryMap::EntryImmutable;
189  break;
190  case 'e':
191  if (allowExecutableValues)
192  entryOptions |= KEntryMap::EntryExpansion;
193  break;
194  case 'd':
195  entryOptions |= KEntryMap::EntryDeleted;
196  aKey = aKey.left(start);
197  printableToString(&aKey, file, lineNo);
198  entryMap.setEntry(currentGroup, aKey.toByteArray(), QByteArray(), entryOptions);
199  goto next_line;
200  default:
201  break;
202  }
203  i++;
204  }
205  } else { // found a locale
206  if (!locale.isNull()) {
207  qWarning() << warningProlog(file, lineNo)
208  << "Invalid entry (second locale!?)";
209  goto next_line;
210  }
211 
212  locale = aKey.mid(start + 1,end - start - 1);
213  }
214  aKey.truncate(start);
215  }
216  if (eqpos < 0) { // Do this here after [$d] was checked
217  qWarning() << warningProlog(file, lineNo) << "Invalid entry (missing '=')";
218  continue;
219  }
220  printableToString(&aKey, file, lineNo);
221  if (!locale.isEmpty()) {
222  if (locale != currentLocale) {
223  // backward compatibility. C == en_US
224  if (locale.at(0) != 'C' || currentLocale != "en_US") {
225  if (merging)
226  entryOptions |= KEntryMap::EntryRawKey;
227  else
228  goto next_line; // skip this entry if we're not merging
229  }
230  }
231  }
232 
233  if (!(entryOptions & KEntryMap::EntryRawKey))
234  printableToString(&aKey, file, lineNo);
235 
236  if (options&ParseGlobal)
237  entryOptions |= KEntryMap::EntryGlobal;
238  if (bDefault)
239  entryOptions |= KEntryMap::EntryDefault;
240  if (!locale.isNull())
241  entryOptions |= KEntryMap::EntryLocalized;
242  printableToString(&line, file, lineNo);
243  if (entryOptions & KEntryMap::EntryRawKey) {
244  QByteArray rawKey;
245  rawKey.reserve(aKey.length() + locale.length() + 2);
246  rawKey.append(aKey.toVolatileByteArray());
247  rawKey.append('[').append(locale.toVolatileByteArray()).append(']');
248  entryMap.setEntry(currentGroup, rawKey, line.toByteArray(), entryOptions);
249  } else {
250  entryMap.setEntry(currentGroup, aKey.toByteArray(), line.toByteArray(), entryOptions);
251  }
252  }
253 next_line:
254  continue;
255  }
256 
257  // now make sure immutable groups are marked immutable
258  foreach(const QByteArray& group, immutableGroups) {
259  entryMap.setEntry(group, QByteArray(), QByteArray(), KEntryMap::EntryImmutable);
260  }
261 
262  return fileOptionImmutable ? ParseImmutable : ParseOk;
263 }
264 
265 void KConfigIniBackend::writeEntries(const QByteArray& locale, QFile& file,
266  const KEntryMap& map, bool defaultGroup, bool &firstEntry)
267 {
268  QByteArray currentGroup;
269  bool groupIsImmutable = false;
270  const KEntryMapConstIterator end = map.constEnd();
271  for (KEntryMapConstIterator it = map.constBegin(); it != end; ++it) {
272  const KEntryKey& key = it.key();
273 
274  // Either process the default group or all others
275  if ((key.mGroup != "<default>") == defaultGroup)
276  continue; // skip
277 
278  // the only thing we care about groups is, is it immutable?
279  if (key.mKey.isNull()) {
280  groupIsImmutable = it->bImmutable;
281  continue; // skip
282  }
283 
284  const KEntry& currentEntry = *it;
285  if (!defaultGroup && currentGroup != key.mGroup) {
286  if (!firstEntry)
287  file.putChar('\n');
288  currentGroup = key.mGroup;
289  for (int start = 0, end;; start = end + 1) {
290  file.putChar('[');
291  end = currentGroup.indexOf('\x1d', start);
292  if (end < 0) {
293  int cgl = currentGroup.length();
294  if (currentGroup.at(start) == '$' && cgl - start <= 10) {
295  for (int i = start + 1; i < cgl; i++) {
296  char c = currentGroup.at(i);
297  if (c < 'a' || c > 'z')
298  goto nope;
299  }
300  file.write("\\x24");
301  start++;
302  }
303  nope:
304  file.write(stringToPrintable(currentGroup.mid(start), GroupString));
305  file.putChar(']');
306  if (groupIsImmutable) {
307  file.write("[$i]", 4);
308  }
309  file.putChar('\n');
310  break;
311  } else {
312  file.write(stringToPrintable(currentGroup.mid(start, end - start), GroupString));
313  file.putChar(']');
314  }
315  }
316  }
317 
318  firstEntry = false;
319  // it is data for a group
320 
321  if (key.bRaw) // unprocessed key with attached locale from merge
322  file.write(key.mKey);
323  else {
324  file.write(stringToPrintable(key.mKey, KeyString)); // Key
325  if (key.bLocal && locale != "C") { // 'C' locale == untranslated
326  file.putChar('[');
327  file.write(locale); // locale tag
328  file.putChar(']');
329  }
330  }
331  if (currentEntry.bDeleted) {
332  if (currentEntry.bImmutable)
333  file.write("[$di]", 5); // Deleted + immutable
334  else
335  file.write("[$d]", 4); // Deleted
336  } else {
337  if (currentEntry.bImmutable || currentEntry.bExpand) {
338  file.write("[$", 2);
339  if (currentEntry.bImmutable)
340  file.putChar('i');
341  if (currentEntry.bExpand)
342  file.putChar('e');
343  file.putChar(']');
344  }
345  file.putChar('=');
346  file.write(stringToPrintable(currentEntry.mValue, ValueString));
347  }
348  file.putChar('\n');
349  }
350 }
351 
352 void KConfigIniBackend::writeEntries(const QByteArray& locale, QFile& file, const KEntryMap& map)
353 {
354  bool firstEntry = true;
355 
356  // write default group
357  writeEntries(locale, file, map, true, firstEntry);
358 
359  // write all other groups
360  writeEntries(locale, file, map, false, firstEntry);
361 }
362 
363 bool KConfigIniBackend::writeConfig(const QByteArray& locale, KEntryMap& entryMap,
364  WriteOptions options, const KComponentData &data)
365 {
366  Q_ASSERT(!filePath().isEmpty());
367 
368  KEntryMap writeMap;
369  const bool bGlobal = options & WriteGlobal;
370 
371  // First, reparse the file on disk, to merge our changes with the ones done by other apps
372  // Store the result into writeMap.
373  {
374  ParseOptions opts = ParseExpansions;
375  if (bGlobal)
376  opts |= ParseGlobal;
377  ParseInfo info = parseConfig(locale, writeMap, opts, true);
378  if (info != ParseOk) // either there was an error or the file became immutable
379  return false;
380  }
381 
382  const KEntryMapIterator end = entryMap.end();
383  for (KEntryMapIterator it=entryMap.begin(); it != end; ++it) {
384  if (!it.key().mKey.isEmpty() && !it->bDirty) // not dirty, doesn't overwrite entry in writeMap. skips default entries, too.
385  continue;
386 
387  const KEntryKey& key = it.key();
388 
389  // only write entries that have the same "globality" as the file
390  if (it->bGlobal == bGlobal) {
391  if (it->bReverted) {
392  writeMap.remove(key);
393  } else if (!it->bDeleted) {
394  writeMap[key] = *it;
395  } else {
396  KEntryKey defaultKey = key;
397  defaultKey.bDefault = true;
398  if (!entryMap.contains(defaultKey)) {
399  writeMap.remove(key); // remove the deleted entry if there is no default
400  //qDebug() << "Detected as deleted=>removed:" << key.mGroup << key.mKey << "global=" << bGlobal;
401  } else {
402  writeMap[key] = *it; // otherwise write an explicitly deleted entry
403  //qDebug() << "Detected as deleted=>[$d]:" << key.mGroup << key.mKey << "global=" << bGlobal;
404  }
405  }
406  it->bDirty = false;
407  }
408  }
409 
410  // now writeMap should contain only entries to be written
411  // so write it out to disk
412 
413  // check if file exists
414  QFile::Permissions fileMode = QFile::ReadUser | QFile::WriteUser;
415  bool createNew = true;
416 
417  QFileInfo fi(filePath());
418  if (fi.exists())
419  {
420  if (fi.ownerId() == ::getuid())
421  {
422  // Preserve file mode if file exists and is owned by user.
423  fileMode = fi.permissions();
424  }
425  else
426  {
427  // File is not owned by user:
428  // Don't create new file but write to existing file instead.
429  createNew = false;
430  }
431  }
432 
433  if (createNew) {
434  KSaveFile file( filePath(), data );
435  if (!file.open()) {
436  return false;
437  }
438 
439  file.setPermissions(fileMode);
440 
441  file.setTextModeEnabled(true); // to get eol translation
442  writeEntries(locale, file, writeMap);
443 
444  if (!file.flush()) {
445  // Couldn't write. Disk full?
446  kWarning() << "Couldn't write" << filePath() << ". Disk full?";
447  file.abort();
448  return false;
449  }
450 
451  if (!file.size() && (fileMode == (QFile::ReadUser | QFile::WriteUser))) {
452  // File is empty and doesn't have special permissions: delete it.
453  file.abort();
454 
455  if (fi.exists()) {
456  // also remove the old file in case it existed. this can happen
457  // when we delete all the entries in an existing config file.
458  // if we don't do this, then deletions and revertToDefault's
459  // will mysteriously fail
460  QFile::remove(filePath());
461  }
462  } else {
463  // Normal case: Close the file
464  return file.finalize();
465  }
466  } else {
467  // Open existing file. *DON'T* create it if it suddenly does not exist!
468 #ifdef Q_OS_UNIX
469  int fd = KDE_open(QFile::encodeName(filePath()), O_WRONLY | O_TRUNC);
470  if (fd < 0) {
471  return false;
472  }
473  FILE *fp = KDE_fdopen(fd, "w");
474  if (!fp) {
475  close(fd);
476  return false;
477  }
478  QFile f;
479  if (!f.open(fp, QIODevice::WriteOnly)) {
480  fclose(fp);
481  return false;
482  }
483  writeEntries(locale, f, writeMap);
484  f.close();
485  fclose(fp);
486 #else
487  QFile f( filePath() );
488  // XXX This is broken - it DOES create the file if it is suddenly gone.
489  if (!f.open( QIODevice::WriteOnly | QIODevice::Truncate )) {
490  return false;
491  }
492  f.setTextModeEnabled(true);
493  writeEntries(locale, f, writeMap);
494 #endif
495  }
496  return true;
497 }
498 
499 bool KConfigIniBackend::isWritable() const
500 {
501  if (!filePath().isEmpty()) {
502  if (KStandardDirs::checkAccess(filePath(), W_OK)) {
503  return true;
504  }
505  // The check might have failed because any of the containing dirs
506  // did not exist. If the file does not exist, check if the deepest
507  // existing dir is writable.
508  QFileInfo file(filePath());
509  if (!file.exists()) {
510  QFileInfo dir(file.absolutePath());
511  while (!dir.exists()) {
512  QString parent = dir.absolutePath(); // Go up. Can't use cdUp() on non-existing dirs.
513  if (parent == dir.filePath()) {
514  // no parent
515  return false;
516  }
517  dir.setFile(parent);
518  }
519  return dir.isDir() && dir.isWritable();
520  }
521  }
522 
523  return false;
524 }
525 
526 QString KConfigIniBackend::nonWritableErrorMessage() const
527 {
528  return i18n("Configuration file \"%1\" not writable.\n", filePath());
529 }
530 
531 void KConfigIniBackend::createEnclosing()
532 {
533  const QString file = filePath();
534  if (file.isEmpty())
535  return; // nothing to do
536 
537  // Create the containing dir, maybe it wasn't there
538  QDir dir;
539  dir.mkpath(QFileInfo(file).absolutePath());
540 }
541 
542 void KConfigIniBackend::setFilePath(const QString& file)
543 {
544  if (file.isEmpty())
545  return;
546 
547  Q_ASSERT(QDir::isAbsolutePath(file));
548 
549  const QFileInfo info(file);
550  if (info.exists()) {
551  setLocalFilePath(info.canonicalFilePath());
552  setLastModified(info.lastModified());
553  setSize(info.size());
554  } else {
555  setLocalFilePath(file);
556  setSize(0);
557  QDateTime dummy;
558  dummy.setTime_t(0);
559  setLastModified(dummy);
560  }
561 }
562 
563 KConfigBase::AccessMode KConfigIniBackend::accessMode() const
564 {
565  if (filePath().isEmpty())
566  return KConfigBase::NoAccess;
567 
568  if (isWritable())
569  return KConfigBase::ReadWrite;
570 
571  return KConfigBase::ReadOnly;
572 }
573 
574 bool KConfigIniBackend::lock(const KComponentData& componentData)
575 {
576  Q_ASSERT(!filePath().isEmpty());
577 
578  if (!lockFile) {
579  lockFile = new KLockFile(filePath() + QLatin1String(".lock"), componentData);
580  }
581 
582  if (lockFile->lock() == KLockFile::LockStale) // attempt to break the lock
583  lockFile->lock(KLockFile::ForceFlag);
584  return lockFile->isLocked();
585 }
586 
587 void KConfigIniBackend::unlock()
588 {
589  lockFile->unlock();
590  lockFile.clear();
591 }
592 
593 bool KConfigIniBackend::isLocked() const
594 {
595  return lockFile && lockFile->isLocked();
596 }
597 
598 QByteArray KConfigIniBackend::stringToPrintable(const QByteArray& aString, StringType type)
599 {
600  static const char nibbleLookup[] = {
601  '0', '1', '2', '3', '4', '5', '6', '7',
602  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
603  };
604 
605  if (aString.isEmpty())
606  return aString;
607  const int l = aString.length();
608 
609  QByteArray result; // Guesstimated that it's good to avoid data() initialization for a length of l*4
610  result.resize(l * 4); // Maximum 4x as long as source string due to \x<ab> escape sequences
611  register const char *s = aString.constData();
612  int i = 0;
613  char *data = result.data();
614  char *start = data;
615 
616  // Protect leading space
617  if (s[0] == ' ' && type != GroupString) {
618  *data++ = '\\';
619  *data++ = 's';
620  i++;
621  }
622 
623  for (; i < l; ++i/*, r++*/) {
624  switch (s[i]) {
625  default:
626  // The \n, \t, \r cases (all < 32) are handled below; we can ignore them here
627  if (((unsigned char)s[i]) < 32)
628  goto doEscape;
629  *data++ = s[i];
630  break;
631  case '\n':
632  *data++ = '\\';
633  *data++ = 'n';
634  break;
635  case '\t':
636  *data++ = '\\';
637  *data++ = 't';
638  break;
639  case '\r':
640  *data++ = '\\';
641  *data++ = 'r';
642  break;
643  case '\\':
644  *data++ = '\\';
645  *data++ = '\\';
646  break;
647  case '=':
648  if (type != KeyString) {
649  *data++ = s[i];
650  break;
651  }
652  goto doEscape;
653  case '[':
654  case ']':
655  // Above chars are OK to put in *value* strings as plaintext
656  if (type == ValueString) {
657  *data++ = s[i];
658  break;
659  }
660  doEscape:
661  *data++ = '\\';
662  *data++ = 'x';
663  *data++ = nibbleLookup[((unsigned char)s[i]) >> 4];
664  *data++ = nibbleLookup[((unsigned char)s[i]) & 0x0f];
665  break;
666  }
667  }
668  *data = 0;
669  result.resize(data - start);
670 
671  // Protect trailing space
672  if (result.endsWith(' ') && type != GroupString) {
673  result.replace(result.length() - 1, 1, "\\s");
674  }
675  result.squeeze();
676 
677  return result;
678 }
679 
680 char KConfigIniBackend::charFromHex(const char *str, const QFile& file, int line)
681 {
682  unsigned char ret = 0;
683  for (int i = 0; i < 2; i++) {
684  ret <<= 4;
685  quint8 c = quint8(str[i]);
686 
687  if (c >= '0' && c <= '9') {
688  ret |= c - '0';
689  } else if (c >= 'a' && c <= 'f') {
690  ret |= c - 'a' + 0x0a;
691  } else if (c >= 'A' && c <= 'F') {
692  ret |= c - 'A' + 0x0a;
693  } else {
694  QByteArray e(str, 2);
695  e.prepend("\\x");
696  qWarning() << warningProlog(file, line) << "Invalid hex character " << c
697  << " in \\x<nn>-type escape sequence \"" << e.constData() << "\".";
698  return 'x';
699  }
700  }
701  return char(ret);
702 }
703 
704 void KConfigIniBackend::printableToString(BufferFragment* aString, const QFile& file, int line)
705 {
706  if (aString->isEmpty() || aString->indexOf('\\')==-1)
707  return;
708  aString->trim();
709  int l = aString->length();
710  char *r = aString->data();
711  char *str=r;
712 
713  for(int i = 0; i < l; i++, r++) {
714  if (str[i]!= '\\') {
715  *r=str[i];
716  } else {
717  // Probable escape sequence
718  i++;
719  if (i >= l) { // Line ends after backslash - stop.
720  *r = '\\';
721  break;
722  }
723 
724  switch(str[i]) {
725  case 's':
726  *r = ' ';
727  break;
728  case 't':
729  *r = '\t';
730  break;
731  case 'n':
732  *r = '\n';
733  break;
734  case 'r':
735  *r = '\r';
736  break;
737  case '\\':
738  *r = '\\';
739  break;
740  case 'x':
741  if (i + 2 < l) {
742  *r = charFromHex(str + i + 1, file, line);
743  i += 2;
744  } else {
745  *r = 'x';
746  i = l - 1;
747  }
748  break;
749  default:
750  *r = '\\';
751  qWarning() << warningProlog(file, line)
752  << QString::fromLatin1("Invalid escape sequence \"\\%1\".").arg(str[i]);
753  }
754  }
755  }
756  aString->truncate(r - aString->constData());
757 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Fri Jul 12 2013 08:50:15 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

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-4.10.5 API Reference

Skip menu "kdelibs-4.10.5 API Reference"
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal