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

vos/applibs/otd/otdutil.cc

Go to the documentation of this file.
00001 /*  $Id: otdutil.cc,v 1.8 2003/07/24 16:12:00 reed Exp $
00002 
00003     Copyright (C) 2002 Reed Hedges <reed@interreality.org>
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (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 
00020 
00021 /*  This code is partially based on the "outline.c" example distributed
00022     with expat. (debian: libexpat1-dev) 
00023 
00024     This code requires expat (debian: libexpat1, libexpat1-dev), and
00025     VOS core libs. 
00026 
00027     See otdutil.hh for documentation.
00028 
00029 */
00030 
00031 #include "otdutil.hh"
00032 #include <vos/corelibs/vos/vos.hh>
00033 
00034 #ifdef HAVE_CONFIG_H
00035 #include "vos_applibs_config.h"
00036 #endif
00037 
00038 
00039 #include <stdio.h>
00040 #include <dirent.h>
00041 #include <sys/stat.h>
00042 #include <string.h>
00043 #include <assert.h>
00044 #include <map>
00045 #include <deque>
00046 
00047 #include <expat.h>
00048 
00049 #ifndef S_ISDIR
00050 #define S_ISDIR(x) (x & _S_IFDIR)
00051 #endif
00052 
00053 #ifndef S_ISREG
00054 #define S_ISREG(x) (x & _S_IFREG)
00055 #endif
00056 
00057 
00058 namespace OTD {
00059 
00060 
00061 #ifndef INSTALL_PREFIX
00062 #    define INSTALL_PREFIX "/usr"
00063 #endif
00064 
00065 #define MAX_BUFF_SIZE   8192
00066 
00067 // if this is true, then only files ending in ".otd" are scanned
00068 static bool ONLY_DOT_OTD = true;
00069 
00070 #define DEBUG(s)    {cerr << "OTDUTIL DEBUG: " << s << endl;}
00071 //#define DEBUG(s)    {}
00072 #define WARNING(s)  {cerr << "OTD Warning: " << s << endl;}
00073 #define NOTIFY(s)   {cerr << "OTD: " << s << endl;}
00074 #define ERROR(s)    {cerr << "OTD Error: " << s << endl;}
00075 
00076 
00077 // struct that is attached to parser for access from handlers
00078 typedef struct {
00079     deque<string>* types;
00080     XML_Parser parser;
00081     int stop;
00082     FILE* file;
00083     string filename;
00084     vRef<Vobject> object;
00085     vRef<Site> site;
00086 } OTD_XML_Info ;
00087 
00088 typedef struct  {
00089     FILE* file;
00090     string filename;
00091     deque<string> extends;
00092 } OTD_Info;
00093 
00094 // map all otd types -> types they extend
00095 map<string, OTD_Info*> otds;
00096 
00097 
00098 /* search & replace in string */
00099 inline static void sreplace(string& s, const string& oldc, const string& newc) {
00100     string::size_type i = 0;
00101     while( (i = s.find(oldc, i)) != s.npos ) {
00102         s.replace(i, oldc.length(), newc);
00103     }
00104 }
00105 inline static void sreplace(string& s, char oldc, char newc) {
00106     string::size_type i = 0;
00107     while( (i = s.find(oldc, i)) != s.npos ) {
00108         s.replace(i, 1, 1, newc);
00109     }
00110 }
00111 
00112 /* split s on sep */
00113 static deque<string> ssplit(const string& s, char sep, char esc = '\\', int max = -1) {
00114     string::size_type cur, start, found, last;
00115     deque<string> r;
00116     cur = start = last = found = 0;
00117     int n = 0;
00118     while( (found = s.find(sep, start)) != s.npos) {
00119         if( s[found-1] == esc ) {
00120             start = found+1;
00121             continue;
00122         }
00123         cur = found;
00124         string part = s.substr(last, cur-last);
00125         sreplace(part, ( string(1, esc) + string(1, sep) ), string(1, sep) );
00126         r.push_back(part);
00127         start = last = cur+1;
00128         if(++n == (max-1) )
00129             break;
00130     }
00131     string rest = s.substr(start);
00132     sreplace(rest, ( string(1, esc) + string(1, sep) ), string(1, sep) );
00133     r.push_back(rest);
00134     return r;
00135 }
00136 
00137 /* <object> element handler: create new object if required */
00138 static void xml_cb_createObjects(void* data, const char* el, const char ** attr) {
00139 
00140     /** @BUG  Assumes 8 bit characters.  */
00141     int i;
00142     int reqd = 1;
00143     OTD_XML_Info* info;
00144     char* type = 0;
00145     char* name = 0;
00146 
00147     assert(data);
00148     info = (OTD_XML_Info*)data;
00149 
00150     if(strcmp(el, "object")) 
00151         return;
00152 
00153     for(i = 0; attr[i]; i+=2) {
00154         if(!strcmp(attr[i], "name")) 
00155             name = strdup(attr[i+1]);
00156         else if(!strcmp(attr[i], "type")) 
00157             type = ( !strcmp(attr[i+1], "property") ) ?  strdup("property:property") : strdup(attr[i+1]);
00158         else if(!strcmp(attr[i], "optional"))
00159             reqd = strcmp(attr[i+1], "yes");
00160     }
00161     if(!name)
00162         name = strdup("foo");
00163     if(!type)
00164         type = strdup("");
00165 
00166     if(reqd) {
00167         if(!strcmp(name, "*"))
00168             name = "something";
00169         NOTIFY("Creating new object \"" << name << "\" of type \"" << type << "\".");
00170         if(!strcmp(type, ""))
00171             type = 0;
00172         assert(&(info->object));
00173         assert(&(info->site));
00174         deque<string> types = ssplit(type, ',');
00175         // TODO: trim WS frem each type
00176         try {
00177             vRef<MetaObject> newobj = info->site->createMetaObject(0, types);
00178             info->object->insertChild(-1, name, &newobj);
00179         } catch(exception& e) {
00180             ERROR("Caught exception while creating new MetaObject \"" << name << "\" : " << e.what());
00181         }
00182     } else {
00183         WARNING("subobject \"" << name << "\" is optional, please create it manually if needed.");
00184     }
00185 
00186     if(name)
00187         free(name);
00188     if(type)
00189         free(type);
00190 }
00191 
00192 
00193 
00194 
00195 /* <otd> element handler: augment global data structures */
00196 static void xml_cb_otdScan(void* data, const char* el, const char** attr) {
00197 
00198     /** @BUG  Assumes 8 bit characters. Should use XML_Char */
00199     int i;
00200     char* group = 0;
00201     char* name = 0;
00202     char* exts= 0;
00203     char* typestring = 0;
00204     OTD_XML_Info* info;
00205     assert(data);
00206     info = (OTD_XML_Info*)data;
00207     DEBUG("xml parser: element is " << el);
00208 
00209     if(strcmp(el, "otd") != 0) 
00210         return;
00211 
00212     // get attributes (type name and extends list)
00213     for(i = 0; attr[i]; i+=2) {
00214         if(!strcmp(attr[i], "group")) 
00215             group = strdup(attr[i+1]);
00216         else if(!strcmp(attr[i], "name")) 
00217             name = strdup(attr[i+1]);
00218         else if(!strcmp(attr[i], "extends")) {
00219             exts = strdup(attr[i+1]);
00220             DEBUG("xml parser: found 'extends' list: " << exts);
00221         }
00222     }
00223 
00224 
00225     if(group) {
00226         typestring = (char*) malloc( strlen(group) + strlen(name) + 1 );
00227         strcpy(typestring, group);
00228         strcat(typestring, ":");
00229         strcat(typestring, name);
00230         free(group);
00231     } else {
00232         typestring = strdup(name);
00233     }
00234         
00235     // save extends list
00236     OTD_Info* otd = new OTD_Info();
00237     otd->file = info->file;
00238     otd->filename = info->filename;
00239     if(exts)
00240         otd->extends = ssplit(exts, ',');
00241     otds[typestring] = otd;
00242 
00243     info->stop = 1;
00244     XML_SetStartElementHandler(info->parser, NULL);
00245 
00246     if(name)
00247         free(name);
00248     if(typestring)
00249         free(typestring);
00250     if(exts)
00251         free(exts);
00252 }
00253 
00254 /* parse an otd on stream 'f' with element handler 'handler'
00255    types, filename, f, vobj and site are passed on to the handler.
00256     vobj, types and site may be null (0).
00257 */
00258 static void do_otd(FILE* f, string filename, deque<string>* types, XML_StartElementHandler handler, Vobject* vobj, Site* site) {
00259     char buff[MAX_BUFF_SIZE];
00260     int len;
00261     int eof = 0;
00262     OTD_XML_Info xml;
00263 
00264     assert(f);
00265 
00266     xml.stop = 0;
00267     xml.types = types; 
00268     xml.file = f;
00269     xml.filename = filename;
00270     xml.object = vobj;
00271     xml.site = site;
00272     xml.parser = XML_ParserCreate(NULL);
00273 
00274     if(!xml.parser) {
00275         ERROR("Could not create XML parser! (not enough memory?)\n");
00276         exit(-1);
00277     }
00278     XML_SetUserData(xml.parser, &xml);
00279     XML_SetStartElementHandler(xml.parser, handler);
00280 
00281     // read text and feed to parser until EOF or handler sets stop flag
00282     while (!eof && !xml.stop) {
00283         len = fread(buff, 1, MAX_BUFF_SIZE, f);
00284         if (ferror(f)) {
00285             ERROR("Read error in " << filename << "\n");
00286             exit(-1);
00287         }
00288         eof = feof(f);
00289         if (! XML_Parse(xml.parser, buff, len, eof)) {
00290             ERROR("Parse error in " << filename  << " at line " << 
00291                     XML_GetCurrentLineNumber(xml.parser) << ": " << 
00292                     XML_ErrorString(XML_GetErrorCode(xml.parser)));
00293             break;
00294         }
00295     }
00296     XML_ParserFree(xml.parser);
00297     return;
00298 }
00299 
00300 
00301 static void accumulate_extends(string t, deque<string>* dest) {
00302     if(otds.find(t) == otds.end()) {
00303         return;
00304     }
00305     DEBUG("checking extends for " << t << "...");
00306     OTD_Info* i = otds[t];
00307     for(deque<string>::const_iterator x = i->extends.begin();
00308         x != i->extends.end(); x++) {
00309           dest->push_back(*x);
00310         DEBUG("added type: " << *x);
00311         accumulate_extends(*x, dest);
00312     }
00313 }
00314 
00315 
00316 
00317 void scan() {
00318     char* path = getenv("VOS_OTD_PATH");
00319     if(path) {
00320         deque<string> ds = ssplit(path, ':');
00321         for(deque<string>::const_iterator d = ds.begin(); d != ds.end(); d++) {
00322             scanDir(*d);
00323         }
00324     } else {
00325 #ifdef INSTALL_PREFIX
00326         string dir = INSTALL_PREFIX;
00327 #else
00328         string dir = "/usr";
00329 #endif
00330         dir += "/share/vos/otd";
00331         scanDir(dir);
00332     }
00333 }
00334 
00335 
00336 void scanDir(string d) {
00337     // scan otds, build types list 
00338     if(d == "") 
00339         return;
00340     DEBUG("Scanning directory: " << d);
00341     DIR* dir = opendir(d.c_str());
00342     if(!dir) {
00343         WARNING("Could not open directory: " << d);
00344         return;
00345     }
00346     string fn;
00347     struct dirent* f;
00348     while( (f = readdir(dir)) != NULL ) {
00349         if( !strcmp(f->d_name, ".") || !strcmp(f->d_name, "..")  )
00350             continue;
00351         fn = d + "/" + f->d_name;
00352         struct stat fileinfo;
00353         stat(fn.c_str(), &fileinfo);
00354         if(S_ISREG(fileinfo.st_mode)) {
00355             // skip files that don't end in .otd
00356             DEBUG("last 4 chars of \"" << fn << "\" is: " << fn.substr( fn.length()-4, 4 ) );
00357             if(ONLY_DOT_OTD && 
00358 #ifdef BACKWARDS_CXX_STRING_COMPARE
00359 #warning Using "Backwards" string::compare function...
00360                 (fn.compare(".otd", (fn.length()-4), 4) != 0)
00361 #else
00362                 (fn.compare((fn.length()-4), 4, ".otd") != 0)
00363 #endif
00364             ) {
00365                 DEBUG("skipping file: " << fn);
00366                 continue;
00367             }
00368             DEBUG("parsing file: " << fn);
00369             do_otd(fopen(fn.c_str(), "r"), fn, 0, xml_cb_otdScan, 0, 0);
00370         } else if(S_ISDIR(fileinfo.st_mode)) {
00371             DEBUG("scanning dir: " << fn);
00372             scanDir(fn);
00373         }
00374     }
00375     closedir(dir);
00376 }
00377 
00378 void cleanup() {
00379     for(map<string,OTD_Info*>::const_iterator i = otds.begin(); i != otds.end();
00380 i++) {
00381         if( i->second->file)
00382             fclose(i->second->file);
00383         delete i->second;
00384     }
00385 }
00386 
00387 void rescan() {
00388     cleanup();
00389     scan();
00390 }
00391     
00392 
00393 void initObject(deque<string>& types, Vobject* vobj, Site* site) {
00394 
00395     assert(vobj);
00396     if(!site) 
00397         site = &(vobj->getSite());
00398 
00399     // build list of types and 'extends' anscestors; add types to object
00400     deque<string> xtypes = types;
00401     for(deque<string>::const_iterator t = types.begin(); t != types.end(); t++) {
00402         accumulate_extends(*t, &xtypes);
00403     }
00404 
00405     // parse otds for each subobject's type and parent type
00406     DEBUG("[parsing otds...]");
00407     for(deque<string>::const_iterator t = xtypes.begin(); t != xtypes.end(); t++) {
00408         if(otds.find(*t) == otds.end()) {
00409             WARNING("OTD not found for type: " << (*t));
00410             continue;
00411         }
00412         DEBUG("parsing otd for " << (*t));
00413         OTD_Info* i = otds[*t];
00414         rewind(i->file);
00415         do_otd(i->file, i->filename, &types, xml_cb_createObjects, vobj, site);
00416     }
00417 
00418     return;
00419 }
00420 
00421 
00422 };  // ns OTD
00423 
00424 
00425 
00426 #if 0
00427 int main() {
00428     LocalSocketSite site(&NoAccessControl::static_);
00429     Vobject* vobj = site.createMetaObject("foo", 0);
00430     OTD::scan();
00431     deque<string> t;
00432     t.push_back("a3dl:object3D.sphere");
00433     t.push_back("misc:hypercard");
00434     OTD::initObject(t, vobj, &site);
00435     OTD::cleanup();
00436     return 0;
00437 }
00438 #endif
00439 
00440 
00441 
00442 

Generated on Tue Aug 12 03:55:37 2003 for Interreality Project - VOS by doxygen 1.3.2