Pristine Ack-5.5
[Ack-5.5.git] / lang / cem / cemcom.ansi / replace.c
1 /*
2  * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
3  * See the copyright notice in the ACK home directory, in the file "Copyright".
4  */
5 /* $Id: replace.c,v 1.22 1998/02/09 09:49:00 ceriel Exp $ */
6 /*  M A C R O   R E P L A C E M E N T */
7 #include        "nopp.h"
8
9 #ifndef NOPP
10
11 #include        "debug.h"
12 #include        "pathlength.h"
13 #include        "strsize.h"
14 #include        "nparams.h"
15 #include        "idfsize.h"
16 #include        "numsize.h"
17 #include        <alloc.h>
18 #include        "idf.h"
19 #include        "input.h"
20 #include        "macro.h"
21 #include        "arith.h"
22 #include        "LLlex.h"
23 #include        "class.h"
24 #include        "assert.h"
25 #include        "static.h"
26 #include        "macbuf.h"
27 #include        "replace.h"
28
29 extern struct idf *GetIdentifier();
30 extern int InputLevel;
31 struct repl *ReplaceList;       /* list of currently active macros */
32 extern char *strcat(), *strcpy();
33
34 int
35 replace(idf)
36         register struct idf *idf;
37 {
38         /*      replace is called by the lexical analyzer to perform
39                 macro replacement. The routine actualy functions as a
40                 higher interface to the real thing: expand_macro().
41         */
42         struct repl *repl;
43
44         if (!(idf->id_macro)) return 0;
45         if (idf->id_macro->mc_flag & NOREPLACE)
46                 return 0;
47         repl = new_repl();
48         repl->r_ptr = repl->r_text = Malloc(repl->r_size = LAPBUF);
49         repl->r_args = new_args();
50         repl->r_idf = idf;
51         if (!expand_macro(repl, idf))
52                 return 0;
53         InputLevel++;
54         InsertText(repl->r_text, (int)(repl->r_ptr - repl->r_text));
55         idf->id_macro->mc_flag |= NOREPLACE;
56         repl->r_level = InputLevel;
57         repl->next = ReplaceList;
58         ReplaceList = repl;
59         return 1;
60 }
61
62 unstackrepl()
63 {
64         Unstacked++;
65 }
66
67 freeargs(args)
68         struct args *args;
69 {
70         register int i;
71
72         /* We must don't know how many parameters were specified, so be
73          * prepared to free all NPARAMS parameters.
74          * When an expvec is !0, the rawvec will also be !0.
75          * When an expvec is 0, all remaining vectors will also be 0.
76          */
77         for (i = 0; i < NPARAMS; i++) {
78                 if (args->a_expvec[i]) {
79                         free(args->a_expvec[i]);
80                         free(args->a_rawvec[i]);
81                 } else break;
82         }
83         free_args(args);
84 }
85
86 EnableMacros()
87 {
88         register struct repl *r = ReplaceList, *prev = 0;
89
90         ASSERT(Unstacked > 0);
91         while(r) {
92                 struct repl *nxt = r->next;
93
94                 if (r->r_level > InputLevel) {
95                         r->r_idf->id_macro->mc_flag &= ~NOREPLACE;
96                         if (!prev) ReplaceList = nxt;
97                         else prev->next = nxt;
98                         free(r->r_text);
99                         freeargs(r->r_args);
100                         free_repl(r);
101                 }
102                 else prev = r;
103                 r = nxt;
104         }
105         Unstacked = 0;
106 }
107
108 expand_macro(repl, idf)
109         register struct repl *repl;
110         register struct idf *idf;
111 {
112         /*      expand_macro() does the actual macro replacement.
113                 "idf" is a description of the identifier which
114                 caused the replacement.
115                 If the identifier represents a function-like macro
116                 call, the number of actual parameters is checked
117                 against the number of formal parameters. Note that
118                 in ANSI C the parameters are expanded first;
119                 this is done by calling getactuals().
120                 When the possible parameters are expanded, the replace-
121                 ment list associated with "idf" is expanded.
122                 expand_macro() returns 1 if the replacement succeeded
123                 and 0 if some error occurred.
124
125                 A special case is "defined". This acts as a unary operator
126                 on a single, unexpanded identifier, which may be surrounded
127                 by parenthesis. The function expand_defined() handles this.
128         */
129         register struct macro *mac = idf->id_macro;
130         struct args *args = repl->r_args;
131         register int ch;
132
133         if (mac->mc_nps != -1) {        /* with parameter list  */
134                 if (mac->mc_flag & FUNC) {
135                         /* the following assertion won't compile:
136                         ASSERT(!strcmp("defined", idf->id_text));
137                         expand the assert macro by hand (??? dirty, temporary)
138                         */
139 #ifdef  DEBUG
140                         if (strcmp("defined", idf->id_text))
141                                 crash("in %s, %u: assertion %s failed",
142                                         __FILE__, __LINE__ - 2,
143                                         "strcmp(\"defined\", idf->id_text)");
144 #endif
145                         if (!AccDefined) return 0;
146                         expand_defined(repl);
147                         return 1;
148                 }
149
150                 ch = GetChar();
151                 ch = skipspaces(ch,1);
152                 if (ch != '(') {        /* no replacement if no () */
153                         ChPushBack(ch);
154                         return 0;
155                 } else
156                         getactuals(repl, idf);
157
158         }
159
160         if (mac->mc_flag & FUNC) /* this macro leads to special action */
161                 macro_func(idf);
162
163         macro2buffer(repl, idf, args);
164
165         /*      According to the ANSI definition:
166
167                         #define a +
168                         a+b; --> + + b ;
169
170                 'a' must be substituded, but the result should be
171                 three tokens: + + ID. Therefore a token separator is
172                 inserted after the replacement.
173         */
174         if (repl->r_text == repl->r_ptr || *(repl->r_ptr - 1) != TOKSEP) {
175                 add2repl(repl, TOKSEP);
176         }
177         return 1;
178 }
179
180 expand_defined(repl)
181         register struct repl *repl;
182 {
183         register int ch = GetChar();
184         struct idf *id;
185         int parens = 0;
186
187         ch = skipspaces(ch, 0);
188
189         if (ch == '(') {
190                 parens++;
191                 ch = GetChar();
192                 ch = skipspaces(ch, 0);
193         }
194         if ((class(ch) != STIDF) && (class(ch) != STELL)) {
195                 error("identifier missing");
196                 if (parens && ch != ')') error(") missing");
197                 if (!parens || ch != ')') ChPushBack(ch);
198                 add2repl(repl, '0');
199                 return;
200         }
201         ChPushBack(ch);
202         id = GetIdentifier(0);
203         ASSERT(id || class(ch) == STELL);
204         ch = GetChar();
205         ch = skipspaces(ch, 0);
206         if (parens && ch != ')') error(") missing");
207         if (!parens || ch != ')') ChPushBack(ch);
208         add2repl(repl, (id && id->id_macro) ? '1' : '0');
209         add2repl(repl, ' ');
210 }
211
212 newarg(args)
213         struct args *args;
214 {
215         args->a_expptr = args->a_expbuf = Malloc(args->a_expsize = ARGBUF);
216         args->a_rawptr = args->a_rawbuf = Malloc(args->a_rawsize = ARGBUF);
217 }
218
219 getactuals(repl, idf)
220         struct repl *repl;
221         register struct idf *idf;
222 {
223         /*      Get the actual parameters from the input stream.
224                 The hard part is done by actual(), only comma's and
225                 other syntactic trivialities are checked here.
226         */
227         register struct args *args = repl->r_args;
228         register int nps = idf->id_macro->mc_nps;
229         register int argcnt;
230         register int ch;
231
232         argcnt = 0;
233         newarg(args);
234         if ((ch = GetChar()) != ')') {
235                 UnGetChar();
236                 while ((ch = actual(repl)) != ')' ) {
237                         if (ch != ',') {
238                                 lexerror("illegal macro call");
239                                 return;
240                         }
241                         stash(repl, '\0', 1);
242                         args->a_expvec[argcnt] = args->a_expbuf;
243                         args->a_rawvec[argcnt] = args->a_rawbuf;
244                         ++argcnt;
245                         if (argcnt == STDC_NPARAMS)
246                                 lexstrict("number of parameters exceeds ANSI standard");
247                         if (argcnt >= NPARAMS)
248                                 fatal("argument vector overflow");
249                         newarg(args);
250                 }
251                 stash(repl, '\0', 1);
252                 args->a_expvec[argcnt] = args->a_expbuf;
253                 args->a_rawvec[argcnt] = args->a_rawbuf;
254                 ++argcnt;
255         }
256         if (argcnt < nps)
257                 lexerror("too few macro arguments");
258         else if (argcnt > nps)
259                 lexerror("too many macro arguments");
260 }
261
262 saveraw(repl)
263 struct repl *repl;
264 {
265         register struct repl *nrepl = ReplaceList;
266         register struct args *ap = nrepl->r_args;
267         register char *p;
268
269         /* stash identifier name */
270         for (p = nrepl->r_idf->id_text; *p != '\0'; p++)
271                 stash(repl, *p, -1);
272
273         /*      The following code deals with expanded function
274                 like macro calls. It makes the following code
275                 work:
276
277                         #define def(a,b)        x(a,b)
278                         #define glue(a,b)       a ## b
279
280                         glue(abc,def(a,b))
281
282                 Results in:
283
284                         abcdef(a,b);
285         */
286         if (ap->a_rawvec[0]) {
287                 /* stash arguments */
288                 register int i;
289
290                 for (i = 0; ap->a_rawvec[i] != (char *)0; i++) {
291                         if (i == 0) stash(repl, '(', -1);
292                         else stash(repl, ',', -1);
293                         for (p = ap->a_rawvec[i]; *p != '\0'; p++)
294                                 stash(repl, *p, -1);
295                 }
296                 stash(repl, ')', -1);
297         }
298 }
299
300 int
301 actual(repl)
302         struct repl *repl;
303 {
304         /*      This routine deals with the scanning of an actual parameter.
305                 It keeps in account the opening and closing brackets,
306                 preprocessor numbers, strings and character constants.
307         */
308         register int ch = 0;
309         register int level = 0, nostashraw = 0;
310         int lastch;
311         static int Unstacked_missed;
312
313         while (1) {
314                 lastch = ch;
315                 ch = GetChar();
316
317                 if (nostashraw 
318                     && nostashraw >= Unstacked_missed) {
319                         nostashraw -= Unstacked_missed;
320                         Unstacked_missed = 0;
321                 }
322                 if (Unstacked) {
323                         nostashraw -= Unstacked;
324                         if (nostashraw < 0) {
325                                 Unstacked_missed = -nostashraw;
326                                 nostashraw = 0;
327                         }
328                         EnableMacros();
329                 }
330                 if (class(ch) == STIDF || class(ch) == STELL) {
331                         /*      Scan a preprocessor identifier token. If the
332                                 token is a macro, it is expanded first.
333                         */
334                         char buf[(IDFSIZE > NUMSIZE ? IDFSIZE : NUMSIZE) + 1];
335                         register char *p = buf;
336                         register struct idf *idef;
337                         register int pos = -1;
338                         extern int idfsize;
339                         int NoExpandMacro;
340
341                         if (ch == NOEXPM) {
342                                 NoExpandMacro= 1;
343                                 ch = GetChar();
344                         } else NoExpandMacro = 0;
345
346                         do {
347                                 if (++pos < idfsize) {
348                                         *p++ = ch;
349                                 }
350                                 ch = GetChar();
351                         } while (in_idf(ch));
352                         *p++ = '\0';
353                         ch = '\0';      /* It could be an unstashed TOKSEP */
354                         UnGetChar();
355
356                         /*      When the identifier has an associated macro
357                                 replacement list, it's expanded.
358                         */
359                         idef = findidf(buf);
360                         if (!idef || NoExpandMacro || !replace(idef)) {
361                                 if (NoExpandMacro
362                                     || (idef && idef->id_macro
363                                         && (idef->id_macro->mc_flag & NOREPLACE)))
364                                         stash(repl, NOEXPM, !nostashraw);
365                                 for (p = buf; *p != '\0'; p++)
366                                         stash(repl, *p, !nostashraw);
367                         } else {
368                                 if (!nostashraw) saveraw(repl);
369                                 nostashraw++;
370                         }
371                 } else if (class(ch) == STNUM) {
372                         /*      a preprocessing number has the following
373                                 regular expression:
374                                     [0-9|"."[0-9]]{[0-9"."a-zA-Z_]|{[Ee][+-]}}*
375                         */
376                         stash(repl, ch, !nostashraw);
377                         if (ch == '.') {
378                                 ch = GetChar();
379                                 if (class(ch) != STNUM) {
380                                         ch = '\0';      /* It could be an unstashed TOKSEP */
381                                         UnGetChar();
382                                         continue;
383                                 }
384                                 else stash(repl, ch, !nostashraw);
385                         }
386                         ch = GetChar();
387                         while (in_idf(ch) || ch == '.') {
388                                 stash(repl, ch, !nostashraw);
389                                 if ((ch = GetChar()) == 'e' || ch == 'E') {
390                                         stash(repl, ch, !nostashraw);
391                                         ch = GetChar();
392                                         if (ch == '+' || ch == '-') {
393                                                 stash(repl, ch, !nostashraw);
394                                                 ch = GetChar();
395                                         }
396                                 }
397                         }
398                         ch = '\0';      /* It could be an unstashed TOKSEP */
399                         UnGetChar();
400                 } else if (ch == '(') {
401                         /* a comma may occur between parentheses */
402                         level++;
403                         stash(repl, ch, !nostashraw);
404                 } else if (ch == ')') {
405                         level--;
406                         /* closing parenthesis of macro call */
407                         if (level < 0) return ')';
408                         stash(repl, ch, !nostashraw);
409                 } else if (ch == ',') {
410                         if (level <= 0) { /* comma separator for next argument */
411                                 if (level)
412                                         lexerror("unbalanced parenthesis");
413                                 if (!nostashraw)
414                                         return ',';     /* ??? */
415                         }
416                         stash(repl, ch, !nostashraw);
417                 } else if (ch == '\n') {
418                         /* newlines are accepted as white spaces */
419                         LineNumber++;
420                         /*      This piece of code needs some explanation:
421                                 consider the call of a macro defined as:
422                                         #define sum(a,b) (a+b)
423                                 in the following form:
424                                         sum(
425                                         /_* comment *_/ #include phone_number
426                                         ,2);
427                                 in which case the include must be handled
428                                 interpreted as such.
429                         */
430
431 a_new_line:             ch = GetChar();
432                         while (class(ch) == STSKIP || ch == '/') {
433                             if (ch == '/') {
434                                     if ((ch = GetChar()) == '*' && !InputLevel) {
435                                         skipcomment();
436                                         stash(repl, ' ', !nostashraw);
437                                         ch = GetChar();
438                                         continue;
439                                     } else {
440                                         UnGetChar();
441                                         ch = '/';
442                                     }
443                                     stash(repl, '/', !nostashraw);
444                                     break;
445                             } else ch = GetChar();
446                         }
447
448                         if (ch == '#') {
449                                 domacro();
450                                 /* Clear File_Inserted since domacro could
451                                  * be called again, which calls GetToken().
452                                  */
453                                 File_Inserted = 0;
454                                 goto a_new_line;
455                         } else if (ch == EOI) {
456                                 lexerror("unterminated macro call");
457                                 return ')';
458                         }
459                         if (ch != '/') {
460                                 UnGetChar();
461                                 ch = ' ';
462                                 stash(repl, ' ', !nostashraw);
463                         }
464                 } else if (ch == '/') {
465                         /* comments are treated as one white space token */
466                         if ((ch = GetChar()) == '*' && !InputLevel) {
467                                 skipcomment();
468                                 stash(repl, ' ', !nostashraw);
469                         } else {
470                                 UnGetChar();
471                                 ch = '/';
472                                 stash(repl, '/', !nostashraw);
473                         }
474                 } else if (ch == '\'' || ch == '"') {
475                         /*      Strings are considered as ONE token, thus no
476                                 replacement within strings.
477                         */
478                         register int match = ch;
479
480                         stash(repl, ch, !nostashraw);
481                         while ((ch = GetChar()) != EOI) {
482                                 if (ch == match)
483                                         break;
484                                 if (ch == '\\') {
485                                         stash(repl, ch, !nostashraw);
486                                         ch = GetChar();
487                                 } else if (ch == '\n') {
488                                         lexerror("newline in string");
489                                         LineNumber++;
490                                         stash(repl, match, !nostashraw);
491                                         break;
492                                 }
493                                 stash(repl, ch, !nostashraw);
494                         }
495                         if (ch != match) {
496                                 lexerror("unterminated macro call");
497                                 return ')';
498                         }
499                         stash(repl, ch, !nostashraw);
500                 } else {
501                         if (lastch == TOKSEP && ch == TOKSEP) continue;
502                         stash(repl, ch, !nostashraw);
503                 }
504         }
505 }
506
507 macro_func(idef)
508         register struct idf *idef;
509 {
510         /*      macro_func() performs the special actions needed with some
511                 macros. These macros are __FILE__ and __LINE__ which
512                 replacement texts must be evaluated at the time they are
513                 used.
514         */
515         register struct macro *mac = idef->id_macro;
516         static char FilNamBuf[PATHLENGTH];
517         char *long2str();
518
519         switch (idef->id_text[2]) {
520         case 'F':                       /* __FILE__     */
521                 FilNamBuf[0] = '"';
522                 strcpy(&FilNamBuf[1], FileName);
523                 strcat(FilNamBuf, "\"");
524                 mac->mc_text = FilNamBuf;
525                 mac->mc_length = strlen(FilNamBuf);
526                 break;
527         case 'L':                       /* __LINE__     */
528                 mac->mc_text = long2str((long)LineNumber, 10);
529                 mac->mc_length = strlen(mac->mc_text);
530                 break;
531         default:
532                 crash("(macro_func)");
533                 /*NOTREACHED*/
534         }
535 }
536
537 macro2buffer(repl, idf, args)
538         register struct repl *repl;
539         register struct idf *idf;
540         register struct args *args;
541 {
542         /*      macro2buffer expands the replacement list and places the
543                 result onto the replacement buffer. It deals with the #
544                 and ## operators, and inserts the actual parameters.
545                 The argument buffer contains the raw argument (needed
546                 for the ## operator), and the expanded argument (for
547                 all other parameter substitutions).
548
549                 The grammar of the replacement list is:
550
551                         repl_list:      TOKEN repl_list
552                                 |       PARAMETER repl_list
553                                 |       '#' PARAMETER
554                                 |       TOKEN '##' TOKEN
555                                 |       PARAMETER '##' TOKEN
556                                 |       TOKEN '##' PARAMETER
557                                 |       PARAMETER '##' PARAMETER
558                                 ;
559
560                 As the grammar indicates, we could make a DFA and
561                 use this finite state machine for the replacement
562                 list parsing (inserting the arguments, etc.).
563
564                 Currently we go through the replacement list in a
565                 linear fashion. This is VERY expensive, something
566                 smarter should be done (but even a DFA is O(|s|)).
567         */
568         register char *ptr = idf->id_macro->mc_text;
569         int err = 0;
570         int func = idf->id_macro->mc_nps != -1;
571         char *stringify();
572
573         ASSERT(ptr[idf->id_macro->mc_length] == '\0');
574         while (*ptr) {
575             if (*ptr == '\'' || *ptr == '"') {
576                 register int delim = *ptr;
577
578                 do {
579                     add2repl(repl, *ptr);
580                     if (*ptr == '\\')
581                         add2repl(repl, *++ptr);
582                     if (*ptr == '\0') {
583                         lexerror("unterminated string");
584                         return;
585                     }
586                     ptr++;
587                 } while (*ptr != delim || *ptr == '\0');
588                 add2repl(repl, *ptr++);
589             } else if (*ptr == '#' && (func || *(ptr+1) == '#')) {
590                 if (*++ptr == '#') {
591                     register int tmpindex;
592                         /* ## - paste operator */
593                     ptr++;
594
595                         /* trim the actual replacement list */
596                     --repl->r_ptr;
597                     while (repl->r_ptr >= repl->r_text
598                             && is_wsp(*repl->r_ptr))
599                         --repl->r_ptr;
600
601                     /*  ## occurred at the beginning of the replacement list.
602                      */
603                     if (repl->r_ptr < repl->r_text) {
604                         err = 1;
605                         break;
606                     }
607
608                     if (repl->r_ptr >= repl->r_text
609                             && *repl->r_ptr == TOKSEP)
610                         --repl->r_ptr;
611
612                     ++repl->r_ptr;
613                     tmpindex = repl->r_ptr - repl->r_text;
614                     /* tmpindex can be 0 */
615
616                     /* skip space in macro replacement list */
617                     while ((*ptr & FORMALP) == 0 && is_wsp(*ptr))
618                             ptr++;
619
620                     /*  ## occurred at the end of the replacement list.
621                      */
622                     if (*ptr & FORMALP) {
623                         register int n = *ptr++ & 0177;
624                         register char *p;
625
626                         ASSERT(n > 0);
627                         p = args->a_rawvec[n-1];
628                         if (p) {        /* else macro argument missing */
629                             while (is_wsp(*p)) p++;
630                             if (*p == NOEXPM) p++;
631                             while (*p)
632                                 add2repl(repl, *p++);
633                         }
634                         while (tmpindex > 0
635                                 && in_idf(repl->r_text[tmpindex]))
636                             tmpindex--;
637                         if (tmpindex >= 0
638                             && repl->r_text[tmpindex] == NOEXPM)
639                                 repl->r_text[tmpindex] = TOKSEP;
640                     } else if (*ptr == '\0') {
641                             err = 1;
642                             break;
643                     } else {
644                             if (in_idf(*ptr)) {
645                                 tmpindex--;
646                                 while (tmpindex > 0
647                                         && in_idf(repl->r_text[tmpindex]))
648                                     tmpindex--;
649                                 if (tmpindex >= 0
650                                     && repl->r_text[tmpindex] == NOEXPM)
651                                         repl->r_text[tmpindex] = TOKSEP;
652                             }
653                     }
654                 } else {                        /* # operator */
655                         ptr = stringify(repl, ptr, args);
656                 }
657             } else if (*ptr & FORMALP) {
658                 /* insert actual parameter */
659                 register int n = *ptr++ & 0177;
660                 register char *p, *q;
661
662                 ASSERT(n > 0);
663
664                 /*      This is VERY dirty, we look ahead for the
665                         ## operator. If it's found we use the raw
666                         argument buffer instead of the expanded
667                         one.
668                 */
669                 for (p = ptr; (*p & FORMALP) == 0 && is_wsp(*p); p++)
670                         /* EMPTY */;
671                 if (*p == '#' && p[1] == '#')
672                         q = args->a_rawvec[n-1];
673                 else
674                         q = args->a_expvec[n-1];
675
676                 if (q)                  /* else macro argument missing */
677                     while (*q)
678                         add2repl(repl, *q++);
679
680                 if (repl->r_text == repl->r_ptr || *(repl->r_ptr - 1) != TOKSEP)
681                         add2repl(repl, TOKSEP);
682             } else {
683                 add2repl(repl, *ptr++);
684             }
685         }
686         if (err)
687                 lexerror("illegal use of the ## operator");
688 }
689
690 char *
691 stringify(repl, ptr, args)
692         register struct repl *repl;
693         register char *ptr;
694         register struct args *args;
695 {
696         /*      If a parameter is immediately preceded by a # token
697                 both are replaced by a single string literal that
698                 contains the spelling of the token sequence for the
699                 corresponding argument.
700                 Each occurrence of white space between the argument's
701                 tokens become a single space character in the string
702                 literal. White spaces before the first token and after
703                 the last token comprising the argument are deleted.
704                 To retain the original spelling we insert backslashes
705                 as appropriate. We only escape backslashes if they
706                 occure within string tokens.
707         */
708         register int space = 1;         /* skip leading spaces */
709         register int delim = 0;         /* string or character constant delim */
710         register int backslash = 0;     /* last character was a \ */
711
712         /* skip spaces macro replacement list */
713         while ((*ptr & FORMALP) == 0 && is_wsp(*ptr))
714                 ptr++;
715
716         if (*ptr & FORMALP) {
717                 register int n = *ptr++ & 0177;
718                 register char *p;
719
720                 ASSERT(n != 0);
721                 p = args->a_rawvec[n-1];
722                 add2repl(repl, '"');
723                 while (*p) {
724                         if (is_wsp(*p)) {
725                                 if (!space) {
726                                         space = 1;
727                                         add2repl(repl, ' ');
728                                 }
729                                 p++;
730                                 continue;
731                         }
732                         space = 0;
733
734                         if (!delim && (*p == '"' || *p == '\''))
735                                 delim = *p;
736                         else if (*p == delim && !backslash)
737                                 delim = 0;
738                         backslash = *p == '\\';
739                         if (*p == '"' || (delim && *p == '\\'))
740                                 add2repl(repl, '\\');
741                         if (*p == TOKSEP || *p == NOEXPM) p++;
742                         else add2repl(repl, *p++);
743                 }
744
745                 /* trim spaces in the replacement list */
746                 for (--repl->r_ptr; is_wsp(*repl->r_ptr); repl->r_ptr--)
747                         /* EMPTY */;
748                 ++repl->r_ptr;          /* oops, one to far */
749                 add2repl(repl, '"');
750         } else
751                 error("illegal use of # operator");
752         return ptr;
753 }
754
755 /* The following routine is also called from domacro.c.
756  */
757 add2repl(repl, ch)
758         register struct repl *repl;
759         int ch;
760 {
761         register int index = repl->r_ptr - repl->r_text;
762
763         ASSERT(index < repl->r_size);
764         if (index + 2 >= repl->r_size) {
765                 repl->r_text = Realloc(repl->r_text, (unsigned) (repl->r_size <<= 1));
766                 repl->r_ptr = repl->r_text + index;
767         }
768         *repl->r_ptr++ = ch;
769         *repl->r_ptr = '\0';
770 }
771
772 /* If the variable stashraw is negative, we must only stash into the raw
773  * buffer. If the variable is zero, we must only stash into the expanded
774  * buffer. Otherwise, we must use both buffers.
775  */
776 stash(repl, ch, stashraw)
777         struct repl *repl;
778         register int ch;
779         int stashraw;
780 {
781         /*      Stash characters into the macro expansion buffer.
782         */
783         register struct args *args = repl->r_args;
784         register int index = args->a_expptr - args->a_expbuf;
785
786         if (stashraw >= 0) {
787                 ASSERT(index < args->a_expsize);
788                 if (index + 1 >= args->a_expsize) {
789                         args->a_expbuf = Realloc(args->a_expbuf,
790                                                     (unsigned) (args->a_expsize <<= 1));
791                         args->a_expptr = args->a_expbuf + index;
792                 }
793                 *args->a_expptr++ = ch;
794         }
795
796         if (stashraw) {
797                 index = args->a_rawptr - args->a_rawbuf;
798                 ASSERT(index < args->a_rawsize);
799                 if (index + 1 >= args->a_rawsize) {
800                         args->a_rawbuf = Realloc(args->a_rawbuf,
801                                                     (unsigned)(args->a_rawsize <<= 1));
802                         args->a_rawptr = args->a_rawbuf + index;
803                 }
804                 *args->a_rawptr++ = ch;
805         }
806 }
807 #endif /* NOPP */