kconfig_compiler.cpp

00001 // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002 /*
00003     This file is part of KDE.
00004 
00005     Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
00006     Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
00007     Copyright (c) 2003 Zack Rusin <zack@kde.org>
00008 
00009     This library is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU Library General Public
00011     License as published by the Free Software Foundation; either
00012     version 2 of the License, or (at your option) any later version.
00013 
00014     This library is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017     Library General Public License for more details.
00018 
00019     You should have received a copy of the GNU Library General Public License
00020     along with this library; see the file COPYING.LIB.  If not, write to
00021     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022     Boston, MA 02110-1301, USA.
00023 */
00024 
00025 #include <qfile.h>
00026 #include <qtextstream.h>
00027 #include <qdom.h>
00028 #include <qregexp.h>
00029 
00030 #include <kaboutdata.h>
00031 #include <kapplication.h>
00032 #include <kdebug.h>
00033 #include <klocale.h>
00034 #include <kcmdlineargs.h>
00035 #include <kglobal.h>
00036 #include <kconfig.h>
00037 #include <ksimpleconfig.h>
00038 #include <kstandarddirs.h>
00039 
00040 #include <iostream>
00041 
00042 static const KCmdLineOptions options[] =
00043 {
00044   { "d", 0, 0 },
00045   { "directory <dir>", I18N_NOOP("Directory to generate files in"), "." },
00046   { "+file.kcfg", I18N_NOOP("Input kcfg XML file"), 0 },
00047   { "+file.kcfgc", I18N_NOOP("Code generation options file"), 0 },
00048   KCmdLineLastOption
00049 };
00050 
00051 
00052 bool globalEnums;
00053 bool itemAccessors;
00054 bool dpointer;
00055 QStringList allNames;
00056 QRegExp *validNameRegexp;
00057 QString This;
00058 QString Const;  
00059 
00060 class CfgEntry
00061 {
00062   public:
00063     struct Choice
00064     {
00065       QString name;
00066       QString label;
00067       QString whatsThis;
00068     };
00069 
00070     CfgEntry( const QString &group, const QString &type, const QString &key,
00071               const QString &name, const QString &label,
00072               const QString &whatsThis, const QString &code,
00073               const QString &defaultValue, const QValueList<Choice> &choices,
00074               bool hidden )
00075       : mGroup( group ), mType( type ), mKey( key ), mName( name ),
00076         mLabel( label ), mWhatsThis( whatsThis ), mCode( code ),
00077         mDefaultValue( defaultValue ),
00078         mChoices( choices ), mHidden( hidden )
00079     {
00080     }
00081 
00082     void setGroup( const QString &group ) { mGroup = group; }
00083     QString group() const { return mGroup; }
00084 
00085     void setType( const QString &type ) { mType = type; }
00086     QString type() const { return mType; }
00087 
00088     void setKey( const QString &key ) { mKey = key; }
00089     QString key() const { return mKey; }
00090 
00091     void setName( const QString &name ) { mName = name; }
00092     QString name() const { return mName; }
00093 
00094     void setLabel( const QString &label ) { mLabel = label; }
00095     QString label() const { return mLabel; }
00096 
00097     void setWhatsThis( const QString &whatsThis ) { mWhatsThis = whatsThis; }
00098     QString whatsThis() const { return mWhatsThis; }
00099 
00100     void setDefaultValue( const QString &d ) { mDefaultValue = d; }
00101     QString defaultValue() const { return mDefaultValue; }
00102 
00103     void setCode( const QString &d ) { mCode = d; }
00104     QString code() const { return mCode; }
00105 
00106     void setMinValue( const QString &d ) { mMin = d; }
00107     QString minValue() const { return mMin; }
00108 
00109     void setMaxValue( const QString &d ) { mMax = d; }
00110     QString maxValue() const { return mMax; }
00111 
00112     void setParam( const QString &d ) { mParam = d; }
00113     QString param() const { return mParam; }
00114 
00115     void setParamName( const QString &d ) { mParamName = d; }
00116     QString paramName() const { return mParamName; }
00117 
00118     void setParamType( const QString &d ) { mParamType = d; }
00119     QString paramType() const { return mParamType; }
00120 
00121     void setChoices( const QValueList<Choice> &d ) { mChoices = d; }
00122     QValueList<Choice> choices() const { return mChoices; }
00123 
00124     void setParamValues( const QStringList &d ) { mParamValues = d; }
00125     QStringList paramValues() const { return mParamValues; }
00126 
00127     void setParamDefaultValues( const QStringList &d ) { mParamDefaultValues = d; }
00128     QString paramDefaultValue(int i) const { return mParamDefaultValues[i]; }
00129 
00130     void setParamMax( int d ) { mParamMax = d; }
00131     int paramMax() const { return mParamMax; }
00132 
00133     bool hidden() const { return mHidden; }
00134 
00135     void dump() const
00136     {
00137       kdDebug() << "<entry>" << endl;
00138       kdDebug() << "  group: " << mGroup << endl;
00139       kdDebug() << "  type: " << mType << endl;
00140       kdDebug() << "  key: " << mKey << endl;
00141       kdDebug() << "  name: " << mName << endl;
00142       kdDebug() << "  label: " << mLabel << endl;
00143 // whatsthis
00144       kdDebug() << "  code: " << mCode << endl;
00145 //      kdDebug() << "  values: " << mValues.join(":") << endl;
00146 
00147       if (!param().isEmpty())
00148       {
00149         kdDebug() << "  param name: "<< mParamName << endl;
00150         kdDebug() << "  param type: "<< mParamType << endl;
00151         kdDebug() << "  paramvalues: " << mParamValues.join(":") << endl;
00152       }
00153       kdDebug() << "  default: " << mDefaultValue << endl;
00154       kdDebug() << "  hidden: " << mHidden << endl;
00155       kdDebug() << "  min: " << mMin << endl;
00156       kdDebug() << "  max: " << mMax << endl;
00157       kdDebug() << "</entry>" << endl;
00158     }
00159 
00160   private:
00161     QString mGroup;
00162     QString mType;
00163     QString mKey;
00164     QString mName;
00165     QString mLabel;
00166     QString mWhatsThis;
00167     QString mCode;
00168     QString mDefaultValue;
00169     QString mParam;
00170     QString mParamName;
00171     QString mParamType;
00172     QValueList<Choice> mChoices;
00173     QStringList mParamValues;
00174     QStringList mParamDefaultValues;
00175     int mParamMax;
00176     bool mHidden;
00177     QString mMin;
00178     QString mMax;
00179 };
00180 
00181 class Param {
00182 public:
00183   QString name;
00184   QString type;
00185 };
00186 
00187 // returns the name of an member variable
00188 // use itemPath to know the full path
00189 // like using d-> in case of dpointer
00190 static QString varName(const QString &n)
00191 {
00192   QString result;
00193   if ( !dpointer ) {
00194     result = "m"+n;
00195     result[1] = result[1].upper();
00196   }
00197   else {
00198     result = n;
00199     result[0] = result[0].lower();
00200   }
00201   return result;
00202 }
00203 
00204 static QString varPath(const QString &n)
00205 {
00206   QString result;
00207   if ( dpointer ) {
00208     result = "d->"+varName(n);
00209   }
00210   else {
00211     result = varName(n);
00212   }
00213   return result;
00214 }
00215 
00216 static QString enumName(const QString &n)
00217 {
00218   QString result = "Enum"+n;
00219   result[4] = result[4].upper();
00220   return result;
00221 }
00222 
00223 static QString setFunction(const QString &n, const QString &className = QString::null)
00224 {
00225   QString result = "set"+n;
00226   result[3] = result[3].upper();
00227 
00228   if ( !className.isEmpty() )
00229     result = className + "::" + result;
00230   return result;
00231 }
00232 
00233 
00234 static QString getFunction(const QString &n, const QString &className = QString::null)
00235 {
00236   QString result = n;
00237   result[0] = result[0].lower();
00238 
00239   if ( !className.isEmpty() )
00240     result = className + "::" + result;
00241   return result;
00242 }
00243 
00244 
00245 static void addQuotes( QString &s )
00246 {
00247   if ( s.left( 1 ) != "\"" ) s.prepend( "\"" );
00248   if ( s.right( 1 ) != "\"" ) s.append( "\"" );
00249 }
00250 
00251 static QString quoteString( const QString &s )
00252 {
00253   QString r = s;
00254   r.replace( "\\", "\\\\" );
00255   r.replace( "\"", "\\\"" );
00256   r.replace( "\r", "" );
00257   r.replace( "\n", "\\n\"\n\"" );
00258   return "\"" + r + "\"";
00259 }
00260 
00261 static QString literalString( const QString &s )
00262 {
00263   bool isAscii = true;
00264   for(int i = s.length(); i--;)
00265      if (s[i].unicode() > 127) isAscii = false;
00266 
00267   if (isAscii)
00268      return "QString::fromLatin1( " + quoteString(s) + " )";
00269   else
00270      return "QString::fromUtf8( " + quoteString(s) + " )";
00271 }
00272 
00273 static QString dumpNode(const QDomNode &node)
00274 {
00275   QString msg;
00276   QTextStream s(&msg, IO_WriteOnly );
00277   node.save(s, 0);
00278 
00279   msg = msg.simplifyWhiteSpace();
00280   if (msg.length() > 40)
00281     return msg.left(37)+"...";
00282   return msg;
00283 }
00284 
00285 static QString filenameOnly(QString path)
00286 {
00287    int i = path.findRev('/');
00288    if (i >= 0)
00289       return path.mid(i+1);
00290    return path;
00291 }
00292 
00293 static void preProcessDefault( QString &defaultValue, const QString &name,
00294                                const QString &type,
00295                                const QValueList<CfgEntry::Choice> &choices,
00296                                QString &code )
00297 {
00298     if ( type == "String" && !defaultValue.isEmpty() ) {
00299       defaultValue = literalString(defaultValue);
00300 
00301     } else if ( type == "Path" && !defaultValue.isEmpty() ) {
00302       defaultValue = literalString( defaultValue );
00303 
00304     } else if ( (type == "StringList" || type == "PathList") && !defaultValue.isEmpty() ) {
00305       QTextStream cpp( &code, IO_WriteOnly | IO_Append );
00306       if (!code.isEmpty())
00307          cpp << endl;
00308 
00309       cpp << "  QStringList default" << name << ";" << endl;
00310       QStringList defaults = QStringList::split( ",", defaultValue );
00311       QStringList::ConstIterator it;
00312       for( it = defaults.begin(); it != defaults.end(); ++it ) {
00313         cpp << "  default" << name << ".append( QString::fromUtf8( \"" << *it << "\" ) );"
00314             << endl;
00315       }
00316       defaultValue = "default" + name;
00317 
00318     } else if ( type == "Color" && !defaultValue.isEmpty() ) {
00319       QRegExp colorRe("\\d+,\\s*\\d+,\\s*\\d+");
00320       if (colorRe.exactMatch(defaultValue))
00321       {
00322         defaultValue = "QColor( " + defaultValue + " )";
00323       }
00324       else
00325       {
00326         defaultValue = "QColor( \"" + defaultValue + "\" )";
00327       }
00328 
00329     } else if ( type == "Enum" ) {
00330       if ( !globalEnums ) {
00331         QValueList<CfgEntry::Choice>::ConstIterator it;
00332         for( it = choices.begin(); it != choices.end(); ++it ) {
00333           if ( (*it).name == defaultValue ) {
00334             defaultValue.prepend( enumName(name) + "::");
00335             break;
00336           }
00337         }
00338       }
00339 
00340     } else if ( type == "IntList" ) {
00341       QTextStream cpp( &code, IO_WriteOnly | IO_Append );
00342       if (!code.isEmpty())
00343          cpp << endl;
00344 
00345       cpp << "  QValueList<int> default" << name << ";" << endl;
00346       QStringList defaults = QStringList::split( ",", defaultValue );
00347       QStringList::ConstIterator it;
00348       for( it = defaults.begin(); it != defaults.end(); ++it ) {
00349         cpp << "  default" << name << ".append( " << *it << " );"
00350             << endl;
00351       }
00352       defaultValue = "default" + name;
00353     }
00354 }
00355 
00356 
00357 CfgEntry *parseEntry( const QString &group, const QDomElement &element )
00358 {
00359   bool defaultCode = false;
00360   QString type = element.attribute( "type" );
00361   QString name = element.attribute( "name" );
00362   QString key = element.attribute( "key" );
00363   QString hidden = element.attribute( "hidden" );
00364   QString label;
00365   QString whatsThis;
00366   QString defaultValue;
00367   QString code;
00368   QString param;
00369   QString paramName;
00370   QString paramType;
00371   QValueList<CfgEntry::Choice> choices;
00372   QStringList paramValues;
00373   QStringList paramDefaultValues;
00374   QString minValue;
00375   QString maxValue;
00376   int paramMax = 0;
00377 
00378   QDomNode n;
00379   for ( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
00380     QDomElement e = n.toElement();
00381     QString tag = e.tagName();
00382     if ( tag == "label" ) label = e.text();
00383     else if ( tag == "whatsthis" ) whatsThis = e.text();
00384     else if ( tag == "min" ) minValue = e.text();
00385     else if ( tag == "max" ) maxValue = e.text();
00386     else if ( tag == "code" ) code = e.text();
00387     else if ( tag == "parameter" )
00388     {
00389       param = e.attribute( "name" );
00390       paramType = e.attribute( "type" );
00391       if ( param.isEmpty() ) {
00392         kdError() << "Parameter must have a name: " << dumpNode(e) << endl;
00393         return 0;
00394       }
00395       if ( paramType.isEmpty() ) {
00396         kdError() << "Parameter must have a type: " << dumpNode(e) << endl;
00397         return 0;
00398       }
00399       if ((paramType == "Int") || (paramType == "UInt"))
00400       {
00401          bool ok;
00402          paramMax = e.attribute("max").toInt(&ok);
00403          if (!ok)
00404          {
00405            kdError() << "Integer parameter must have a maximum (e.g. max=\"0\"): " << dumpNode(e) << endl;
00406            return 0;
00407          }
00408       }
00409       else if (paramType == "Enum")
00410       {
00411          QDomNode n2;
00412          for ( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) {
00413            QDomElement e2 = n2.toElement();
00414            if (e2.tagName() == "values")
00415            {
00416              QDomNode n3;
00417              for ( n3 = e2.firstChild(); !n3.isNull(); n3 = n3.nextSibling() ) {
00418                QDomElement e3 = n3.toElement();
00419                if (e3.tagName() == "value")
00420                {
00421                   paramValues.append( e3.text() );
00422                }
00423              }
00424              break;
00425            }
00426          }
00427          if (paramValues.isEmpty())
00428          {
00429            kdError() << "No values specified for parameter '" << param << "'." << endl;
00430            return 0;
00431          }
00432          paramMax = paramValues.count()-1;
00433       }
00434       else
00435       {
00436         kdError() << "Parameter '" << param << "' has type " << paramType << " but must be of type int, uint or Enum." << endl;
00437         return 0;
00438       }
00439     }
00440     else if ( tag == "default" )
00441     {
00442       if (e.attribute("param").isEmpty())
00443       {
00444         defaultValue = e.text();
00445         if (e.attribute( "code" ) == "true")
00446           defaultCode = true;
00447       }
00448     }
00449     else if ( tag == "choices" ) {
00450       QDomNode n2;
00451       for( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) {
00452         QDomElement e2 = n2.toElement();
00453         if ( e2.tagName() == "choice" ) {
00454           QDomNode n3;
00455           CfgEntry::Choice choice;
00456           choice.name = e2.attribute( "name" );
00457           if ( choice.name.isEmpty() ) {
00458             kdError() << "Tag <choice> requires attribute 'name'." << endl;
00459           }
00460           for( n3 = e2.firstChild(); !n3.isNull(); n3 = n3.nextSibling() ) {
00461             QDomElement e3 = n3.toElement();
00462             if ( e3.tagName() == "label" ) choice.label = e3.text();
00463             if ( e3.tagName() == "whatsthis" ) choice.whatsThis = e3.text();
00464           }
00465           choices.append( choice );
00466         }
00467       }
00468     }
00469   }
00470 
00471   bool nameIsEmpty = name.isEmpty();
00472   if ( nameIsEmpty && key.isEmpty() ) {
00473     kdError() << "Entry must have a name or a key: " << dumpNode(element) << endl;
00474     return 0;
00475   }
00476 
00477   if ( key.isEmpty() ) {
00478     key = name;
00479   }
00480 
00481   if ( nameIsEmpty ) {
00482     name = key;
00483     name.replace( " ", QString::null );
00484   } else if ( name.contains( ' ' ) ) {
00485     kdWarning()<<"Entry '"<<name<<"' contains spaces! <name> elements can't contain speces!"<<endl;
00486     name.remove( ' ' );
00487   }
00488 
00489   if (name.contains("$("))
00490   {
00491     if (param.isEmpty())
00492     {
00493       kdError() << "Name may not be parameterized: " << name << endl;
00494       return 0;
00495     }
00496   }
00497   else
00498   {
00499     if (!param.isEmpty())
00500     {
00501       kdError() << "Name must contain '$(" << param << ")': " << name << endl;
00502       return 0;
00503     }
00504   }
00505 
00506   if ( label.isEmpty() ) {
00507     label = key;
00508   }
00509 
00510   if ( type.isEmpty() ) type = "String"; // XXX : implicit type might be bad
00511 
00512   if (!param.isEmpty())
00513   {
00514     // Adjust name
00515     paramName = name;
00516     name.replace("$("+param+")", QString::null);
00517     // Lookup defaults for indexed entries
00518     for(int i = 0; i <= paramMax; i++)
00519     {
00520       paramDefaultValues.append(QString::null);
00521     }
00522 
00523     QDomNode n;
00524     for ( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
00525       QDomElement e = n.toElement();
00526       QString tag = e.tagName();
00527       if ( tag == "default" )
00528       {
00529         QString index = e.attribute("param");
00530         if (index.isEmpty())
00531            continue;
00532 
00533         bool ok;
00534         int i = index.toInt(&ok);
00535         if (!ok)
00536         {
00537           i = paramValues.findIndex(index);
00538           if (i == -1)
00539           {
00540             kdError() << "Index '" << index << "' for default value is unknown." << endl;
00541             return 0;
00542           }
00543         }
00544 
00545         if ((i < 0) || (i > paramMax))
00546         {
00547           kdError() << "Index '" << i << "' for default value is out of range [0, "<< paramMax<<"]." << endl;
00548           return 0;
00549         }
00550 
00551         QString tmpDefaultValue = e.text();
00552 
00553         if (e.attribute( "code" ) != "true")
00554            preProcessDefault(tmpDefaultValue, name, type, choices, code);
00555 
00556         paramDefaultValues[i] = tmpDefaultValue;
00557       }
00558     }
00559   }
00560 
00561   if (!validNameRegexp->exactMatch(name))
00562   {
00563     if (nameIsEmpty)
00564       kdError() << "The key '" << key << "' can not be used as name for the entry because "
00565                    "it is not a valid name. You need to specify a valid name for this entry." << endl;
00566     else
00567       kdError() << "The name '" << name << "' is not a valid name for an entry." << endl;
00568     return 0;
00569   }
00570 
00571   if (allNames.contains(name))
00572   {
00573     if (nameIsEmpty)
00574       kdError() << "The key '" << key << "' can not be used as name for the entry because "
00575                    "it does not result in a unique name. You need to specify a unique name for this entry." << endl;
00576     else
00577       kdError() << "The name '" << name << "' is not unique." << endl;
00578     return 0;
00579   }
00580   allNames.append(name);
00581 
00582   if (!defaultCode)
00583   {
00584     preProcessDefault(defaultValue, name, type, choices, code);
00585   }
00586 
00587   CfgEntry *result = new CfgEntry( group, type, key, name, label, whatsThis,
00588                                    code, defaultValue, choices,
00589                                    hidden == "true" );
00590   if (!param.isEmpty())
00591   {
00592     result->setParam(param);
00593     result->setParamName(paramName);
00594     result->setParamType(paramType);
00595     result->setParamValues(paramValues);
00596     result->setParamDefaultValues(paramDefaultValues);
00597     result->setParamMax(paramMax);
00598   }
00599   result->setMinValue(minValue);
00600   result->setMaxValue(maxValue);
00601 
00602   return result;
00603 }
00604 
00608 QString param( const QString &type )
00609 {
00610     if ( type == "String" )           return "const QString &";
00611     else if ( type == "StringList" )  return "const QStringList &";
00612     else if ( type == "Font" )        return "const QFont &";
00613     else if ( type == "Rect" )        return "const QRect &";
00614     else if ( type == "Size" )        return "const QSize &";
00615     else if ( type == "Color" )       return "const QColor &";
00616     else if ( type == "Point" )       return "const QPoint &";
00617     else if ( type == "Int" )         return "int";
00618     else if ( type == "UInt" )        return "uint";
00619     else if ( type == "Bool" )        return "bool";
00620     else if ( type == "Double" )      return "double";
00621     else if ( type == "DateTime" )    return "const QDateTime &";
00622     else if ( type == "Int64" )       return "Q_INT64";
00623     else if ( type == "UInt64" )      return "Q_UINT64";
00624     else if ( type == "IntList" )     return "const QValueList<int> &";
00625     else if ( type == "Enum" )        return "int";
00626     else if ( type == "Path" )        return "const QString &";
00627     else if ( type == "PathList" )    return "const QStringList &";
00628     else if ( type == "Password" )    return "const QString &";
00629     else {
00630         kdError() <<"kconfig_compiler does not support type \""<< type <<"\""<<endl;
00631         return "QString"; //For now, but an assert would be better
00632     }
00633 }
00634 
00638 QString cppType( const QString &type )
00639 {
00640     if ( type == "String" )           return "QString";
00641     else if ( type == "StringList" )  return "QStringList";
00642     else if ( type == "Font" )        return "QFont";
00643     else if ( type == "Rect" )        return "QRect";
00644     else if ( type == "Size" )        return "QSize";
00645     else if ( type == "Color" )       return "QColor";
00646     else if ( type == "Point" )       return "QPoint";
00647     else if ( type == "Int" )         return "int";
00648     else if ( type == "UInt" )        return "uint";
00649     else if ( type == "Bool" )        return "bool";
00650     else if ( type == "Double" )      return "double";
00651     else if ( type == "DateTime" )    return "QDateTime";
00652     else if ( type == "Int64" )       return "Q_INT64";
00653     else if ( type == "UInt64" )      return "Q_UINT64";
00654     else if ( type == "IntList" )     return "QValueList<int>";
00655     else if ( type == "Enum" )        return "int";
00656     else if ( type == "Path" )        return "QString";
00657     else if ( type == "PathList" )    return "QStringList";
00658     else if ( type == "Password" )    return "QString";
00659     else {
00660         kdError()<<"kconfig_compiler does not support type \""<< type <<"\""<<endl;
00661         return "QString"; //For now, but an assert would be better
00662     }
00663 }
00664 
00665 QString defaultValue( const QString &type )
00666 {
00667     if ( type == "String" )           return "\"\""; // Use empty string, not null string!
00668     else if ( type == "StringList" )  return "QStringList()";
00669     else if ( type == "Font" )        return "KGlobalSettings::generalFont()";
00670     else if ( type == "Rect" )        return "QRect()";
00671     else if ( type == "Size" )        return "QSize()";
00672     else if ( type == "Color" )       return "QColor(128, 128, 128)";
00673     else if ( type == "Point" )       return "QPoint()";
00674     else if ( type == "Int" )         return "0";
00675     else if ( type == "UInt" )        return "0";
00676     else if ( type == "Bool" )        return "false";
00677     else if ( type == "Double" )      return "0.0";
00678     else if ( type == "DateTime" )    return "QDateTime()";
00679     else if ( type == "Int64" )       return "0";
00680     else if ( type == "UInt64" )      return "0";
00681     else if ( type == "IntList" )     return "QValueList<int>()";
00682     else if ( type == "Enum" )        return "0";
00683     else if ( type == "Path" )        return "\"\""; // Use empty string, not null string!
00684     else if ( type == "PathList" )    return "QStringList()";
00685     else if ( type == "Password" )    return "\"\""; // Use empty string, not null string!
00686     else {
00687         kdWarning()<<"Error, kconfig_compiler doesn't support the \""<< type <<"\" type!"<<endl;
00688         return "QString"; //For now, but an assert would be better
00689     }
00690 }
00691 
00692 QString itemType( const QString &type )
00693 {
00694   QString t;
00695 
00696   t = type;
00697   t.replace( 0, 1, t.left( 1 ).upper() );
00698 
00699   return t;
00700 }
00701 
00702 static QString itemDeclaration(const CfgEntry *e)
00703 {
00704   if (itemAccessors)
00705      return QString::null;
00706 
00707   QString fCap = e->name();
00708   fCap[0] = fCap[0].upper();
00709   return "  KConfigSkeleton::Item"+itemType( e->type() ) +
00710          "  *item" + fCap +
00711          ( (!e->param().isEmpty())?(QString("[%1]").arg(e->paramMax()+1)) : QString::null) +
00712          ";\n";
00713 }
00714 
00715 // returns the name of an item variable
00716 // use itemPath to know the full path
00717 // like using d-> in case of dpointer
00718 static QString itemVar(const CfgEntry *e)
00719 {
00720   QString result;
00721   if (itemAccessors)
00722   {
00723     if ( !dpointer )  
00724     {
00725       result = "m" + e->name() + "Item";
00726       result[1] = result[1].upper();
00727     }
00728     else
00729     {
00730       result = e->name() + "Item";
00731       result[0] = result[0].lower();
00732     }
00733   }
00734   else
00735   {
00736     result = "item" + e->name();
00737     result[4] = result[4].upper();
00738   }
00739   return result;
00740 }
00741 
00742 static QString itemPath(const CfgEntry *e)
00743 {
00744   QString result;
00745   if ( dpointer ) {
00746     result = "d->"+itemVar(e);
00747   }
00748   else {
00749     result = itemVar(e);
00750   }
00751   return result;
00752 }
00753 
00754 QString newItem( const QString &type, const QString &name, const QString &key,
00755                  const QString &defaultValue, const QString &param = QString::null)
00756 {
00757   QString t = "new KConfigSkeleton::Item" + itemType( type ) +
00758               "( currentGroup(), " + key + ", " + varPath( name ) + param;
00759   if ( type == "Enum" ) t += ", values" + name;
00760   if ( !defaultValue.isEmpty() ) {
00761     t += ", ";
00762     if ( type == "String" ) t += defaultValue;
00763     else t+= defaultValue;
00764   }
00765   t += " );";
00766 
00767   return t;
00768 }
00769 
00770 QString paramString(const QString &s, const CfgEntry *e, int i)
00771 {
00772   QString result = s;
00773   QString needle = "$("+e->param()+")";
00774   if (result.contains(needle))
00775   {
00776     QString tmp;
00777     if (e->paramType() == "Enum")
00778     {
00779       tmp = e->paramValues()[i];
00780     }
00781     else
00782     {
00783       tmp = QString::number(i);
00784     }
00785 
00786     result.replace(needle, tmp);
00787   }
00788   return result;
00789 }
00790 
00791 QString paramString(const QString &group, const QValueList<Param> &parameters)
00792 {
00793   QString paramString = group;
00794   QString arguments;
00795   int i = 1;
00796   for (QValueList<Param>::ConstIterator it = parameters.begin();
00797        it != parameters.end(); ++it)
00798   {
00799      if (paramString.contains("$("+(*it).name+")"))
00800      {
00801        QString tmp;
00802        tmp.sprintf("%%%d", i++);
00803        paramString.replace("$("+(*it).name+")", tmp);
00804        arguments += ".arg( mParam"+(*it).name+" )";
00805      }
00806   }
00807   if (arguments.isEmpty())
00808     return "QString::fromLatin1( \""+group+"\" )";
00809 
00810   return "QString::fromLatin1( \""+paramString+"\" )"+arguments;
00811 }
00812 
00813 /* int i is the value of the parameter */
00814 QString userTextsFunctions( CfgEntry *e, QString itemVarStr=QString::null, QString i=QString::null )
00815 {
00816   QString txt;
00817   if (itemVarStr.isNull()) itemVarStr=itemPath(e);
00818   if ( !e->label().isEmpty() ) {
00819     txt += "  " + itemVarStr + "->setLabel( i18n(";
00820     if ( !e->param().isEmpty() )
00821       txt += quoteString(e->label().replace("$("+e->param()+")", i));
00822     else
00823       txt+= quoteString(e->label());
00824     txt+= ") );\n";
00825   }
00826   if ( !e->whatsThis().isEmpty() ) {
00827     txt += "  " + itemVarStr + "->setWhatsThis( i18n(";
00828     if ( !e->param().isEmpty() )
00829       txt += quoteString(e->whatsThis().replace("$("+e->param()+")", i));
00830     else
00831       txt+= quoteString(e->whatsThis());
00832     txt+=") );\n";
00833   }
00834   return txt;
00835 }
00836 
00837 // returns the member accesor implementation
00838 // which should go in the h file if inline
00839 // or the cpp file if not inline
00840 QString memberAccessorBody( CfgEntry *e )
00841 {    
00842     QString result;
00843     QTextStream out(&result, IO_WriteOnly);
00844     QString n = e->name();
00845     QString t = e->type();
00846 
00847     out << "return " << This << varPath(n);
00848     if (!e->param().isEmpty()) out << "[i]";
00849     out << ";" << endl;
00850    
00851     return result;
00852 }
00853 
00854 // returns the member mutator implementation
00855 // which should go in the h file if inline
00856 // or the cpp file if not inline
00857 QString memberMutatorBody( CfgEntry *e )
00858 {
00859   QString result;
00860   QTextStream out(&result, IO_WriteOnly);
00861   QString n = e->name();
00862   QString t = e->type();
00863 
00864   if (!e->minValue().isEmpty())
00865   {
00866     out << "if (v < " << e->minValue() << ")" << endl;
00867     out << "{" << endl;
00868     out << "  kdDebug() << \"" << setFunction(n);
00869     out << ": value \" << v << \" is less than the minimum value of ";
00870     out << e->minValue()<< "\" << endl;" << endl;
00871     out << "  v = " << e->minValue() << ";" << endl;
00872     out << "}" << endl;
00873   }
00874   
00875   if (!e->maxValue().isEmpty())
00876   {
00877     out << endl << "if (v > " << e->maxValue() << ")" << endl;
00878     out << "{" << endl;
00879     out << "  kdDebug() << \"" << setFunction(n);
00880     out << ": value \" << v << \" is greater than the maximum value of ";
00881     out << e->maxValue()<< "\" << endl;" << endl;
00882     out << "  v = " << e->maxValue() << ";" << endl;
00883     out << "}" << endl << endl;
00884   }
00885 
00886   out << "if (!" << This << "isImmutable( QString::fromLatin1( \"";
00887   if (!e->param().isEmpty())
00888   {
00889     out << e->paramName().replace("$("+e->param()+")", "%1") << "\" ).arg( ";
00890     if ( e->paramType() == "Enum" ) {
00891       out << "QString::fromLatin1( ";
00892 
00893       if (globalEnums)
00894         out << enumName(e->param()) << "ToString[i]";
00895       else
00896         out << enumName(e->param()) << "::enumToString[i]";
00897         
00898         out << " )";
00899     }
00900     else
00901     {
00902       out << "i";
00903     }
00904     out << " )";
00905   }
00906   else
00907   {
00908     out << n << "\" )";
00909   }
00910   out << " ))" << endl;
00911   out << "  " << This << varPath(n);
00912   if (!e->param().isEmpty())
00913     out << "[i]";
00914   out << " = v;" << endl;    
00915 
00916   return result;
00917 }
00918 
00919 // returns the item accesor implementation
00920 // which should go in the h file if inline
00921 // or the cpp file if not inline
00922 QString itemAccessorBody( CfgEntry *e )
00923 {    
00924     QString result;
00925     QTextStream out(&result, IO_WriteOnly);
00926 
00927     out << "return " << itemPath(e);
00928     if (!e->param().isEmpty()) out << "[i]";
00929     out << ";" << endl;
00930 
00931     return result;
00932 }
00933 
00934 //indents text adding X spaces per line
00935 QString indent(QString text, int spaces)
00936 {    
00937     QString result;
00938     QTextStream out(&result, IO_WriteOnly);
00939     QTextStream in(&text, IO_ReadOnly);
00940     QString currLine;
00941     while ( !in.atEnd() )
00942     {
00943       currLine = in.readLine();
00944       if (!currLine.isEmpty())
00945         for (int i=0; i < spaces; i++)
00946           out << " ";
00947       out << currLine << endl;
00948     }
00949     return result;
00950 }
00951 
00952 
00953 int main( int argc, char **argv )
00954 {
00955   KAboutData aboutData( "kconfig_compiler", I18N_NOOP("KDE .kcfg compiler"), "0.3",
00956     I18N_NOOP("KConfig Compiler") , KAboutData::License_LGPL );
00957   aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" );
00958   aboutData.addAuthor( "Waldo Bastian", 0, "bastian@kde.org" );
00959   aboutData.addAuthor( "Zack Rusin", 0, "zack@kde.org" );
00960   aboutData.addCredit( "Reinhold Kainhofer", "Fix for parametrized entries",
00961       "reinhold@kainhofer.com", "http://reinhold.kainhofer.com" );
00962   aboutData.addCredit( "Duncan Mac-Vicar P.", "dpointer support",
00963       "duncan@kde.org", "http://www.mac-vicar.com/~duncan" );
00964 
00965   KCmdLineArgs::init( argc, argv, &aboutData );
00966   KCmdLineArgs::addCmdLineOptions( options );
00967 
00968   KInstance app( &aboutData );
00969 
00970   KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00971 
00972   if ( args->