#include "StuffHeaders.hpp" #include #include #define __mbschr(s,c) (char*)_mbschr((const unsigned char*)(s),(c)) #define __mbsrchr(s,c) (char*)_mbsrchr((const unsigned char*)(s),(c)) #pragma warning(push) #include #pragma warning(pop) #define MAX_LINE_SIZE 512 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Macro::Macro(MString *replace) { m_replacement = *replace; m_inUse = false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Macro::AddValue( void *macro_tree, const char* name, const char* value ) { Check_Pointer(macro_tree); Check_Pointer(name); Check_Pointer(value); // //---------------------------------------------------- // Create MStrings for all this, and create a new plug //---------------------------------------------------- // MString ms_define(name); ms_define.ToLower(); MString ms_replace(value); Macro *mr_new = new(g_Heap) Macro(&ms_replace); Check_Object(mr_new); // //--------------------------------------------------------- // See if a previous definition is there. If so, remove it //--------------------------------------------------------- // stlport::map *map = (stlport::map*)macro_tree; stlport::map::iterator seeker = map->find(ms_define); if (seeker != map->end()) { Macro *old = (*seeker).second; Check_Object(old); delete old; (*seeker).second = NULL; map->erase(seeker); } (*map)[ms_define] = mr_new; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Macro::ReplaceMacros( void *macro_tree, const char *buffer, char *new_buf, int new_buf_size ) { Check_Pointer(macro_tree); Check_Pointer(buffer); Check_Pointer(new_buf); // //---------------------------------------------------- // Copy the characters one by one until we find the $( //---------------------------------------------------- // stlport::map *map = (stlport::map*)macro_tree; while (1) { while (*buffer && (*buffer != '$' || buffer[1] != '(')) { *new_buf++ = *buffer++; --new_buf_size; Verify(new_buf_size > 0); } // //-------------------------------------- // If we are out of buffer, search again //-------------------------------------- // if (!*buffer) { Verify(new_buf_size > 0); *new_buf = 0; return; } // //------------------------------- // Find the end of the macro name //------------------------------- // const char *p = strchr(buffer, ')'); if (!p) { Verify(new_buf_size > 0); *new_buf = '\0'; return; } // //------------------- // Isolate macro name //------------------- // int len = p-buffer - 2; MString macro_name; macro_name.AllocateLength(len+1); int i=0; for (p = buffer+2; *p != ')'; ++p,++i) { Verify(i::iterator seeker = map->find(macro_name); if (seeker != map->end()) { Macro *macro = (*seeker).second; Check_Object(macro); if (!macro->m_inUse) { macro->m_inUse = true; ReplaceMacros( macro_tree, macro->m_replacement, new_buf, new_buf_size ); len = strlen(new_buf); new_buf_size -= len; new_buf += len; macro->m_inUse = false; } } } } //############################################################################# //############## NotationFile ########################################### //############################################################################# void *NotationFile::s_cachedIncludes = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NotationFile::NotationFile( const char *file_name, Type type, bool preserve_case ): m_pages(NULL), m_preserveCase(preserve_case) { Check_Pointer(this); Check_Pointer(file_name); // //----------------------------------------- // See if we need to read stuff from a file //----------------------------------------- // FileStream file_stream; if (gos_DoesFileExist(file_name)) { file_stream.Open(file_name); m_fileName = file_stream.GetFileName(); m_fileDependencies.AddDependency(&file_stream); } else m_fileName = file_name; // //--------------------------------------------------------------- // Set up the file dependencies, and finish constructing the file //--------------------------------------------------------------- // m_type = type; CommonConstruction(&file_stream, NULL); // //----------------------------------------------------------- // Make sure that we don't allow a non-empty file to be empty //----------------------------------------------------------- // if (m_type==NonEmpty && IsEmpty()) STOP(("%s is missing or empty!", file_name)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NotationFile::NotationFile(MemoryStream *memory_stream): m_pages(NULL), m_preserveCase(false) { Check_Pointer(this); // //-------------------------------------------------------------------- // If we were given a stream and it was a file, get the dependency and // filename info from it //-------------------------------------------------------------------- // if (memory_stream) { Check_Object(memory_stream); if (memory_stream->IsDerivedFrom(FileStream::DefaultData)) { FileStream* file_stream = Cast_Object(FileStream*, memory_stream); m_fileDependencies.AddDependency(file_stream); m_fileName = file_stream->GetFileName(); } } // //------------------- // Finish building it //------------------- // m_type = Standard; CommonConstruction(memory_stream, NULL); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NotationFile::NotationFile( MemoryStream *memory_stream, void *macro_tree ): m_pages(NULL), m_preserveCase(false) { Check_Pointer(this); // //-------------------------------------------------------------------- // If we were given a stream and it was a file, get the dependency and // filename info from it //-------------------------------------------------------------------- // if (memory_stream) { Check_Object(memory_stream); if (memory_stream->IsDerivedFrom(FileStream::DefaultData)) { FileStream* file_stream = Cast_Object(FileStream*, memory_stream); m_fileDependencies.AddDependency(file_stream); m_fileName = file_stream->GetFileName(); } } // //------------------- // Finish building it //------------------- // m_type = Standard; CommonConstruction(memory_stream, macro_tree); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NotationFile::~NotationFile() { Check_Object(this); // //------------------------ // Clear out the notepages //------------------------ // stlport::map *map = (stlport::map *)m_pageMap; delete map; Verify(!IsChanged()); m_pages.DeletePlugs(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::TestInstance() const { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::CommonConstruction( MemoryStream *memory_stream, void *macro_tree ) { Check_Pointer(this); // //-------------------------- // Set up our nasty stl hack //-------------------------- // m_pageMap = new stlport::map; Check_Pointer(m_pageMap); // //------------------- // Read the stream in //------------------- // m_concatenate = true; if (memory_stream) { Check_Object(memory_stream); Read(memory_stream, macro_tree, NULL, false); } m_dirtyFlag = false; Check_Object(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::Read( MemoryStream *stream, void *macro_tree, Page **page, bool nested ) { Check_Object(this); Check_Pointer(stream); // //------------------------------------------------------------------ // If aren't already reading a page, make a pointer to hold the info //------------------------------------------------------------------ // Page *temp_page = NULL; if (!page) page = &temp_page; // //------------------------------------------------ // If we don't have a macro tree yet, make one now //------------------------------------------------ // void *orig_tree = macro_tree; if (!macro_tree) macro_tree = new(g_Heap) stlport::map; Check_Pointer(macro_tree); // //-------------------------------- // Read each line till we are done //-------------------------------- // bool comment_mode = false; char buffer[MAX_LINE_SIZE]; while (stream->ReadLine(buffer, sizeof(buffer))) { char *p; if (*buffer == '@') { p = buffer; while (*p) { *p = p[1]; ++p; } p = buffer; } else if (!m_preserveCase) p = strlwr(buffer); else p = buffer; // //---------------------------------------------------------------- // Deal with any comments - C++ style are the easiest to deal with //---------------------------------------------------------------- // char *comment_start = NULL; while ((p = strchr(p, '/')) != NULL) { if (p[1] == '/') break; // //-------------------------------------------------------------- // If we are ending a C style comment, move the memory to where // the comment started on this line. If it didn't start on this // line, move it to the beginning of the buffer //-------------------------------------------------------------- // else if (p[-1] == '*') { if (!comment_mode) { strcpy(p-1, p+1); --p; } else if (!comment_start) { strcpy(buffer, p+1); p = buffer; } else { strcpy(comment_start, p+1); p = comment_start; } comment_start = NULL; comment_mode = false; continue; } // //------------------------------------------------------------ // If we are starting a C style comment, remember where we are //------------------------------------------------------------ // else if (p[1] == '*') { comment_mode = true; comment_start = p; } ++p; } // //-------------------------------------------------------------------- // If we started a comment on this line, clip the line there. If not, // and we are within a C comment block, skip the whole line //-------------------------------------------------------------------- // if (comment_start) p = comment_start; else if (comment_mode) continue; // //--------------------- // Clip off the comment //--------------------- // if (p) *p = '\0'; // //----------------------- // Trim ending whitespace //----------------------- // p = buffer + strlen(buffer) - 1; while (p >= buffer && (*p==' ' || *p=='\t')) *p-- = '\0'; p = buffer; #if defined(_ARMOR) bool macroed=false; #endif // //-------------------------- // Ignore leading whitespace //-------------------------- // Parse: while (*p==' ' || *p=='\t') ++p; if(!*p) continue; // //-------------------------------------------------------------------- // If the line consists of a nested block end, handle it appropriately //-------------------------------------------------------------------- // if (!strcmp(p, "}")) { if (nested) { m_dirtyFlag = false; if (!orig_tree) { Check_Pointer(macro_tree); stlport::map *map = (stlport::map*)macro_tree; stlport::map::iterator seeker = map->begin(); while (seeker != map->end()) { Macro *macro = (*seeker).second; Check_Object(macro); delete macro; ++seeker; } delete map; } return; } else continue; } // //--------------------------------------------------------------- // If we aren't in raw mode, look for our preprocessor directives //--------------------------------------------------------------- // if (m_type != Raw) { char *q; if (*p == '!') HandleBangStuff(p+1, macro_tree, page); else if(*p == '#') HandlePoundStuff(p+1, macro_tree, page); // //------------------------------------------- // Look to see if we need to deal with macros //------------------------------------------- // else if((q = strchr(p, '$')) != NULL && q[1]=='(') { #if defined(_ARMOR) Verify(!macroed); macroed = true; #endif char new_buf[MAX_LINE_SIZE]; Macro::ReplaceMacros(macro_tree, buffer, new_buf, sizeof(new_buf)); p = new_buf; goto Parse; } else ProcessLine(stream, macro_tree, page, p); } // //------------------------------------------------------------ // We are in raw mode, so just process each line as it happens //------------------------------------------------------------ // else ProcessLine(stream, macro_tree, page, p); } if (comment_mode) PAUSE(("There is a missing */ in %s", (const char*)m_fileName)); if (nested) PAUSE(("There is a missing } in %s", (const char*)m_fileName)); // //------------------------------------------------------------------------ // If we allocated the macro tree, delete it now. The extra braces are to // remove the iterator needed for deletion //------------------------------------------------------------------------ // if (!orig_tree) { Check_Pointer(macro_tree); stlport::map *map = (stlport::map*)macro_tree; stlport::map::iterator seeker = map->begin(); while (seeker != map->end()) { Macro *macro = (*seeker).second; Check_Object(macro); delete macro; ++seeker; } delete map; } // //--------------------- // Nothing to save, yet //--------------------- // m_dirtyFlag = false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::Write(MemoryStream *stream) { Check_Object(this); Check_Object(stream); // //---------------------------------- // Write each page out to the stream //---------------------------------- // Page *null_page=FindPage(""); if(null_page) { Check_Object(null_page); null_page->WriteNotes(stream); } PageIterator pages(&m_pages); Page *page; while ((page = pages.ReadAndNext()) != NULL) { if(page!=null_page) { Check_Object(page); page->WriteNotes(stream); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::Save() { Check_Object(this); // //------------------------------------------------------ // If the file is dirty and has a filename, write it out //------------------------------------------------------ // if (m_dirtyFlag && m_fileName.GetLength()>0) { FileStream output(m_fileName, FileStream::WriteOnly); Write(&output); } IgnoreChanges(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::SaveAs(const char* file_name) { Check_Object(this); // //------------------------------------------------------ // If the file is dirty and has a filename, write it out //------------------------------------------------------ // FileStream output(file_name, FileStream::WriteOnly); m_fileName = output.GetFileName(); Write(&output); IgnoreChanges(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::ProcessLine( MemoryStream *stream, void *macro_tree, Page **notepage, char *buffer ) { Check_Object(this); Check_Object(stream); Check_Pointer(macro_tree); Check_Pointer(buffer); Check_Pointer(notepage); // //-------------------------------- // find first non-blank character //-------------------------------- // char *p = buffer; while (*p == ' ' || *p == '\t') ++p; // //---------------------------------------------------------------------- // If this is a page name, isolate the name, then only add it if it is a // new page //---------------------------------------------------------------------- // char *token; if (*p == '[') { token = p+1; while (*token == ' ' || *token == '\t') ++token; if ((p = strchr(token, ']')) != NULL) { p--; while (p>token && *p==' ' || *p=='\t') p--; p[1] = '\0'; } if (m_concatenate) *notepage = SetPage(token); else *notepage = AddPage(token); return; } // //--------------------------------------------- // If this is a comment or empty line, stop now //--------------------------------------------- // if (!*p) return; // //-------------------------------------------------------------- // Look for an entry, and if we find one, clear out the trailing // whitespace on the name //-------------------------------------------------------------- // token = p; char *entry = NULL; if ((p = strchr(token, '=')) != NULL) { *p = '\0'; entry = p + 1; --p; while (*p == ' ' || *p == '\t') { *p = '\0'; if (--p < token) break; } } // //---------------------------------------------------------------- // If a notepage structure does not exist yet, make the empty page //---------------------------------------------------------------- // if (!*notepage) *notepage = AddPage(""); Check_Object(*notepage); // //-------------------------------------------------- // Create the new entry if it is not a complex entry //-------------------------------------------------- // Note *notation = (*notepage)->AddNote(token); Check_Object(notation); if (entry) { if (strcmp(entry, "{")) notation->SetEntry(entry); // //---------------------------------------------------------------------- // Now we create a new notation file to read the next part of the stream // into //---------------------------------------------------------------------- // else { NotationFile nested_file; nested_file.m_preserveCase = m_preserveCase; nested_file.Read(stream, macro_tree, NULL, true); DynamicMemoryStream file_buffer(5); file_buffer << "{\r\n"; if (m_preserveCase) file_buffer << "!preservecase=true\r\n"; nested_file.Write(&file_buffer); file_buffer << "}" << '\0'; file_buffer.Rewind(); notation->m_text = static_cast(file_buffer.GetPointer()); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::HandleBangStuff( char *buffer, void *macro_tree, Page **page ) { Check_Pointer(buffer); Check_Pointer(page); Check_Pointer(macro_tree); // //------------------------- // Move past any whitespace //------------------------- // while (*buffer == ' ' || *buffer == '\t') ++buffer; if (!*buffer) return; // //---------------------------- // Handle an include directive //---------------------------- // char *p; if (!strnicmp(buffer, "include", 7)) { p = buffer+7; // //--------------------------------------- // Look for the beginning of the filename //--------------------------------------- // while (*p == ' ' || *p == '\t') ++p; if (*p == '=') { ++p; while (*p == ' ' || *p == '\t') ++p; } // //-------------------------------------------------- // If the filename starts with a quote, strip it off //-------------------------------------------------- // char *file_name; if (*p == '"') { file_name = ++p; p = strrchr(p, '"'); if (p) *p = '\0'; } else file_name = p; // //--------------------------------------------------------------- // If there is a pathname in the file, it is searched for by gos, // otherwise we assume it is relative to us //--------------------------------------------------------------- // char path[MAX_LINE_SIZE]; if (!__mbschr(file_name, '\\')) { Str_Copy(path, GetFileName(), sizeof(path)); p = __mbsrchr(path, '\\'); if (p) { Str_Copy(p+1, file_name, sizeof(path) - (p - path) - 1); file_name = path; } } // //--------------- // Now open it up //--------------- // if (s_cachedIncludes) { stlport::map *map = (stlport::map*)s_cachedIncludes; MString name(file_name); stlport::map::iterator seeker = map->find(file_name); FileStream *header; if (seeker != map->end()) { header = (*seeker).second; header->Rewind(); } else { header = new FileStream(name); (*map)[name] = header; } Check_Object(header); m_fileDependencies.AddDependency(header); Read(header, macro_tree, page, false); } else { FileStream input(file_name); m_fileDependencies.AddDependency(&input); Read(&input, macro_tree, page, false); } } // //------------------------------- // Handle a concatenate directive //------------------------------- // else if (!strnicmp(buffer, "concatenate", 11)) { p = buffer+11; // //------------------------------------ // Look for the beginning of the state //------------------------------------ // while (*p == ' ' || *p == '\t') ++p; if (*p == '=') { ++p; while (*p == ' ' || *p == '\t') ++p; } // //-------------- // set the value //-------------- // m_concatenate = (!stricmp(p, "true") || !stricmp(p, "yes") || atoi(p) != 0); } // //------------------------------- // Handle a concatenate directive //------------------------------- // else if (!strnicmp(buffer, "preservecase", 12)) { p = buffer+12; // //------------------------------------ // Look for the beginning of the state //------------------------------------ // while (*p == ' ' || *p == '\t') ++p; if (*p == '=') { ++p; while (*p == ' ' || *p == '\t') ++p; } // //-------------- // set the value //-------------- // m_preserveCase = (!stricmp(p, "true") || !stricmp(p, "yes") || atoi(p) != 0); } // //------------------------------------------ // Otherwise, we must have ourselves a macro //------------------------------------------ // else { char *entry = NULL; // //-------------------------------- // trim the spaces off of the name //-------------------------------- // if ((p = strchr(buffer, '=')) != NULL) { *p = '\0'; entry = p + 1; --p; while (*p == ' ' || *p == '\t') { *p = '\0'; if (--p < buffer) break; } } // //--------------------------------------------------------- // Make sure the name is decent and that the entry is valid //--------------------------------------------------------- // if (!*buffer || !entry || !*entry) return; Macro::AddValue(macro_tree, buffer, entry); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::HandlePoundStuff( char *buffer, void *macro_tree, Page **page ) { Check_Pointer(buffer); Check_Pointer(page); Check_Pointer(macro_tree); // //------------------------- // Move past any whitespace //------------------------- // while (*buffer == ' ' || *buffer == '\t') ++buffer; if (!*buffer) return; // //---------------------------- // Handle an include directive //---------------------------- // char *p; if (!strnicmp(buffer, "define", 6)) { p = buffer+6; char *entry = NULL; // //-------------------------------- // trim the spaces before the name //-------------------------------- // while (*p == ' ' || *p == '\t') { p++; } buffer = p; // //------------------------------------------- // Skip the name before the first white space //------------------------------------------- // while (*p != ' ' && *p != '\t' && *p != '\0') { p++; } *p=0; p++; // //----------------------------------------------- // trim the spaces between the name and the macro //----------------------------------------------- // while (*p == ' ' || *p == '\t') { p++; } entry = p; // //--------------------------------------------------------- // Make sure the name is decent and that the entry is valid //--------------------------------------------------------- // if (!*buffer || !entry || !*entry) return; Macro::AddValue(macro_tree, buffer, entry); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Page* NotationFile::FindPage(const char *pagename) { Check_Object(this); PageIterator pages(&m_pages); MString page_lwr = pagename; if (pagename) page_lwr.ToLower(); else page_lwr = ""; stlport::map *map = (stlport::map*)m_pageMap; stlport::map::iterator seeker = map->find(page_lwr); if (seeker != map->end()) return (*seeker).second; return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Page* NotationFile::GetPage(const char *pagename) { Page *page = FindPage(pagename); if (!page) STOP(( "%s: [%s] is a required page!", GetFileName(), pagename )); return page; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Page* NotationFile::SetPage(const char *pagename) { Check_Object(this); Page *page = FindPage(pagename); if (!page) page = AddPage(pagename); Check_Object(page); return page; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Page* NotationFile::AddPage(const char *pagename) { Check_Object(this); SetDirty(); Page *page = new(g_Heap) Page(this); Check_Object(page); if (!pagename) page->SetName(""); else page->SetName(pagename); m_pages.Add(page); stlport::map *map = (stlport::map*)m_pageMap; (*map)[page->m_name] = page; return page; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::DeletePage(const char *pagename) { Check_Object(this); Check_Pointer(pagename); MString page_lwr = pagename; if (pagename) page_lwr.ToLower(); else page_lwr = ""; stlport::map *map = (stlport::map*)m_pageMap; stlport::map::iterator seeker = map->find(page_lwr); if (seeker != map->end()) { Page *page = (*seeker).second; map->erase(seeker); Check_Object(page); delete page; SetDirty(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void NotationFile::DeleteAllPages() { Check_Object(this); PageIterator pages(&m_pages); Page *page; while ((page = pages.ReadAndNext()) != NULL) { Check_Object(page); delete page; } stlport::map *map = (stlport::map*)m_pageMap; delete map; m_pageMap = new stlport::map; Check_Pointer(m_pageMap); }