26 #ifdef HAVE_SYS_MMAN_H
29 #include <sys/types.h>
30 #ifdef HAVE_SYS_STAT_H
38 #include <tqfileinfo.h>
39 #include <tqtextcodec.h>
40 #include <tqtextstream.h>
42 #include "kconfigbackend.h"
43 #include "kconfigbase.h"
44 #include <kapplication.h>
48 #include <kstandarddirs.h>
49 #include <ksavefile.h>
53 extern bool checkAccess(
const TQString& pathname,
int mode);
55 static TQCString printableToString(
const char *str,
int l)
59 ((*str ==
' ') || (*str ==
'\t') || (*str ==
'\r')))
66 ((str[l-1] ==
' ') || (str[l-1] ==
'\t') || (str[l-1] ==
'\r')))
71 TQCString result(l + 1);
72 char *r = result.data();
74 for(
int i = 0; i < l;i++, str++)
111 result.truncate(r-result.data());
115 static TQCString stringToPrintable(
const TQCString& str){
116 TQCString result(str.length()*2);
117 register char *r =
const_cast<TQCString&
>(result).data();
118 register char *s =
const_cast<TQCString&
>(str).data();
120 if (!s)
return TQCString(
"");
125 *r++ =
'\\'; *r++ =
's';
135 *r++ =
'\\'; *r++ =
'n';
139 *r++ =
'\\'; *r++ =
't';
143 *r++ =
'\\'; *r++ =
'r';
147 *r++ =
'\\'; *r++ =
'\\';
158 *(r-1) =
'\\'; *r++ =
's';
162 result.truncate(r - result.data());
166 static TQCString decodeGroup(
const char*s,
int l)
169 register char *r = result.data();
174 if ((*s ==
'[') && (l > 1))
182 if ((*s ==
']') && (l > 1))
193 result.truncate(r - result.data());
197 static TQCString encodeGroup(
const TQCString &str)
199 int l = str.length();
200 TQCString result(l*2+1);
201 register char *r =
const_cast<TQCString&
>(result).data();
202 register char *s =
const_cast<TQCString&
>(str).data();
205 if ((*s ==
'[') || (*s ==
']'))
210 result.truncate(r - result.data());
214 static TQCString encodeKey(
const char* key)
216 TQCString newKey(key);
218 newKey.replace(
'[',
"%5b");
219 newKey.replace(
']',
"%5d");
224 static TQCString decodeKey(
const char* key)
226 TQCString newKey(key);
228 newKey.replace(
"%5b",
"[");
229 newKey.replace(
"%5d",
"]");
234 class KConfigBackEnd::KConfigBackEndPrivate
237 TQDateTime localLastModified;
244 const char * _resType,
247 mfileName = _fileName;
249 useKDEGlobals = _useKDEGlobals;
250 if (mfileName.isEmpty()) {
251 mLocalFileName = TQString::null;
253 else if (!TQDir::isRelativePath(mfileName)) {
254 mLocalFileName = mfileName;
264 mGlobalFileName = TQString::null;
267 d->localLastModified = TQDateTime();
268 d->localLastSize = 0;
269 d->localLockFile = 0;
270 d->globalLockFile = 0;
277 if (d->globalLockFile)
278 return d->globalLockFile;
280 if (!mGlobalFileName.isEmpty())
282 d->globalLockFile =
new KLockFile(mGlobalFileName+
".lock");
283 return d->globalLockFile;
288 if (d->localLockFile)
289 return d->localLockFile;
291 if (!mLocalFileName.isEmpty())
293 d->localLockFile =
new KLockFile(mLocalFileName+
".lock");
294 return d->localLockFile;
301 const TQString &_fileName,
302 const char * _resType,
304 : pConfig(_config), bFileImmutable(false), mConfigState(
KConfigBase::NoAccess), mFileMode(-1)
306 d =
new KConfigBackEndPrivate;
323 mConfigState = KConfigBase::ReadOnly;
324 if (!mLocalFileName.isEmpty() && !pConfig->
isReadOnly())
326 if (checkAccess(mLocalFileName, W_OK))
328 mConfigState = KConfigBase::ReadWrite;
338 if (checkAccess(mLocalFileName, W_OK))
340 mConfigState = KConfigBase::ReadWrite;
343 TQFileInfo info(mLocalFileName);
344 d->localLastModified = info.lastModified();
345 d->localLastSize = info.size();
349 bFileImmutable =
false;
354 findAllResources(
"config", TQString::fromLatin1(
"kdeglobals"));
357 TQString etc_kderc = TQFile::decodeName( TQCString(getenv(
"WINDIR")) +
"\\kderc" );
359 TQString etc_kderc = TQString::fromLatin1(
"/etc/kderc");
362 if (checkAccess(etc_kderc, R_OK))
366 findAllResources(
"config", TQString::fromLatin1(
"system.kdeglobals"));
368 TQStringList::ConstIterator it;
370 for (it = kdercs.fromLast(); it != kdercs.end(); --it) {
372 TQFile aConfigFile( *it );
373 if (!aConfigFile.open( IO_ReadOnly ))
382 bool bReadFile = !mfileName.isEmpty();
385 TQString bootLanguage;
386 if (useKDEGlobals && localeString.isEmpty() && !KGlobal::_locale) {
388 bootLanguage = KLocale::_initLanguage(pConfig);
392 bFileImmutable =
false;
394 if ( !TQDir::isRelativePath(mfileName) )
399 TQStringList::ConstIterator it;
401 for (it = list.fromLast(); it != list.end(); --it) {
403 TQFile aConfigFile( *it );
405 bool bIsLocal = (*it == mLocalFileName);
406 if (aConfigFile.open( IO_ReadOnly )) {
413 if (
KGlobal::dirs()->isRestrictedResource(resType, mfileName))
414 bFileImmutable =
true;
415 TQString currentLanguage;
416 if (!bootLanguage.isEmpty())
418 currentLanguage = KLocale::_initLanguage(pConfig);
421 if (bootLanguage != currentLanguage)
429 mConfigState = KConfigBase::ReadOnly;
436 static sigjmp_buf mmap_jmpbuf;
437 struct sigaction mmap_old_sigact;
440 static void mmap_sigbus_handler(
int)
442 siglongjmp (mmap_jmpbuf, 1);
448 extern bool kde_kiosk_exception;
452 bool bGlobal,
bool bDefault)
466 TQCString aCurrentGroup(
"<default>");
468 unsigned int ll = localeString.length();
471 static volatile const char *map;
472 map = (
const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE,
475 if ( map != MAP_FAILED )
477 s = (
const char*) map;
478 eof = s + rFile.size();
481 struct sigaction act;
482 act.sa_handler = mmap_sigbus_handler;
483 sigemptyset( &act.sa_mask );
485 act.sa_flags = SA_ONESHOT;
487 act.sa_flags = SA_RESETHAND;
489 sigaction( SIGBUS, &act, &mmap_old_sigact );
491 if (sigsetjmp (mmap_jmpbuf, 1))
493 qWarning(
"SIGBUS while reading %s", rFile.name().latin1());
494 munmap((
char* )map, rFile.size());
495 sigaction (SIGBUS, &mmap_old_sigact, 0);
504 data = rFile.readAll();
506 eof = s + data.size();
509 bool fileOptionImmutable =
false;
510 bool groupOptionImmutable =
false;
511 bool groupSkip =
false;
512 bool foundGettextDomain =
false;
513 TQCString gettextDomain;
520 while((s < eof) && isspace(*s) && (*s !=
'\n'))
524 if ((s < eof) && ((*s ==
'\n') || (*s ==
'#')))
527 while ((s < eof) && (*s !=
'\n'))
531 const char *startLine = s;
536 while ((s < eof) && (*s !=
'\n'))
540 if ((s+1 < eof) && (*(s+1) ==
']'))
549 while ((s < eof) && (*s !=
'\n')) s++;
550 if ((e >= eof) || (*e !=
']'))
552 fprintf(stderr,
"Invalid group header at %s:%d\n", rFile.name().latin1(), line);
557 if ((e-startLine == 3) &&
558 (startLine[1] ==
'$') &&
559 (startLine[2] ==
'i'))
561 if (!kde_kiosk_exception)
562 fileOptionImmutable =
true;
566 aCurrentGroup = decodeGroup(startLine + 1, e - startLine);
570 if (aCurrentGroup ==
"KDE Desktop Entry")
571 aCurrentGroup =
"Desktop Entry";
573 groupOptionImmutable = fileOptionImmutable;
576 if ((e+2 < eof) && (*e++ ==
'[') && (*e++ ==
'$'))
578 if ((*e ==
'i') && !kde_kiosk_exception)
580 groupOptionImmutable =
true;
588 if (groupSkip && !bDefault)
592 pConfig->
putData(groupKey, entry,
false);
597 (*pWriteBackMap)[groupKey] = entry;
602 if (groupSkip && !bDefault)
606 bool optionImmutable = groupOptionImmutable;
607 bool optionDeleted =
false;
608 bool optionExpand =
false;
609 const char *endOfKey = 0, *locale = 0, *elocale = 0;
610 for (; (s < eof) && (*s !=
'\n'); s++)
626 if ((s >= eof) || (*s ==
'\n') || (*s ==
'=')) {
627 fprintf(stderr,
"Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line);
638 fprintf(stderr,
"Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line);
647 while (option < eoption)
650 if ((*option ==
'i') && !kde_kiosk_exception)
651 optionImmutable =
true;
652 else if (*option ==
'e')
654 else if (*option ==
'd')
656 optionDeleted =
true;
659 else if (*option ==
']')
665 fprintf(stderr,
"Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line);
669 for (endOfKey--; ; endOfKey--)
671 if (endOfKey < startLine)
673 fprintf(stderr,
"Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line);
676 if (!isspace(*endOfKey))
680 const char *st = ++s;
681 while ((s < eof) && (*s !=
'\n')) s++;
684 unsigned int cl =
static_cast<unsigned int>(elocale - locale);
685 if ((ll != cl) || memcmp(locale, localeString.data(), ll))
688 if ( cl != 1 || ll != 5 || *locale !=
'C' || memcmp(localeString.data(),
"en_US", 5)) {
701 TQCString key(startLine, endOfKey - startLine + 2);
702 TQCString val = printableToString(st, s - st);
705 if (TQString(key.data()) ==
"X-Ubuntu-Gettext-Domain") {
706 gettextDomain = val.data();
707 foundGettextDomain =
true;
710 KEntryKey aEntryKey(aCurrentGroup, decodeKey(key));
711 aEntryKey.
bLocal = (locale != 0);
720 aEntry.
bNLS = (locale != 0);
725 pWriteBackMap->insert(aEntryKey, aEntry);
730 pConfig->
putData(aEntryKey, aEntry,
false);
740 if (!pWriteBackMap) {
741 TQFile file(
"file.txt");
742 if (foundGettextDomain) {
746 TQString language = locale.
language();
747 translateKey(locale, aCurrentGroup, TQCString(
"Name"));
748 translateKey(locale, aCurrentGroup, TQCString(
"Comment"));
749 translateKey(locale, aCurrentGroup, TQCString(
"Language"));
750 translateKey(locale, aCurrentGroup, TQCString(
"Keywords"));
751 translateKey(locale, aCurrentGroup, TQCString(
"About"));
752 translateKey(locale, aCurrentGroup, TQCString(
"Description"));
753 translateKey(locale, aCurrentGroup, TQCString(
"GenericName"));
754 translateKey(locale, aCurrentGroup, TQCString(
"Query"));
755 translateKey(locale, aCurrentGroup, TQCString(
"ExtraNames"));
756 translateKey(locale, aCurrentGroup, TQCString(
"X-KDE-Submenu"));
761 if (fileOptionImmutable)
762 bFileImmutable =
true;
767 munmap((
char* )map, rFile.size());
769 sigaction (SIGBUS, &mmap_old_sigact, 0);
775 void KConfigINIBackEnd::translateKey(
KLocale& locale, TQCString currentGroup, TQCString key) {
778 if (TQString(entry.mValue) !=
"") {
779 TQString orig = key +
"=" + entry.mValue;
780 TQString translate = locale.
translate(key +
"=" + entry.mValue);
781 if (TQString::compare(orig, translate) != 0) {
782 translate = translate.mid(key.length() + 1);
783 entry.mValue = translate.utf8();
786 pConfig->
putData(entryKey, entry,
false);
797 bool bEntriesLeft =
true;
802 if (!mfileName.isEmpty()) {
804 if ((resType!=
"config") && !TQDir::isRelativePath(mLocalFileName))
816 if (checkAccess(mLocalFileName, W_OK)) {
820 bool mergeLocalFile = bMerge;
834 TQFileInfo info(mLocalFileName);
835 if ((d->localLastSize == info.size()) &&
836 (d->localLastModified == info.lastModified()))
839 mergeLocalFile =
false;
844 d->localLastModified = TQDateTime();
845 d->localLastSize = 0;
849 bEntriesLeft =
writeConfigFile( mLocalFileName,
false, mergeLocalFile );
861 TQFileInfo info(mLocalFileName);
862 d->localLastModified = info.lastModified();
863 d->localLastSize = info.size();
872 if (bEntriesLeft && useKDEGlobals) {
875 if (checkAccess ( mGlobalFileName, W_OK )) {
892 static void writeEntries(FILE *pStream,
const KEntryMap& entryMap,
bool defaultGroup,
bool &firstEntry,
const TQCString &localeString)
895 TQCString currentGroup;
897 aIt != entryMap.end(); ++aIt)
902 if ((key.
mGroup !=
"<default>") == defaultGroup)
909 const KEntry ¤tEntry = *aIt;
913 bool hasDefault = (aTestIt != entryMap.end());
916 const KEntryKey &defaultKey = aTestIt.key();
928 if ((currentEntry.mValue == (*aTestIt).mValue) &&
929 (currentEntry.
bDeleted == (*aTestIt).bDeleted))
939 if (!defaultGroup && (currentGroup != key.
mGroup)) {
941 fprintf(pStream,
"\n");
942 currentGroup = key.
mGroup;
943 fprintf(pStream,
"[%s]\n", encodeGroup(currentGroup).data());
948 fputs(encodeKey(key.
mKey.data()), pStream);
950 if ( currentEntry.
bNLS )
953 fputs(localeString.data(), pStream);
959 fputs(
"[$d]\n", pStream);
975 fputs(stringToPrintable(currentEntry.mValue).data(), pStream);
976 fputc(
'\n', pStream);
984 bool bEntriesLeft =
false;
985 bFileImmutable =
false;
988 if (mergeFile && mergeFile->open(IO_ReadOnly))
1000 for (KEntryMapIterator aIt = aMap.begin();
1001 aIt != aMap.end(); ++aIt)
1003 const KEntry ¤tEntry = *aIt;
1004 if(aIt.key().bDefault)
1006 aTempMap.replace(aIt.key(), currentEntry);
1010 if (mergeFile && !currentEntry.
bDirty)
1015 if (currentEntry.
bGlobal != bGlobal)
1018 bEntriesLeft =
true;
1024 KEntryMapIterator aIt2 = aTempMap.find(aIt.key());
1025 if (aIt2 != aTempMap.end() && (*aIt2).bImmutable)
1028 aTempMap.insert(aIt.key(), currentEntry,
true);
1031 return bEntriesLeft;
1043 TQFile *mergeFile = (bMerge ?
new TQFile(filename) : 0);
1044 bool bEntriesLeft =
getEntryMap(aTempMap, bGlobal, mergeFile);
1054 bool createNew =
true;
1056 KDE_struct_stat buf;
1057 if (KDE_stat(TQFile::encodeName(filename), &buf) == 0)
1059 if (buf.st_uid == getuid())
1062 fileMode = buf.st_mode & 0777;
1077 pConfigFile =
new KSaveFile( filename, 0600 );
1079 if (pConfigFile->
status() != 0)
1082 return bEntriesLeft;
1085 if (!bGlobal && (fileMode == -1))
1086 fileMode = mFileMode;
1090 fchmod(pConfigFile->
handle(), fileMode);
1093 pStream = pConfigFile->
fstream();
1099 int fd = KDE_open( TQFile::encodeName(filename), O_WRONLY | O_TRUNC );
1102 return bEntriesLeft;
1104 pStream = KDE_fdopen( fd,
"w");
1108 return bEntriesLeft;
1116 bool bEmptyFile = (ftell(pStream) == 0);
1117 if ( bEmptyFile && ((fileMode == -1) || (fileMode == 0600)) )
1120 ::unlink(TQFile::encodeName(filename));
1121 pConfigFile->
abort();
1126 pConfigFile->
close();
1135 return bEntriesLeft;
1140 bool firstEntry =
true;
1143 ::writeEntries(pStream, aTempMap,
true, firstEntry, localeString);
1146 ::writeEntries(pStream, aTempMap,
false, firstEntry, localeString);
1149 void KConfigBackEnd::virtual_hook(
int,
void* )
1152 void KConfigINIBackEnd::virtual_hook(
int id,
void* data )
1153 { KConfigBackEnd::virtual_hook(
id, data ); }
1158 bool allWritable =
true;
1160 if ( !mLocalFileName.isEmpty() && !bFileImmutable && !checkAccess(mLocalFileName,W_OK) )
1162 errorMsg = i18n(
"Will not save configuration.\n");
1163 allWritable =
false;
1164 errorMsg += i18n(
"Configuration file \"%1\" not writable.\n").arg(mLocalFileName);
1168 if ( !mGlobalFileName.isEmpty() && useKDEGlobals && !bFileImmutable && !checkAccess(mGlobalFileName,W_OK) )
1170 if ( errorMsg.isEmpty() )
1171 errorMsg = i18n(
"Will not save configuration.\n");
1172 errorMsg += i18n(
"Configuration file \"%1\" not writable.\n").arg(mGlobalFileName);
1173 allWritable =
false;
1176 if (warnUser && !allWritable)
1179 errorMsg += i18n(
"Please contact your system administrator.");
1182 if (!cmdToExec.isEmpty() && app)
1185 lprocess << cmdToExec <<
"--title" << app->
instanceName() <<
"--msgbox" << TQCString(errorMsg.local8Bit());