Commit | Line | Data |
---|---|---|
0bdacbe1 AM |
1 | /* File : serv.c |
2 | Author : Ozan Yigit | |
3 | Updated: 4 May 1992 | |
4 | Defines: Principal built-in macros for PD M4. | |
5 | */ | |
15637ed4 | 6 | |
15637ed4 | 7 | #include "mdef.h" |
0bdacbe1 AM |
8 | #include "extr.h" |
9 | #include "ourlims.h" | |
10 | ||
11 | #define ucArgv(n) ((unsigned char *)argv[n]) | |
12 | ||
13 | /* 26-Mar-1993 Made m4trim() 8-bit clean. | |
14 | */ | |
15 | ||
16 | /* expand(<DS FN A1 ... An>) | |
17 | 0 1 2 n+1 -- initial indices in argv[] | |
18 | -1 0 1 n -- after adjusting argv++, argc-- | |
19 | This expands a user-defined macro; FN is the name of the macro, DS | |
20 | is its definition string, and A1 ... An are its arguments. | |
21 | */ | |
22 | void expand(argv, argc) | |
23 | char **argv; | |
24 | int argc; | |
25 | { | |
26 | register char *t; | |
27 | register char *p; | |
28 | register int n; | |
29 | ||
30 | #ifdef DEBUG | |
31 | fprintf(stderr, "expand(%s,%d)\n", argv[1], argc); | |
32 | #endif | |
33 | argc--; /* discount definition string (-1th arg) */ | |
34 | t = *argv++; /* definition string as a whole */ | |
35 | for (p = t; *p++; ) ; | |
36 | p -= 2; /* points to last character of definition */ | |
37 | while (p > t) { /* if definition is empty, fails at once */ | |
38 | if (*--p != ARGFLAG) { | |
39 | putback(p[1]); | |
40 | } else { | |
41 | switch (p[1]) { | |
42 | case '#': | |
43 | pbnum(argc-1); | |
44 | break; | |
45 | case '0': case '1': case '2': case '3': case '4': | |
46 | case '5': case '6': case '7': case '8': case '9': | |
47 | if ((n = p[1]-'0') < argc) pbstr(argv[n]); | |
48 | break; | |
49 | case '*': /* push all arguments back */ | |
50 | for (n = argc-1; n > 1; n--) { | |
51 | pbstr(argv[n]); | |
52 | putback(','); | |
53 | } | |
54 | pbstr(argv[1]); | |
55 | break; | |
56 | case '@': /* push arguments back quoted */ | |
57 | for (n = argc-1; n > 1; n--) { | |
58 | pbqtd(argv[n]); | |
59 | putback(','); | |
60 | } | |
61 | pbqtd(argv[1]); | |
62 | break; | |
63 | case '$': /* $$ => $ */ | |
64 | break; | |
65 | default: | |
66 | putback(p[1]); | |
67 | putback(p[0]); | |
68 | break; | |
69 | } | |
70 | p--; | |
71 | } | |
72 | } | |
73 | if (p == t) putback(*p); /* do last character */ | |
74 | } | |
75 | ||
76 | ||
77 | static char nuldefmsg[] = "m4: defining null name."; | |
78 | static char recdefmsg[] = "m4: macro defined as itself."; | |
79 | ||
80 | /* dodefine(Name, Definition) | |
81 | install Definition as the only definition of Name in the hash table. | |
15637ed4 | 82 | */ |
0bdacbe1 AM |
83 | void dodefine(name, defn) |
84 | register char *name; | |
85 | register char *defn; | |
86 | { | |
87 | register ndptr p; | |
88 | ||
89 | if (!name || !*name) error(nuldefmsg); | |
90 | if (strcmp(name, defn) == 0) error(recdefmsg); | |
91 | #ifdef DEBUG | |
92 | fprintf(stderr, "define(%s,--)\n", name); | |
93 | #endif | |
94 | if ((p = lookup(name)) == nil) { | |
95 | p = addent(name); | |
96 | } else | |
97 | if (p->defn != null) { /* what if p->type & STATIC ? */ | |
98 | free(p->defn); | |
99 | } | |
100 | p->defn = !defn || !*defn ? null : strsave(defn); | |
101 | p->type = MACRTYPE; | |
102 | } | |
103 | ||
104 | ||
105 | /* dopushdef(Name, Definition) | |
106 | install Definition as the *first* definition of Name in the hash table, | |
107 | but do not remove any existing definitions. The new definition will | |
108 | hide any old ones until a popdef() removes it. | |
109 | */ | |
110 | void dopushdef(name, defn) | |
111 | register char *name; | |
112 | register char *defn; | |
113 | { | |
114 | register ndptr p; | |
115 | ||
116 | if (!name || !*name) error(nuldefmsg); | |
117 | if (strcmp(name, defn) == 0) error(recdefmsg); | |
118 | #ifdef DEBUG | |
119 | fprintf(stderr, "pushdef(%s,--)\n", name); | |
120 | #endif | |
121 | p = addent(name); | |
122 | p->defn = !defn || !*defn ? null : strsave(defn); | |
123 | p->type = MACRTYPE; | |
124 | } | |
125 | ||
126 | ||
127 | /* dodefn(Name) | |
128 | push back a *quoted* copy of Name's definition. | |
129 | */ | |
130 | void dodefn(name) | |
131 | char *name; | |
132 | { | |
133 | register ndptr p; | |
134 | ||
135 | if ((p = lookup(name)) != nil && p->defn != null) pbqtd(p->defn); | |
136 | } | |
137 | ||
138 | ||
139 | /* dodump(<? dump>) dump all definition in the hash table | |
140 | dodump(<? dump F1 ... Fn>) dump the definitions of F1 ... Fn in that order | |
141 | The requested definitions are written to stderr. What happens to names | |
142 | which have a built-in (numeric) definition? | |
143 | */ | |
144 | void dodump(argv, argc) | |
145 | register char **argv; | |
146 | register int argc; | |
147 | { | |
148 | register int n; | |
149 | ndptr p; | |
150 | static char dumpfmt[] = "define(`%s',\t`%s')\n"; | |
151 | ||
152 | if (argc > 2) { | |
153 | for (n = 2; n < argc; n++) | |
154 | if ((p = lookup(argv[n])) != nil) | |
155 | fprintf(stderr, dumpfmt, p->name, p->defn); | |
156 | } else { | |
157 | for (n = 0; n < HASHSIZE; n++) | |
158 | for (p = hashtab[n]; p != nil; p = p->nxtptr) | |
159 | fprintf(stderr, dumpfmt, p->name, p->defn); | |
160 | } | |
161 | } | |
162 | ||
163 | ||
164 | /* doifelse(<? ifelse {x y ifx=y}... [else]>) | |
165 | 0 1 2 3 4 [2 when we get to it] | |
166 | */ | |
167 | void doifelse(argv, argc) | |
168 | register char **argv; | |
169 | register int argc; | |
170 | { | |
171 | for (; argc >= 5; argv += 3, argc -= 3) | |
172 | if (strcmp(argv[2], argv[3]) == 0) { | |
173 | pbstr(argv[4]); | |
174 | return; | |
175 | } | |
176 | if (argc >= 3) pbstr(argv[2]); | |
177 | } | |
178 | ||
179 | ||
180 | /* doinclude(FileName) | |
181 | include a given file. | |
182 | */ | |
183 | int doincl(FileName) | |
184 | char *FileName; | |
185 | { | |
186 | if (ilevel+1 == MAXINP) error("m4: too many include files."); | |
187 | #ifdef DEBUG | |
188 | fprintf(stderr, "include(%s)\n", FileName); | |
189 | #endif | |
190 | if ((infile[ilevel+1] = fopen(FileName, "r")) != NULL) { | |
191 | #ifndef NO__FILE | |
192 | dopushdef("__FILE__", FileName); | |
193 | #endif | |
194 | bbstack[ilevel+1] = bb; | |
195 | bb = bp; | |
196 | ilevel++; | |
197 | return 1; | |
198 | } else { | |
199 | return 0; | |
200 | } | |
201 | } | |
202 | ||
203 | ||
15637ed4 | 204 | #ifdef EXTENDED |
0bdacbe1 AM |
205 | /* dopaste(FileName) |
206 | copy a given file to the output stream without any macro processing. | |
207 | */ | |
208 | int dopaste(FileName) | |
209 | char *FileName; | |
210 | { | |
211 | register FILE *pf; | |
212 | register FILE *afil = active; | |
213 | register int c; | |
214 | ||
215 | if ((pf = fopen(FileName, "r")) != NULL) { | |
216 | while ((c = getc(pf)) != EOF) putc(c, afil); | |
217 | (void) fclose(pf); | |
218 | return 1; | |
219 | } else { | |
220 | return 0; | |
221 | } | |
222 | } | |
15637ed4 | 223 | #endif |
0bdacbe1 AM |
224 | |
225 | ||
226 | /* dochq(<? changequote [left [right [verbatim]]]>) | |
227 | 0 1 2 3 4 | |
228 | change the quote characters; to single characters only. | |
229 | Empty arguments result in no change for that parameter. | |
230 | Missing arguments result in defaults: | |
231 | changequote => ` ' ^V | |
232 | changequote(q) => q q ^V | |
233 | changequote(l,r) => l r ^V | |
234 | changequote(l,r,v) => l r v | |
235 | There isn't any way of switching the verbatim-quote off, | |
236 | but if you make it the same as the right quote it won't | |
237 | be able to do anything (we check for R, L, V in that order). | |
238 | */ | |
239 | void dochq(argv, argc) | |
240 | register char **argv; | |
241 | register int argc; | |
242 | { | |
243 | if (argc > 2) { | |
244 | if (*argv[2]) lquote = *argv[2]; | |
245 | if (argc > 3) { | |
246 | if (*argv[3]) rquote = *argv[3]; | |
247 | if (argc > 4 && *argv[4]) vquote = *argv[4]; | |
248 | } else { | |
249 | rquote = lquote; | |
250 | } | |
251 | } else { | |
252 | lquote = LQUOTE; | |
253 | rquote = RQUOTE; | |
254 | vquote = VQUOTE; | |
255 | } | |
256 | } | |
257 | ||
258 | ||
259 | /* dochc(<? changecomment [left [right]]>) | |
260 | 0 1 2 3 | |
261 | change the comment delimiters; to single characters only. | |
262 | */ | |
263 | void dochc(argv, argc) | |
264 | register char **argv; | |
265 | register int argc; | |
266 | { | |
267 | if (argc > 2) { | |
268 | if (*argv[2]) scommt = *argv[2]; | |
269 | if (argc > 3) { | |
270 | if (*argv[3]) ecommt = *argv[3]; | |
271 | } else { | |
272 | ecommt = ECOMMT; | |
273 | } | |
274 | } else { | |
63464f7b JH |
275 | scommt = '\0'; /* assuming no nulls in input */ |
276 | ecommt = '\0'; | |
0bdacbe1 AM |
277 | } |
278 | } | |
279 | ||
280 | ||
281 | /* dodivert - divert the output to a temporary file | |
282 | */ | |
283 | void dodiv(n) | |
284 | register int n; | |
285 | { | |
286 | if (n < 0 || n >= MAXOUT) n = 0; /* bitbucket */ | |
287 | if (outfile[n] == NULL) { | |
288 | m4temp[UNIQUE] = '0' + n; | |
289 | if ((outfile[n] = fopen(m4temp, "w")) == NULL) | |
290 | error("m4: cannot divert."); | |
291 | } | |
292 | oindex = n; | |
293 | active = outfile[n]; | |
294 | } | |
295 | ||
296 | ||
297 | /* doundivert - undivert a specified output, or all | |
15637ed4 | 298 | * other outputs, in numerical order. |
0bdacbe1 AM |
299 | */ |
300 | void doundiv(argv, argc) | |
301 | register char **argv; | |
302 | register int argc; | |
303 | { | |
304 | register int ind; | |
305 | register int n; | |
306 | ||
307 | if (argc > 2) { | |
308 | for (ind = 2; ind < argc; ind++) { | |
309 | n = expr(argv[ind]); | |
310 | if (n > 0 && n < MAXOUT && outfile[n] != NULL) getdiv(n); | |
311 | } | |
312 | } else { | |
313 | for (n = 1; n < MAXOUT; n++) | |
314 | if (outfile[n] != NULL) getdiv(n); | |
315 | } | |
316 | } | |
317 | ||
318 | ||
319 | /* dosub(<? substr {offset} [{length}]>) | |
320 | The System V Interface Definition does not say what happens when the | |
321 | offset or length are out of range. I have chosen to force them into | |
322 | range, with the result that unlike the former version of this code, | |
323 | dosub cannot be tricked into SIGSEGV. | |
324 | ||
325 | BUG: This is not 8-bit clean yet. | |
326 | */ | |
327 | void dosub(argv, argc) | |
328 | char **argv; | |
329 | int argc; | |
330 | { | |
331 | register int nc; /* number of characters */ | |
332 | register char *ap = argv[2]; /* target string */ | |
333 | register int al = strlen(ap); /* its length */ | |
334 | register int df = expr(argv[3]);/* offset */ | |
335 | ||
336 | if (df < 0) df = 0; else /* force df back into the range */ | |
337 | if (df > al) df = al; /* 0 <= df <= al */ | |
338 | al -= df; /* now al limits nc */ | |
339 | ||
340 | if (argc >= 5) { /* nc is provided */ | |
341 | nc = expr(argv[4]); | |
342 | if (nc < 0) nc = 0; else /* force nc back into the range */ | |
343 | if (nc > al) nc = al; /* 0 <= nc <= strlen(ap)-df */ | |
344 | } else { | |
345 | nc = al; /* default is all rest of ap */ | |
346 | } | |
347 | ap += df + nc; | |
348 | while (--nc >= 0) putback(*--ap); | |
349 | } | |
350 | ||
351 | ||
352 | /* map(dest, src, from, to) | |
353 | map every character of src that is specified in from | |
354 | into "to" and replace in dest. (source "src" remains untouched) | |
355 | ||
356 | This is a standard implementation of Icon's map(s,from,to) function. | |
357 | Within mapvec, we replace every character of "from" with the | |
358 | corresponding character in "to". If "to" is shorter than "from", | |
359 | then the corresponding entries are null, which means that those | |
360 | characters disappear altogether. Furthermore, imagine a call like | |
361 | map(dest, "sourcestring", "srtin", "rn..*"). In this case, `s' maps | |
362 | to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' ultimately | |
363 | maps to `*'. In order to achieve this effect in an efficient manner | |
364 | (i.e. without multiple passes over the destination string), we loop | |
365 | over mapvec, starting with the initial source character. If the | |
366 | character value (dch) in this location is different from the source | |
367 | character (sch), sch becomes dch, once again to index into mapvec, | |
368 | until the character value stabilizes (i.e. sch = dch, in other words | |
369 | mapvec[n] == n). Even if the entry in the mapvec is null for an | |
370 | ordinary character, it will stabilize, since mapvec[0] == 0 at all | |
371 | times. At the end, we restore mapvec* back to normal where | |
372 | mapvec[n] == n for 0 <= n <= 127. This strategy, along with the | |
373 | restoration of mapvec, is about 5 times faster than any algorithm | |
374 | that makes multiple passes over the destination string. | |
375 | */ | |
376 | ||
377 | void map(d, s, f, t) | |
378 | char *d, *s, *f, *t; | |
379 | { | |
380 | register unsigned char *dest = (unsigned char *)d; | |
381 | register unsigned char *src = (unsigned char *)s; | |
382 | unsigned char *from = (unsigned char *)f; | |
383 | register unsigned char *to = (unsigned char *)t; | |
384 | register unsigned char *tmp; | |
385 | register unsigned char sch, dch; | |
386 | static unsigned char mapvec[1+UCHAR_MAX] = {1}; | |
387 | ||
388 | if (mapvec[0]) { | |
389 | register int i; | |
390 | for (i = 0; i <= UCHAR_MAX; i++) mapvec[i] = i; | |
391 | } | |
392 | if (src && *src) { | |
393 | /* create a mapping between "from" and "to" */ | |
394 | if (to && *to) | |
395 | for (tmp = from; sch = *tmp++; ) mapvec[sch] = *to++; | |
396 | else | |
397 | for (tmp = from; sch = *tmp++; ) mapvec[sch] = '\0'; | |
398 | ||
399 | while (sch = *src++) { | |
400 | while ((dch = mapvec[sch]) != sch) sch = dch; | |
401 | if (*dest = dch) dest++; | |
402 | } | |
403 | /* restore all the changed characters */ | |
404 | for (tmp = from; sch = *tmp++; ) mapvec[sch] = sch; | |
405 | } | |
406 | *dest = '\0'; | |
407 | } | |
408 | ||
409 | ||
410 | #ifdef EXTENDED | |
411 | ||
412 | /* m4trim(<? m4trim [string [leading [trailing [middle [rep]]]]]>) | |
413 | 0 1 2 3 4 5 6 | |
414 | ||
415 | (1) Any prefix consisting of characters in the "leading" set is removed. | |
416 | The default is " \t\n". | |
417 | (2) Any suffix consisting of characters in the "trailing" set is removed. | |
418 | The default is to be the same as leading. | |
419 | (3) Any block of consecutive characters in the "middle" set is replaced | |
420 | by the rep string. The default for middle is " \t\n", and the | |
421 | default for rep is the first character of middle. | |
422 | */ | |
423 | void m4trim(argv, argc) | |
424 | char **argv; | |
425 | int argc; | |
426 | { | |
427 | static unsigned char repbuf[2] = " "; | |
428 | static unsigned char layout[] = " \t\n\r\f"; | |
429 | unsigned char *string = argc > 2 ? ucArgv(2) : repbuf+1; | |
430 | unsigned char *leading = argc > 3 ? ucArgv(3) : layout; | |
431 | unsigned char *trailing = argc > 4 ? ucArgv(4) : leading; | |
432 | unsigned char *middle = argc > 5 ? ucArgv(5) : trailing; | |
433 | unsigned char *rep = argc > 6 ? ucArgv(6) : | |
434 | (repbuf[0] = *middle, repbuf); | |
435 | static unsigned char sets[1+UCHAR_MAX]; | |
436 | # define PREF 1 | |
437 | # define SUFF 2 | |
438 | # define MIDL 4 | |
439 | register int i, n; | |
440 | ||
441 | for (i = UCHAR_MAX; i >= 0; ) sets[i--] = 0; | |
442 | while (*leading) sets[*leading++] |= PREF; | |
443 | while (*trailing) sets[*trailing++] |= SUFF; | |
444 | while (*middle) sets[*middle++] |= MIDL; | |
445 | ||
446 | while (*string && sets[*string]&PREF) string++; | |
447 | n = strlen((char *)string); | |
448 | while (n > 0 && sets[string[n-1]]&SUFF) n--; | |
449 | while (n > 0) { | |
450 | i = string[--n]; | |
451 | if (sets[i]&MIDL) { | |
452 | pbstr((char*)rep); | |
453 | while (n > 0 && sets[string[n-1]]&MIDL) n--; | |
454 | } else { | |
455 | putback(i); | |
456 | } | |
457 | } | |
458 | } | |
459 | ||
460 | ||
461 | /* defquote(MacroName # The name of the "quoter" macro to be defined. | |
462 | [, Opener # default: "'". The characters to place at the | |
463 | # beginning of the result. | |
464 | [, Separator # default: ",". The characters to place between | |
465 | # successive arguments. | |
466 | [, Closer # default: same as Opener. The characters to | |
467 | # place at the end of the result. | |
468 | [, Escape # default: `' The escape character to put in | |
469 | # front of things that need escaping. | |
470 | [, Default # default: simple. Possible values are | |
471 | # [lL].* = letter, corresponds to PLAIN1. | |
472 | # [dD].* = digit, corresponds to PLAIN2. | |
473 | # [sS].* = simple, corresponds to SIMPLE. | |
474 | # [eE].* = escaped,corresponds to SCAPED. | |
475 | # .*, corresponds to FANCY | |
476 | [, Letters # default: `'. The characters of type "L". | |
477 | [, Digits # default: `'. The characters of type "D". | |
478 | [, Simple # default: `'. The characters of type "S". | |
479 | [, Escaped # default: `'. The characters of type "E". | |
480 | {, Fancy # default: none. Each has the form `C'`Repr' | |
481 | # saying that the character C is to be represented | |
482 | # as Repr. Can be used for trigraphs, \n, &c. | |
483 | }]]]]]]]]]) | |
484 | ||
485 | Examples: | |
486 | defquote(DOUBLEQT, ") | |
487 | defquote(SINGLEQT, ') | |
488 | After these definitions, | |
489 | DOUBLEQT(a, " b", c) => "a,"" b"",c" | |
490 | SINGLEQT("Don't`, 'he said.") => '"Don''t, he said."' | |
491 | Other examples defining quote styles for several languages will be | |
492 | provided later. | |
493 | ||
494 | A quoter is represented in M4 by a special identifying number and a | |
495 | pointer to a Quoter record. I expect that there will be few quoters | |
496 | but that they will need to go fairly fast. | |
497 | ||
498 | */ | |
499 | ||
500 | #define PLAIN1 0 | |
501 | #define PLAIN2 1 | |
502 | #define SIMPLE 2 | |
503 | #define SCAPED 3 | |
504 | #define FANCY 4 | |
505 | ||
506 | struct Quoter | |
507 | { | |
508 | char *opener; | |
509 | char *separator; | |
510 | char *closer; | |
511 | char *escape; | |
512 | char *fancy[1+UCHAR_MAX]; | |
513 | char class[1+UCHAR_MAX]; | |
514 | }; | |
515 | ||
516 | void freeQuoter(q) | |
517 | struct Quoter *q; | |
518 | { | |
519 | int i; | |
520 | ||
521 | free(q->opener); | |
522 | free(q->separator); | |
523 | free(q->closer); | |
524 | free(q->escape); | |
525 | for (i = UCHAR_MAX; i >= 0; i--) | |
526 | if (q->fancy[i]) free(q->fancy[i]); | |
527 | free((char *)q); | |
528 | } | |
529 | ||
530 | /* dodefqt(< | |
531 | 0 ? | |
532 | 1 defquote | |
533 | 2 MacroName | |
534 | [ 3 Opener | |
535 | [ 4 Separator | |
536 | [ 5 Closer | |
537 | [ 6 Escape | |
538 | [ 7 Default | |
539 | [ 8 Letters | |
540 | [ 9 Digits | |
541 | [10 Simple | |
542 | [11 Escaped | |
543 | [11+i Fancy[i] ]]]]]]]]]]>) | |
544 | */ | |
545 | ||
546 | void dodefqt(argv, argc) | |
547 | char **argv; | |
548 | int argc; | |
549 | { | |
550 | struct Quoter q, *r; | |
551 | register int i; | |
552 | register unsigned char *s; | |
553 | register int c; | |
554 | ndptr p; | |
555 | ||
556 | if (!(argc > 2 && *argv[2])) error(nuldefmsg); | |
557 | switch (argc > 7 ? argv[7][0] : '\0') { | |
558 | case 'l': case 'L': c = PLAIN1; break; | |
559 | case 'd': case 'D': c = PLAIN2; break; | |
560 | case 'e': case 'E': c = SCAPED; break; | |
561 | case 'f': case 'F': c = FANCY; break; | |
562 | default: c = SIMPLE; | |
563 | } | |
564 | for (i = UCHAR_MAX; --i >= 0; ) q.class[i] = c; | |
565 | for (i = UCHAR_MAX; --i >= 0; ) q.fancy[i] = 0; | |
566 | q.opener = strsave(argc > 3 ? argv[3] : ""); | |
567 | q.separator = strsave(argc > 4 ? argv[4] : ","); | |
568 | q.closer = strsave(argc > 5 ? argv[5] : q.opener); | |
569 | q.escape = strsave(argc > 6 ? argv[6] : ""); | |
570 | if (argc > 8) | |
571 | for (s = (unsigned char *)argv[8]; c = *s++; ) | |
572 | q.class[c] = PLAIN1; | |
573 | if (argc > 9) | |
574 | for (s = (unsigned char *)argv[9]; c = *s++; ) | |
575 | q.class[c] = PLAIN2; | |
576 | if (argc > 10) | |
577 | for (s = (unsigned char *)argv[10]; c = *s++; ) | |
578 | q.class[c] = SIMPLE; | |
579 | if (argc > 11) | |
580 | for (s = (unsigned char *)argv[11]; c = *s++; ) | |
581 | q.class[c] = SCAPED; | |
582 | for (i = 12; i < argc; i++) { | |
583 | s = (unsigned char *)argv[i]; | |
584 | c = *s++; | |
585 | q.fancy[c] = strsave((char *)s); | |
586 | q.class[c] = FANCY; | |
587 | } | |
588 | /* Now we have to make sure that the closing quote works. */ | |
589 | if ((c = q.closer[0]) && q.class[c] <= SIMPLE) { | |
590 | if (q.escape[0]) { | |
591 | q.class[c] = SCAPED; | |
592 | } else { | |
593 | char buf[3]; | |
594 | buf[0] = c, buf[1] = c, buf[2] = '\0'; | |
595 | q.fancy[c] = strsave(buf); | |
596 | q.class[c] = FANCY; | |
597 | } | |
598 | } | |
599 | /* We also have to make sure that the escape (if any) works. */ | |
600 | if ((c = q.escape[0]) && q.class[c] <= SIMPLE) { | |
601 | q.class[c] = SCAPED; | |
602 | } | |
603 | r = (struct Quoter *)malloc(sizeof *r); | |
604 | if (r == NULL) error("m4: no more memory"); | |
605 | *r = q; | |
606 | p = addent(argv[2]); | |
607 | p->defn = (char *)r; | |
608 | p->type = QUTRTYPE; | |
609 | } | |
610 | ||
611 | ||
612 | /* doqutr(<DB MN A1 ... An>) | |
613 | 0 1 2 n+1 argc | |
614 | argv[0] points to the struct Quoter. | |
615 | argv[1] points to the name of this quoting macro | |
616 | argv[2..argc-1] point to the arguments. | |
617 | This applies a user-defined quoting macro. For example, we could | |
618 | define a macro to produce Prolog identifiers: | |
619 | defquote(plid, ', , ', , simple, | |
620 | abcdefghijklmnopqrstuvwxyz, | |
621 | ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789) | |
622 | ||
623 | After doing that, | |
624 | plid(foo) => foo | |
625 | plid(*) => '*' | |
626 | plid(Don't) => 'Don''t' | |
627 | plid(foo,) => 'foo' | |
628 | */ | |
629 | void doqutr(argv, argc) | |
630 | char **argv; | |
631 | int argc; | |
632 | /* DEFINITION-BLOCK MacroName Arg1 ... Argn | |
633 | 0 1 2 n-1 argc | |
634 | */ | |
635 | { | |
636 | struct Quoter *r = (struct Quoter *)argv[0]; | |
637 | char *p; | |
638 | register unsigned char *b, *e; | |
639 | int i; | |
640 | register int c; | |
641 | ||
642 | for (;;) { /* does not actually loop */ | |
643 | if (argc != 3) break; | |
644 | b = ucArgv(2); | |
645 | e = b + strlen((char*)b); | |
646 | if (e == b) break; | |
647 | if (r->class[*b++] != PLAIN1) break; | |
648 | while (b != e && r->class[*b] <= PLAIN2) b++; | |
649 | if (b != e) break; | |
650 | pbstr(argv[2]); | |
651 | return; | |
652 | } | |
653 | ||
654 | p = r->closer; | |
655 | if (argc < 3) { | |
656 | pbstr(p); | |
657 | } else | |
658 | for (i = argc-1; i >= 2; i--) { | |
659 | pbstr(p); | |
660 | b = ucArgv(i); | |
661 | e = b+strlen((char *)b); | |
662 | while (e != b) | |
663 | switch (r->class[c = *--e]) { | |
664 | case FANCY: | |
665 | p = r->fancy[c]; | |
666 | if (p) { | |
667 | pbstr(p); | |
668 | } else { | |
669 | pbrad(c, 8, 1); | |
670 | pbstr(r->escape); | |
671 | } | |
672 | break; | |
673 | case SCAPED: | |
674 | putback(c); | |
675 | pbstr(r->escape); | |
676 | break; | |
677 | default: | |
678 | putback(c); | |
679 | break; | |
680 | } | |
681 | p = r->separator; | |
682 | } | |
683 | pbstr(r->opener); | |
684 | } | |
685 | ||
15637ed4 | 686 | #endif |