1 |
/* ASCEND modelling environment |
2 |
Copyright (C) 1990, 1993, 1994 Thomas Guthrie Epperly |
3 |
Copyright (C) 2006 Carnegie Mellon University |
4 |
|
5 |
This program is free software; you can redistribute it and/or modify |
6 |
it under the terms of the GNU General Public License as published by |
7 |
the Free Software Foundation; either version 2, or (at your option) |
8 |
any later version. |
9 |
|
10 |
This program is distributed in the hope that it will be useful, |
11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
GNU General Public License for more details. |
14 |
|
15 |
You should have received a copy of the GNU General Public License |
16 |
along with this program; if not, write to the Free Software |
17 |
Foundation, Inc., 59 Temple Place - Suite 330, |
18 |
Boston, MA 02111-1307, USA. |
19 |
*//** @file |
20 |
Ascend Module Control |
21 |
*//* |
22 |
by Tom Epperly |
23 |
Created: 1/11/90 |
24 |
Last in CVS: $Revision: 1.25 $ $Date: 1998/03/17 22:09:12 $ $Author: ballan $ |
25 |
*/ |
26 |
|
27 |
#include <errno.h> |
28 |
#include <stdarg.h> |
29 |
#include <time.h> |
30 |
#include <ctype.h> |
31 |
#include <sys/stat.h> |
32 |
#include <utilities/ascConfig.h> |
33 |
#include <utilities/ascMalloc.h> |
34 |
#include <utilities/ascEnvVar.h> |
35 |
#include <utilities/ascPanic.h> |
36 |
#include <general/list.h> |
37 |
#include "scanner.h" |
38 |
|
39 |
#include "instance_enum.h" |
40 |
#include "cmpfunc.h" |
41 |
#include "symtab.h" |
42 |
#include "module.h" |
43 |
#include "library.h" |
44 |
#include <general/ospath.h> |
45 |
|
46 |
/* #define SEARCH_DEBUG */ |
47 |
|
48 |
struct module_t { |
49 |
symchar *name; /* module's name, including extension |
50 |
* and version number, no path information. |
51 |
* This is how clients access and find |
52 |
* modules. |
53 |
*/ |
54 |
symchar *filename; /* module's "full" path name---i.e., what |
55 |
* we need to feed to fopen() to open the |
56 |
* file. NULL implies a string module. |
57 |
*/ |
58 |
symchar *base_name; /* module's name including extension |
59 |
* and WITHOUT version number. Clients |
60 |
* should never see this. |
61 |
*/ |
62 |
FILE *f; /* module's FILE pointer. Non null when |
63 |
* reading file, NULL otherwise---the result |
64 |
* of calling fopen() on filename. |
65 |
*/ |
66 |
CONST char *s; /* module's string pointer. Never NULL |
67 |
* except in creation process, destruction |
68 |
* process or if module wraps a file instead |
69 |
* of a string. NULL implies a file. |
70 |
* Not a symchar pointer. |
71 |
*/ |
72 |
struct module_t *required_by; /* A pointer to the module that issued |
73 |
* the REQUIRE statement that caused this |
74 |
* module to be loaded. |
75 |
* Always NULL for string modules. |
76 |
*/ |
77 |
CONST struct module_t *provided_by; |
78 |
/* A pointer to the module that issued |
79 |
* the PROVIDE statement that caused this |
80 |
* module-alias to be created. |
81 |
*/ |
82 |
time_t time_last_modified; /* when filename was last modified */ |
83 |
unsigned long linenum; /* used to store line number when parsing */ |
84 |
unsigned long open_count; /* the number of times this has been opened */ |
85 |
unsigned long version; /* the module's version. Initially zero; is |
86 |
* incremented when a module with the same |
87 |
* name is loaded. |
88 |
*/ |
89 |
struct gl_list_t *stats; /* A pointer to a list of |
90 |
* struct StatementList maintained at |
91 |
* NULL or length 1. It's a gl_list |
92 |
* so interfaces using module don't |
93 |
* have to see all the compiler headers. |
94 |
*/ |
95 |
void *scanbuffer; /* A YY_BUFFER_STATE while the string |
96 |
* is being parsed or in the stack. |
97 |
* NULL otherwise. |
98 |
*/ |
99 |
}; |
100 |
|
101 |
|
102 |
|
103 |
CONST char *g_alt_ending[MOD_FILE_EXTS] = { |
104 |
MOD_OLD_CODE, |
105 |
MOD_OLD_LIBRARY, |
106 |
MOD_NEW_CODE, |
107 |
MOD_NEW_LIBRARY, |
108 |
MOD_NEW_UNITS, |
109 |
MOD_CATCHALL |
110 |
}; |
111 |
|
112 |
|
113 |
/* extern */ |
114 |
struct module_t *g_current_module = NULL; |
115 |
/**< |
116 |
* The current module. Even though this variable is "extern", |
117 |
* do NOT use this variable directly. Instead, use a call to |
118 |
* Asc_CurrentModule() to get the current module. |
119 |
*/ |
120 |
|
121 |
static int g_string_modules_processed = 0; |
122 |
/**< |
123 |
* This is a counter, to be incremented each time it is used to |
124 |
* create a string module name. Should not be reset to 0 |
125 |
* unless all modules have been destroyed. |
126 |
*/ |
127 |
|
128 |
static struct gl_list_t *g_module_list = NULL; |
129 |
#define G_MODULE_LIST_INIT_SIZE 20L |
130 |
|
131 |
/*---------- |
132 |
forward declarations |
133 |
*/ |
134 |
|
135 |
static int CmpModulesNameVers(CONST struct module_t*, CONST struct module_t*); |
136 |
static struct module_t *FindModuleFile(CONST char *, int * CONST, int); |
137 |
static struct module_t *CreateStringModule(CONST char *, int * CONST, CONST char *); |
138 |
/* jds20041214 - winbase.h defines FreeModule(), so changed here to DeleteModule(). */ |
139 |
#define DeleteModule(m) ascfree(m) |
140 |
static unsigned long ModuleNameToInternalNameVers(CONST char *, char * CONST); |
141 |
static int ModuleSearchPath(CONST char*, char*, struct module_t*, int * CONST); |
142 |
static struct module_t *NewModule(CONST char *); |
143 |
static struct module_t *OpenModuleInternal(CONST char *, int * CONST, |
144 |
int, CONST char *); |
145 |
static void RemoveModule(CONST struct module_t *); |
146 |
static struct module_t *SearchForModule(CONST struct module_t *); |
147 |
static int StoreModule(CONST struct module_t *); |
148 |
static void WriteWhyNotFound(char *, int); |
149 |
|
150 |
/*------------------------------------------------------------------------------ |
151 |
MODULE HANDLING |
152 |
*/ |
153 |
|
154 |
/* see module.h for details of functions not documented here */ |
155 |
|
156 |
int Asc_InitModules(unsigned long init_size){ |
157 |
if( g_module_list != NULL ) { |
158 |
return 0; |
159 |
} |
160 |
g_module_list = gl_create(init_size); |
161 |
if( g_module_list == NULL ) { |
162 |
FPRINTF(ASCERR,"FAILED TO CREATE MODULE LIST\n"); |
163 |
return 1; |
164 |
} |
165 |
return 0; |
166 |
} |
167 |
|
168 |
|
169 |
static DestroyFunc g_sldestroy; |
170 |
|
171 |
static void DestroyModule(struct module_t *m){ |
172 |
if (m == NULL) return; |
173 |
if (m->s != NULL) { |
174 |
ascfree((char *)m->s); |
175 |
m->s = NULL; |
176 |
if (m->stats != NULL) { |
177 |
gl_iterate(m->stats,g_sldestroy); |
178 |
gl_destroy(m->stats); |
179 |
m->stats = NULL; |
180 |
} |
181 |
} |
182 |
if (m->scanbuffer!= NULL) { |
183 |
FPRINTF(ASCERR, "Module %s being destroyed while opened by lexer\n", |
184 |
SCP(m->name)); |
185 |
} |
186 |
DeleteModule(m); |
187 |
} |
188 |
|
189 |
|
190 |
void Asc_DestroyModules(DestroyFunc f){ |
191 |
g_sldestroy = f; |
192 |
if (g_module_list != NULL) { |
193 |
gl_iterate(g_module_list,(DestroyFunc)DestroyModule); |
194 |
gl_destroy( g_module_list ); |
195 |
g_module_list = NULL; |
196 |
} |
197 |
} |
198 |
|
199 |
|
200 |
extern struct module_t *Asc_OpenStringModule(CONST char *inputstring, |
201 |
int *status, |
202 |
CONST char *prefix |
203 |
){ |
204 |
char *name = NULL; |
205 |
char *keep_string; |
206 |
int dummy; |
207 |
int input_len; |
208 |
struct module_t *result; |
209 |
|
210 |
if (inputstring != NULL) { |
211 |
/* copy and configure to string we keep for flex processing */ |
212 |
input_len = strlen(inputstring); |
213 |
keep_string = ASC_NEW_ARRAY(char,input_len+3); |
214 |
if (keep_string != NULL) { |
215 |
strcpy(keep_string,inputstring); |
216 |
keep_string[++input_len] = '\0'; |
217 |
keep_string[++input_len] = '\0'; |
218 |
/* create name only if we successfully copied inputstring */ |
219 |
name = ASC_NEW_ARRAY(char,strlen((prefix != NULL)? prefix : "")+35); |
220 |
if (name != NULL) { |
221 |
sprintf(name, "%s_global_%d", ((prefix != NULL) ? prefix : ""), |
222 |
++g_string_modules_processed); |
223 |
} |
224 |
} |
225 |
}else{ |
226 |
keep_string = NULL; |
227 |
} |
228 |
|
229 |
if( status != NULL ) { |
230 |
result = OpenModuleInternal(name, status, FALSE, keep_string); |
231 |
} else { |
232 |
result = OpenModuleInternal(name, &dummy, FALSE, keep_string); |
233 |
} |
234 |
ascfree(name); |
235 |
return result; |
236 |
} |
237 |
|
238 |
|
239 |
extern struct module_t *Asc_OpenModule(CONST char *name, int *status){ |
240 |
if( status != NULL ) { |
241 |
return OpenModuleInternal(name, status, FALSE, NULL); |
242 |
} else { |
243 |
int dummy; |
244 |
return OpenModuleInternal(name, &dummy, FALSE, NULL); |
245 |
} |
246 |
} |
247 |
|
248 |
|
249 |
extern struct module_t *Asc_RequireModule(CONST char *name, int *status){ |
250 |
if( status != NULL ) { |
251 |
return OpenModuleInternal(name, status, TRUE, NULL); |
252 |
} else { |
253 |
int dummy; |
254 |
return OpenModuleInternal(name, &dummy, TRUE, NULL); |
255 |
} |
256 |
} |
257 |
|
258 |
|
259 |
/** |
260 |
This function is the glue between the user callable Asc_OpenModule() |
261 |
and Asc_RequireModule() functions and FindModuleFile(). Consult |
262 |
those functions' documentation for more information. The |
263 |
`do_not_overwrite' flag tells if we were called from |
264 |
Asc_OpenModule() (==FALSE) or Asc_RequireModule() (==TRUE). |
265 |
|
266 |
@param name filename of the module to find |
267 |
@param status status to return to caller |
268 |
@param do_not_overwrite Should we keep existing modules? |
269 |
@param keep_string String we keep and parse if not NULL. |
270 |
|
271 |
When str is NULL: |
272 |
This function calls FindModuleFile() to find the module named `name'. |
273 |
The status of FindModuleFile is returned to the caller in `status'. |
274 |
If the call to FindModuleFile is successful and do_not_overwrite is |
275 |
FALSE, we make the returned module the current module and inform |
276 |
the scanner of the change. |
277 |
When str is NOT NULL: |
278 |
Calls FindModuleFile to guard against duplication, then sets up |
279 |
module and scanner for string scanning. |
280 |
*/ |
281 |
static |
282 |
struct module_t *OpenModuleInternal(CONST char *name, |
283 |
int * CONST status, |
284 |
int do_not_overwrite, |
285 |
CONST char *keep_string) |
286 |
{ |
287 |
struct module_t *new_module; |
288 |
|
289 |
/* |
290 |
* Make sure the user gave us valid data. |
291 |
* This is valid for both string and file modules. |
292 |
* String modules do this if user gives bad input or if |
293 |
* malloc failed in copying string. |
294 |
*/ |
295 |
if(( name == NULL ) || ( *name == '\0' )) { |
296 |
*status = -4; |
297 |
return NULL; |
298 |
} |
299 |
|
300 |
/* |
301 |
* Find the file and create the struct module_t |
302 |
* or create module from string. |
303 |
*/ |
304 |
if (keep_string == NULL) { |
305 |
new_module = FindModuleFile(name, status, do_not_overwrite); |
306 |
if( new_module == NULL ) { |
307 |
/* FindModuleFile set *status */ |
308 |
return NULL; |
309 |
} |
310 |
}else{ |
311 |
new_module = CreateStringModule(name, status, keep_string); |
312 |
if( new_module == NULL ) { |
313 |
/* CreateStringModule set *status */ |
314 |
return NULL; |
315 |
} |
316 |
} |
317 |
|
318 |
/* |
319 |
* Check the return status of FindModuleFile() to determine |
320 |
* if we should tell the scanner about the module |
321 |
*/ |
322 |
if( *status == 5 ) { |
323 |
/* We were requiring the module and it already exists, do |
324 |
* not change the file in the scanner |
325 |
*/ |
326 |
return new_module; |
327 |
} |
328 |
if( *status == 4 ) { |
329 |
/* Recursive require, do not change the files in the scanner */ |
330 |
return new_module; |
331 |
} |
332 |
|
333 |
/* |
334 |
* Store the scanner's position in the current module |
335 |
*/ |
336 |
if( g_current_module != NULL ) { |
337 |
g_current_module->linenum = LineNum(); |
338 |
} |
339 |
|
340 |
/* |
341 |
* Wire up the modules: push existing module onto the new |
342 |
* module's required_by pointer |
343 |
*/ |
344 |
new_module->required_by = g_current_module; |
345 |
g_current_module = new_module; |
346 |
|
347 |
/* |
348 |
* Tell the scanner to parse the new module |
349 |
*/ |
350 |
if (keep_string == NULL) { |
351 |
Asc_ScannerAssignFile(new_module->f,1); |
352 |
} else { |
353 |
asc_assert(new_module->scanbuffer != NULL); |
354 |
Asc_ScannerAssignString(new_module->scanbuffer,1,1); |
355 |
} |
356 |
|
357 |
/* |
358 |
* Return the new module |
359 |
*/ |
360 |
return new_module; |
361 |
} |
362 |
|
363 |
|
364 |
/** |
365 |
Find or create a module named `name'. Return the module and put |
366 |
an exit code in `status'. If `do_not_overwrite' is TRUE, an existing |
367 |
module named `name' will be returned; otherwise, a new module will |
368 |
be created, overwriting any existing module having that name. |
369 |
|
370 |
The value put into `status' is one of: |
371 |
-3 a memory error occurred: not enough memory to create module; |
372 |
returning NULL |
373 |
-2 a potential file matching module name was found but the |
374 |
file could not be opened for reading; returning NULL |
375 |
-1 could not find a file for the module `name'; returning NULL |
376 |
0 a new module was successfully created and is being returned |
377 |
1 a module with `name' already existed; the file it points to |
378 |
does NOT match the file just found for module `name'; the old |
379 |
module was overwritten and the new module is being returned |
380 |
2 a module with `name' already exited; the file it points to |
381 |
matches the file just found for module `name'; the existing |
382 |
module is being returned |
383 |
3 a module with `name' already existed; it was an alias for |
384 |
for another module; the old module was overwritten and the |
385 |
new module is being returned |
386 |
4 a module with `name' already existed; it was being read when |
387 |
we tried to open it again for reading (recursive require). |
388 |
The existing module is being returned. |
389 |
5 The argument `do_not_overwrite' is TRUE and a module named |
390 |
`name' was found, returning it |
391 |
*/ |
392 |
static |
393 |
struct module_t *FindModuleFile(CONST char *name, |
394 |
int * CONST status, |
395 |
int do_not_overwrite) |
396 |
{ |
397 |
struct module_t *new_module; /* the newly created module */ |
398 |
struct module_t *dup = NULL; /* an existing module with the same name */ |
399 |
char filename[PATH_MAX]; /* work area to find module's filename */ |
400 |
int result; /* return value when searching for module */ |
401 |
int error; /* error number returned by fopen() or stat() */ |
402 |
|
403 |
asc_assert(name != NULL); |
404 |
asc_assert(status != NULL); |
405 |
asc_assert(strcmp(name,"")!=0); |
406 |
|
407 |
/* |
408 |
Create space for the module and set its base_name to a "proper" |
409 |
name---i.e,, to the first character after the rightmost slash in |
410 |
the name the user passed to us. |
411 |
*/ |
412 |
new_module = NewModule( name ); |
413 |
if( new_module == NULL ) { |
414 |
/*FPRINTF(ASCERR,"NEW MODULE RETURNED NULL\n");*/ |
415 |
*status = -3; |
416 |
return NULL; |
417 |
} |
418 |
/*FPRINTF(ASCERR,"NEW MODULE RETURNED NON-NULL\n");*/ |
419 |
|
420 |
|
421 |
/* |
422 |
Check to see if a module having the same base_name exists. |
423 |
If so, fetch it. |
424 |
*/ |
425 |
dup = SearchForModule(new_module); |
426 |
|
427 |
if(dup){ |
428 |
CONSOLE_DEBUG("Duplicate module named '%s' was found" |
429 |
,SCP(new_module->base_name) |
430 |
); |
431 |
} |
432 |
|
433 |
/* |
434 |
If we were called from RequireModule, return if a module |
435 |
having this name already exists |
436 |
*/ |
437 |
if(( do_not_overwrite == TRUE ) && ( dup != NULL )) { |
438 |
DeleteModule( new_module ); |
439 |
*status = 5; |
440 |
return dup; |
441 |
} |
442 |
|
443 |
/* |
444 |
Search for the module in ASCENDLIBRARY and, if found, set its |
445 |
`f', `time_last_modified', and `line_number' fields. |
446 |
*/ |
447 |
result = ModuleSearchPath( name, filename, new_module, &error ); |
448 |
|
449 |
/* |
450 |
Check for a memory error in ModuleSearchPath. |
451 |
*/ |
452 |
if( result == -3 ) { |
453 |
DeleteModule( new_module ); |
454 |
*status = -4; |
455 |
return NULL; |
456 |
}/* else{ |
457 |
CONSOLE_DEBUG("FOUND MODULE FILE, result=%d\n",result); |
458 |
}*/ |
459 |
|
460 |
/* |
461 |
If we couldn't find the module or a fopen error occurred, print |
462 |
a message and exit the function |
463 |
*/ |
464 |
if( result == -1 ) { |
465 |
CONSOLE_DEBUG("ModuleSearchPath returned -1, name=%s",name); |
466 |
WriteWhyNotFound(name, error); |
467 |
DeleteModule(new_module); |
468 |
*status = -2; |
469 |
return NULL; |
470 |
} |
471 |
if( result == 1 ) { |
472 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"Unable to locate file for module '%s'", name); |
473 |
DeleteModule(new_module); |
474 |
*status = -1; |
475 |
return NULL; |
476 |
} |
477 |
|
478 |
/* |
479 |
Create a symbol for the filename. We created a symbol for the |
480 |
base_name when we created the module. |
481 |
*/ |
482 |
new_module->filename = AddSymbol(filename); |
483 |
|
484 |
asc_assert(new_module->f != NULL); |
485 |
|
486 |
/* |
487 |
If a module having the same base_name does not exist, |
488 |
set the version number to zero, insert the new module into |
489 |
the module list and return it. |
490 |
*/ |
491 |
if( dup == NULL ) { |
492 |
/* no module with name 'name' exists. Save it and return */ |
493 |
new_module->open_count = 1; |
494 |
new_module->version = 0; |
495 |
sprintf(filename,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
496 |
/*FPRINTF(ASCERR,"SYMBOL FOR FILE IS %s\n",filename);*/ |
497 |
new_module->name = AddSymbol(filename); |
498 |
|
499 |
|
500 |
if( StoreModule( new_module ) != 0 ) { |
501 |
DeleteModule( new_module ); |
502 |
ERROR_REPORTER_HERE(ASC_PROG_ERROR,"COULDN'T STORE MODULE %s",new_module->filename); |
503 |
*status = -3; |
504 |
return NULL; |
505 |
} |
506 |
*status = 0; |
507 |
return new_module; |
508 |
} |
509 |
|
510 |
/* |
511 |
If the existing module's (dup) provided_by pointer is not null, |
512 |
we are overwriting a module-alias. |
513 |
*/ |
514 |
if( dup->provided_by != NULL ) { |
515 |
/* remove the module-alias from the module list and destroy it */ |
516 |
RemoveModule( dup ); |
517 |
DeleteModule( dup ); |
518 |
/* add the new_module to the list and return */ |
519 |
new_module->open_count = 1; |
520 |
new_module->version = 0; |
521 |
sprintf(filename,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
522 |
new_module->name = AddSymbol(filename); |
523 |
if( StoreModule( new_module ) != 0 ) { |
524 |
DeleteModule( new_module ); |
525 |
*status = -3; |
526 |
return NULL; |
527 |
} |
528 |
*status = 3; |
529 |
return new_module; |
530 |
} |
531 |
|
532 |
/* |
533 |
If the existing module's (dup) file pointer `f' is not null, |
534 |
we have some bizarre situation. Either return or PANIC. |
535 |
*/ |
536 |
if( dup->f != NULL ) { |
537 |
if( CmpSymchar( dup->filename, new_module->filename ) != 0 ) { |
538 |
/* |
539 |
we were reading `dup' as if it were module `mod_name' when we |
540 |
were told to treat `new_module' as if it were module `mod_name'. |
541 |
PANIC! |
542 |
*/ |
543 |
fclose(dup->f); |
544 |
fclose(new_module->f); |
545 |
ASC_PANIC("While reading file \"%s\" for module %s," |
546 |
"\n\treceived a request to read \"%s\" as module %s", |
547 |
SCP(dup->filename), SCP(dup->base_name), |
548 |
SCP(new_module->filename), SCP(new_module->base_name)); |
549 |
} |
550 |
if( dup->time_last_modified != new_module->time_last_modified ) { |
551 |
/* timestamp changed while reading the file. PANIC! */ |
552 |
fclose(dup->f); |
553 |
fclose(new_module->f); |
554 |
ASC_PANIC("Timestamp on file \"%s\" changed\n" |
555 |
"while file was open for reading", SCP(dup->filename)); |
556 |
} |
557 |
/* recursive require! */ |
558 |
ERROR_REPORTER_HERE(ASC_USER_WARNING |
559 |
,"Module '%s' includes itself either directly or indirectly (ignoring)" |
560 |
, SCP(new_module->base_name) |
561 |
); |
562 |
DeleteModule( new_module ); |
563 |
*status = 4; |
564 |
return dup; |
565 |
} |
566 |
|
567 |
if(( CmpSymchar(dup->filename, new_module->filename) == 0 ) |
568 |
&& ( dup->time_last_modified == new_module->time_last_modified )) |
569 |
{ |
570 |
/* |
571 |
The same module. Copy the file pointer (f) from `new_module' |
572 |
to `dup', increment dup's open count, free `new_module', and |
573 |
return `dup' |
574 |
*/ |
575 |
dup->f = new_module->f; |
576 |
dup->linenum = new_module->linenum; |
577 |
dup->open_count++; |
578 |
DeleteModule(new_module); |
579 |
*status = 2; |
580 |
return dup; |
581 |
} |
582 |
|
583 |
/* |
584 |
Either two different files both want to be called module `nmae', |
585 |
or the timestamp on the file has changed. In either case, treat |
586 |
`new_module' as a new version of `dup'. |
587 |
*/ |
588 |
new_module->open_count = 1; |
589 |
new_module->version = 1 + dup->version; |
590 |
sprintf(filename,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
591 |
new_module->name = AddSymbol(filename); |
592 |
if( StoreModule( new_module ) != 0 ) { |
593 |
DeleteModule( new_module ); |
594 |
*status = -3; |
595 |
return NULL; |
596 |
} |
597 |
*status = 1; |
598 |
return new_module; |
599 |
} |
600 |
|
601 |
/** Create a module named `name'. Return the module and put |
602 |
* an exit code in `status'. |
603 |
* Name is expected to be unique for all time (or at least until we |
604 |
* reinit the compiler). |
605 |
* |
606 |
* The value put into `status' is one of (following FindModuleFile): |
607 |
* -3 a memory error occurred: not enough memory to create module; |
608 |
* returning NULL. |
609 |
* -2 a potential file matching module name was found but the |
610 |
* file could not be opened for reading; returning NULL. |
611 |
* (never occurs). |
612 |
* -1 could not find a file for the module `name'; returning NULL |
613 |
* (never occurs). |
614 |
* 0 a new module was successfully created and is being returned |
615 |
* 1 a module with `name' already existed; the file it points to |
616 |
* does NOT match the file just found for module `name'; the old |
617 |
* module was overwritten and the new module is being returned |
618 |
* (never occurs). |
619 |
* 2 a module with `name' already exited; the file it points to |
620 |
* matches the file just found for module `name'; the existing |
621 |
* module is being returned |
622 |
* (never occurs). |
623 |
* 3 a module with `name' already existed; it was an alias for |
624 |
* for another module; the old module was overwritten and the |
625 |
* new module is being returned |
626 |
* (never occurs). |
627 |
* 4 a module with `name' already existed; it was being read when |
628 |
* we tried to open it again for reading (recursive require). |
629 |
* The existing module is being returned. |
630 |
* (never occurs). |
631 |
* 5 The argument `do_not_overwrite' is TRUE and a module named |
632 |
* `name' was found, returning it |
633 |
* (never occurs). |
634 |
* |
635 |
* In summary, only -3 and 0 are expected returns. If we decide to |
636 |
* have some less fascist string module naming scheme, we may reinstate |
637 |
* some of these returns. |
638 |
*/ |
639 |
static |
640 |
struct module_t *CreateStringModule(CONST char *name, |
641 |
int * CONST status, |
642 |
CONST char *keep_string) |
643 |
{ |
644 |
/* if this is 1, lots of inappropriate for string buffers code is |
645 |
* uncommented |
646 |
*/ |
647 |
#define USE_FOR_STRINGS 0 |
648 |
|
649 |
struct module_t *new_module; /* the newly created module */ |
650 |
struct module_t *dup = NULL; /* an existing module with the same name */ |
651 |
char filename[PATH_MAX]; /* work area to find module's filename */ |
652 |
#if USE_FOR_STRINGS |
653 |
int error; /* errno returned by fopen() or stat() */ |
654 |
int result; /* return value when searching for module */ |
655 |
#endif /* use for strings */ |
656 |
|
657 |
asc_assert(name != NULL); |
658 |
asc_assert(keep_string != NULL); |
659 |
asc_assert(status != NULL); |
660 |
|
661 |
/* |
662 |
* Create space for the module and set its base_name to a "proper" |
663 |
* name---i.e,, to the first character after the rightmost slash in |
664 |
* the name the user passed to us. |
665 |
*/ |
666 |
new_module = NewModule( name ); |
667 |
if( new_module == NULL ) { |
668 |
*status = -3; |
669 |
return NULL; |
670 |
} |
671 |
|
672 |
/* |
673 |
* Check to see if a module having the same base_name exists. |
674 |
* If so, fetch it. |
675 |
*/ |
676 |
dup = SearchForModule( new_module ); |
677 |
asc_assert(dup == NULL); /* string modules are to be unique */ |
678 |
/* probably should be ascpanic to avoid mystery messages */ |
679 |
|
680 |
#if USE_FOR_STRINGS |
681 |
/* |
682 |
* If were we called from RequireModule, return if a module |
683 |
* having this name already exists |
684 |
*/ |
685 |
if(( do_not_overwrite == TRUE ) && ( dup != NULL )) { |
686 |
DeleteModule( new_module ); |
687 |
*status = 5; |
688 |
return dup; |
689 |
} |
690 |
#endif /* use for strings */ |
691 |
|
692 |
|
693 |
#if USE_FOR_STRINGS |
694 |
/* |
695 |
* Search for the module in ASCENDLIBRARY and, if found, set its |
696 |
* `f', `time_last_modified', and `line_number' fields. |
697 |
*/ |
698 |
result = ModuleSearchPath( name, filename, new_module, &error ); |
699 |
|
700 |
/* |
701 |
* Check for a memory error in ModuleSearchPath. |
702 |
*/ |
703 |
if( result == -3 ) { |
704 |
DeleteModule( new_module ); |
705 |
*status = -3; |
706 |
return NULL; |
707 |
} |
708 |
|
709 |
/* |
710 |
* If we couldn't find the module or a fopen error occurred, print |
711 |
* a message and exit the function |
712 |
*/ |
713 |
if( result == -1 ) { |
714 |
CONSOLE_DEBUG("ModuleSearchPath returned -1, name=%s, filename=%s",name,filename); |
715 |
WriteWhyNotFound(filename, error ); |
716 |
DeleteModule(new_module); |
717 |
*status = -2; |
718 |
return NULL; |
719 |
} |
720 |
if( result == 1 ) { |
721 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"Unable to locate file for module '%s'", name); |
722 |
DeleteModule(new_module); |
723 |
*status = -1; |
724 |
return NULL; |
725 |
} |
726 |
|
727 |
#else |
728 |
new_module->s = keep_string; |
729 |
new_module->f = NULL; |
730 |
new_module->linenum = 1; |
731 |
/* The following is really dumb, but should keep everyone happy. |
732 |
* I'll be damned if I can find a function call that returns |
733 |
* time since the epoch without some other input pointer I don't have |
734 |
* just by looking at the man pages. |
735 |
*/ |
736 |
new_module->time_last_modified = (time_t)g_string_modules_processed; |
737 |
#endif /* use for strings */ |
738 |
|
739 |
/* |
740 |
* Create a symbol for the filename. We created a symbol for the |
741 |
* base_name when we created the module. |
742 |
*/ |
743 |
#if USE_FOR_STRINGS |
744 |
new_module->filename = AddSymbol(filename); |
745 |
#else |
746 |
new_module->filename = NULL; |
747 |
#endif /* use for strings */ |
748 |
|
749 |
/* |
750 |
* If a module having the same base_name does not exist, |
751 |
* set the version number to zero, insert the new module into |
752 |
* the module list and return it. |
753 |
*/ |
754 |
if( dup == NULL ) { /* always TRUE */ |
755 |
/* no module with name 'name' exists. Save it and return */ |
756 |
new_module->open_count = 1; |
757 |
new_module->version = 0; |
758 |
sprintf(filename,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
759 |
new_module->name = AddSymbol(filename); |
760 |
if( StoreModule( new_module ) != 0 ) { |
761 |
DeleteModule( new_module ); |
762 |
*status = -3; |
763 |
return NULL; |
764 |
} |
765 |
new_module->scanbuffer = |
766 |
Asc_ScannerCreateStringBuffer(keep_string,strlen(keep_string)); |
767 |
*status = 0; |
768 |
return new_module; |
769 |
} |
770 |
|
771 |
#if USE_FOR_STRINGS |
772 |
/* |
773 |
* If the existing module's (dup) provided_by pointer is not null, |
774 |
* we are overwriting a module-alias. |
775 |
*/ |
776 |
if( dup->provided_by != NULL ) { |
777 |
/* remove the module-alias from the module list and destroy it */ |
778 |
RemoveModule( dup ); |
779 |
DeleteModule( dup ); |
780 |
/* add the new_module to the list and return */ |
781 |
new_module->open_count = 1; |
782 |
new_module->version = 0; |
783 |
sprintf(filename,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
784 |
new_module->name = AddSymbol(filename); |
785 |
if( StoreModule( new_module ) != 0 ) { |
786 |
DeleteModule( new_module ); |
787 |
*status = -3; |
788 |
return NULL; |
789 |
} |
790 |
*status = 3; |
791 |
return new_module; |
792 |
} |
793 |
|
794 |
/* |
795 |
* If the existing module's (dup) file pointer `f' is not null, |
796 |
* we have some bizarre situation. Either return or PANIC. |
797 |
*/ |
798 |
if( dup->f != NULL ) { |
799 |
if( CmpSymchar( dup->filename, new_module->filename ) != 0 ) { |
800 |
/* we were reading `dup' as if it were module `mod_name' when we |
801 |
* were told to treat `new_module' as if it were module `mod_name'. |
802 |
* PANIC! |
803 |
*/ |
804 |
fclose(dup->f); |
805 |
fclose(new_module->f); |
806 |
ASC_PANIC("While reading file \"%s\" for module %s," |
807 |
"\n\treceived a request to read \"%s\" as module %s", |
808 |
SCP(dup->filename), SCP(dup->base_name), |
809 |
SCP(new_module->filename), SCP(new_module->base_name)); |
810 |
} |
811 |
if( dup->time_last_modified != new_module->time_last_modified ) { |
812 |
/* timestamp changed while reading the file. PANIC! |
813 |
*/ |
814 |
fclose(dup->f); |
815 |
fclose(new_module->f); |
816 |
ASC_PANIC("Timestamp on file \"%s\" changed\n" |
817 |
"while file was open for reading", SCP(dup->filename)); |
818 |
} |
819 |
/* recursive require! */ |
820 |
ERROR_REPORTER_HERE(ASC_USER_WARNING |
821 |
,"Module %s includes itself either directly or indirectly (ignoring)" |
822 |
, SCP(new_module->base_name) |
823 |
); |
824 |
DeleteModule( new_module ); |
825 |
*status = 4; |
826 |
return dup; |
827 |
} |
828 |
|
829 |
if(( CmpSymchar(dup->filename, new_module->filename) == 0 ) |
830 |
&& ( dup->time_last_modified == new_module->time_last_modified )) |
831 |
{ |
832 |
/* |
833 |
* The same module. Copy the file pointer (f) from `new_module' |
834 |
* to `dup', increment dup's open count, free `new_module', and |
835 |
* return `dup' |
836 |
*/ |
837 |
dup->f = new_module->f; |
838 |
dup->linenum = new_module->linenum; |
839 |
dup->open_count++; |
840 |
DeleteModule(new_module); |
841 |
*status = 2; |
842 |
return dup; |
843 |
} |
844 |
|
845 |
/* |
846 |
* Either two different files both want to be called module `nmae', |
847 |
* or the timestamp on the file has changed. In either case, treat |
848 |
* `new_module' as a new version of `dup'. |
849 |
*/ |
850 |
new_module->open_count = 1; |
851 |
new_module->version = 1 + dup->version; |
852 |
sprintf(filename,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
853 |
new_module->name = AddSymbol(filename); |
854 |
if( StoreModule( new_module ) != 0 ) { |
855 |
DeleteModule( new_module ); |
856 |
*status = -3; |
857 |
return NULL; |
858 |
} |
859 |
*status = 1; |
860 |
return new_module; |
861 |
#endif /* use for strings */ |
862 |
ASC_PANIC("String buffer \"%s\" misunderstood" |
863 |
"while opening for reading", new_module->name); |
864 |
|
865 |
} |
866 |
|
867 |
|
868 |
/*------------------------------------------------------------------------------ |
869 |
OPENING A MODULE FROM THE SEARCHPATH |
870 |
*/ |
871 |
|
872 |
struct ModuleSearchData{ |
873 |
struct FilePath *fp; /**< the relative path we're searching for */ |
874 |
int error; /**< error code (in case stat or fopen failed) */ |
875 |
time_t mtime; /**< mtime (from the stat command) */ |
876 |
FILE *f; /**< return the open file pointer */ |
877 |
ospath_stat_t buf; |
878 |
struct FilePath *fp_found; /**< the full path we found */ |
879 |
}; |
880 |
|
881 |
FilePathTestFn module_searchpath_test; |
882 |
|
883 |
/** |
884 |
@return 1 on success |
885 |
*/ |
886 |
int module_searchpath_test(struct FilePath *path,void *searchdata){ |
887 |
struct FilePath *fp1; |
888 |
struct ModuleSearchData *sd; |
889 |
FILE *f; |
890 |
#ifdef SEARCH_DEBUG |
891 |
char *tmp; |
892 |
#endif |
893 |
|
894 |
sd = (struct ModuleSearchData *)searchdata; |
895 |
asc_assert(sd!=NULL); |
896 |
asc_assert(sd->fp!=NULL); |
897 |
|
898 |
|
899 |
#ifdef SEARCH_DEBUG |
900 |
tmp=ospath_str(sd->fp); |
901 |
CONSOLE_DEBUG("About to concat path '%s'...",tmp); |
902 |
ospath_free_str(tmp); |
903 |
#endif |
904 |
|
905 |
fp1 = ospath_concat(path,sd->fp); |
906 |
if(fp1==NULL){ |
907 |
CONSOLE_DEBUG("Couldn't concatenate path"); |
908 |
return 0; |
909 |
} |
910 |
|
911 |
#ifdef SEARCH_DEBUG |
912 |
tmp = ospath_str(sd->fp); |
913 |
CONSOLE_DEBUG("Checking for path '%s'...",tmp); |
914 |
ospath_free_str(tmp); |
915 |
#endif |
916 |
|
917 |
if(ospath_stat(fp1,&sd->buf)){ |
918 |
sd->error = errno; |
919 |
/* CONSOLE_DEBUG("Stat failed");*/ |
920 |
ospath_free(fp1); |
921 |
return 0; |
922 |
} |
923 |
|
924 |
f = ospath_fopen(fp1, "r"); |
925 |
if(f==NULL){ |
926 |
sd->error = errno; |
927 |
#ifdef SEARCH_DEBUG |
928 |
CONSOLE_DEBUG("Fopen failed"); |
929 |
#endif |
930 |
ospath_free(fp1); |
931 |
return 0; |
932 |
} |
933 |
|
934 |
sd->mtime = sd->buf.st_mtime; |
935 |
sd->f = f; |
936 |
sd->fp_found = fp1; |
937 |
|
938 |
#ifdef SEARCH_DEBUG |
939 |
CONSOLE_DEBUG("File found"); |
940 |
#endif |
941 |
return 1; |
942 |
} |
943 |
|
944 |
|
945 |
/** |
946 |
This function tries to find a file corresponding to the argument |
947 |
"name" by sending "name" to the function ModuleStatFile() which |
948 |
will attempt to open "name" as a file. If that fails, this |
949 |
function then prepends each entry in the search path |
950 |
ASC_ENV_LIBRARY to "name" and attempts to open the resulting |
951 |
file (by once again calling ModuleStatFile()). |
952 |
|
953 |
On success, the argument "filename" will be set to the path to the |
954 |
file, and the `f', `time_last_modified', and `linenum' members of |
955 |
the module `m' will be set. (The 'FILE *' will be left open for |
956 |
reading and the handle returned in the module struct.) |
957 |
|
958 |
If ModuleStatFile() encounters an error opening the file, the |
959 |
value of errno will be passed back to the caller in the `error' |
960 |
argument. |
961 |
|
962 |
@param name filename to be searched for (in cwd & in search path). |
963 |
@param filename (output) the location of the found file (if found) |
964 |
@param error (output) error code from ModuleStatFile |
965 |
@param m (output) a pointer to the opened module_t object (if found) |
966 |
|
967 |
@return |
968 |
-4 Invalid partial path in parameter 'filename'. |
969 |
-3 Memory error occurred when trying to get ASC_ENV_LIBRARY |
970 |
-1 Error encountered in ModuleStatFile, check `error' argument |
971 |
0 Success |
972 |
1 Could not find a file named "name" |
973 |
*/ |
974 |
static |
975 |
int ModuleSearchPath(CONST char *name, |
976 |
char *filename, |
977 |
struct module_t *m, |
978 |
int * CONST error |
979 |
){ |
980 |
struct FilePath *fp1, *fp2; |
981 |
char *tmp; |
982 |
struct FilePath **sp1 = NULL; |
983 |
struct ModuleSearchData sd; |
984 |
|
985 |
asc_assert( name != NULL ); |
986 |
asc_assert( filename != NULL ); |
987 |
asc_assert( m != NULL ); |
988 |
asc_assert( error != NULL ); |
989 |
|
990 |
/* CONSOLE_DEBUG("Launching ModuleSearchPath with '%s'",name); */ |
991 |
|
992 |
fp1 = ospath_new_noclean(name); |
993 |
if(fp1==NULL){ |
994 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"Invalid partial path '%s'",name); |
995 |
ospath_free(fp1); |
996 |
return -4; |
997 |
} |
998 |
|
999 |
tmp = ospath_str(fp1); |
1000 |
/* CONSOLE_DEBUG("Searching for '%s'",tmp); */ |
1001 |
ospath_free_str(tmp); |
1002 |
|
1003 |
/* attempt to open "name" directly */ |
1004 |
if(0==ospath_stat(fp1,&sd.buf) && NULL!=(sd.f = ospath_fopen(fp1,"r")) ){ |
1005 |
|
1006 |
CONSOLE_DEBUG("File '%s' opened directly, without path search",name); |
1007 |
sd.fp_found = fp1; |
1008 |
|
1009 |
}else{ |
1010 |
|
1011 |
/* CONSOLE_DEBUG("ENV var name is '%s'",ASC_ENV_LIBRARY); */ |
1012 |
|
1013 |
tmp = Asc_GetEnv(ASC_ENV_LIBRARY); |
1014 |
if(tmp==NULL){ |
1015 |
ERROR_REPORTER_HERE(ASC_PROG_ERROR,"No paths to search (is env var '%s' set?)",ASC_ENV_LIBRARY); |
1016 |
ospath_free(fp1); |
1017 |
return 1; |
1018 |
} |
1019 |
|
1020 |
/* CONSOLE_DEBUG("ENV var value is '%s'",tmp); */ |
1021 |
|
1022 |
sp1 = ospath_searchpath_new(tmp); |
1023 |
if(sp1==NULL){ |
1024 |
ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Unable to process %s value '%s'",ASC_ENV_LIBRARY,tmp); |
1025 |
/* memory error */ |
1026 |
ASC_FREE(tmp); |
1027 |
ospath_free(fp1); |
1028 |
return -3; |
1029 |
} |
1030 |
ascfree(tmp); |
1031 |
|
1032 |
/* CONSOLE_DEBUG("Created SP with %d elements",ospath_searchpath_length(sp1)); */ |
1033 |
|
1034 |
sd.fp = fp1; |
1035 |
|
1036 |
fp2 = ospath_searchpath_iterate(sp1, &module_searchpath_test, &sd); |
1037 |
|
1038 |
if(fp2==NULL){ |
1039 |
*error = sd.error; |
1040 |
CONSOLE_DEBUG("File '%s' not found in search path (%d)",name,sd.error); |
1041 |
ospath_searchpath_free(sp1); |
1042 |
return -1; |
1043 |
} |
1044 |
|
1045 |
tmp = ospath_str(fp2); |
1046 |
asc_assert(tmp!=NULL); |
1047 |
/* CONSOLE_DEBUG("Found file in '%s' in search path",tmp); */ |
1048 |
ospath_searchpath_free(sp1); |
1049 |
ospath_free_str(tmp); |
1050 |
} |
1051 |
|
1052 |
m->f = sd.f; |
1053 |
m->time_last_modified = sd.mtime; |
1054 |
m->linenum = 1; |
1055 |
ospath_strncpy(sd.fp_found,filename,PATH_MAX); |
1056 |
if(fp1!=sd.fp_found){ |
1057 |
ospath_free(fp1); |
1058 |
} |
1059 |
ospath_free(sd.fp_found); |
1060 |
return 0; /* success */ |
1061 |
} |
1062 |
|
1063 |
|
1064 |
/** |
1065 |
Print an error (based on the errno `error') explaining why we could |
1066 |
not open/stat the file named `filename'. |
1067 |
*/ |
1068 |
static |
1069 |
void WriteWhyNotFound(char *filename, int error) |
1070 |
{ |
1071 |
switch( error ) { |
1072 |
case EACCES: |
1073 |
ERROR_REPORTER_HERE(ASC_USER_ERROR, |
1074 |
"File or directory permissions don't allow you to access '%s'.", |
1075 |
filename |
1076 |
); |
1077 |
break; |
1078 |
case EFAULT: |
1079 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1080 |
,"Filename pointer or buffer pointer was bad." |
1081 |
); |
1082 |
break; |
1083 |
case EIO: |
1084 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1085 |
,"I/O error in reading '%s'.",filename |
1086 |
); |
1087 |
break; |
1088 |
case ENAMETOOLONG: |
1089 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1090 |
,"The path for '%s' is too long.",filename |
1091 |
); |
1092 |
break; |
1093 |
case ENOENT: |
1094 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1095 |
,"File '%s' doesn't exist.",filename |
1096 |
); |
1097 |
break; |
1098 |
case ENOTDIR: |
1099 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1100 |
,"A component of the path name '%s' is not a directory.",filename |
1101 |
); |
1102 |
break; |
1103 |
#ifndef __WIN32__ |
1104 |
case ELOOP: |
1105 |
/* no symlinks in windows land */ |
1106 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1107 |
,"There are too many symbolic links in '%s'.",filename |
1108 |
); |
1109 |
break; |
1110 |
#endif /* __WIN32__ */ |
1111 |
default: |
1112 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1113 |
,"File not available for unknown reasons (error %d)" |
1114 |
,error |
1115 |
); |
1116 |
break; |
1117 |
} |
1118 |
} |
1119 |
|
1120 |
|
1121 |
/** See the header file for this function's documentation |
1122 |
**/ |
1123 |
extern int Asc_ModuleCreateAlias(CONST struct module_t *m, CONST char *name) |
1124 |
{ |
1125 |
struct module_t *new_module; |
1126 |
struct module_t *dup; |
1127 |
char mod_name[PATH_MAX]; |
1128 |
|
1129 |
asc_assert( m != NULL ); |
1130 |
|
1131 |
/* |
1132 |
* Make sure the user gave us good data |
1133 |
*/ |
1134 |
if(( name == NULL ) || ( *name == '\0' )) { |
1135 |
return -4; |
1136 |
} |
1137 |
|
1138 |
/* |
1139 |
* Make sure m is not a module-alias. This shouldn't happen since |
1140 |
* the user should not be able to get his hands on module-aliases. |
1141 |
*/ |
1142 |
if( m->provided_by != NULL ) { |
1143 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
1144 |
,"Module '%s' is a module-alias. Module to alias must not be a module-alias\n" |
1145 |
,SCP(m->name) |
1146 |
); |
1147 |
return -4; |
1148 |
} |
1149 |
|
1150 |
/* |
1151 |
* Create a new module or return -3 if we could not |
1152 |
*/ |
1153 |
new_module = NewModule( name ); |
1154 |
if( new_module == NULL ) { |
1155 |
return -3; |
1156 |
} |
1157 |
|
1158 |
dup = SearchForModule( new_module ); |
1159 |
|
1160 |
if( dup == NULL ) { |
1161 |
/* no module with this name exists. Set up the module-alias |
1162 |
* and store it |
1163 |
*/ |
1164 |
new_module->provided_by = m; |
1165 |
/* next line probably redundant */ |
1166 |
new_module->base_name = AddSymbol(SCP(new_module->base_name)); |
1167 |
new_module->version = 0; |
1168 |
sprintf(mod_name,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
1169 |
new_module->name = AddSymbol(mod_name); |
1170 |
if( StoreModule( new_module ) != 0 ) { |
1171 |
DeleteModule( new_module ); |
1172 |
return -3; |
1173 |
} |
1174 |
return 0; |
1175 |
} |
1176 |
|
1177 |
/* |
1178 |
* Check to see if a module-alias with this name already exists |
1179 |
* that points to this module. If so, destroy the new one |
1180 |
* silently and return. |
1181 |
*/ |
1182 |
if( dup->provided_by == m ) { |
1183 |
DeleteModule( new_module ); |
1184 |
return 2; |
1185 |
} |
1186 |
|
1187 |
/* |
1188 |
* Check to see if a module-alias with this name already exists |
1189 |
* that points to different module. If so, check to see if the |
1190 |
* filenames are the same. If the filename not the same, overwrite |
1191 |
* the existing module noisly; else, overwrite it silently. |
1192 |
*/ |
1193 |
if( dup->provided_by != NULL ) { |
1194 |
if( CmpSymchar( dup->provided_by->filename, m->filename ) != 0 ) { |
1195 |
ERROR_REPORTER_HERE(ASC_USER_WARNING, |
1196 |
"PROVIDE \"%s\" in file '%s' overwrites PROVIDE \"%s\" in file '%s'" |
1197 |
,SCP(new_module->base_name), SCP(m->filename) |
1198 |
,SCP(dup->base_name), SCP(dup->provided_by->filename) |
1199 |
); |
1200 |
} |
1201 |
RemoveModule( dup ); |
1202 |
DeleteModule( dup ); |
1203 |
new_module->provided_by = m; |
1204 |
/* probably redundant addsymbol next line */ |
1205 |
new_module->base_name = AddSymbol(SCP(new_module->base_name)); |
1206 |
new_module->version = 0; |
1207 |
sprintf(mod_name,"%s<%lu>",SCP(new_module->base_name),new_module->version); |
1208 |
new_module->name = AddSymbol(mod_name); |
1209 |
if( StoreModule( new_module ) != 0 ) { |
1210 |
DeleteModule( new_module ); |
1211 |
return -3; |
1212 |
} |
1213 |
return 3; |
1214 |
} |
1215 |
|
1216 |
/* |
1217 |
* Check to see if the duplicate module is actually the current |
1218 |
* module---i.e., module ``foo.a4c'' contains the statement |
1219 |
* ``PROVIDE "foo.a4c";'' If so, destroy the new_module silently. |
1220 |
*/ |
1221 |
if( dup == g_current_module ) { |
1222 |
DeleteModule( new_module ); |
1223 |
return 1; |
1224 |
} |
1225 |
|
1226 |
/* |
1227 |
* If we made it here, we attempting to PROVIDE a real module that |
1228 |
* already exists. Issue an error, destroy the new_module, and |
1229 |
* return. |
1230 |
*/ |
1231 |
ERROR_REPORTER_HERE(ASC_USER_ERROR, |
1232 |
"File \"%s\" cannot PROVIDE '%s' because a module with that name already exists (%s)." |
1233 |
, SCP(m->filename), SCP(new_module->base_name), SCP(dup->name) |
1234 |
); |
1235 |
DeleteModule( new_module ); |
1236 |
return -2; |
1237 |
} |
1238 |
|
1239 |
/** |
1240 |
Allocate space for a new module and set its fields to some |
1241 |
reasonable defaults. If `name' is not NULL, set the module's |
1242 |
base_name to point to the first character after the rightmost |
1243 |
slash (`/' on UNIX, `/' on Windows) in `name', or to `name' if |
1244 |
it contains no slashes. Note that this function will create |
1245 |
a symbol for base_name. Return NULL if malloc fails. |
1246 |
*/ |
1247 |
static |
1248 |
struct module_t *NewModule(CONST char *name){ |
1249 |
struct module_t *newmodule; /* the new module */ |
1250 |
char *tmp; /* result of strrchr(); used to get base_name */ |
1251 |
struct FilePath *fp1; |
1252 |
|
1253 |
newmodule = ASC_NEW(struct module_t); |
1254 |
if( newmodule == NULL ) { |
1255 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"UNABLE TO MALLOC FOR NEW MODULE"); |
1256 |
return NULL; |
1257 |
} |
1258 |
newmodule->name = NULL; |
1259 |
newmodule->filename = NULL; |
1260 |
newmodule->f = NULL; |
1261 |
newmodule->s = NULL; |
1262 |
newmodule->stats = NULL; |
1263 |
newmodule->scanbuffer = NULL; |
1264 |
newmodule->required_by = NULL; |
1265 |
newmodule->provided_by = NULL; |
1266 |
newmodule->time_last_modified = (time_t)0; |
1267 |
newmodule->linenum = 0; |
1268 |
newmodule->open_count = 0; |
1269 |
newmodule->version = ULONG_MAX; |
1270 |
|
1271 |
/* CONSOLE_DEBUG("New path: %s",name); */ |
1272 |
fp1 = ospath_new(name); |
1273 |
tmp = ospath_getbasefilename(fp1); |
1274 |
if(tmp!=NULL && strlen(tmp)!=0){ |
1275 |
newmodule->base_name = AddSymbol(tmp); |
1276 |
} |
1277 |
ospath_free_str(tmp); /* we can free tmp, since 'AddSymbol' takes a copy */ |
1278 |
ospath_free(fp1); |
1279 |
|
1280 |
/* CONSOLE_DEBUG("Module base-name: %s",newmodule->base_name); */ |
1281 |
return newmodule; |
1282 |
} |
1283 |
|
1284 |
/*------------------------------------------------------------------------------ |
1285 |
GLOBAL MODULE LIST MANAGEMENT |
1286 |
|
1287 |
* The following four functions are used to store, remove, and search |
1288 |
* for modules in the global module list, the gl_list `g_module_list'. |
1289 |
* We do this (instead of inlining the calls to gl_*()) to provide a |
1290 |
* bit of data abstraction, though I'm sure there are some implicit |
1291 |
* assumptions in the code as to how modules are stored on the gl_list. |
1292 |
* |
1293 |
* These functions handle a NULL g_module_list gracefully. |
1294 |
* |
1295 |
* Modules are appended to the g_module_list in the order they are |
1296 |
* OPENED, so later versions of the same file appear toward the end of |
1297 |
* the list. Note that a module in the back of the list may define |
1298 |
* types needed by module at the front of the list, due to the magic of |
1299 |
* the REQUIRE statement. |
1300 |
* |
1301 |
* Since the modules are in the order opened, implementing a |
1302 |
* Re-Read-All-Files function wouldn't be too difficult, but the |
1303 |
* REQUIRE statements in each file would still have to be followed. |
1304 |
* |
1305 |
* Since module-aliases are implemented as modules and are stored on |
1306 |
* the g_module_list, they must sometimes be replaced by actual |
1307 |
* modules or new module-aliases. When this occurs, a module-alias |
1308 |
* must be removed from the g_module_list. |
1309 |
* |
1310 |
* When searching for a module, we start at the end of the list and |
1311 |
* move towards the front. The comparison function looks for the |
1312 |
* base-names to be equal and for the version number of the found |
1313 |
* module being less than or equal to the version number of the module |
1314 |
* passed into the SearchForModule() function. The comparison |
1315 |
* function uses less than or equal for the version number because one |
1316 |
* doesn't know the version number of the most recently read version. |
1317 |
* By setting the version number in the module passed to |
1318 |
* SearchForModule() to be ULONG_MAX, you will get the first module |
1319 |
* where the base-names match, which should be the most recently read |
1320 |
* version of that module. |
1321 |
*------------------------------------------------- |
1322 |
*/ |
1323 |
|
1324 |
|
1325 |
/** Store `m' in the global module gl_list `g_module_list', creating |
1326 |
* the g_module_list if needed. Return 0 for success or 1 if the |
1327 |
* g_module_list could not be created. |
1328 |
* |
1329 |
* Storing `m' in g_module_list simply appends it to the list. |
1330 |
*/ |
1331 |
static |
1332 |
int StoreModule(CONST struct module_t *m) |
1333 |
{ |
1334 |
/* initialize the global module list if required */ |
1335 |
if((g_module_list==NULL) && (Asc_InitModules(G_MODULE_LIST_INIT_SIZE)!=0)) { |
1336 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"FAILED TO ASC_INITMODULES"); |
1337 |
return 1; |
1338 |
} |
1339 |
gl_append_ptr( g_module_list, (VOIDPTR)m ); |
1340 |
return 0; |
1341 |
} |
1342 |
|
1343 |
|
1344 |
/** Remove `m' from the global module gl_list `g_module_list'. |
1345 |
* |
1346 |
* This function searches backwards through g_module_list and uses |
1347 |
* CmpModulesNameVers() to determine which module to remove. |
1348 |
*/ |
1349 |
static |
1350 |
void RemoveModule(CONST struct module_t *m) |
1351 |
{ |
1352 |
unsigned long place; |
1353 |
|
1354 |
if( g_module_list == NULL ) { |
1355 |
return; |
1356 |
} |
1357 |
place = gl_search_reverse( g_module_list, m, (CmpFunc)CmpModulesNameVers ); |
1358 |
if( place == 0 ) { |
1359 |
return; |
1360 |
} |
1361 |
gl_delete( g_module_list, place, 0 ); |
1362 |
return; |
1363 |
} |
1364 |
|
1365 |
|
1366 |
/** Search for `m' in the global module gl_list `g_module_list' |
1367 |
* and return it. Return the matching module or NULL if no matching |
1368 |
* module exists or if the g_module_list is empty. |
1369 |
* |
1370 |
* This function searches backwards through g_module_list and uses |
1371 |
* CmpModulesNameVers() to determine which module return. |
1372 |
*/ |
1373 |
static |
1374 |
struct module_t *SearchForModule(CONST struct module_t *m){ |
1375 |
unsigned long place; |
1376 |
|
1377 |
asc_assert(m != NULL); |
1378 |
|
1379 |
if( g_module_list == NULL ) { |
1380 |
return NULL; |
1381 |
} |
1382 |
|
1383 |
place = gl_search_reverse( g_module_list, m, (CmpFunc)CmpModulesNameVers ); |
1384 |
if( place == 0 ) { |
1385 |
return NULL; |
1386 |
} |
1387 |
return gl_fetch( g_module_list, place ); |
1388 |
} |
1389 |
|
1390 |
|
1391 |
/** Compare the base_names and version numbers of the modules |
1392 |
* `m1' and `m2'. |
1393 |
* Return: |
1394 |
* >0 m1->base_name > m2->base_name |
1395 |
* || (m1->base_name == m2->base_name |
1396 |
* && m1->version > m2->version) |
1397 |
* <0 m1->base_name < m2->base_name |
1398 |
* 0 m1->base_name == m2->base_name |
1399 |
* && m1->version <= m2->version |
1400 |
*/ |
1401 |
static |
1402 |
int CmpModulesNameVers(CONST struct module_t *m1, CONST struct module_t *m2){ |
1403 |
int result; |
1404 |
|
1405 |
if( (result = CmpSymchar(m1->base_name, m2->base_name)) != 0 ) { |
1406 |
return result; |
1407 |
} |
1408 |
if( m1->version > m2->version ) { |
1409 |
return 1; |
1410 |
} |
1411 |
return 0; |
1412 |
} |
1413 |
|
1414 |
|
1415 |
|
1416 |
extern int Asc_CloseCurrentModule(void){ |
1417 |
struct module_t *prev; |
1418 |
|
1419 |
/* |
1420 |
* If no current module, return TRUE |
1421 |
*/ |
1422 |
if (g_current_module == NULL) { |
1423 |
return TRUE; |
1424 |
} |
1425 |
|
1426 |
/* |
1427 |
* Store the scanner's position in the current module |
1428 |
*/ |
1429 |
g_current_module->linenum = LineNum(); |
1430 |
|
1431 |
/* |
1432 |
* Close the current module's file or buffer. |
1433 |
*/ |
1434 |
if (g_current_module->f != NULL) { |
1435 |
fclose(g_current_module->f); |
1436 |
g_current_module->f = NULL; |
1437 |
} else { |
1438 |
asc_assert(g_current_module->s != NULL); |
1439 |
Asc_ScannerReleaseStringBuffer(g_current_module->scanbuffer); |
1440 |
g_current_module->scanbuffer = NULL; |
1441 |
} |
1442 |
|
1443 |
/* |
1444 |
* Pop the module that REQUIRED the current module so |
1445 |
* it becomes the current module |
1446 |
*/ |
1447 |
prev = g_current_module->required_by; |
1448 |
g_current_module->required_by = NULL; /* unlink it */ |
1449 |
g_current_module = prev; |
1450 |
|
1451 |
/* |
1452 |
* No more modules |
1453 |
*/ |
1454 |
if (g_current_module == NULL) { |
1455 |
return TRUE; |
1456 |
} |
1457 |
|
1458 |
/* |
1459 |
* Inform the scanner after pop |
1460 |
*/ |
1461 |
if (g_current_module->s == NULL) { |
1462 |
Asc_ScannerAssignFile(g_current_module->f,g_current_module->linenum); |
1463 |
} else { |
1464 |
asc_assert(g_current_module->scanbuffer != NULL); |
1465 |
Asc_ScannerAssignString(g_current_module->scanbuffer, |
1466 |
g_current_module->linenum,0); |
1467 |
} |
1468 |
return FALSE; |
1469 |
} |
1470 |
|
1471 |
|
1472 |
int Asc_ModuleAddStatements(struct module_t *m, struct gl_list_t *l) |
1473 |
{ |
1474 |
if (l == NULL || gl_length(l) == 0 || |
1475 |
m == NULL || m->s == NULL || m->f != NULL) { |
1476 |
return -1; |
1477 |
/* list is empty or module is not a string module. |
1478 |
* caller should dump l and contents. |
1479 |
*/ |
1480 |
} |
1481 |
if (m->stats == NULL) { |
1482 |
m->stats = l; |
1483 |
/* we're keeping list and contents. */ |
1484 |
return 0; |
1485 |
} else { |
1486 |
gl_append_list(m->stats,l); |
1487 |
/* we're keeping contents but not list. caller should destroy l. */ |
1488 |
return 1; |
1489 |
} |
1490 |
} |
1491 |
|
1492 |
|
1493 |
extern CONST struct module_t *Asc_GetModuleByName(CONST char *module_name){ |
1494 |
char name[PATH_MAX]; |
1495 |
unsigned long vers; |
1496 |
struct module_t *mod; |
1497 |
struct module_t *result; |
1498 |
|
1499 |
if( g_module_list == NULL ) { |
1500 |
return NULL; |
1501 |
} |
1502 |
|
1503 |
/* |
1504 |
* Convert the given name to an base name and version |
1505 |
*/ |
1506 |
vers = ModuleNameToInternalNameVers(module_name, name); |
1507 |
if( vers == ULONG_MAX ) { |
1508 |
ERROR_REPORTER_HERE(ASC_PROG_ERR |
1509 |
,"Bad format for module name %s, no version number found." |
1510 |
,name |
1511 |
); |
1512 |
return NULL; |
1513 |
} |
1514 |
|
1515 |
/* |
1516 |
* Create a new module with this name and version---needed to |
1517 |
* call SearchForModule() |
1518 |
*/ |
1519 |
mod = NewModule( name ); |
1520 |
if( mod == NULL ) { |
1521 |
return NULL; |
1522 |
} |
1523 |
mod->version = vers; |
1524 |
|
1525 |
/* |
1526 |
* Search for the module and free the module we used for searching. |
1527 |
*/ |
1528 |
result = SearchForModule( mod ); |
1529 |
DeleteModule( mod ); |
1530 |
|
1531 |
/* |
1532 |
* If result is a module-alias, return the module that PROVIDED it |
1533 |
*/ |
1534 |
if(( result != NULL ) && ( result->provided_by != NULL )) { |
1535 |
return result->provided_by; |
1536 |
} |
1537 |
return result; |
1538 |
} |
1539 |
|
1540 |
|
1541 |
/** |
1542 |
* Parse the module name given in `module_name' (e.g., "foo.a4c<0>") |
1543 |
* into an base name and a version number. Copy the base_name |
1544 |
* into the string `name' and use the version number as the return |
1545 |
* value. Leading and trailing whitespace on `module_name' is removed. |
1546 |
* |
1547 |
* If `module_name' does not contain a valid module name, return |
1548 |
* ULONG_MAX. |
1549 |
*/ |
1550 |
static |
1551 |
unsigned long ModuleNameToInternalNameVers(CONST char *module_name, |
1552 |
char * CONST name) |
1553 |
{ |
1554 |
unsigned int t; |
1555 |
unsigned long vers = ULONG_MAX; |
1556 |
|
1557 |
asc_assert( name != NULL ); |
1558 |
|
1559 |
/* |
1560 |
* Make sure we got good data |
1561 |
*/ |
1562 |
if(( module_name == NULL ) || ( *module_name == '\0' )) { |
1563 |
return vers; |
1564 |
} |
1565 |
|
1566 |
/* |
1567 |
* Ignore leading whitespace |
1568 |
*/ |
1569 |
while( isspace(*module_name) ) { |
1570 |
module_name++; |
1571 |
} |
1572 |
|
1573 |
/* |
1574 |
* Copy all of module_name into name |
1575 |
*/ |
1576 |
for( t = 0 ; ((name[t] = module_name[t]) != '\0') ; t++ ); |
1577 |
|
1578 |
/* |
1579 |
* Remove trailing whitespace |
1580 |
* Use t>1 in the while since we decrement t twice before we |
1581 |
* check it again and we don't want to fall off the front of |
1582 |
* the string. |
1583 |
*/ |
1584 |
while(( t > 1 ) && ( isspace(name[--t]) )); |
1585 |
|
1586 |
/* |
1587 |
* Check for '>' preceded by a number |
1588 |
*/ |
1589 |
if(( name[t] != '>' ) || ( ! isdigit(name[--t]) )) { |
1590 |
return vers; |
1591 |
} |
1592 |
|
1593 |
/* |
1594 |
* Back up over all the digits that make up the version number |
1595 |
* Use t>1 in while since a valid base name has to have |
1596 |
* at least one character. |
1597 |
*/ |
1598 |
while(( t > 1 ) && ( isdigit(name[--t]) )); |
1599 |
|
1600 |
/* |
1601 |
* Make sure we have a '<' |
1602 |
*/ |
1603 |
if( name[t] != '<' ) { |
1604 |
return vers; |
1605 |
} |
1606 |
|
1607 |
/* |
1608 |
* Convert the '<' to '\0' since this is where the name ends |
1609 |
*/ |
1610 |
name[t] = '\0'; |
1611 |
|
1612 |
/* |
1613 |
* Covert the number to an unsigned long in base 10 |
1614 |
*/ |
1615 |
vers = strtoul( (name + t + 1), NULL, 10 ); |
1616 |
if( vers == ULONG_MAX ) { |
1617 |
return vers; |
1618 |
} |
1619 |
|
1620 |
return vers; |
1621 |
} |
1622 |
|
1623 |
CONST char *Asc_ModuleString(CONST struct module_t *m) |
1624 |
{ |
1625 |
if (m==NULL) return NULL; |
1626 |
return m->s; |
1627 |
} |
1628 |
|
1629 |
struct gl_list_t *Asc_ModuleStatementLists(CONST struct module_t *m) |
1630 |
{ |
1631 |
if (m==NULL) return NULL; |
1632 |
return m->stats; |
1633 |
} |
1634 |
|
1635 |
|
1636 |
|
1637 |
extern struct gl_list_t *Asc_ModuleList(int module_type){ |
1638 |
struct gl_list_t *new = NULL; |
1639 |
struct module_t *m; |
1640 |
struct gl_list_t *types = NULL; |
1641 |
unsigned long c; |
1642 |
unsigned long length; |
1643 |
|
1644 |
if( g_module_list == NULL ) { |
1645 |
return NULL; |
1646 |
} |
1647 |
length = gl_length(g_module_list); |
1648 |
new = gl_create(length); |
1649 |
if( new == NULL ) { |
1650 |
return NULL; |
1651 |
} |
1652 |
for( c = length; c > 0; c-- ) { |
1653 |
m = (struct module_t *)gl_fetch(g_module_list, c); |
1654 |
switch (module_type) { |
1655 |
case 0: |
1656 |
types = Asc_TypeByModule(m); |
1657 |
if( types != NULL ) { |
1658 |
if( gl_length(types) > 0L ) { |
1659 |
gl_append_ptr(new, (VOIDPTR)(m->name)); |
1660 |
} |
1661 |
gl_destroy(types); |
1662 |
types = NULL; |
1663 |
} |
1664 |
break; |
1665 |
case 1: |
1666 |
if (m != NULL && m->s != NULL) { |
1667 |
gl_append_ptr(new, (VOIDPTR)(m->name)); |
1668 |
} |
1669 |
break; |
1670 |
case 2: |
1671 |
if (m != NULL && m->stats != NULL) { |
1672 |
gl_append_ptr(new, (VOIDPTR)(m->name)); |
1673 |
} |
1674 |
break; |
1675 |
default: |
1676 |
break; |
1677 |
} |
1678 |
} |
1679 |
return new; |
1680 |
} |
1681 |
|
1682 |
|
1683 |
extern void Asc_ModuleWrite(FILE *f, CONST struct module_t *m){ |
1684 |
asc_assert(m!=NULL); |
1685 |
FPRINTF(f,"MODULE: %s\nFILENAME: %s\n",SCP(m->name),SCP(m->filename)); |
1686 |
FPRINTF(f,(m->f!=NULL)?"OPEN\n":"CLOSED\n"); |
1687 |
FPRINTF(f,"FILE DATE: %s",asctime(localtime( &(m->time_last_modified) ))); |
1688 |
} |
1689 |
|
1690 |
|
1691 |
extern struct module_t *Asc_CurrentModuleF(void){ |
1692 |
return g_current_module; |
1693 |
} |
1694 |
|
1695 |
|
1696 |
extern CONST char *Asc_ModuleName(CONST struct module_t *m){ |
1697 |
return (( m != NULL ) ? SCP(m->name) : ""); |
1698 |
} |
1699 |
|
1700 |
|
1701 |
extern CONST char *Asc_ModuleFileName(CONST struct module_t *m){ |
1702 |
return ((m != NULL) ? SCP(m->filename) : ""); |
1703 |
} |
1704 |
|
1705 |
extern CONST char *Asc_ModuleBestName(CONST struct module_t *m){ |
1706 |
static char unk[] = "<UNKNOWN>"; |
1707 |
if (m == NULL) return ""; |
1708 |
if (SCP(m->filename) != NULL) return SCP(m->filename); |
1709 |
if (SCP(m->name)!=NULL) return SCP(m->name); |
1710 |
return unk; |
1711 |
} |
1712 |
|
1713 |
|
1714 |
extern unsigned long Asc_ModuleTimesOpened(CONST struct module_t *m){ |
1715 |
return ((m != NULL) ? (m->open_count) : 0); |
1716 |
} |
1717 |
|
1718 |
|
1719 |
extern struct tm *Asc_ModuleTimeModified(CONST struct module_t *m){ |
1720 |
return ((m != NULL) ? localtime(&(m->time_last_modified)) : NULL); |
1721 |
} |
1722 |
|
1723 |
extern int Asc_ModuleStringIndex(CONST struct module_t *m){ |
1724 |
return ((m != NULL) ? (int)(m->time_last_modified) : -1); |
1725 |
} |