/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include "FileContents.h" #include "IBMApplication.h" #include "ListIterator.h" #include "Strings.h" List* FileContents::QueuedForDeletion = NUL(List*); XtIntervalId FileContents::CleanUpTimer = 0; // // Don't fill the file system up with junk nets. // Problem: each time you press the visualize button, you // have to copy a net from $DXROOT/ui. So when can I delete that file? The // code is already running synced with dxlink, which makes me think it should // be safe to unlink the file in this destructor, however dxui sometimes can't // find the .cfg file because it's gone already. So, I'll use a timer which // is real hack, but it tends to work. If the user quits before the timer // goes off, then the QuitCommand will call us to cleanup one last time. // FileContents::~FileContents() { if (this->contents) delete this->contents; if (this->out_fp) fclose(this->out_fp); if (this->in_file_name) delete this->in_file_name; if (this->sans_extension) { if (FileContents::QueuedForDeletion == NUL(List*)) FileContents::QueuedForDeletion = new List; FileContents::QueuedForDeletion->appendElement((void*)this->sans_extension); } if (this->out_file_name) { if (FileContents::QueuedForDeletion == NUL(List*)) FileContents::QueuedForDeletion = new List; // I want to unlink the file here, but a dxlink-ed dxui is going to // read the file asnychronously, so if I do delete it, then dxui won't // be able to read it. I'll set a timer which should go off after // dxui has had its shot at the file. XtAppContext apcxt = theApplication->getApplicationContext(); FileContents::QueuedForDeletion->appendElement((void*)this->out_file_name); if (FileContents::CleanUpTimer) XtRemoveTimeOut(FileContents::CleanUpTimer); FileContents::CleanUpTimer = XtAppAddTimeOut (apcxt, 10000, (XtTimerCallbackProc) FileContents_UnlinkTO, (XtPointer)0); } } extern "C" void FileContents_CleanUp() { FileContents::CleanUp(); } extern "C" void FileContents_UnlinkTO (XtPointer , XtIntervalId*) { FileContents::CleanUpTimer = 0; FileContents::CleanUp(); } void FileContents::CleanUp() { if (FileContents::CleanUpTimer) { XtRemoveTimeOut(FileContents::CleanUpTimer); FileContents::CleanUpTimer = 0; } if (!FileContents::QueuedForDeletion) return ; ListIterator it(*FileContents::QueuedForDeletion); char *out_file_name; while (out_file_name = (char*)it.getNext()) { if (out_file_name) { if (out_file_name[0]) unlink (out_file_name); delete out_file_name; } } FileContents::QueuedForDeletion->clear(); } // // We need 2 methods of initialization because when you start working on the // second file (the .cfg file), it's name must match the first file's except // for the extension. Another c++ class is called for - something like // NetCfgFileContents - in order to enforce this, but my fingers are tired // of typing. // boolean FileContents::initialize (const char* base_name) { ASSERT(this->sans_extension == NUL(char*)); this->sans_extension = DuplicateString(base_name); return this->initialize(); } const char* FileContents::sansExtension() { ASSERT (this->out_file_name); int name_len = strlen(this->out_file_name); if (this->sans_extension == NUL(char*)) { this->sans_extension = DuplicateString(this->out_file_name); this->sans_extension[name_len-4] = '\0'; } return this->sans_extension; } boolean FileContents::initialize () { // // In order to build output filename, find the base name then // duplicate it onto the end of our tmp directory name. // int name_len = strlen(this->in_file_name); char* ext = &this->in_file_name[name_len-4]; if (this->sans_extension == NUL(char*)) { const char* tmpdir = theIBMApplication->getTmpDirectory(); char junk_file[512]; sprintf (junk_file, "%s/foo.bar", tmpdir); char* base_name = GetFileBaseName (this->in_file_name, ext); this->sans_extension = UniqueFilename(junk_file); if (!this->sans_extension) return FALSE; this->out_file_name = new char[strlen(this->sans_extension) + 32]; sprintf (this->out_file_name, "%s%s", this->sans_extension,ext); FILE* junk = fopen(this->sans_extension, "w"); if (junk) fclose(junk); } else { this->out_file_name = new char[strlen(this->sans_extension) + 32]; sprintf (this->out_file_name, "%s%s", this->sans_extension,ext); } // // Read the contents of the file. // FILE* in_fp = fopen(this->in_file_name, "r"); ASSERT (in_fp); int filedes = fileno (in_fp); #if defined(DXD_WIN) #define stat _stat #define fstat _fstat #endif struct stat statbuf; fstat (filedes, &statbuf); this->contents = new char[statbuf.st_size + 1]; int bread = fread (this->contents, sizeof(char), (int)statbuf.st_size, in_fp); ASSERT (bread == statbuf.st_size); fclose (in_fp); this->contents[bread] = '\0'; // // Open the output file. This should truncate the file if it exists already. // Because of the way we use files, it's likely to exist already. // this->out_fp = fopen (this->out_file_name, "w"); if (!this->out_fp) return FALSE; return TRUE; } void FileContents::close() { if (!this->out_fp) return ; int len = strlen(this->contents); int brite = fwrite (this->contents, sizeof(char), len, this->out_fp); // // FIXME: what do you do with an error? // //ASSERT (brite == len); fclose (this->out_fp); this->out_fp = NUL(FILE*); } // // It should be easy to implement this using regcmp/regex, but these functions // don't exist on the pc platforms, and as far as I know, there isn't any // decent substitute. (They're also missing on sun4 and alphax, but those 2 // unix machines do have re_comp instead.) See ui++/dxui/MacroDefinition.C // for an example of some regular expression code. // void FileContents::replace (const char* pattern, const char* replacement) { if (!this->out_fp) return ; if (!this->contents) return ; Changling* cling; List changlings; int pattern_len = strlen(pattern); ASSERT(pattern_len); // // Make a list of all the occurrences of pattern in this->contents. // We'll go back later and scan the resulting list and perform the replacing. // char* ptr = this->contents; char* cp; do { if (cp = strstr (ptr, pattern)) cling = new Changling(ptr, cp, pattern, replacement); else cling = new Changling(ptr); changlings.appendElement((void*)cling); ptr = cp + pattern_len; } while (cp); char* tmp = this->contents; this->contents = this->processReplacements (&changlings); delete tmp; ListIterator it(changlings); while (cling = (Changling*)it.getNext()) delete cling; } // // clings is a list of objects. Each obj identifies the first char in src // of a match and the first char in src after the end of the match. Replace // the intervening chars with the replacement pattern. // char* FileContents::processReplacements (List* clings) { ASSERT ((clings) && (clings->getSize())); // // compute the size of the output. // int size_diff = 0; Changling* cling; ListIterator it(*clings); while (cling = (Changling*)it.getNext()) size_diff+= cling->size_diff; char* new_contents = new char[strlen(this->contents)+1 + size_diff]; int next = 0; it.setList(*clings); while (cling = (Changling*)it.getNext()) { const char* cp; for (cp = cling->start; ((*cp) && (cp!=cling->begin)); cp++) new_contents[next++] = *cp; if (cling->replacement) { strcpy (&new_contents[next], cling->replacement); next+= strlen(cling->replacement); } new_contents[next] = '\0'; } return new_contents; }