00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
00068 static bool ONLY_DOT_OTD = true;
00069
00070 #define DEBUG(s) {cerr << "OTDUTIL DEBUG: " << s << endl;}
00071
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
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
00095 map<string, OTD_Info*> otds;
00096
00097
00098
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
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
00138 static void xml_cb_createObjects(void* data, const char* el, const char ** attr) {
00139
00140
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
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
00196 static void xml_cb_otdScan(void* data, const char* el, const char** attr) {
00197
00198
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
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
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
00255
00256
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
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
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
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
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
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 };
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