/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: MixedGen.C /main/10 1996/11/26 12:29:15 cde-hal $ * * (c) Copyright 1996 Digital Equipment Corporation. * (c) Copyright 1996 Hewlett-Packard Company. * (c) Copyright 1996 International Business Machines Corp. * (c) Copyright 1996 Sun Microsystems, Inc. * (c) Copyright 1996 Novell, Inc. * (c) Copyright 1996 FUJITSU LIMITED. * (c) Copyright 1996 Hitachi. */ #include #include #include #include #include "dti_excs/Exceptions.hh" #include "FlexBuffer.h" #include "oliasdb/mmdb.h" #include "oliasdb/asciiIn_filters.h" #include "DataBase.h" #include "BookCaseDB.h" #include "StringList.h" #include "GraphicsTask.h" #include "Handler.h" #include "BTCollectable.h" #include "dti_cc/CC_String.h" #include "dti_cc/cc_hdict.h" // #include "BT_StrHashDict.h" #define SKIP_CODE -1 #define NULL_OID "0.0" #define NULL_LOCATOR "0" #ifdef FISH_DEBUG #include "dbug.h" /* Fred Fish's dbug.h */ #endif extern void insert_remotelink( hashTable *, char *, size_t, FlexBuffer *); static unsigned hash_func(const CC_String &str ) { return( str.hash() ); } //------------------------------------------------------------ static void checkTocLink( const char *toc_file_name, hashTable &linkTab, hashTable &nodeTab ) { int flag = 0; char *ToCFileName = strdup ( toc_file_name ); hashTableIterator nodeit( nodeTab ); while ( ++nodeit ) { CC_String *key = (CC_String *)nodeit.key(); BTCollectable *value = (BTCollectable *)nodeit.value(); if ( !linkTab.contains( key ) ) { flag = 1; const char *file_name = value->filename(); int line_num = value->linenum(); cerr << "(ERROR) Section ID = " << (const char *)*key << endl << " of file = " << file_name << endl << " at line = " << line_num << endl << " does not have a corresponding link with the value = " << (const char *)*key << endl << " from the ToC file = " << ToCFileName << "\n\n"; } } hashTableIterator linkit( linkTab ); while ( ++linkit ) { CC_String *link = linkit.key(); int *line_num = linkit.value(); if ( !nodeTab.contains( link ) ) { flag = 1; cerr << "(ERROR) IDREF = " << (const char *)*link << endl << " of ToC file = " << ToCFileName << endl << " at line = " << *line_num << endl << " is pointing to an unavailable node\n\n"; } } free(ToCFileName); if ( flag ) { throw(Unexpected("TOC validation failed\n")); } } //------------------------------------------------------------ static void create_node_dict( hashTable &dict, BookCaseDB& db ) { DBTable *nodeMeta = db.table(BookCaseDB::NodeMeta, DB::READ); DBCursor cursor(*nodeMeta); const char *bookLocator; const char *nodeLocator; const char *filename; int line_num; const char *title; const char *stitle; const char *style; int throw_exception = 0; while(cursor.next(STRING_CODE, &bookLocator, STRING_CODE, &nodeLocator, STRING_CODE, &filename, INTEGER_CODE, &line_num, SKIP_CODE, /* TOC num */ STRING_CODE, &title, STRING_CODE, &stitle, STRING_CODE, &style, NULL)){ CC_String *key = new CC_String ( nodeLocator ); BTCollectable *value = new BTCollectable ( filename,line_num,bookLocator ); BTCollectable *val = (BTCollectable *)dict.findValue( key ); if ( !val ) { dict.insertKeyAndValue( key, value ); } else { throw_exception = 1; cerr << "(ERROR) Duplicate section ID = " << (const char *)*key << endl << " found in file = " << filename << endl << " at line = " << line_num << endl << " is in conflict with " << endl << " section ID = " << (const char *)*key << endl << " found in file = " << val->filename() << endl << " at line = " << val->linenum() << "\n\n"; delete key; delete value; } } if ( throw_exception ) { throw(Unexpected("Duplicate section IDs are found")); } } //------------------------------------------------------------ static void node_table( DBCursor *node_meta_cursor, const char *this_book, hashTable &this_node_table ) { assert( node_meta_cursor != NULL ); assert( this_book != NULL ); const char *bookLocator; const char *nodeLocator; const char *file_name; int line_num; const char *title; const char *stitle; const char *style; while ( node_meta_cursor->next(STRING_CODE, &bookLocator, STRING_CODE, &nodeLocator, STRING_CODE, &file_name, INTEGER_CODE, &line_num, SKIP_CODE, /* TOC num */ STRING_CODE, &title, STRING_CODE, &stitle, STRING_CODE, &style, NULL)){ /* this book has changed */ if ( strcmp( bookLocator, this_book ) != 0 ) { node_meta_cursor->undoNext(); break; } CC_String *key = new CC_String( nodeLocator ); BTCollectable *value = new BTCollectable( file_name,line_num,bookLocator ); this_node_table.insertKeyAndValue( key, value ); } } //------------------------------------------------------------ /* MMDB interfaces use char*; I like to use const char * * whenever possible * Plus, in TOCTask, we write "" to indicate "no locator", * but the MMDB code expected "0". * so we do the conversion here. * * Hmmm.... get_oid_2(... "0") doesn't seem to work. I'm going * to bypass it and return "0.0" directly. */ static const char * to_oid(info_lib *mmdb, const char *bcname, const char *str) { const char *ret = NULL_OID; if(*str){ ret = get_oid_2(mmdb, (char*)bcname, (char*)str); } return ret; } //------------------------------------------------------------ static void locator_table( BookCaseDB& db, hashTable &hd) { DBTable *locTable = db.table(BookCaseDB::Locator, DB::READ); DBCursor cursor(*locTable); const char *locator; const char *nodeloc; const char *reflabel; const char *filename; int line_num; int dup_count = 0; while(cursor.next(STRING_CODE, &locator, STRING_CODE, &nodeloc, STRING_CODE, &reflabel, STRING_CODE, &filename, INTEGER_CODE, &line_num, NULL)){ int buflen = strlen(nodeloc) + strlen(reflabel) + 2; char *buf = new char[buflen]; snprintf(buf, buflen, "%s\t%s", nodeloc, reflabel); CC_String *loc_collect = new CC_String( locator ); BTCollectable *node_collect = new BTCollectable( filename, line_num, buf ); delete[] buf; BTCollectable *val = hd.findValue( loc_collect ); if ( !val ) { hd.insertKeyAndValue( loc_collect, node_collect ); } else { delete loc_collect; delete node_collect; dup_count++; cerr << "(ERROR) Duplicate ID = " << locator << endl << " found in file = " << filename << endl << " at line = " << line_num << endl << " is in conflict with " << endl << " ID = " << locator << endl << " found in file = " << val->filename() << endl << " at line = " << val->linenum() << "\n\n"; } } if ( dup_count ) { throw(Unexpected(form("Number of duplicate IDs found = %d\n", dup_count))); } } //------------------------------------------------------------ static void writeCCF(BookCaseDB& db, info_lib *mmdb, const char *bcname ) { DBTable *bookMeta = db.table(BookCaseDB::BookMeta, DB::READ); DBCursor cursor(*bookMeta); DBTable *ccf = db.DB::table(DATABASE_STDIO, DOC_CODE, BT_NUM_DOC_FIELDS, DB::CREATE); const char *bookLocator; const char *stitle; const char *title; int seq_no; int tabQty; const char **tabLines; const char *access; /* * First put the global node table into hash dictionary */ hashTable global_node_tab(hash_func); create_node_dict( global_node_tab , db); /* throw exception if duplicate node locator is found */ int exception_flag = 0; while(cursor.next(STRING_CODE, &bookLocator, STRING_CODE, &stitle, STRING_CODE, &title, INTEGER_CODE, &seq_no, SHORT_LIST_CODE, &tabQty, STRING_CODE, &tabLines, STRING_CODE, &access, NULL)){ StringList heap; /* convert tab to oids in forst tabQty items in heap */ for(int i = 0; i < tabQty; i++){ char *t = (char *)tabLines[i]; const char *name = strtok( t, "\t" ); const char *loc = strtok( NULL, "\t"); const char *line = strtok( NULL, "\t"); const char *file_name = strtok( NULL, "\t"); int nameLen = strlen( name ); /* * First check if the tab link is resolved */ CC_String key ( loc ); BTCollectable *tab_link = (BTCollectable *)global_node_tab. findValue( &key ); if ( !tab_link ) { cerr << "(ERROR) Tab ID = " << loc << endl << " of book = " << title << endl << " specified in file = " << file_name << endl << " at line = " << line << endl << " is not pointing to any section ID found in the book\n\n"; exception_flag = 1; } else { /* * see if it is a node locator within the same book */ if ( strcmp( tab_link->get_value(), bookLocator )) { cerr << "(ERROR) Tab ID = " << loc << endl << " of book = " << title << endl << " specified in file = " << file_name << endl << " at line = " << line << endl << " is not pointing to any section ID found in the book\n\n"; exception_flag = 1; } } // if exception_flag is set, calling to_oid will throw exception // It will just loop through all the tabs, and report all the bad ones if ( !exception_flag ) { const char *tabOID = to_oid(mmdb, bcname, loc); int resultlen = nameLen + 1 + strlen(tabOID) + 1; char *result = new char[resultlen]; snprintf(result, resultlen, "%s\t%s", name, tabOID ); heap.add(result); } } if ( !exception_flag ) { const char *bookOID = heap.append(to_oid(mmdb, bcname, bookLocator)); #ifdef FISH_DEBUG DBUG_PRINT("CCF", ("Load Book: O:%s `%s'\n", bookOID, title)); #endif ccf->insert(OID_CODE, bookOID, STRING_CODE, stitle, STRING_CODE, title, INTEGER_CODE, seq_no, SHORT_LIST_CODE, tabQty, STRING_CODE, heap.array(), STRING_CODE, access, NULL); } } if ( exception_flag ) { throw(Unexpected("Tab validation failed")); } delete ccf; global_node_tab.clearAndDestroy(); } //------------------------------------------------------------ static void writeTOC(BookCaseDB &db, info_lib *mmdb, const char *bcname, const char *thisBook, DBCursor &toc_cursor ) { DBTable *out = db.DB::table(DATABASE_STDIO, TOC_CODE, NUM_TOC_FIELDS, DB::CREATE); const char *aBook; const char *nodeLoc; const char *parent; int childQty; char **children; int treeSize; while(toc_cursor.next(STRING_CODE, &aBook, STRING_CODE, &nodeLoc, STRING_CODE, &parent, SHORT_LIST_CODE, &childQty, STRING_CODE, &children, INTEGER_CODE, &treeSize, NULL)){ StringList heap; if(strcmp(aBook, thisBook) != 0){ /* book id has changed! We're done... */ toc_cursor.undoNext(); break; } for(int i = 0; i < childQty; i++){ heap.append(to_oid(mmdb, bcname, children[i])); } const char *nodeOID = heap.append(to_oid(mmdb, bcname, nodeLoc)); const char *parentOID = heap.append(to_oid(mmdb, bcname, parent)); #ifdef FISH_DEBUG DBUG_PRINT("TOC", ("TOC Entry: O:%s treesize: %d\n", nodeOID, treeSize)); #endif out->insert(OID_CODE, nodeOID, OID_CODE, parentOID, INTEGER_CODE, treeSize, /* first childQty strings in heap are oids for children */ OID_LIST_CODE, childQty, heap.array(), NULL); } delete out; } //------------------------------------------------------------ static void writeDLP(BookCaseDB &db, info_lib *mmdb, const char *bcname, const char *thisBook, DBCursor &dlp_cursor, hashTable &node_tab) { DBTable *out = db.DB::table(DATABASE_STDIO, DLP_CODE, 1, DB::CREATE); const char *aBook; const char *nodeLoc; int line_num; const char *toc_file_name; hashTable link_table(hash_func); int record_pos = dlp_cursor.tell(); // create the link table while(dlp_cursor.next(STRING_CODE, &aBook, STRING_CODE, &nodeLoc, INTEGER_CODE, &line_num, STRING_CODE, &toc_file_name, NULL)){ if(strcmp(aBook, thisBook) != 0){ /* book id has changed! We're done... */ dlp_cursor.undoNext(); break; } CC_String *key = new CC_String ( nodeLoc ); link_table.insertKeyAndValue( key, new int(line_num) ); #ifdef FISH_DEBUG // DBUG_PRINT("DLP", ("DLP Entry: O:%s\n", nodeOID)); #endif } checkTocLink( toc_file_name, link_table, node_tab ); /* throw exception if unresolved TOC links are found */ link_table.clearAndDestroy(); dlp_cursor.seekToRec( record_pos ); out->start_list(); while ( dlp_cursor.next( STRING_CODE, &aBook, STRING_CODE, &nodeLoc, INTEGER_CODE, &line_num, STRING_CODE, &toc_file_name, NULL)) { if(strcmp(aBook, thisBook) != 0){ /* book id has changed! We're done... */ dlp_cursor.undoNext(); break; } const char *nodeOID = to_oid(mmdb, bcname, nodeLoc); out->insert_untagged(OID_CODE, nodeOID, NULL); #ifdef FISH_DEBUG DBUG_PRINT("DLP", ("DLP Entry: O:%s\n", nodeOID)); #endif } out->end_list(); /* exception thrown in this if failed */ delete out; } //------------------------------------------------------------ // Parsing functions for LAST indicator determination //------------------------------------------------------------ char * getGI( char * bufptr ) { char* cptr; for (cptr = bufptr; *cptr != '>'; cptr++) { } *cptr = '\0'; char * retval = strdup(bufptr); *cptr = '>'; return(retval); } char * getIndicator( char * bufptr ) { char *iptr = bufptr; // Find the end of attributes, assuming that every start tag now has // attributes because of the last indicator at least. while ((*iptr != '<') || (*(iptr+1) != '/') || (*(iptr+2) != '#') || (*(iptr+3) != '>')) { iptr++; } // Work back to the indicator value (subtract "" and 1 for the // digit) iptr = iptr - 9; return(iptr); } void lookahead( char * bufptr, char * currentgi, char * indpos) { char * laptr = bufptr; while (*laptr != '<') { laptr++; } laptr++; if (*laptr == '/') { // end tag ahead so the current indicator must be changed to say last // on level. *indpos = '2'; // LASTONLEVEL } else { // get the startag gi and compare it against the one passed in char * nextgi = getGI(laptr); if (strcmp(currentgi, nextgi) != 0) { // next start tag is different, so the current GI is the last sibling *indpos = '1'; // LASTSIBLING } // otherwise the next start tag is the same as the current one, so no // change to the indicator is needed. free(nextgi); } return; } char * parse4last( char * bufptr ) { char *startname, *indicator; while (*bufptr != 0) { if (*bufptr == '<') { // TAG bufptr++; if (*bufptr != '/') { // START TAG startname = getGI(bufptr); bufptr = bufptr + strlen(startname); indicator = getIndicator(bufptr); // move buffer pointer past the end of the attributes. Assuming // that indicator is pointing at the character position where the // value is stored we need to move past it and the characters // "" bufptr = indicator + 13; // parse the content of this tag past its end tag and when it // returns get to the end of where that parse got to. bufptr = parse4last( bufptr ); // Handle the end tag if (*bufptr == 0) { // end of the buffer, so it is automatically last... *indicator = '2'; // LASTONLEVEL } else { lookahead(bufptr, startname, indicator); } free(startname); } else { // END TAG for ( ; *bufptr != '>'; bufptr++) { // Just move the pointer past the tag name. } bufptr++; // Get past the '>' return(bufptr); } } else { // Data bufptr++; } } return(bufptr); } //------------------------------------------------------------ //------------------------------------------------------------ static void writeSGML(BookCaseDB &db, const char *thisBook, DBCursor &sgml_cursor, hashTable &hd) { DBTable *out = db.DB::table(DATABASE_STDIO, SGML_CONTENT_CODE, 2, DB::CREATE); const char *aBook; const char *nodeLoc; size_t dataLen; const char *data; char *bufptr; // Walk through SGML data stream and update LAST fields while(sgml_cursor.next(STRING_CODE, &aBook, STRING_CODE, &nodeLoc, -STRING_CODE, &data, &dataLen, NULL)){ if(strcmp(aBook, thisBook) != 0){ /* book id has changed! We're done... */ sgml_cursor.undoNext(); break; } #ifdef FISH_DEBUG DBUG_PRINT("SGML", ("SGML data for: ::%s `%.40s...' \n", nodeLoc, data)); #endif FlexBuffer new_node_buffer; bufptr = (char *) data; parse4last( bufptr ); insert_remotelink( &hd, (char *)data, dataLen, &new_node_buffer); out->insert_untagged(STRING_CODE, nodeLoc, -STRING_CODE, new_node_buffer.GetBuffer(), (size_t)new_node_buffer.GetSize(), NULL); } delete out; } //------------------------------------------------------------ static void writeGraphics(BookCaseDB &db, const char *thisBook, DBCursor &gr_cursor, int compressed, char *comp_agent) { DBTable *out = db.DB::table(DATABASE_STDIO, GRAPHIC_CODE, BT_NUM_GRAPHIC_FIELDS, DB::CREATE); const char *aBook; const char *gid; const char *name; const char *version; const char *typeInfo; const char *data; size_t len; const char *title; while(gr_cursor.next(STRING_CODE, &aBook, STRING_CODE, &gid, STRING_CODE, &name, STRING_CODE, &version, STRING_CODE, &typeInfo, -STRING_CODE, &data, &len, STRING_CODE, &title, NULL)){ if(strcmp(aBook, thisBook) != 0){ /* book id has changed! We're done... */ gr_cursor.undoNext(); break; } #ifdef FISH_DEBUG DBUG_PRINT("Graphics", ("Graphics data for: ::%s `%.40s...' \n", gid, data)); #endif if ( typeInfo[0] - '0' == GR_TYPE_POSTSCRIPT && compressed ) { out->insert(STRING_CODE, gid, STRING_CODE, name, STRING_CODE, version, STRING_CODE, typeInfo, -COMPRESSED_STRING_CODE, comp_agent, data, len, STRING_CODE, title, NULL); } else { out->insert(STRING_CODE, gid, STRING_CODE, name, STRING_CODE, version, STRING_CODE, typeInfo, -STRING_CODE, data, len, STRING_CODE, title, NULL); } } delete out; } //------------------------------------------------------------ static void writeBooks(BookCaseDB& db, info_lib *mmdb, const char *bcname, DBCursor *node_meta_cursor, int compressed, char *comp_agent, hashTable &hd) { DBTable *bookMeta = db.table(BookCaseDB::BookMeta, DB::READ); DBCursor BookCursor(*bookMeta); DBTable *toc = db.table(BookCaseDB::TOCTree, DB::READ); DBCursor TocCursor(*toc); DBTable *dlp = db.table(BookCaseDB::TOCPath, DB::READ); DBCursor DlpCursor(*dlp); DBTable *sgml = db.table(BookCaseDB::NodeSGML, DB::READ); DBCursor SgmlCursor(*sgml); DBCursor *GraphicsCursorPtr = NULL; int process_graphics = 1; mtry { DBTable *graphics = db.table(BookCaseDB::Graphics, DB::READ); GraphicsCursorPtr = new DBCursor( *graphics ); } mcatch (PosixError&, p){ /* error opening graphics stuff... skip graphics */ process_graphics = 0; }end_try const char *bookLocator; int tabQty; char **tabLines; while(BookCursor.next(STRING_CODE, &bookLocator, SKIP_CODE, SKIP_CODE, SKIP_CODE, /* @# skipping shortlists not suported yet... */ SHORT_LIST_CODE, &tabQty, STRING_CODE, &tabLines, SKIP_CODE, /* skip access data */ NULL)){ hashTable node_tab(hash_func); node_table( node_meta_cursor, bookLocator, node_tab); writeDLP(db, mmdb, bcname, bookLocator, DlpCursor, node_tab); writeTOC(db, mmdb, bcname, bookLocator, TocCursor); writeSGML(db, bookLocator, SgmlCursor, hd); if ( process_graphics ) { writeGraphics(db, bookLocator, *GraphicsCursorPtr, compressed, comp_agent ); } node_tab.clearAndDestroy(); } if ( process_graphics ) { delete GraphicsCursorPtr; } } //------------------------------------------------------------ static void writeLCF(BookCaseDB& db, info_lib *mmdb, const char *bcname, hashTable &hd) { DBTable *out = db.DB::table(DATABASE_STDIO, LOCATOR_CODE, BT_NUM_LOCATOR_FIELDS, DB::CREATE); hashTableIterator loc_it( hd ); while ( ++loc_it ) { CC_String *locator_str = ( CC_String *)loc_it.key(); const char *locator = (const char *)*locator_str; BTCollectable *loc_val = ( BTCollectable *)loc_it.value(); const char *opaque, *nodeloc; char* reflabel; nodeloc = opaque = (const char *)loc_val->get_value(); reflabel = strchr((char*)opaque, '\t'); *reflabel++ = 0; const char *nodeOID = to_oid(mmdb, bcname, nodeloc); #ifdef FISH_DEBUG DBUG_PRINT("LCF", ("LCF entry: L:%s->O:%s\n", locator, nodeOID)); #endif out->insert(STRING_CODE, locator, OID_CODE, nodeOID, STRING_CODE, reflabel, NULL); } delete out; } //------------------------------------------------------------ static void split_path(char * path, const char*& dir, const char*& name) { char *p = strrchr(path, '/'); if(p){ *p = 0; dir = path; name = p+1; }else{ dir = name = path; } } //------------------------------------------------------------ static void usage(const char *progname) { fprintf(stderr, "usage: %s [-compressed] \n", progname); exit(1); } int main(int argc, char **argv) { INIT_EXCEPTIONS(); set_new_handler ( FreeStoreException ); int ret = 1; const char *progname = argv[0]; int compressed = 0; #ifdef FISH_DEBUG DBUG_PROCESS(argv[0]); if(getenv("FISH_DBUG")) DBUG_PUSH(getenv("FISH_DBUG")); #endif argv++; argc--; while(argc > 0 && argv[0][0] == '-'){ const char *opt = argv[0]; argv++; argc--; if(strcmp(opt, "-compressed") == 0){ compressed = 1; } else{ usage(progname); } } if(argc == 2){ char *bookcaseDir = argv[0]; char *infobaseDir = argv[1]; mtry{ BookCaseDB db(bookcaseDir); const char *infolibDir; const char *bcname; split_path(infobaseDir, infolibDir, bcname); OLIAS_DB mmdb_handle; info_lib *mmdb = mmdb_handle.openInfoLib(infolibDir, bcname); // 30 will be enough for now #define COMPRESSED_AGENT_SIZE 30 char comp_agent[ COMPRESSED_AGENT_SIZE ]; if ( compressed ) { for ( int i = 0; i < COMPRESSED_AGENT_SIZE; i++ ) { comp_agent[i] = 0; } ostringstream str_buf( comp_agent ); info_base *bcptr = mmdb->get_info_base(bcname); handler *x = (bcptr->get_obj_dict()).get_handler( form("%s.%s", bcname, "ps.dict")); x->its_oid().asciiOut(str_buf); memcpy(comp_agent, str_buf.str().c_str(), COMPRESSED_AGENT_SIZE); } hashTable hd(hash_func); locator_table( db, hd); /* throw exception if duplicate locator is found */ DBTable *nodeMeta = db.table(BookCaseDB::NodeMeta, DB::READ); DBCursor node_cursor( *nodeMeta ); writeCCF(db, mmdb, bcname); writeBooks(db, mmdb, bcname, &node_cursor, compressed, comp_agent, hd); writeLCF(db, mmdb, bcname, hd); hd.clearAndDestroy(); ret = 0; } mcatch(PosixError&, pe){ fprintf(stderr, "%s: error on %s: %s\n", progname, bookcaseDir, pe.msg()); } mcatch(Unexpected&, pe) { fprintf(stderr, "(ERROR) %s\n\n", pe.msg() ); exit(1); }end_try; }else{ usage(progname); } return ret; }