Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members  

ConfigFile.cpp

00001 //  ConfigFile - interface to configuration data
00002 //  Copyright (C) 2000 Stephan Beal & Rusty Ballinger
00003 //                     stephan@wanderinghorse.net & rusty@sgi.com
00004 //
00005 //  This program is free software; you can redistribute it and/or
00006 //  modify it under the terms of the GNU General Public License
00007 //  as published by the Free Software Foundation; either version 2
00008 //  of the License, or (at your option) any later version.
00009 //
00010 //  This program is distributed in the hope that it will be useful,
00011 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 //  GNU General Public License for more details.
00014 //
00015 //  You should have received a copy of the GNU General Public License
00016 //  along with this program; if not, write to the Free Software
00017 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00018 
00019 #include <qcolor.h>
00020 #include <qregexp.h>
00021 #include <qdict.h>
00022 #include <qdir.h>
00023 #include <qvaluelist.h>
00024 #include <iostream>
00025 #include <qregexp.h>
00026 #include <fun/SerialTree.h>
00027 #include <fun/Serializer.h>
00028 #include <fun/Deserializer.h>
00029 #include <fun/ConfigFile.h>
00030 //#include <fun/Serializer.h>
00031 //#include <fun/Deserializer.h>
00032 
00033 
00034 namespace fun {
00035         INSTANTIATOR(ConfigFile)
00036 
00037 
00038                 ConfigFile ConfigFile::funconfig;
00039 
00040         ConfigFile::ConfigFile( const QString &filename ) : PropertyList()
00041         {
00042                 setFilename( filename );
00043                 //CERR << hex << this << " instantiated! ["<<this->filename<<"]" << endl;
00044                 //  if(boolTable.isEmpty()) initBoolTable();
00045         }
00046 
00047         ConfigFile::~ConfigFile()
00048         {
00049                 //CERR << hex << this << " dying! ["<<this->filename<<"]" << endl;
00050                 saveToXML();
00051         }
00052 
00053         bool
00054         ConfigFile::saveToText( std::ostream &os) const
00055         {
00056                 NOT_DONE("saveToText() should be better about wrapping long lines... what it does is unsafe!");
00057                 QString ts;
00058                 QRegExp trailingNewline("\\n$");
00059                 QRegExp newline("\\n");
00060                 QRegExp tab("\\t");
00061                 for(QValueList<QString>::ConstIterator it = order.begin(); it != order.end(); ++it)
00062                 {
00063                         Entry *e = data.find(*it);
00064                         //XXX assert(e)?
00065                         if(!e->cmnt.isEmpty())
00066                         {
00067                                 ts = e->cmnt;
00068                                 ts.replace(trailingNewline, "");  //  crude.
00069                                 ts.replace(newline, "\n#  ");
00070                                 os << "#  " << ts << endl;
00071                         }
00072                         ts = e->val;
00073                         //      ts.replace(newline, "\\n");
00074                         //cout << "before replace, ts == \"" << ts << "\"" << endl;
00075                         ts.replace(newline, "\\n\\\n");  // technically unsafe as it could put
00076                         //cout << "  after,        ts == \"" << ts << "\"" << endl;
00077                         ts.replace(tab, "\\t");          // embedded #'s at the start of line.
00078                         //cerr << *it << " = " << ts << endl;
00079                         os << *it << " = " << ts << endl;
00080                 }
00081                 return true;
00082         }
00083 
00084         bool
00085         ConfigFile::loadFromText( std::istream &is)
00086         {
00087                 //debug( "========== loading..." );
00088                 //  stomp our current contents.
00089                 data.clear();
00090                 order.clear();
00091                 setNeedsSave( false );
00092 
00093                 char line[1000];  //  no lines longer than that, ok?
00094                 //  QRegExp continuedLine("(^\\\\|[^\\\\]\\\\)$");  //  not quite right, as
00095                 QRegExp continuedLine("\\\\$");                 //  not quite right, as
00096                 QRegExp trailingSlash("\\\\$");                 //  \\\ shouldn't continue
00097                 QRegExp noNonSpace("^\\s*$");
00098                 QRegExp leadingComment("^# ? ?");
00099                 QRegExp nonFirstLineComment("\\n# ? ?");  // horrible
00100                 QRegExp slashT("\\\\t");      //  none of these are quite right, as we
00101                 QRegExp slashN("\\\\n");      //  don't want \\t and \\\\t to match.
00102                 QString buf;
00103                 QString cbuf;  //  preceding comment buffer
00104                 QString key, val;
00105                 int len;
00106                 int lno = 0;
00107                 bool inval = false;
00108                 while(++lno)  //  first line below exits loop.
00109                 {
00110                         if(!is.getline(line, sizeof(line)).good()) break;
00111                         //cout << "read \"" << line << "\"" << endl;
00112                         if(line[0] == '#')
00113                         {
00114                                 if(buf.isEmpty())
00115                                 {
00116                                         cbuf += line;  //  otherwise this is in a multi-
00117                                         cbuf += "\n";  //  line value, and gets tossed.
00118                                 }
00119                                 //  Otherwise this comment is embedded in a multi-line value; we
00120                                 //  don't really have any place to put it.
00121                                 continue;
00122                         }
00123                         len = strlen(line);
00124                         buf += line;
00125                         //cout << "  concat, buf now \"" << buf << "\"" << endl;
00126                         if(buf.contains(continuedLine))
00127                         {
00128                                 buf.replace(trailingSlash, "");
00129                                 //cout << "  continued, buf now \"" << buf << "\"" << endl;
00130                                 continue;
00131                         }
00132                         if(buf.isEmpty() || buf.contains(noNonSpace))
00133                         {
00134                                 buf = cbuf = "";
00135                                 continue;
00136                         }
00137                         //  Otherwise, we're still here, so buf contains a complete entry,
00138                         //  and we'll try to split it into key & value, replace escaped
00139                         //  chars, etc.
00140                         int epos = buf.find("=");
00141                         if(epos == -1)
00142                         {
00143                                 NOT_DONE("loadFromText doesn't handle missing \"=\" in entry ending on line " << dec << lno);
00144                                 return false;
00145                         }
00146                         key = buf.left(epos).stripWhiteSpace();
00147                         val = buf.right(buf.length() - epos - 1).stripWhiteSpace();
00148                         //cout << "  key == \"" << key << "\", val == \"" << val << "\"" << endl;
00149                         if(key.isEmpty())
00150                         {
00151                                 NOT_DONE("loadFromText doesn't handle null key in entry ending on line " << dec << lno);
00152                                 return false;
00153                         }
00154                         val.replace(slashT, "\t");
00155                         val.replace(slashN, "\n");
00156                         //cout << "  after replace, val == \"" << val << "\"" << endl;
00157                         set(key, val);
00158                         if(!cbuf.isEmpty())
00159                         {
00160                                 cbuf.replace(leadingComment, "");
00161                                 cbuf.replace(nonFirstLineComment, "\n");
00162                                 setComment(key, cbuf);
00163                         }
00164                         buf = cbuf = "";
00165                 }
00166                 //debug( "========== Done reading ini file." );
00167                 return true;
00168         }
00169 
00170 
00171 
00172 
00173         bool
00174         ConfigFile::loadFromXML(const QString &fn )
00175         {
00176                 if( !fn.isEmpty() ) setFilename( fn ); // arguable, to set it before seeing if we can save there.
00177                 SerialTree *tree = SerialTree::loadFromXMLFile(filename);
00178                 if(tree == NULL)
00179                 {
00180                         //CERR << "ConfigFile::loadFromXML(" << filename.data()<<"): SerialTree == NULL" << endl;
00181                         return false;
00182                 }
00183                 ConfigFile *cf = 0;
00184                 cf = dynamic_cast<ConfigFile *>( tree->getSerializable("configfile") );
00185                 if ( ! cf ) 
00186                 {
00187                         //CERR << "ConfigFile::deserialize() could not find configfile node!" << endl;
00188                         return false;
00189                 }
00190                 //  cf->deserialize( *tree ); // ???
00191                 mergeIn( cf );
00192                 delete( cf );
00193 
00194                 return true;
00195         }
00196 
00197         void ConfigFile::deserialize(const Deserializer &ser)
00198         {
00199                 PropertyList::deserialize(ser);
00200         }
00201 
00202         bool
00203         ConfigFile::saveToXML(const QString &fn) const
00204         {
00205                 if( ! fn.isEmpty() ) this->filename = fn; // arguable, to set it before seeing if we can save there.
00206                 if( filename.isEmpty() ) return false;
00207                 //CERR << "saveToXML( "<<fn<<" == "<<filename<<" )"<<endl;
00208                 SerialTree tree;
00209                 tree.putSerializable( "configfile", *this );
00210                 return tree.saveToXMLFile( filename );
00211         }
00212 
00213         void ConfigFile::serialize(Serializer &ser) const
00214         {
00215                 PropertyList::serialize(ser);
00216                 ser.setSerializableClass("ConfigFile");
00217         }
00218 
00219 
00220         void 
00221         ConfigFile::setFilename( const QString &fn )
00222         {
00223                 //  CERR << hex << this << "["<<this->filename<< "] new filename: ["<<fn<<"]" << endl;
00224                 this->filename = fn;
00225         }
00226 
00227         QString
00228         ConfigFile::getFilename()
00229         {
00230                 return this->filename;
00231         }
00232 
00233         // static 
00234         ConfigFile &
00235         ConfigFile::funConfig()
00236         {
00237                 if( funconfig.getFilename().isEmpty() )
00238                 {
00239                         //         CERR << "funconfig starting up." << endl;
00240                         funconfig.loadFromXML( QDir::homeDirPath() + QDir::separator() + ".libfun.conf" );
00241                 }
00242                 return funconfig;
00243         }
00244 
00245         bool
00246         ConfigFile::save()
00247         {
00248                 return this->saveToXML();
00249         }
00250 
00251         bool
00252         ConfigFile::load()
00253         {
00254                 return this->loadFromXML();
00255         }
00256 
00257 }; // namespace fun

Generated on Mon Aug 11 14:06:54 2003 for libfunutil by doxygen1.2.18