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