Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

uniinigen.cc

Go to the documentation of this file.
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * A generator for .ini files.
00006  */
00007 #include "uniinigen.h"
00008 #include "unitempgen.h"
00009 #include "wvtclstring.h"
00010 #include "strutils.h"
00011 #include "wvfile.h"
00012 #include "wvmoniker.h"
00013 
00014 static UniConfGen *creator(WvStringParm s, IObject *, void *)
00015 {
00016     return new UniIniGen(s);
00017 }
00018 
00019 static WvMoniker<UniConfGen> reg("ini", creator);
00020 
00021 
00022 
00023 static void printsection(WvStream &file, const UniConfKey &key)
00024 {
00025     file.print("[%s]\n", wvtcl_escape(key, "\t\r\n[]"));
00026 }
00027 
00028 static void printkey(WvStream &file, const UniConfKey &key,
00029     WvStringParm value)
00030 {
00031     // need to escape []#= in key only to distinguish a key/value
00032     // pair from a section name or comment and to delimit the value
00033     file.print("%s = %s\n",
00034         wvtcl_escape(key, " \t\r\n[]=#"),
00035         wvtcl_escape(value, " \t\r\n"));
00036 }
00037 
00038 
00039 /***** UniIniGen *****/
00040 
00041 UniIniGen::UniIniGen(WvStringParm _filename, int _create_mode)
00042     : filename(_filename), create_mode(_create_mode), log(filename)
00043 {
00044     log(WvLog::Debug1, "Using IniFile \"%s\"\n", filename);
00045     // consider the generator dirty until it is first refreshed
00046     dirty = true;
00047 }
00048 
00049 
00050 UniIniGen::~UniIniGen()
00051 {
00052 }
00053 
00054 
00055 bool UniIniGen::refresh()
00056 {
00057     /** open the file **/
00058     WvFile file(filename, O_RDONLY);
00059     if (! file.isok())
00060     {
00061         log("Cannot open config file for reading: \"%s\"\n",
00062             file.errstr());
00063         file.close();
00064         return false;
00065     }
00066     
00067     /** loop over all Tcl words in the file **/
00068     UniTempGen *newgen = new UniTempGen();
00069     UniConfKey section;
00070     WvDynBuf buf;
00071     for (bool eof = false; ! eof; )
00072     {
00073         if (file.isok())
00074         {
00075             // read entire lines to ensure that we get whole values
00076             char *line = file.getline(-1);
00077             if (line)
00078                 buf.putstr(line);
00079             else
00080                 eof = true;
00081         }
00082         else
00083             eof = true;
00084 
00085         if (eof)
00086         {
00087             // detect missing newline at end of file
00088             size_t avail = buf.used();
00089             if (avail == 0)
00090                 break;
00091             if (buf.peek(avail - 1) == '\n')
00092                 break;
00093             // run the loop one more time to compensate
00094         }
00095         buf.put('\n');
00096         
00097         for (WvString word;
00098             ! (word = wvtcl_getword(buf, "\r\n", false)).isnull(); )
00099         {
00100             char *str = trim_string(word.edit());
00101             int len = strlen(str);
00102             if (len == 0)
00103             {
00104                 // we have an empty line
00105                 continue;
00106             }
00107             if (str[0] == '#')
00108             {
00109                 // we have a comment line
00110                 log(WvLog::Debug5, "Comment: \"%s\"\n", str + 1);
00111                 continue;
00112             }
00113             if (str[0] == '[' && str[len - 1] == ']')
00114             {
00115                 // we have a section name line
00116                 str[len - 1] = '\0';
00117                 WvString name(wvtcl_unescape(trim_string(str + 1)));
00118                 section = UniConfKey(name.unique());
00119                 log(WvLog::Debug5, "Refresh section: \"%s\"\n", section);
00120                 continue;
00121             }
00122             // we possibly have a key = value line
00123             WvConstStringBuffer line(word);
00124             WvString temp = wvtcl_getword(line, "=", false);
00125             if (! temp.isnull() && line.peek(-1) == '=')
00126             {
00127                 WvString name(wvtcl_unescape(trim_string(temp.edit())));
00128                 UniConfKey key(name.unique());
00129                 if (! key.isempty())
00130                 {
00131                     key.prepend(section);
00132                     temp = line.getstr();
00133                     WvString value(wvtcl_unescape(trim_string(temp.edit())));
00134                     newgen->set(key, value.unique());
00135                     //log(WvLog::Debug5, "Refresh: (\"%s\", \"%s\")\n",
00136                     //    key, value);
00137                     continue;
00138                 }
00139             }
00140             log("Ignoring malformed input line: \"%s\"\n", word);
00141         }
00142     }
00143 
00144     /** close the file **/
00145     file.close();
00146     if (file.geterr())
00147     {
00148         log("Error reading from config file: \"%s\"\n", file.errstr());
00149         delete newgen;
00150         return false;
00151     }
00152 
00153     /** handle unparsed input **/
00154     size_t avail = buf.used();
00155     while (avail > 0 && buf.peek(avail - 1) == '\n')
00156     {
00157         buf.unalloc(1); // strip off uninteresting trailing newlines
00158         avail -= 1;
00159     }
00160     if (avail > 0)
00161     {
00162         // last line must have contained junk
00163         log("XXX Ignoring malformed input line: \"%s\"\n", buf.getstr());
00164     }
00165 
00166     /** switch the trees send notifications **/
00167     hold_delta();
00168     UniConfValueTree *oldtree = root;
00169     UniConfValueTree *newtree = newgen->root;
00170     root = newtree;
00171     newgen->root = NULL;
00172     dirty = false;
00173     if (oldtree && newtree)
00174     {
00175         oldtree->compare(newtree, UniConfValueTree::Comparator
00176             (this, &UniIniGen::refreshcomparator), NULL);
00177         delete oldtree;
00178     }
00179     else
00180     {
00181         delta(UniConfKey::EMPTY, WvString::null); // REMOVED
00182     }
00183     unhold_delta();
00184 
00185     delete newgen;
00186 
00187     /** done **/
00188     return true;
00189 }
00190 
00191 
00192 // returns: true if a==b
00193 bool UniIniGen::refreshcomparator(const UniConfValueTree *a,
00194     const UniConfValueTree *b, void *userdata)
00195 {
00196     if (a)
00197     {
00198         if (b)
00199         {
00200             if (a->value() != b->value())
00201             {
00202                 // key changed
00203                 delta(b->fullkey(), b->value()); // CHANGED
00204                 return false;
00205             }
00206             return true;
00207         }
00208         else
00209         {
00210             // key removed
00211             delta(a->fullkey(), WvString::null); // REMOVED
00212             return false;
00213         }
00214     }
00215     else // a didn't exist
00216     {
00217         assert(b);
00218         // key added
00219         delta(b->fullkey(), b->value()); // ADDED
00220         return false;
00221     }
00222 }
00223 
00224 
00225 void UniIniGen::commit()
00226 {
00227     /** check dirtiness **/
00228     if (! dirty)
00229         return;
00230 
00231     /** open the file **/
00232     WvFile file(filename, O_WRONLY | O_TRUNC | O_CREAT, create_mode);
00233     if (! file.isok())
00234     {
00235         //FIXME: Should use wverror
00236         log("Cannot open config file for writing: \"%s\"\n",
00237             file.errstr());
00238         return;
00239     }
00240 
00241     /** iterate over all keys **/
00242     if (root)
00243         save(file, *root);
00244 
00245     /** close the file **/
00246     file.close();
00247     if (file.geterr())
00248     {
00249         //FIXME: Should use wverror
00250         log("Error writing to config file: \"%s\"\n", file.errstr());
00251         return;
00252     }
00253 
00254     dirty = false;
00255 
00256     /** done **/
00257     return;
00258 }
00259 
00260 
00261 void UniIniGen::save(WvStream &file, UniConfValueTree &parent)
00262 {
00263     UniConfValueTree::Iter it(parent);
00264     
00265     /** output values for non-empty direct or barren nodes **/
00266     // we want to ensure that a key with an empty value will
00267     // get created either by writing its value or by ensuring
00268     // that some subkey will create it implictly with empty value
00269     bool printedsection = false;
00270     for (it.rewind(); it.next() && file.isok(); )
00271     {
00272         UniConfValueTree *node = it.ptr();
00273         if (!! node->value() || ! node->haschildren())
00274         {
00275             if (! printedsection)
00276             {
00277                 printsection(file, parent.fullkey());
00278                 printedsection = true;
00279             }
00280             printkey(file, node->key(), node->value());
00281         }
00282     }
00283     if (printedsection)
00284         file.print("\n");
00285 
00286     /** output child sections **/
00287     for (it.rewind(); it.next() && file.isok(); )
00288     {
00289         save(file, *it);
00290     }
00291 }

Generated on Sat Mar 13 14:55:22 2004 for WvStreams by doxygen 1.3.6-20040222