1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 #include "../base/defines.h"
11 
12 
13 #include "FileContents.h"
14 #include "IBMApplication.h"
15 #include "ListIterator.h"
16 #include "DXStrings.h"
17 
18 List* FileContents::QueuedForDeletion = NUL(List*);
19 XtIntervalId FileContents::CleanUpTimer = 0;
20 
21 //
22 // Don't fill the file system up with junk nets.
23 // Problem: each time you press the visualize button, you
24 // have to copy a net from $DXROOT/ui.  So when can I delete that file?  The
25 // code is already running synced with dxlink, which makes me think it should
26 // be safe to unlink the file in this destructor, however dxui sometimes can't
27 // find the .cfg file because it's gone already.  So, I'll use a timer which
28 // is real hack, but it tends to work.  If the user quits before the timer
29 // goes off, then the QuitCommand will call us to cleanup one last time.
30 //
~FileContents()31 FileContents::~FileContents()
32 {
33     if (this->contents) delete this->contents;
34     if (this->out_fp) fclose(this->out_fp);
35     if (this->in_file_name) delete this->in_file_name;
36 
37     if (this->sans_extension) {
38 	if (FileContents::QueuedForDeletion == NUL(List*))
39 	    FileContents::QueuedForDeletion = new List;
40 	FileContents::QueuedForDeletion->appendElement((void*)this->sans_extension);
41     }
42 
43     if (this->out_file_name) {
44 	if (FileContents::QueuedForDeletion == NUL(List*))
45 	    FileContents::QueuedForDeletion = new List;
46 
47 	// I want to unlink the file here, but a dxlink-ed dxui is going to
48 	// read the file asnychronously, so if I do delete it, then dxui won't
49 	// be able to read it.  I'll set a timer which should go off after
50 	// dxui has had its shot at the file.
51 	XtAppContext apcxt = theApplication->getApplicationContext();
52 
53 	FileContents::QueuedForDeletion->appendElement((void*)this->out_file_name);
54 	if (FileContents::CleanUpTimer)
55 	    XtRemoveTimeOut(FileContents::CleanUpTimer);
56 	FileContents::CleanUpTimer =
57 	    XtAppAddTimeOut (apcxt, 10000, (XtTimerCallbackProc)
58 		FileContents_UnlinkTO, (XtPointer)0);
59     }
60 }
61 
FileContents_CleanUp()62 extern "C" void FileContents_CleanUp()
63 {
64     FileContents::CleanUp();
65 }
66 
FileContents_UnlinkTO(XtPointer,XtIntervalId *)67 extern "C" void FileContents_UnlinkTO (XtPointer , XtIntervalId*)
68 {
69     FileContents::CleanUpTimer = 0;
70     FileContents::CleanUp();
71 }
72 
CleanUp()73 void FileContents::CleanUp()
74 {
75     if (FileContents::CleanUpTimer) {
76 	XtRemoveTimeOut(FileContents::CleanUpTimer);
77 	FileContents::CleanUpTimer = 0;
78     }
79     if (!FileContents::QueuedForDeletion) return ;
80     ListIterator it(*FileContents::QueuedForDeletion);
81     char *out_file_name;
82     while (out_file_name = (char*)it.getNext()) {
83 	if (out_file_name) {
84 	    if (out_file_name[0]) unlink (out_file_name);
85 	    delete out_file_name;
86 	}
87     }
88     FileContents::QueuedForDeletion->clear();
89 }
90 
91 //
92 // We need 2 methods of initialization because when you start working on the
93 // second file (the .cfg file), it's name must match the first file's except
94 // for the extension.  Another c++ class is called for - something like
95 // NetCfgFileContents - in order to enforce this, but my fingers are tired
96 // of typing.
97 //
initialize(const char * base_name)98 boolean FileContents::initialize (const char* base_name)
99 {
100     ASSERT(this->sans_extension == NUL(char*));
101     this->sans_extension = DuplicateString(base_name);
102     return this->initialize();
103 }
104 
sansExtension()105 const char* FileContents::sansExtension()
106 {
107     ASSERT (this->out_file_name);
108     int name_len = strlen(this->out_file_name);
109     if (this->sans_extension == NUL(char*)) {
110 	this->sans_extension = DuplicateString(this->out_file_name);
111 	this->sans_extension[name_len-4] = '\0';
112     }
113     return this->sans_extension;
114 }
115 
initialize()116 boolean FileContents::initialize ()
117 {
118     //
119     // In order to build output filename, find the base name then
120     // duplicate it onto the end of our tmp directory name.
121     //
122     int name_len = strlen(this->in_file_name);
123     char* ext = &this->in_file_name[name_len-4];
124     if (this->sans_extension == NUL(char*)) {
125 	const char* tmpdir = theIBMApplication->getTmpDirectory();
126 	char junk_file[512];
127 	sprintf (junk_file, "%s/foo.bar", tmpdir);
128 	char* base_name = GetFileBaseName (this->in_file_name, ext);
129 	this->sans_extension = UniqueFilename(junk_file);
130 	if (!this->sans_extension) return FALSE;
131 	this->out_file_name = new char[strlen(this->sans_extension) + 32];
132 	sprintf (this->out_file_name, "%s%s", this->sans_extension,ext);
133 	FILE* junk = fopen(this->sans_extension, "w");
134 	if (junk) fclose(junk);
135     } else {
136 	this->out_file_name = new char[strlen(this->sans_extension) + 32];
137 	sprintf (this->out_file_name, "%s%s", this->sans_extension,ext);
138     }
139 
140     //
141     // Read the contents of the file.
142     //
143     FILE* in_fp = fopen(this->in_file_name, "r");
144     ASSERT (in_fp);
145     /*int filedes = fileno (in_fp);*/
146 
147     struct STATSTRUCT statbuf;
148     STATFUNC(this->in_file_name, &statbuf);
149     this->contents = new char[statbuf.st_size + 1];
150 
151     int bread = fread (this->contents, sizeof(char), (int)statbuf.st_size, in_fp);
152     ASSERT (bread == statbuf.st_size);
153     fclose (in_fp);
154     this->contents[bread] = '\0';
155 
156     //
157     // Open the output file.  This should truncate the file if it exists already.
158     // Because of the way we use files, it's likely to exist already.
159     //
160     this->out_fp = fopen (this->out_file_name, "w");
161     if (!this->out_fp) return FALSE;
162 
163     return TRUE;
164 }
165 
close()166 void FileContents::close()
167 {
168     if (!this->out_fp) return ;
169 
170     int len = strlen(this->contents);
171     int brite = fwrite (this->contents, sizeof(char), len, this->out_fp);
172     //
173     // FIXME: what do you do with an error?
174     //
175     //ASSERT (brite == len);
176 
177     fclose (this->out_fp);
178     this->out_fp = NUL(FILE*);
179 }
180 
181 //
182 // It should be easy to implement this using regcmp/regex, but these functions
183 // don't exist on the pc platforms, and as far as I know, there isn't any
184 // decent substitute.  (They're also missing on sun4 and alphax, but those 2
185 // unix machines do have re_comp instead.)  See ui++/dxui/MacroDefinition.C
186 // for an example of some regular expression code.
187 //
replace(const char * pattern,const char * replacement)188 void FileContents::replace (const char* pattern, const char* replacement)
189 {
190     if (!this->out_fp) return ;
191     if (!this->contents) return ;
192 
193     Changling* cling;
194     List changlings;
195     int pattern_len = strlen(pattern);
196     ASSERT(pattern_len);
197 
198     //
199     // Make a list of all the occurrences of pattern in this->contents.
200     // We'll go back later and scan the resulting list and perform the replacing.
201     //
202     char* ptr = this->contents;
203     char* cp;
204     do {
205 	if (cp = strstr (ptr, pattern))
206 	    cling = new Changling(ptr, cp, pattern, replacement);
207 	else
208 	    cling = new Changling(ptr);
209 	changlings.appendElement((void*)cling);
210 	ptr = cp + pattern_len;
211     } while (cp);
212 
213     char* tmp = this->contents;
214     this->contents = this->processReplacements (&changlings);
215     delete tmp;
216 
217     ListIterator it(changlings);
218     while (cling = (Changling*)it.getNext())
219 	delete cling;
220 }
221 
222 
223 //
224 // clings is a list of objects.  Each obj identifies the first char  in src
225 // of a match and the first char in src after the end of the match.  Replace
226 // the intervening chars with the replacement pattern.
227 //
processReplacements(List * clings)228 char* FileContents::processReplacements (List* clings)
229 {
230     ASSERT ((clings) && (clings->getSize()));
231 
232     //
233     // compute the size of the output.
234     //
235     int size_diff = 0;
236     Changling* cling;
237     ListIterator it(*clings);
238     while (cling = (Changling*)it.getNext()) size_diff+= cling->size_diff;
239 
240     char* new_contents = new char[strlen(this->contents)+1 + size_diff];
241     int next = 0;
242 
243     it.setList(*clings);
244     while (cling = (Changling*)it.getNext()) {
245 	const char* cp;
246 	for (cp = cling->start; ((*cp) && (cp!=cling->begin)); cp++)
247 	    new_contents[next++] = *cp;
248 	if (cling->replacement) {
249 	    strcpy (&new_contents[next], cling->replacement);
250 	    next+= strlen(cling->replacement);
251 	}
252 	new_contents[next] = '\0';
253     }
254     return new_contents;
255 }
256