Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | |
2 | /* Copyright (c) 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2004, 2005, 2006 by Arkkra Enterprises */ | |
3 | /* All rights reserved */ | |
4 | ||
5 | /* functions to support Mup macros */ | |
6 | ||
7 | /* When a macro is defined, its text is copied to a file. When a macro is | |
8 | * invoked, information about the current input file is pushed on a stack, | |
9 | * and the macro text is read from the macro file. This file also has the | |
10 | * code to handle include files. They are handled similarly. Info about the | |
11 | * current file is pushed on a stack, and text is read from the included | |
12 | * file. */ | |
13 | ||
14 | #include <string.h> | |
15 | #include <fcntl.h> | |
16 | #include "globals.h" | |
17 | ||
18 | #ifdef VMS | |
19 | #define unlink delete | |
20 | #endif | |
21 | ||
22 | /* size of macro name hash table. should be prime, big enough to not have too | |
23 | * many collisions, small enough to not use too much memory. */ | |
24 | #define MTSIZE (79) | |
25 | ||
26 | /* how many bytes to allocate at a time when collecting macro arguments */ | |
27 | #define MAC_ARG_SZ (512) | |
28 | ||
29 | /* We may need just a plain copy of the text passed as a macro parameter, | |
30 | * or a quoted copy, or both. These flags say which we need. | |
31 | * This is currently only actually implemented in a limited form: | |
32 | * we always generate an unquoted copy, and if there is any chance any | |
33 | * parameter in a macro will be needed in quoted form, we make quoted | |
34 | * copies of all parameters for that macro. This is because to really | |
35 | * figure out unequivicably which we need would be complicated enough | |
36 | * that we'd probably wind up spending more than we'd save, | |
37 | * so just doing this half-hearted optimization seems like the best tradeoff. | |
38 | */ | |
39 | #define QS_UNQUOTED (0x1) | |
40 | #define QS_QUOTED (0x2) | |
41 | ||
42 | /* information about a macro parameter */ | |
43 | struct MAC_PARAM { | |
44 | char *param_name; /* name of the parameter */ | |
45 | int quote_style; /* bitmap of QS_* values */ | |
46 | struct MAC_PARAM *next; /* for linked list */ | |
47 | }; | |
48 | ||
49 | /* info about a macro */ | |
50 | struct MACRO { | |
51 | char *macname; /* name of macro */ | |
52 | char *filename; /* file in which macro was defined */ | |
53 | int lineno; /* line in file where macro definition began */ | |
54 | int lineoffset; /* how many lines we are into macro */ | |
55 | long offset; /* offset in macro temporary file where the | |
56 | * text of the macro is stored for later use */ | |
57 | long quoted_offset; /* offset into macro temp file where the | |
58 | * quoted version is stored, if any. | |
59 | * Only used on macro parameters. */ | |
60 | struct MACRO *next; /* for hash collision list */ | |
61 | int recursion; /* incremented each time the macro is called, | |
62 | * and decremented on completion. If this gets | |
63 | * above 1 we are in trouble and ufatal */ | |
64 | int num_params; /* how many parameters */ | |
65 | struct MAC_PARAM *parameters_p; /* list of macro parameter | |
66 | * names. Null if this macro doesn't | |
67 | * have parameters */ | |
68 | }; | |
69 | ||
70 | /* macro information hash table */ | |
71 | static struct MACRO *Mactable[MTSIZE]; | |
72 | ||
73 | /* temporary file for saving text of macros. Need separate handles for reading | |
74 | * and writing */ | |
75 | static FILE *Mactmp_read = (FILE *) 0; | |
76 | static FILE *Mactmp_write = (FILE *) 0; | |
77 | #ifdef UNIX_LIKE_FILES | |
78 | static char Macfile[] = "mupmacXXXXXX"; /* name of temp file */ | |
79 | #else | |
80 | /* As last resort, we use a temp file name of 11 character length, | |
81 | * so make sure we have enough room for that. Usually L_tmpnam is | |
82 | * already longer than that, but better safe than core dump. | |
83 | */ | |
84 | #if L_tmpnam < 12 | |
85 | #undef L_tmpnam | |
86 | #define L_tmpnam 12 | |
87 | #endif | |
88 | static char Macfile[L_tmpnam]; /* name of temp file */ | |
89 | #endif | |
90 | ||
91 | /* Some OSs require us to open files in binary mode. Most implementations | |
92 | * of fopen these days accept the 'b' suffix, even when they don't care | |
93 | * about it, but to be safe, we only add on systems that appear to have | |
94 | * binary mode defined. */ | |
95 | #ifdef O_BINARY | |
96 | static char *Read_mode = "rb"; | |
97 | #ifndef UNIX_LIKE_FILES | |
98 | static char *Write_mode = "wb"; | |
99 | static char *Append_mode = "ab"; | |
100 | #endif | |
101 | #else | |
102 | static char *Read_mode = "r"; | |
103 | #ifndef UNIX_LIKE_FILES | |
104 | static char *Write_mode = "w"; | |
105 | static char *Append_mode = "a"; | |
106 | #endif | |
107 | #endif | |
108 | ||
109 | /* maximum number of files on file stack */ | |
110 | #ifndef FOPEN_MAX | |
111 | #ifdef _NFILE | |
112 | #define FOPEN_MAX _NFILE | |
113 | #else | |
114 | #define FOPEN_MAX (20) | |
115 | #endif | |
116 | #endif | |
117 | /* The -5 is to account for stdin, stdout, stderr, and the 2 tmp file handles. | |
118 | * The +20 is to allow for nested macros. They can take 2 stack slots per call | |
119 | * since argument expansion also uses a slot, but they don't take extra | |
120 | * file descriptors, since all the macros are kept in one file. */ | |
121 | #define MAXFSTK (FOPEN_MAX - 5 + 20) | |
122 | ||
123 | ||
124 | struct FILESTACK { | |
125 | FILE *file; | |
126 | char *filename; | |
127 | long fileoffset; /* fseek position in file */ | |
128 | int lineno; /* where we are in the file */ | |
129 | struct MACRO *mac_p; /* if pushing because of a macro call, | |
130 | * this will point to info about the | |
131 | * macro, otherwise 0. */ | |
132 | }; | |
133 | ||
134 | static struct FILESTACK Filestack[MAXFSTK]; | |
135 | ||
136 | static int Fstkptr = -1; /* stack pointer for Filestack */ | |
137 | static char quote_designator[] = "`"; | |
138 | ||
139 | extern int unlink(); /* to remove temp file */ | |
140 | extern char *tmpnam(); /* generate temp file name */ | |
141 | ||
142 | /* static function declarations */ | |
143 | static void pushfile P((FILE *file, char *filename, int lineno, | |
144 | struct MACRO *mac_p)); | |
145 | static char *path_combiner P((char *prefix)); | |
146 | static int is_absolute_path P((char *filename)); | |
147 | static struct MACRO *findMacro P((char *macname)); | |
148 | static int hashmac P(( char *macname)); | |
149 | static struct MACRO *setup_macro P((char *macname)); | |
150 | static void free_parameters P((struct MAC_PARAM *param_p, char *macname, | |
151 | int values_only)); | |
152 | static char *dupstring P((char *string)); | |
153 | static char *mkmacparm_name P((char *macname, char *param_name)); | |
154 | static struct MACRO *resolve_mac_name P((char *macname)); | |
155 | static int has_quote_designator P((char *macname)); | |
156 | ||
157 | ||
158 | \f | |
159 | ||
160 | /* add macro name to hash table and arrange to save its text in temp file */ | |
161 | ||
162 | void | |
163 | define_macro(macname) | |
164 | ||
165 | char *macname; /* name of macro to be defined */ | |
166 | ||
167 | { | |
168 | int has_params = NO; | |
169 | char *mac_name; /* copy of macro name, because what gets | |
170 | * passed in can get overwritten by parameter | |
171 | * names */ | |
172 | struct MACRO *mac_p; | |
173 | struct MAC_PARAM *parm_p; | |
174 | ||
175 | debug (4, "define_macro macname=%s\n", macname); | |
176 | ||
177 | /* there might be some leading white space in front of macro name, | |
178 | * if so, ignore that */ | |
179 | while (*macname == ' ' || *macname == '\t') { | |
180 | macname++; | |
181 | } | |
182 | ||
183 | /* if ends with a ( this macro has parameters */ | |
184 | if (macname[strlen(macname) - 1] == '(') { | |
185 | /* this is a macro with parameters */ | |
186 | macname[strlen(macname) - 1] = '\0'; | |
187 | has_params = YES; | |
188 | } | |
189 | ||
190 | mac_p = setup_macro(macname); | |
191 | mac_name = mac_p->macname; | |
192 | ||
193 | if (has_params == YES) { | |
194 | get_parameters(mac_name); | |
195 | } | |
196 | ||
197 | /* copy the macro text to the macro temp file */ | |
198 | if (save_macro(Mactmp_write) == YES) { | |
199 | /* There may have been a reference to a parameter to be | |
200 | * quoted. So remind ourseleves to create quoted copies | |
201 | * of parameters when this macro is called later. | |
202 | * This is a half-hearted optimization: | |
203 | * we will sometimes create extra copies unnecessarily, | |
204 | * but much of the time we will get the benefit of a | |
205 | * full optimization with only a fraction of the work. */ | |
206 | for (parm_p = mac_p->parameters_p; | |
207 | parm_p != (struct MAC_PARAM *) 0; | |
208 | parm_p = parm_p->next) { | |
209 | parm_p->quote_style |= QS_QUOTED; | |
210 | } | |
211 | } | |
212 | ||
213 | ||
214 | /* terminate the macro with a NULL, so that when lex hits it when | |
215 | * the macro is called, it will think it hit EOF */ | |
216 | putc('\0', Mactmp_write); | |
217 | ||
218 | /* make sure macro has really been written and not still buffered */ | |
219 | (void) fflush(Mactmp_write); | |
220 | ||
221 | #ifndef UNIX_LIKE_FILES | |
222 | /* on non-unix system, multiple opens on the same file may not work, | |
223 | * so open and close each time */ | |
224 | fclose(Mactmp_write); | |
225 | #endif | |
226 | } | |
227 | \f | |
228 | ||
229 | /* save macro info in hash table and make sure macro temporary file is | |
230 | * set up and ready to use */ | |
231 | ||
232 | static struct MACRO * | |
233 | setup_macro(macname) | |
234 | ||
235 | char *macname; /* name of macro being defined */ | |
236 | ||
237 | { | |
238 | struct MACRO *mac_p; /* info about current macro */ | |
239 | int h; /* hash number */ | |
240 | static int have_mac_file = NO; | |
241 | #ifdef UNIX_LIKE_FILES | |
242 | int filedesc; /* of tmp file for storing macros */ | |
243 | #endif | |
244 | ||
245 | ||
246 | /* if macro has not been defined before, add to hash table */ | |
247 | if ((mac_p = findMacro(macname)) == (struct MACRO *) 0) { | |
248 | ||
249 | MALLOC(MACRO, mac_p, 1); | |
250 | h = hashmac(macname); | |
251 | mac_p->next = Mactable[h]; | |
252 | Mactable[h] = mac_p; | |
253 | ||
254 | mac_p->macname = dupstring(macname); | |
255 | mac_p->num_params = 0; | |
256 | mac_p->parameters_p = (struct MAC_PARAM *) 0; | |
257 | mac_p->recursion = 0; | |
258 | } | |
259 | else { | |
260 | l_warning(Curr_filename, yylineno, | |
261 | "macro '%s' redefined", macname); | |
262 | free_parameters(mac_p->parameters_p, macname, NO); | |
263 | mac_p->parameters_p = (struct MAC_PARAM *) 0; | |
264 | mac_p->num_params = 0; | |
265 | } | |
266 | ||
267 | /* save current filename and line number so they can be used to give | |
268 | * useful error messages when the macro is called */ | |
269 | if (Fstkptr >= 0 && Filestack[Fstkptr].mac_p != (struct MACRO *) 0) { | |
270 | /* if current expanding a macro, get file/line info relative | |
271 | * to the macro */ | |
272 | mac_p->filename = Filestack[Fstkptr].mac_p->filename; | |
273 | mac_p->lineno = Filestack[Fstkptr].mac_p->lineno + | |
274 | Filestack[Fstkptr].mac_p->lineoffset; | |
275 | } | |
276 | else { | |
277 | mac_p->filename = Curr_filename; | |
278 | mac_p->lineno = yylineno; | |
279 | } | |
280 | ||
281 | /* if we don't yet have a temp file to store macro info, open one */ | |
282 | if (have_mac_file == NO) { | |
283 | /* We need separate read/write file pointers in case of defining | |
284 | * one macro inside another, so can't easily use tmpfile(). | |
285 | * The most straightforward way to do this is to use tmpnam. | |
286 | * But recent versions of gcc complain loudly that tmpnam | |
287 | * is dangerous--you should use mkstemp instead. While it's | |
288 | * true tmpnam could have problems under certain conditions, | |
289 | * Mup's use of it hardly qualifies as "dangerous." Yes, | |
290 | * perhaps if you tried really hard by running lots and | |
291 | * lots of instances of Mup simultaneously, and they all | |
292 | * used macros, maybe you could get one of them to fail once | |
293 | * in a while. But some systems may not support mkstemp, | |
294 | * since it's more from BSD than System V, and it's unclear | |
295 | * how it ought to function on a system that has DOS-style | |
296 | * (8.3 character) filenames if you gave it a longer string | |
297 | * than would be a legal file name. | |
298 | * And, interestingly, the Solaris manual pages say | |
299 | * that you should NOT use mkstemp, in direct conflict with | |
300 | * gcc's opinion. They say to use tmpfile, which we can't do | |
301 | * because that just gives a single file pointer for writing, | |
302 | * but we want one for writing and one for reading. | |
303 | * So it's very unclear what to do. For unix-ish systems | |
304 | * we'll go with mkstemp, and for others use tmpnam. | |
305 | * We get a temp file name, open it twice, once for writing, | |
306 | * once for reading, then remove the file. This will make it a | |
307 | * hidden file that will disappear when the 2 file pointers | |
308 | * are closed. | |
309 | */ | |
310 | #ifdef UNIX_LIKE_FILES | |
311 | if ((filedesc = mkstemp(Macfile)) < 0) { | |
312 | ufatal("can't create temporary file for macro storage (possibly you don't have write permissions on directory/file?)"); | |
313 | } | |
314 | if ((Mactmp_write = fdopen(filedesc, "w")) == (FILE *) 0) { | |
315 | pfatal("can't fdopen temporary file for macro storage"); | |
316 | } | |
317 | if ((Mactmp_read = fopen(Macfile, "r")) == (FILE *) 0) { | |
318 | pfatal("can't open temporary file for reading macro"); | |
319 | } | |
320 | ||
321 | /* arrange for file to vanish */ | |
322 | (void) unlink(Macfile); | |
323 | #else | |
324 | /* On non-UNIX systems, unlinking an open file may not have | |
325 | * the desired effect, and some systems don't properly | |
326 | * handle both a read and write FILE * on the same file. | |
327 | * So for those we just create it here and have to open | |
328 | * and close the file each time. */ | |
329 | (void) tmpnam(Macfile); | |
330 | if ((Mactmp_write = fopen(Macfile, Write_mode)) == (FILE *) 0) { | |
331 | /* If tmpnam isn't implemented or fails, | |
332 | * try a hard-coded temp name and hope | |
333 | * for the best... */ | |
334 | (void) strcpy(Macfile, "MupMacF.tmp"); | |
335 | if ((Mactmp_write = fopen(Macfile, Write_mode)) | |
336 | == (FILE *) 0) { | |
337 | ufatal("can't open temporary file for macro storage (possibly you don't have write permissions on directory/file?)"); | |
338 | } | |
339 | } | |
340 | #endif | |
341 | have_mac_file = YES; | |
342 | } | |
343 | #ifndef UNIX_LIKE_FILES | |
344 | else { | |
345 | if ((Mactmp_write = fopen(Macfile, Append_mode)) == (FILE *) 0) { | |
346 | pfatal("can't open temporary file for macro storage (maybe out of memory?)"); | |
347 | } | |
348 | } | |
349 | #endif | |
350 | /* make sure we're at the end. Now that we have separate read/write | |
351 | * file pointers, this should be unnecessary. However, on one non-UNIX | |
352 | * system is seemed the opening for append didn't really work, | |
353 | * requiring this fseek to actually go to the end. And in a previous | |
354 | * variation of this code, this fseek was needed for the UNIX way too. | |
355 | * In any case, it doesn't hurt to be sure we are where we want to be. */ | |
356 | if (fseek(Mactmp_write, 0L, SEEK_END) != 0) { | |
357 | pfatal("fseek failed in setup_macro"); | |
358 | } | |
359 | ||
360 | /* keep track of where this macro will begin in the temp file */ | |
361 | mac_p->offset = ftell(Mactmp_write); | |
362 | ||
363 | return(mac_p); | |
364 | } | |
365 | \f | |
366 | ||
367 | #ifndef UNIX_LIKE_FILES | |
368 | /* on non-unix systems, unlinking an open file may produce undesired results, | |
369 | * so clean up the macro temp file after parsing is done. Unfortunately, | |
370 | * this leave a higher possibility of leaving an orphan temp file. */ | |
371 | ||
372 | void | |
373 | mac_cleanup() | |
374 | ||
375 | { | |
376 | if (Macfile[0] != '\0') { | |
377 | unlink(Macfile); | |
378 | } | |
379 | Macfile[0] = '\0'; | |
380 | } | |
381 | #endif | |
382 | \f | |
383 | ||
384 | /* look up macro name in hash table and return info about it, or null if | |
385 | * not defined */ | |
386 | ||
387 | static struct MACRO * | |
388 | findMacro(macname) | |
389 | ||
390 | char *macname; /* which macro to look up */ | |
391 | ||
392 | { | |
393 | struct MACRO *mac_p; /* pointer to info about macro */ | |
394 | ||
395 | ||
396 | /* search hash table and collision chain off the table for match | |
397 | * of macro name */ | |
398 | for (mac_p = Mactable[ hashmac(macname) ]; mac_p != (struct MACRO *) 0; | |
399 | mac_p = mac_p->next) { | |
400 | ||
401 | if (strcmp(mac_p->macname, macname) == 0) { | |
402 | /* found it! */ | |
403 | return(mac_p); | |
404 | } | |
405 | } | |
406 | ||
407 | /* macro not defined */ | |
408 | return( (struct MACRO *) 0); | |
409 | } | |
410 | \f | |
411 | ||
412 | /* remove a macro definition. We just remove its entry from the hash table. | |
413 | * Its text will still remain in the macro temp file, because it seems like | |
414 | * too much trouble to do storage management on a temp file. */ | |
415 | /* Note that if asked to undef a macro that isn't defined, it silently | |
416 | * does nothing. */ | |
417 | ||
418 | void | |
419 | undef_macro(macname) | |
420 | ||
421 | char *macname; /* which macro to undefine */ | |
422 | ||
423 | { | |
424 | struct MACRO *mac_p; /* to walk though list of info about macros */ | |
425 | struct MACRO **mac_p_p; /* to keep track of delete place */ | |
426 | ||
427 | ||
428 | /* there might be some leading white space in front of macro name, | |
429 | * if so, ignore that */ | |
430 | while (*macname == ' ' || *macname == '\t') { | |
431 | macname++; | |
432 | } | |
433 | ||
434 | /* keep track of where to delete from linked list */ | |
435 | for (mac_p_p = &(Mactable[ hashmac(macname) ]); | |
436 | *mac_p_p != (struct MACRO *) 0; | |
437 | mac_p_p = &((*mac_p_p)->next)) { | |
438 | mac_p = *mac_p_p; | |
439 | if (strcmp(mac_p->macname, macname) == 0) { | |
440 | ||
441 | /* found it--delete this entry from list */ | |
442 | *mac_p_p = mac_p->next; | |
443 | FREE(mac_p->macname); | |
444 | free_parameters(mac_p->parameters_p, macname, NO); | |
445 | FREE(mac_p); | |
446 | return; | |
447 | } | |
448 | } | |
449 | } | |
450 | \f | |
451 | ||
452 | /* generate hash number from macro name */ | |
453 | ||
454 | static int | |
455 | hashmac(macname) | |
456 | ||
457 | char *macname; | |
458 | ||
459 | { | |
460 | int h; | |
461 | ||
462 | /* add up characters of name and take sum modulo hash table size */ | |
463 | for (h = 0; *macname != '\0'; macname++) { | |
464 | h += *macname; | |
465 | } | |
466 | return(h % MTSIZE); | |
467 | } | |
468 | \f | |
469 | ||
470 | /* when macro is called, arrange to read its text from macro file */ | |
471 | ||
472 | void | |
473 | call_macro(macname) | |
474 | ||
475 | char *macname; /* which macro to call */ | |
476 | ||
477 | { | |
478 | struct MACRO *mac_p; /* info about the macro */ | |
479 | ||
480 | ||
481 | debug(4, "call_macro macname=%s\n", macname); | |
482 | ||
483 | if ((mac_p = resolve_mac_name(macname)) == (struct MACRO *) 0) { | |
484 | l_yyerror(Curr_filename, yylineno, | |
485 | "macro '%s' not defined", macname); | |
486 | return; | |
487 | } | |
488 | ||
489 | ||
490 | /* if macro has parameter, remove any previous arguments | |
491 | * and gather the arguments for this call */ | |
492 | if (mac_p->parameters_p != (struct MAC_PARAM *) 0) { | |
493 | free_parameters(mac_p->parameters_p, macname, YES); | |
494 | if (get_mac_arguments(macname, mac_p->num_params) == NO) { | |
495 | /* something was wrong with argument. Don't bother | |
496 | * trying to expand, because we'll probably just | |
497 | * just lots more error messages */ | |
498 | return; | |
499 | } | |
500 | } | |
501 | ||
502 | #ifndef UNIX_LIKE_FILES | |
503 | if ((Mactmp_read = fopen(Macfile, Read_mode)) == (FILE *) 0) { | |
504 | pfatal("can't open macro file for reading (maybe out of memory?)"); | |
505 | } | |
506 | #endif | |
507 | ||
508 | /* save old yyin value and make macro definition the input */ | |
509 | pushfile(Mactmp_read, mac_p->filename, mac_p->lineno, mac_p); | |
510 | ||
511 | /* go to where macro definition begins */ | |
512 | if (fseek(Mactmp_read, (has_quote_designator(macname) | |
513 | ? mac_p->quoted_offset : mac_p->offset), | |
514 | SEEK_SET) != 0) { | |
515 | pfatal("fseek failed in call_macro"); | |
516 | } | |
517 | } | |
518 | \f | |
519 | ||
520 | /* save info about current yyin and set yyin to specified file */ | |
521 | ||
522 | static void | |
523 | pushfile(file, filename, lineno, mac_p) | |
524 | ||
525 | FILE *file; /* replace current file with this file */ | |
526 | char *filename; /* name of new file to use */ | |
527 | int lineno; /* current linenumber in new file */ | |
528 | struct MACRO *mac_p; /* if switching to macro temp file because of a | |
529 | * macro call, this is information about macro. | |
530 | * Otherwise, it will be null. */ | |
531 | ||
532 | { | |
533 | debug(4, "pushfile file=%s", filename); | |
534 | ||
535 | /* do error checks */ | |
536 | if (++Fstkptr >= MAXFSTK) { | |
537 | l_ufatal(filename, lineno, | |
538 | "too many nested files or macros (%d levels maximum)\n", | |
539 | MAXFSTK); | |
540 | } | |
541 | ||
542 | if (mac_p != (struct MACRO *) 0) { | |
543 | if ( ++(mac_p->recursion) != 1) { | |
544 | l_ufatal(Curr_filename, yylineno, | |
545 | "macro '%s' called recursively", | |
546 | mac_p->macname); | |
547 | } | |
548 | } | |
549 | ||
550 | /* save current info */ | |
551 | Filestack[Fstkptr].file = yyin; | |
552 | Filestack[Fstkptr].fileoffset = ftell(yyin); | |
553 | Filestack[Fstkptr].filename = Curr_filename; | |
554 | Filestack[Fstkptr].lineno = yylineno; | |
555 | Filestack[Fstkptr].mac_p = mac_p; | |
556 | ||
557 | /* arrange to use the new file */ | |
558 | new_lexbuff(file); | |
559 | ||
560 | /* if we are now expanding a macro, we don't change the input line | |
561 | * number. If doing an include, then we do. */ | |
562 | if (Filestack[Fstkptr].mac_p == (struct MACRO *) 0) { | |
563 | yylineno = lineno; | |
564 | Curr_filename = filename; | |
565 | } | |
566 | else { | |
567 | Filestack[Fstkptr].mac_p->lineoffset = 0; | |
568 | } | |
569 | } | |
570 | \f | |
571 | ||
572 | /* if there are any files on the Filestack, go back to the previous one | |
573 | * on the stack. Return 1 if something was popped, 0 if stack was empty */ | |
574 | ||
575 | int | |
576 | popfile() | |
577 | ||
578 | { | |
579 | debug(4, "popfile"); | |
580 | ||
581 | /* if nothing on file stack, nothing to do except return 0 */ | |
582 | if (Fstkptr < 0) { | |
583 | return(0); | |
584 | } | |
585 | ||
586 | if (Filestack[Fstkptr].mac_p != (struct MACRO *) 0) { | |
587 | /* returning from macro call */ | |
588 | Filestack[Fstkptr].mac_p->recursion = 0; | |
589 | #ifndef UNIX_LIKE_FILES | |
590 | (void) fclose(yyin); | |
591 | #endif | |
592 | } | |
593 | else { | |
594 | /* this is an include rather than a macro file, so close it */ | |
595 | (void) fclose(yyin); | |
596 | } | |
597 | ||
598 | /* set things back to the previous file */ | |
599 | yyin = Filestack[Fstkptr].file; | |
600 | Curr_filename = Filestack[Fstkptr].filename; | |
601 | if (Filestack[Fstkptr].fileoffset >= 0) { | |
602 | if (fseek(yyin, Filestack[Fstkptr].fileoffset, SEEK_SET) != 0) { | |
603 | pfatal("fseek failed in popfile"); | |
604 | } | |
605 | } | |
606 | yylineno = Filestack[Fstkptr].lineno; | |
607 | ||
608 | /* go back to previous file */ | |
609 | del_lexbuff(); | |
610 | ||
611 | /* decrement stackpointer */ | |
612 | Fstkptr--; | |
613 | ||
614 | return(1); | |
615 | } | |
616 | \f | |
617 | ||
618 | /* return 1 if we are NOT currently expanding a macro, 0 if we are. | |
619 | * This backwards logic is used because when we ARE doing a macro, we | |
620 | * should NOT muck with yylineno, and vice versa. If in a macro, adjust | |
621 | * the line offset within the macro by the specified amount. */ | |
622 | ||
623 | int | |
624 | not_in_mac(inc_dec) | |
625 | ||
626 | int inc_dec; /* how much to increment/decrement the in-macro line offset */ | |
627 | { | |
628 | if (Fstkptr >= 0 && Filestack[Fstkptr].mac_p != (struct MACRO *) 0) { | |
629 | /* we are in a macro */ | |
630 | Filestack[Fstkptr].mac_p->lineoffset += inc_dec; | |
631 | return(0); | |
632 | } | |
633 | else { | |
634 | return(1); | |
635 | } | |
636 | } | |
637 | \f | |
638 | ||
639 | /* if an error occurs while expanding a macro, give additional help */ | |
640 | ||
641 | void | |
642 | mac_error() | |
643 | ||
644 | { | |
645 | struct MACRO *mac_p; | |
646 | ||
647 | if (Fstkptr >= 0 && (mac_p = Filestack[Fstkptr].mac_p) | |
648 | != (struct MACRO *) 0) { | |
649 | (void) fprintf(stderr, "note: previous error found while expanding macro %s'%s' from %s: line %d:\n", | |
650 | (strchr(mac_p->macname, '(') ? "parameter " : ""), | |
651 | mac_p->macname, | |
652 | mac_p->filename, | |
653 | mac_p->lineno + mac_p->lineoffset); | |
654 | print_offending_line(mac_p->filename, mac_p->lineno + mac_p->lineoffset); | |
655 | } | |
656 | } | |
657 | \f | |
658 | ||
659 | /* process an included file */ | |
660 | ||
661 | void | |
662 | includefile(fname) | |
663 | ||
664 | char *fname; /* name of file to include */ | |
665 | ||
666 | { | |
667 | FILE *file; /* the included file */ | |
668 | char *fnamecopy; | |
669 | ||
670 | ||
671 | /* attempt to open file. Give message if fail */ | |
672 | /* Note that if we find the file somewhere up the MUPPATH | |
673 | * rather than directly, fname will be updated to contain the | |
674 | * actual path of the file we are using, so we can tell that | |
675 | * to the user. That way if they have several files by the same | |
676 | * name but in different directories, and we're picking up a different | |
677 | * one than they had intended, we can give them a clue of what's | |
678 | * going on... */ | |
679 | if ((file = find_file(&fname)) == (FILE *) 0) { | |
680 | ||
681 | l_ufatal(Curr_filename, yylineno, | |
682 | "can't open include file '%s'", fname); | |
683 | } | |
684 | ||
685 | /* need to make copy of file name */ | |
686 | fnamecopy = dupstring(fname); | |
687 | ||
688 | /* arrange to connect yyin to the included file, save info, etc */ | |
689 | pushfile(file, fnamecopy, 1, (struct MACRO *) 0); | |
690 | } | |
691 | \f | |
692 | ||
693 | /* Find a file to be included. First look using file name as is. | |
694 | * If not found, check if is absolute path name, and if so, give up. | |
695 | * Otherwise, try prepending each component of $MUPPATH in turn, | |
696 | * and trying that as a path. If a file is found, return that. | |
697 | * If we reach the end of the list, give up. | |
698 | * Giving up means returning 0. If we find it somewhere up the MUPPATH | |
699 | * rather than directly, update filename to point to the actual path used. | |
700 | */ | |
701 | ||
702 | FILE * | |
703 | find_file(filename_p) | |
704 | ||
705 | char **filename_p; | |
706 | ||
707 | { | |
708 | char *filename; | |
709 | FILE *file; | |
710 | char *fullpath; | |
711 | char *combiner; /* what goes between path components */ | |
712 | char *muppath; /* value of $MUPPATH */ | |
713 | char *prefix; /* component of $MUPPATH, | |
714 | * to prepend to filename */ | |
715 | char *path_separator = "\0"; /* between components in $MUPPATH */ | |
716 | ||
717 | ||
718 | /* first try name just as it is. */ | |
719 | filename = *filename_p; | |
720 | if ((file = fopen(filename, Read_mode)) != (FILE *) 0) { | |
721 | return(file); | |
722 | } | |
723 | ||
724 | /* If it's an absolute path, we have to give up */ | |
725 | if (is_absolute_path(filename)) { | |
726 | return ((FILE *) 0); | |
727 | } | |
728 | ||
729 | /* See if user set a $MUPPATH for where to look for includes */ | |
730 | if ((muppath = getenv("MUPPATH")) != (char *) 0) { | |
731 | ||
732 | #ifdef UNIX_LIKE_PATH_RULES | |
733 | path_separator = ":"; | |
734 | #endif | |
735 | #ifdef DOS_LIKE_PATH_RULES | |
736 | path_separator = ";"; | |
737 | #endif | |
738 | if (*path_separator == '\0') { | |
739 | /* If user went to the trouble of setting $MUPPATH, | |
740 | * they probably think it will work, so we better | |
741 | * let them know it doesn't. Hopefully they will | |
742 | * tell us what path rules their operating system | |
743 | * uses, so we support it in the next release. */ | |
744 | warning("MUPPATH facility not implemented for this operating system"); | |
745 | return (FILE *) 0; | |
746 | } | |
747 | ||
748 | /* walk through $MUPPATH */ | |
749 | for (prefix = strtok(muppath, path_separator); | |
750 | prefix != (char *) 0; | |
751 | prefix = strtok(0, path_separator)) { | |
752 | ||
753 | combiner = path_combiner(prefix); | |
754 | ||
755 | /* get enough space for the full name */ | |
756 | MALLOCA(char, fullpath, strlen(filename) + | |
757 | strlen(prefix) + 1 + strlen(combiner)); | |
758 | ||
759 | /* create full path */ | |
760 | sprintf(fullpath, "%s%s%s", prefix, combiner, filename); | |
761 | ||
762 | /* See if this file exists */ | |
763 | debug(4, "checking '%s' for include, using MUPPATH", | |
764 | fullpath); | |
765 | if ((file = fopen(fullpath, Read_mode)) != (FILE *) 0) { | |
766 | *filename_p = fullpath; | |
767 | return(file); | |
768 | } | |
769 | ||
770 | /* no file here, no need to save this path */ | |
771 | FREE(fullpath); | |
772 | } | |
773 | } | |
774 | ||
775 | return (FILE *) 0; | |
776 | } | |
777 | \f | |
778 | ||
779 | /* return true if given filename is an absolute path name */ | |
780 | ||
781 | static int | |
782 | is_absolute_path(filename) | |
783 | ||
784 | char *filename; | |
785 | ||
786 | { | |
787 | ||
788 | #ifdef UNIX_LIKE_PATH_RULES | |
789 | /* For Unix, a pathname is absolute if it starts with a slash */ | |
790 | return (*filename == '/'); | |
791 | #endif | |
792 | ||
793 | #ifdef DOS_LIKE_PATH_RULES | |
794 | /* If second character is a colon, then absolute */ | |
795 | if (*filename != '\0' && *(filename + 1) == ':') { | |
796 | return(1); | |
797 | } | |
798 | else { | |
799 | return(0); | |
800 | } | |
801 | #endif | |
802 | ||
803 | #if ! defined(UNIX_LIKE_PATH_RULES) && ! defined(DOS_LIKE_PATH_RULES) | |
804 | /* Not implemented for this operating system. We'll pretend | |
805 | * it's not, which will make it fall through and fail later. */ | |
806 | return(0); | |
807 | #endif | |
808 | } | |
809 | \f | |
810 | ||
811 | /* What to use to glue together a prefix and relative path to get a | |
812 | * full path. */ | |
813 | ||
814 | static char * | |
815 | path_combiner(prefix) | |
816 | ||
817 | char *prefix; | |
818 | ||
819 | { | |
820 | ||
821 | #ifdef UNIX_LIKE_PATH_RULES | |
822 | /* Unix separator is slash. If there was already a slash at the | |
823 | * end of the prefix, no problem: multiples slashes are like one */ | |
824 | return ("/"); | |
825 | #endif | |
826 | ||
827 | #ifdef DOS_LIKE_PATH_RULES | |
828 | /* Use backslash, unless prefix ended with slash or backslash, | |
829 | * in which case we don't need anything. */ | |
830 | char last_ch; | |
831 | last_ch = prefix[strlen(prefix) - 1]; | |
832 | return ((last_ch == '\\' || last_ch == '/') ? "" : "\\"); | |
833 | #endif | |
834 | ||
835 | #if ! defined(UNIX_LIKE_PATH_RULES) && ! defined(DOS_LIKE_PATH_RULES) | |
836 | /* Shouldn't be here. Unimplemented for this operating system */ | |
837 | return (""); | |
838 | #endif | |
839 | } | |
840 | \f | |
841 | ||
842 | /* return YES if macro is currently defined, NO if it isn't */ | |
843 | ||
844 | int | |
845 | is_defined(macname, paramtoo) | |
846 | ||
847 | char *macname; | |
848 | int paramtoo; /* if YES, also look for macro parameter by this name, | |
849 | * otherwise just macro */ | |
850 | ||
851 | { | |
852 | if (paramtoo) { | |
853 | return(resolve_mac_name(macname) == 0 ? NO : YES); | |
854 | } | |
855 | else { | |
856 | return(findMacro(macname) == 0 ? NO : YES); | |
857 | } | |
858 | } | |
859 | \f | |
860 | ||
861 | /* when the -D option is used on the command line, save the macro definition. */ | |
862 | ||
863 | void | |
864 | cmdline_macro(macdef) | |
865 | ||
866 | char *macdef; /* MACRO=definition */ | |
867 | ||
868 | { | |
869 | char *def; | |
870 | ||
871 | ||
872 | /* separate out the macro name */ | |
873 | if (*macdef == '_') { | |
874 | if (Mupmate == YES) { | |
875 | l_yyerror(0, -1, "Run > Set Options > Define Macros: macro name cannot start with underscore."); | |
876 | } | |
877 | else { | |
878 | l_yyerror(0, -1, "argument for %cD is invalid: macro name cannot start with underscore", Optch); | |
879 | } | |
880 | return; | |
881 | } | |
882 | for (def = macdef; *def != '\0'; def++) { | |
883 | if ( ! isupper(*def) && ! isdigit(*def) && *def != '_') { | |
884 | break; | |
885 | } | |
886 | } | |
887 | ||
888 | /* make sure has form XXX=definition */ | |
889 | if (def == macdef) { | |
890 | if (Mupmate == YES) { | |
891 | l_yyerror(0, -1, "Run > Set Options > Define Macros: macro name only allowed to contain upper case letters, numbers and underscores."); | |
892 | } | |
893 | else { | |
894 | l_yyerror(0, -1, "argument for %cD is missing or wrong format", Optch); | |
895 | } | |
896 | return; | |
897 | } | |
898 | ||
899 | if (*def == '=') { | |
900 | *def++ = '\0'; | |
901 | } | |
902 | else if (*def != '\0') { | |
903 | if (Mupmate == YES) { | |
904 | l_yyerror(0, -1, "Run > Set Options > Define Macros: macro name is invalid or missing '=' on macro definition."); | |
905 | } | |
906 | else { | |
907 | l_yyerror(0, -1, "argument for %cD had invalid name or missing '=' on macro definition", Optch); | |
908 | } | |
909 | return; | |
910 | } | |
911 | ||
912 | Curr_filename = "Command line argument"; | |
913 | (void) setup_macro(macdef); | |
914 | ||
915 | /* copy the macro to the macro temp file */ | |
916 | do { | |
917 | putc(*def, Mactmp_write); | |
918 | } while ( *def++ != '\0'); | |
919 | (void) fflush(Mactmp_write); | |
920 | #ifndef UNIX_LIKE_FILES | |
921 | fclose(Mactmp_write); | |
922 | #endif | |
923 | } | |
924 | \f | |
925 | ||
926 | /* recursively free a list of macro parameters */ | |
927 | ||
928 | static void | |
929 | free_parameters(param_p, macname, values_only) | |
930 | ||
931 | struct MAC_PARAM *param_p; /* what to free */ | |
932 | char *macname; /* name of macro having this parameter */ | |
933 | int values_only; /* if YES, just get rid of current | |
934 | * argument values, otherwise dispose of | |
935 | * the entire parameters list */ | |
936 | ||
937 | { | |
938 | char *mp_name; /* internal name of macro parameter */ | |
939 | ||
940 | ||
941 | if (param_p == (struct MAC_PARAM *) 0) { | |
942 | /* end of list */ | |
943 | return; | |
944 | } | |
945 | ||
946 | /* recurse */ | |
947 | free_parameters(param_p->next, macname, values_only); | |
948 | ||
949 | /* need to undef the internal name */ | |
950 | mp_name = mkmacparm_name(macname, param_p->param_name); | |
951 | undef_macro(mp_name); | |
952 | FREE(mp_name); | |
953 | ||
954 | if (values_only == NO) { | |
955 | ||
956 | /* release space */ | |
957 | if (param_p->param_name != (char *) 0) { | |
958 | FREE(param_p->param_name); | |
959 | } | |
960 | FREE(param_p); | |
961 | } | |
962 | } | |
963 | \f | |
964 | ||
965 | /* given a macro name and a parameter name, add the parameter name to the | |
966 | * list of parameters for the macro */ | |
967 | ||
968 | void | |
969 | add_parameter(macname, param_name) | |
970 | ||
971 | char *macname; | |
972 | char *param_name; /* name of parameter to add */ | |
973 | ||
974 | { | |
975 | struct MACRO *macinfo_p; /* which macro to add to */ | |
976 | struct MAC_PARAM *new_p; /* new parameter */ | |
977 | struct MAC_PARAM *param_p; /* to walk through parameter list */ | |
978 | ||
979 | ||
980 | /* get space to store info about the parameter */ | |
981 | MALLOC(MAC_PARAM, new_p, 1); | |
982 | ||
983 | /* get the macro information to know where to attach */ | |
984 | if ((macinfo_p = findMacro(macname)) == (struct MACRO *) 0) { | |
985 | pfatal("add_parameter unable to find macro %s", macname); | |
986 | } | |
987 | ||
988 | /* if this is first parameter, link directly to macro, otherwise | |
989 | * to the end of the parameter linked list */ | |
990 | if (macinfo_p->parameters_p == (struct MAC_PARAM *) 0) { | |
991 | macinfo_p->parameters_p = new_p; | |
992 | } | |
993 | else { | |
994 | /* walk down current parameter list */ | |
995 | for (param_p = macinfo_p->parameters_p; | |
996 | param_p != (struct MAC_PARAM *) 0; | |
997 | param_p = param_p->next) { | |
998 | ||
999 | /* check for duplicate name */ | |
1000 | if (strcmp(param_name, param_p->param_name) == 0) { | |
1001 | l_yyerror(Curr_filename, yylineno, | |
1002 | "duplicate parameter name %s for macro %s", | |
1003 | param_name, macname); | |
1004 | } | |
1005 | ||
1006 | /* link onto end of list */ | |
1007 | if (param_p->next == (struct MAC_PARAM *) 0) { | |
1008 | param_p->next = new_p; | |
1009 | break; | |
1010 | } | |
1011 | } | |
1012 | } | |
1013 | ||
1014 | /* fill in the info */ | |
1015 | new_p->param_name = dupstring(param_name); | |
1016 | /* assume non-quoted for now */ | |
1017 | new_p->quote_style = QS_UNQUOTED; | |
1018 | new_p->next = (struct MAC_PARAM *) 0; | |
1019 | ||
1020 | (macinfo_p->num_params)++; | |
1021 | } | |
1022 | \f | |
1023 | ||
1024 | /* given a string to duplicate, allocate space for a copy and return pointer | |
1025 | * to the copy. Caller is responsible for freeing if it needs to be freed */ | |
1026 | ||
1027 | static char * | |
1028 | dupstring(string) | |
1029 | ||
1030 | char *string; /* what to duplicate */ | |
1031 | ||
1032 | { | |
1033 | char *newstr; /* the duplicate */ | |
1034 | ||
1035 | /* get space and copy the old to new */ | |
1036 | MALLOCA(char, newstr, strlen(string) + 1); | |
1037 | (void) strcpy(newstr, string); | |
1038 | ||
1039 | return(newstr); | |
1040 | } | |
1041 | \f | |
1042 | ||
1043 | /* save the value of a macro argument by making a macro out of it */ | |
1044 | ||
1045 | void | |
1046 | set_parm_value(macname, argbuff, argnum) | |
1047 | ||
1048 | char *macname; /* name of macro */ | |
1049 | char *argbuff; /* value of argument */ | |
1050 | int argnum; /* which argument. 1 for the first, 2 for second, etc */ | |
1051 | ||
1052 | { | |
1053 | static struct MAC_PARAM *param_p; /* keep track of current | |
1054 | * parameter. The first time we are called for a | |
1055 | * given macro, we look up the macro and get its | |
1056 | * first parameter. After that, we just follow | |
1057 | * the linked list of parameters */ | |
1058 | struct MACRO *mac_p; /* info about macro */ | |
1059 | struct MACRO *argmac_p; /* info about the parameter */ | |
1060 | char *mp_name; /* pointer to malloc-ed space containing internal | |
1061 | * name of MACRO(PARAMETER) */ | |
1062 | char *p; /* to copy value to macro temp file */ | |
1063 | ||
1064 | if (argnum == 1) { | |
1065 | /* this is the first argument, so we have to look up the | |
1066 | * macro */ | |
1067 | if ((mac_p = findMacro(macname)) == (struct MACRO *) 0) { | |
1068 | pfatal("set_parm_value can't find macro"); | |
1069 | } | |
1070 | ||
1071 | /* point to head of parameters list */ | |
1072 | param_p = mac_p->parameters_p; | |
1073 | } | |
1074 | else { | |
1075 | /* just advance to the next parameter */ | |
1076 | if (param_p != (struct MAC_PARAM *) 0) { | |
1077 | param_p = param_p->next; | |
1078 | } | |
1079 | if (param_p == (struct MAC_PARAM *) 0) { | |
1080 | /* no next parameter. Error msg is printed elsewhere, | |
1081 | * so just clean up and return */ | |
1082 | FREE(argbuff); | |
1083 | return; | |
1084 | } | |
1085 | } | |
1086 | ||
1087 | /* if argbuff is null, there is no argument, which really means the | |
1088 | * argument is the null string. */ | |
1089 | if (argbuff == (char *) 0) { | |
1090 | argbuff = dupstring(""); | |
1091 | } | |
1092 | ||
1093 | /* now associate the value with the parameter */ | |
1094 | mp_name = mkmacparm_name(macname, param_p->param_name); | |
1095 | argmac_p = setup_macro(mp_name); | |
1096 | ||
1097 | /* copy the macro to the macro temp file */ | |
1098 | if (param_p->quote_style & QS_UNQUOTED) { | |
1099 | fprintf(Mactmp_write, "%s", argbuff); | |
1100 | putc('\0', Mactmp_write); | |
1101 | } | |
1102 | ||
1103 | if (param_p->quote_style & QS_QUOTED) { | |
1104 | short in_string; /* are we within double quotes? */ | |
1105 | short escaped; /* did we just see a backslash? */ | |
1106 | ||
1107 | /* Remember where we are stashing this quoted copy */ | |
1108 | argmac_p->quoted_offset = ftell(Mactmp_write); | |
1109 | ||
1110 | /* Add the initial quote */ | |
1111 | putc('"', Mactmp_write); | |
1112 | ||
1113 | /* Have to copy a character at a time, because we | |
1114 | * need to backslash any embedded quotes. This follows | |
1115 | * rules like the ANSI C preprocessor, except we only have | |
1116 | * to worry about strings, not character constants, because | |
1117 | * Mup doesn't have character constants. Also, we don't | |
1118 | * squeeze not-in-string white space runs to a single space, | |
1119 | * because this keeps the code simpler and there's no | |
1120 | * particular benefit in squeezing, other than perhaps | |
1121 | * saving a few bytes in the macro temp file. This code | |
1122 | * is similar to the gcc implementation of cpp. */ | |
1123 | in_string = escaped = NO; | |
1124 | for (p = argbuff; *p != '\0'; p++) { | |
1125 | if (escaped == YES) { | |
1126 | escaped = NO; | |
1127 | } | |
1128 | else { | |
1129 | if (*p == '\\') { | |
1130 | escaped = YES; | |
1131 | } | |
1132 | if (in_string == YES) { | |
1133 | if (*p == '"') { | |
1134 | /* reached end of string */ | |
1135 | in_string = NO; | |
1136 | } | |
1137 | } | |
1138 | else if (*p == '"') { | |
1139 | /* starting a string */ | |
1140 | in_string = YES; | |
1141 | } | |
1142 | } | |
1143 | ||
1144 | /* Escape quotes always; escape backslashes | |
1145 | * when they are inside strings. */ | |
1146 | if (*p == '"' || (in_string == YES && *p == '\\')) { | |
1147 | putc('\\', Mactmp_write); | |
1148 | } | |
1149 | putc(*p, Mactmp_write); | |
1150 | } | |
1151 | ||
1152 | /* Add the final quote */ | |
1153 | putc('"', Mactmp_write); | |
1154 | putc('\0', Mactmp_write); | |
1155 | } | |
1156 | (void) fflush(Mactmp_write); | |
1157 | #ifndef UNIX_LIKE_FILES | |
1158 | fclose(Mactmp_write); | |
1159 | #endif | |
1160 | ||
1161 | /* temp space no longer needed */ | |
1162 | FREE(mp_name); | |
1163 | FREE(argbuff); | |
1164 | } | |
1165 | \f | |
1166 | ||
1167 | /* make an internal macro name for a macro parameter name. The internal | |
1168 | * name is MACRO(PARAMETER). Space for name is malloc-ed, caller must free */ | |
1169 | ||
1170 | static char * | |
1171 | mkmacparm_name(macname, param_name) | |
1172 | ||
1173 | char *macname; | |
1174 | char *param_name; | |
1175 | ||
1176 | { | |
1177 | char *internal_name; | |
1178 | ||
1179 | /* add 3 for the 2 parentheses and the null */ | |
1180 | MALLOCA(char, internal_name, | |
1181 | strlen(macname) + strlen(param_name) + 3); | |
1182 | ||
1183 | (void) sprintf(internal_name, "%s(%s)", macname, param_name); | |
1184 | return(internal_name); | |
1185 | } | |
1186 | \f | |
1187 | ||
1188 | /* add a character to the current macro argument buffer. */ | |
1189 | ||
1190 | char * | |
1191 | add2argbuff(argbuff, c) | |
1192 | ||
1193 | char *argbuff; /* the argument buffer so far */ | |
1194 | int c; /* a character to add to the buffer */ | |
1195 | ||
1196 | { | |
1197 | static int offset; /* where in argbuff to put character */ | |
1198 | static int length; /* how many characters we have allocated */ | |
1199 | ||
1200 | ||
1201 | if (argbuff == (char *) 0) { | |
1202 | /* first time we were called, so malloc some space */ | |
1203 | MALLOCA(char, argbuff, MAC_ARG_SZ); | |
1204 | offset = 0; | |
1205 | length = MAC_ARG_SZ; | |
1206 | } | |
1207 | else if (offset == length - 1) { | |
1208 | /* need more space */ | |
1209 | length += MAC_ARG_SZ; | |
1210 | REALLOCA(char, argbuff, length); | |
1211 | } | |
1212 | ||
1213 | /* put character in the buffer and null terminate it */ | |
1214 | argbuff[offset++] = (char) c; | |
1215 | argbuff[offset] = '\0'; | |
1216 | ||
1217 | return(argbuff); | |
1218 | } | |
1219 | \f | |
1220 | ||
1221 | /* given a macro name that might be either a macro parameter or a regular | |
1222 | * macro, return pointer to the proper MACRO struct. We do this by | |
1223 | * searching up the stack of macros being expanded. If the name matches | |
1224 | * that of a macro parameter, use that, otherwise treat as a normal macro. | |
1225 | * Return null if can't resolve */ | |
1226 | ||
1227 | static struct MACRO * | |
1228 | resolve_mac_name(macname) | |
1229 | ||
1230 | char *macname; | |
1231 | ||
1232 | { | |
1233 | struct MACRO *mac_p; /* macro that matches the macname */ | |
1234 | int i; /* index through file stack */ | |
1235 | char *mp_name; /* macro parameter internal name */ | |
1236 | int quoted; /* if has quote designator */ | |
1237 | char *basename; /* macname not counting quote designator */ | |
1238 | ||
1239 | ||
1240 | if ((quoted = has_quote_designator(macname)) == YES) { | |
1241 | basename = macname + strlen(quote_designator); | |
1242 | } | |
1243 | else { | |
1244 | basename = macname; | |
1245 | } | |
1246 | ||
1247 | /* first go up the stack of macro calls, seeing if the macro | |
1248 | * name matches the parameter name of any macro. If so, that's | |
1249 | * the correct macro to use. Failing that, we look for the macro | |
1250 | * name just as is. If that fails too, we have to admit defeat | |
1251 | * and return null. */ | |
1252 | for (i = 0; i <= Fstkptr; i++) { | |
1253 | if (Filestack[i].mac_p != (struct MACRO *) 0) { | |
1254 | /* we are expanding a macro. See if that macro | |
1255 | * has a parameter by the name we are trying to | |
1256 | * resolve. If so, that's what we want */ | |
1257 | mp_name = mkmacparm_name(Filestack[i].mac_p->macname, | |
1258 | basename); | |
1259 | mac_p = findMacro(mp_name); | |
1260 | FREE(mp_name); | |
1261 | if (mac_p != 0) { | |
1262 | /* Eureka! We found it */ | |
1263 | return(mac_p); | |
1264 | } | |
1265 | } | |
1266 | } | |
1267 | ||
1268 | /* well, guess it wasn't a macro parameter. Try treating as | |
1269 | * just an ordinary macro name */ | |
1270 | /* Not allowed to be a special quoted macro; you can only do that | |
1271 | * to a macro parameter */ | |
1272 | if (quoted == YES) { | |
1273 | l_yyerror(Curr_filename, yylineno, | |
1274 | "cannot use '%s' on a macro, only on a macro parameter", | |
1275 | quote_designator); | |
1276 | return ((struct MACRO *) 0); | |
1277 | } | |
1278 | ||
1279 | return(findMacro(macname)); | |
1280 | } | |
1281 | \f | |
1282 | ||
1283 | /* Return YES if macro name reference includes designator denoting | |
1284 | * it is to be quoted (like usage of # in ANSI C preprocessor) */ | |
1285 | ||
1286 | static int | |
1287 | has_quote_designator(macname) | |
1288 | ||
1289 | char *macname; | |
1290 | ||
1291 | { | |
1292 | return (strncmp(macname, quote_designator, strlen(quote_designator)) | |
1293 | == 0 ? YES : NO ); | |
1294 | } | |
1295 | \f | |
1296 | ||
1297 | /* For "preprocessor" option, similar to the C compiler option to | |
1298 | * just run the macro preprocessor, instead of the usual yacc-generated | |
1299 | * yyparse(), we have this function that simply writes tokens out. | |
1300 | */ | |
1301 | ||
1302 | void | |
1303 | preproc() | |
1304 | { | |
1305 | while (yylex() != 0) { | |
1306 | /* In strings, the backslashes before any embedded quotes | |
1307 | * will have been swallowed, so we have to recreate them. */ | |
1308 | if (yytext[0] == '"') { | |
1309 | char *t; | |
1310 | putchar('"'); | |
1311 | for (t = yytext + 1; *t != '\0'; t++) { | |
1312 | if (*t == '"' && *(t+1) != '\0') { | |
1313 | putchar('\\'); | |
1314 | } | |
1315 | putchar(*t); | |
1316 | } | |
1317 | } | |
1318 | else { | |
1319 | printf("%s", yytext); | |
1320 | } | |
1321 | } | |
1322 | } |