Pristine Ack-5.5
[Ack-5.5.git] / lang / cem / cemcom.ansi / domacro.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: domacro.c,v 1.36 1998/02/09 09:48:59 ceriel Exp $ */
6 /* PREPROCESSOR: CONTROLLINE INTERPRETER */
7
8 #include        "debug.h"
9 #include        "arith.h"
10 #include        "LLlex.h"
11 #include        "Lpars.h"
12 #include        "idf.h"
13 #include        "input.h"
14 #include        "nopp.h"
15 #include        "lint.h"
16
17 #ifndef NOPP
18 #include        "ifdepth.h"
19 #include        "botch_free.h"
20 #include        "nparams.h"
21 #include        "parbufsize.h"
22 #include        "textsize.h"
23 #include        "idfsize.h"     
24 #include        "assert.h"
25 #include        <alloc.h>
26 #include        "class.h"
27 #include        "macro.h"
28 #include        "macbuf.h"
29 #include        "replace.h"
30 #include        "dbsymtab.h"
31 #ifdef DBSYMTAB
32 #include        <stb.h>
33 #include        <em.h>
34 int             IncludeLevel = 0;
35 #endif
36
37 extern char options[];
38 extern  char **inctable;        /* list of include directories          */
39 extern  char *getwdir();
40 char ifstack[IFDEPTH];  /* if-stack: the content of an entry is */
41                                 /* 1 if a corresponding ELSE has been   */
42                                 /* encountered.                         */
43
44 int     nestlevel = -1;
45
46 struct idf *
47 GetIdentifier(skiponerr)
48         int skiponerr;          /* skip the rest of the line on error */
49 {
50         /*      returns a pointer to the descriptor of the identifier that is
51                 read from the input stream. When the input doe not contain
52                 an identifier, the rest of the line is skipped when
53                 skiponerr is on, and a null-pointer is returned.
54                 The substitution of macros is disabled.
55         */
56         int tmp = UnknownIdIsZero;
57         int tok;
58         struct token tk;
59
60         UnknownIdIsZero = ReplaceMacros = 0;
61         tok = GetToken(&tk);
62         ReplaceMacros = 1;
63         UnknownIdIsZero = tmp;
64         if (tok != IDENTIFIER) {
65                 if (skiponerr && tok != EOI) SkipToNewLine();
66                 return (struct idf *)0;
67         }
68         return tk.tk_idf;
69 }
70
71 /*      domacro() is the control line interpreter. The '#' has already
72         been read by the lexical analyzer by which domacro() is called.
73         The token appearing directly after the '#' is obtained by calling
74         the basic lexical analyzing function GetToken() and is interpreted
75         to perform the action belonging to that token.
76         An error message is produced when the token is not recognized,
77         i.e. it is not one of "define" .. "undef" , integer or newline.
78 */
79 domacro()
80 {
81         struct token tk;        /* the token itself                     */
82         int toknum;
83
84         EoiForNewline = 1;
85         ReplaceMacros = 0;
86         toknum = GetToken(&tk);
87         ReplaceMacros = 1;
88         switch(toknum) {                /* select control line action   */
89         case IDENTIFIER:                /* is it a macro keyword?       */
90                 switch (tk.tk_idf->id_resmac) {
91                 case K_DEFINE:                          /* "define"     */
92                         do_define();
93                         break;
94                 case K_ELIF:                            /* "elif"       */
95                         do_elif();
96                         break;
97                 case K_ELSE:                            /* "else"       */
98                         do_else();
99                         break;
100                 case K_ENDIF:                           /* "endif"      */
101                         do_endif();
102                         break;
103                 case K_IF:                              /* "if"         */
104                         do_if();
105                         break;
106                 case K_IFDEF:                           /* "ifdef"      */
107                         do_ifdef(1);
108                         break;
109                 case K_IFNDEF:                          /* "ifndef"     */
110                         do_ifdef(0);
111                         break;
112                 case K_INCLUDE:                         /* "include"    */
113                         do_include();
114                         break;
115                 case K_LINE:                            /* "line"       */
116                         /*      set LineNumber and FileName according to
117                                 the arguments.
118                         */
119                         if (GetToken(&tk) != INTEGER) {
120                                 lexerror("bad #line syntax");
121                                 SkipToNewLine();
122                         }
123                         else
124                                 do_line((unsigned int)tk.tk_ival);
125                         break;
126                 case K_ERROR:                           /* "error"      */
127                         do_error();
128                         break;
129                 case K_PRAGMA:                          /* "pragma"     */
130                         do_pragma();
131                         break;
132                 case K_UNDEF:                           /* "undef"      */
133                         do_undef((struct idf *) 0);
134                         break;
135                 default:
136                         /* invalid word seen after the '#'      */
137                         lexerror("%s: unknown control", tk.tk_idf->id_text);
138                         SkipToNewLine();
139                 }
140                 break;
141         case INTEGER:           /* # <integer> [<filespecifier>]?       */
142                 do_line((unsigned int)tk.tk_ival);
143                 break;
144         case EOI:       /* only `#' on this line: do nothing, ignore    */
145                 break;
146         default:        /* invalid token following '#'          */
147                 lexerror("illegal # line");
148                 SkipToNewLine();
149         }
150         EoiForNewline = 0;
151 }
152
153 #ifdef LINT
154 int lint_skip_comment;
155 #endif
156
157 skip_block(to_endif)
158 int to_endif;
159 {
160         /*      skip_block() skips the input from
161                 1)      a false #if, #ifdef, #ifndef or #elif until the
162                         corresponding #elif (resulting in true), #else or
163                         #endif is read.
164                 2)      a #else corresponding to a true #if, #ifdef,
165                         #ifndef or #elif until the corresponding #endif is
166                         seen.
167         */
168         register int ch;
169         register int skiplevel = nestlevel; /* current nesting level    */
170         struct token tk;
171         int toknum;
172
173 #ifdef LINT
174         lint_skip_comment++;
175 #endif
176         NoUnstack++;
177         for (;;) {
178                 ch = GetChar(); /* read first character after newline   */
179                 while (class(ch) == STSKIP)
180                         ch = GetChar();
181                 if (ch != '#') {
182                         if (ch == EOI) {
183                                 NoUnstack--;
184 #ifdef LINT
185                                 lint_skip_comment--;
186 #endif
187                                 return;
188                         }
189                         /* A possible '/' is not pushed back */
190                         if (ch == '/') {
191                                 ch = GetChar();
192                                 if (ch != '*') UnGetChar();
193                                 else {
194                                         skipcomment();
195                                         continue;
196                                 }
197                         } else UnGetChar();
198                         SkipToNewLine();
199                         continue;
200                 }
201                 ReplaceMacros = 0;
202                 toknum = GetToken(&tk);
203                 ReplaceMacros = 1;
204                 if (toknum != IDENTIFIER) {
205                         if (toknum != INTEGER) {
206                                 lexerror("illegal # line");
207                         }
208                         SkipToNewLine();
209                         continue;
210                 }
211                 /*      an IDENTIFIER: look for #if, #ifdef and #ifndef
212                         without interpreting them.
213                         Interpret #else, #elif and #endif if they occur
214                         on the same level.
215                 */
216                 switch(tk.tk_idf->id_resmac) {
217                 default:
218                 case K_UNKNOWN:
219                         /* invalid word seen after the '#'      */
220                         lexwarning("%s: unknown control", tk.tk_idf->id_text);
221                         /* fallthrough */
222                 case K_DEFINE:
223                 case K_ERROR:
224                 case K_INCLUDE:
225                 case K_LINE:
226                 case K_PRAGMA:
227                 case K_UNDEF:
228                 case K_FILE:
229                         SkipToNewLine();
230                         break;
231                 case K_IF:
232                 case K_IFDEF:
233                 case K_IFNDEF:
234                         push_if();
235                         SkipToNewLine();
236                         break;
237                 case K_ELIF:
238                         if (ifstack[nestlevel])
239                                 lexerror("#elif after #else");
240                         if (!to_endif && nestlevel == skiplevel) {
241                                 nestlevel--;
242                                 push_if();
243                                 if (ifexpr()) {
244                                         NoUnstack--;
245 #ifdef LINT
246                                         lint_skip_comment--;
247 #endif
248                                         return;
249                                 }
250                         }
251                         else SkipToNewLine();   /* otherwise done in ifexpr() */
252                         break;
253                 case K_ELSE:
254                         if (ifstack[nestlevel])
255                                 lexerror("#else after #else");
256                         ++(ifstack[nestlevel]);
257                         if (!to_endif && nestlevel == skiplevel) {
258                                 if (SkipToNewLine()) {
259                                         if (!options['o'])
260                                                 lexstrict("garbage following #else");
261                                 }
262                                 NoUnstack--;
263 #ifdef LINT
264                                 lint_skip_comment--;
265 #endif
266                                 return;
267                         }
268                         else SkipToNewLine();
269                         break;
270                 case K_ENDIF:
271                         ASSERT(nestlevel > nestlow);
272                         if (nestlevel == skiplevel) {
273                                 if (SkipToNewLine()) {
274                                         if (!options['o'])
275                                                 lexstrict("garbage following #endif");
276                                 }
277                                 nestlevel--;
278                                 NoUnstack--;
279 #ifdef LINT
280                                 lint_skip_comment--;
281 #endif
282                                 return;
283                         }
284                         else SkipToNewLine();
285                         nestlevel--;
286                         break;
287                 }
288         }
289 }
290
291
292 ifexpr()
293 {
294         /*      ifexpr() returns whether the restricted constant
295                 expression following #if or #elif evaluates to true.  This
296                 is done by calling the LLgen generated subparser for
297                 constant expressions.  The result of this expression will
298                 be given in the extern long variable "ifval".
299         */
300         extern arith ifval;
301         int errors = err_occurred;
302
303         ifval = (arith)0;
304         AccDefined = 1;
305         UnknownIdIsZero = 1;
306         PushLex();      /* NEW parser */
307         If_expr();      /* invoke constant expression parser    */
308         PopLex();       /* OLD parser */
309         AccDefined = 0;
310         UnknownIdIsZero = 0;
311         return (errors == err_occurred) && (ifval != (arith)0);
312 }
313
314 do_include()
315 {
316         /*      do_include() performs the inclusion of a file.
317         */
318         char *filenm;
319         char *result;
320         int tok;
321         struct token tk;
322
323         AccFileSpecifier = 1;
324         if (((tok = GetToken(&tk)) == FILESPECIFIER) || tok == STRING)
325                 filenm = tk.tk_bts;
326         else {
327                 lexerror("bad include syntax");
328                 filenm = (char *)0;
329         }
330         AccFileSpecifier = 0;
331         if (SkipToNewLine()) {
332                 lexerror("bad include syntax");
333         }
334         inctable[0] = WorkingDir;
335         if (filenm) {
336                 if (!InsertFile(filenm, &inctable[tok==FILESPECIFIER],&result)){
337                         lexerror("cannot open include file \"%s\"", filenm);
338                         add_dependency(filenm);
339                         free(filenm);
340                 }
341                 else {
342                         add_dependency(result);
343                         WorkingDir = getwdir(result);
344                         File_Inserted = 1;
345                         FileName = result;
346                         LineNumber = 0;
347                         nestlow = nestlevel;
348 #ifdef DBSYMTAB
349                         IncludeLevel++;
350                         if (options['g']) {
351                                 C_ms_stb_cst(FileName, N_BINCL, 0, (arith) 0);
352                         }
353 #endif /* DBSYMTAB */
354                         if (result != filenm) free(filenm);
355                 }
356         }
357 }
358
359 do_define()
360 {
361         /*      do_define() interprets a #define control line.
362         */
363         struct idf *id;         /* the #defined identifier's descriptor */
364         int nformals = -1;      /* keep track of the number of formals  */
365         char *formals[NPARAMS]; /* pointers to the names of the formals */
366         char parbuf[PARBUFSIZE];                /* names of formals     */
367         char *repl_text;        /* start of the replacement text        */
368         int length;             /* length of the replacement text       */
369         register ch;
370         char *get_text();
371
372         /* read the #defined macro's name       */
373         if (!(id = GetIdentifier(1))) {
374                 lexerror("illegal #define line");
375                 return;
376         }
377         /*      there is a formal parameter list if the identifier is
378                 followed immediately by a '('.
379         */
380         ch = GetChar();
381         if (ch == '(') {
382                 if ((nformals = getparams(formals, parbuf)) == -1) {
383                         SkipToNewLine();
384                         return; /* an error occurred    */
385                 }
386                 ch = GetChar();
387         }
388         /* read the replacement text if there is any                    */
389         ch = skipspaces(ch,0);  /* find first character of the text     */
390         ASSERT(ch != EOI);
391         /* UnGetChar() is not right when replacement starts with a '/' */
392         ChPushBack(ch);
393         repl_text = get_text((nformals > 0) ? formals : 0, &length);
394         macro_def(id, repl_text, nformals, length, NOFLAG);
395         LineNumber++;
396 }
397
398 push_if()
399 {
400         if (nestlevel >= IFDEPTH)
401                 fatal("too many nested #if/#ifdef/#ifndef");
402         else
403                 ifstack[++nestlevel] = 0;
404 }
405
406 do_elif()
407 {
408         if (nestlevel <= nestlow) {
409                 lexerror("#elif without corresponding #if");
410                 SkipToNewLine();
411         }
412         else {          /* restart at this level as if a #if is detected.  */
413                 if (ifstack[nestlevel]) {
414                         lexerror("#elif after #else");
415                         SkipToNewLine();
416                 }
417                 nestlevel--;
418                 push_if();
419                 skip_block(1);
420         }
421 }
422
423 do_else()
424 {
425         if (SkipToNewLine())
426                 if (!options['o'])
427                         lexstrict("garbage following #else");
428         if (nestlevel <= nestlow)
429                 lexerror("#else without corresponding #if");
430         else {  /* mark this level as else-d */
431                 if (ifstack[nestlevel]) {
432                         lexerror("#else after #else");
433                 }
434                 ++(ifstack[nestlevel]);
435                 skip_block(1);
436         }
437 }
438
439 do_endif()
440 {
441         if (SkipToNewLine()) {
442                 if (!options['o'])
443                         lexstrict("garbage following #endif");
444         }
445         if (nestlevel <= nestlow)       {
446                 lexerror("#endif without corresponding #if");
447         }
448         else    nestlevel--;
449 }
450
451 do_if()
452 {
453         push_if();
454         if (!ifexpr())  /* a false #if/#elif expression */
455                 skip_block(0);
456 }
457
458 do_ifdef(how)
459 {
460         register struct idf *id;
461
462         /*      how == 1 : ifdef; how == 0 : ifndef
463         */
464         push_if();
465         if (!(id = GetIdentifier(1)))
466                 lexerror("illegal #ifdef construction");
467         else if (SkipToNewLine())
468                 if (!options['o'])
469                         lexstrict("garbage following #%s <identifier>",
470                                   how ? "ifdef" : "ifndef");
471
472         /* The next test is a shorthand for:
473                 (how && !id->id_macro) || (!how && id->id_macro)
474         */
475         if (how ^ (id && id->id_macro != 0))
476                 skip_block(0);
477 }
478
479 /* argidf != NULL when the undef came from a -U option */
480 do_undef(argidf)
481         struct idf *argidf;
482 {
483         register struct idf *id = argidf;
484
485         /* Forget a macro definition.   */
486         if (id || (id = GetIdentifier(1))) {
487                 if (id->id_macro) { /* forget the macro */
488                         if (id->id_macro->mc_flag & NOUNDEF) {
489                                 lexerror("it is not allowed to undef %s", id->id_text);
490                         } else {
491                                 free(id->id_macro->mc_text);
492                                 free_macro(id->id_macro);
493                                 id->id_macro = (struct macro *) 0;
494                         }
495                 } /* else: don't complain */
496                 if (!argidf) {
497                         if (SkipToNewLine())
498                                 if (!options['o'])
499                                         lexstrict("garbage following #undef");
500                 }
501         }
502         else
503                 lexerror("illegal #undef construction");
504 }
505
506 do_error()
507 {
508         int len;
509         char *get_text();
510         char *bp = get_text((char **) 0, &len);
511
512         lexerror("user error: %s", bp);
513         free(bp);
514         LineNumber++;
515 }
516
517 int
518 getparams(buf, parbuf)
519         char *buf[];
520         char parbuf[];
521 {
522         /*      getparams() reads the formal parameter list of a macro
523                 definition.
524                 The number of parameters is returned.
525                 As a formal parameter list is expected when calling this
526                 routine, -1 is returned if an error is detected, for
527                 example:
528                         #define one(1), where 1 is not an identifier.
529                 Note that the '(' has already been eaten.
530                 The names of the formal parameters are stored into parbuf.
531         */
532         register char **pbuf = &buf[0];
533         register int c;
534         register char *ptr = &parbuf[0];
535         register char **pbuf2;
536
537         c = GetChar();
538         c = skipspaces(c,0);
539         if (c == ')') {         /* no parameters: #define name()        */
540                 *pbuf = (char *) 0;
541                 return 0;
542         }
543         for (;;) {              /* eat the formal parameter list        */
544                 if (class(c) != STIDF && class(c) != STELL) {
545                         lexerror("#define: bad formal parameter");
546                         return -1;
547                 }
548                 *pbuf = ptr;    /* name of the formal   */
549                 *ptr++ = c;
550                 if (ptr >= &parbuf[PARBUFSIZE])
551                         fatal("formal parameter buffer overflow");
552                 do {                    /* eat the identifier name      */
553                         c = GetChar();
554                         *ptr++ = c;
555                         if (ptr >= &parbuf[PARBUFSIZE])
556                                 fatal("formal parameter buffer overflow");
557                 } while (in_idf(c));
558                 *(ptr - 1) = '\0';      /* mark end of the name         */
559
560                 /*      Check if this formal parameter is already used.
561                         Usually, macros do not have many parameters, so ...
562                 */
563                 for (pbuf2 = pbuf - 1; pbuf2 >= &buf[0]; pbuf2--) {
564                         if (!strcmp(*pbuf2, *pbuf)) {
565                                 lexerror("formal parameter \"%s\" already used",
566                                         *pbuf);
567                         }
568                 }
569
570                 pbuf++;
571                 c = skipspaces(c,0);
572                 if (c == ')') { /* end of the formal parameter list     */
573                         *pbuf = (char *) 0;
574                         return pbuf - buf;
575                 }
576                 if (c != ',') {
577                         lexerror("#define: bad formal parameter list");
578                         return -1;
579                 }
580                 c = GetChar();
581                 c = skipspaces(c,0);
582         }
583         /*NOTREACHED*/
584 }
585
586 macro_def(id, text, nformals, length, flags)
587         register struct idf *id;
588         char *text;
589 {
590         register struct macro *newdef = id->id_macro;
591
592         /*      macro_def() puts the contents and information of a macro
593                 definition into a structure and stores it into the symbol
594                 table entry belonging to the name of the macro.
595                 An error is given if there was already a definition
596         */
597         if (newdef) {           /* is there a redefinition?     */
598                 if (newdef->mc_flag & NOUNDEF) {
599                         lexerror("it is not allowed to redefine %s", id->id_text);
600                 } else if (!macroeq(newdef->mc_text, text))
601                         lexerror("illegal redefine of \"%s\"", id->id_text);
602                 free(text);
603                 return;
604         }
605         id->id_macro = newdef = new_macro();
606         newdef->mc_text = text;         /* replacement text     */
607         newdef->mc_nps  = nformals;     /* nr of formals        */
608         newdef->mc_length = length;     /* length of repl. text */
609         newdef->mc_flag = flags;        /* special flags        */
610 }
611
612 int
613 find_name(nm, index)
614         char *nm, *index[];
615 {
616         /*      find_name() returns the index of "nm" in the namelist
617                 "index" if it can be found there. 0 is returned if it is
618                 not there.
619         */
620         register char **ip = &index[0];
621
622         while (*ip)
623                 if (strcmp(nm, *ip++) == 0)
624                         return ip - &index[0];
625         /* arrived here, nm is not in the name list.    */
626         return 0;
627 }
628
629 #define BLANK(ch)       ((ch == ' ') || (ch == '\t'))
630
631 char *
632 get_text(formals, length)
633         char *formals[];
634         int *length;
635 {
636         /*      get_text() copies the replacement text of a macro
637                 definition with zero, one or more parameters, thereby
638                 substituting each formal parameter by a special character
639                 (non-ascii: 0200 & (order-number in the formal parameter
640                 list)) in order to substitute this character later by the
641                 actual parameter. The replacement text is copied into
642                 itself because the copied text will contain fewer or the
643                 same amount of characters. The length of the replacement
644                 text is returned.
645
646                 Implementation:
647                 finite automaton : we are interested in
648                 1-  white space, sequences must be mapped onto 1 single
649                     blank.
650                 2-  identifiers, since they might be replaced by some
651                     actual parameter.
652                 3-  strings and character constants, since replacing
653                     variables within them is illegal, and white-space is
654                     significant.
655                 4-  comment, same as for 1
656                 Other tokens will not be seen as such.
657         */
658         register int c;
659         struct repl repls;
660         register struct repl *repl = &repls;
661         int blank = 0;
662
663         c = GetChar();
664
665         repl->r_ptr = repl->r_text = Malloc(repl->r_size = ITEXTSIZE);
666         *repl->r_ptr = '\0';
667         while ((c != EOI) && (class(c) != STNL)) {
668                 if (BLANK(c)) {
669                         blank++;
670                         c = GetChar();
671                         continue;
672                 }
673
674                 if (c == '\'' || c == '"') {
675                         register int delim = c;
676
677                         if (blank) {
678                                 blank = 0;
679                                 add2repl(repl, ' ');
680                         }
681                         do {
682                             add2repl(repl, c);
683                             if (c == '\\') add2repl(repl, GetChar());
684                             c = GetChar();
685                         } while (c != delim && c != EOI && class(c) != STNL);
686                         if (c == EOI || class(c) == STNL) {
687                                 lexstrict("unclosed opening %c", delim);
688                                 break;
689                         }
690                         add2repl(repl, c);
691                         c = GetChar();
692                 } else if (c == '/') {
693                         c = GetChar();
694                         if (c == '*') {
695                                 skipcomment();
696                                 blank++;
697                                 c = GetChar();
698                                 continue;
699                         } 
700                         if (blank) {
701                                 blank = 0;
702                                 add2repl(repl, ' ');
703                         }
704                         add2repl(repl, '/');
705                 } else if (formals
706                             && (class(c) == STIDF || class(c) == STELL)) {
707                         char id_buf[IDFSIZE + 1];
708                         register char *idp = id_buf;
709                         int n;
710
711                         /* read identifier: it may be a formal parameter */
712                         *idp++ = c;
713                         do {
714                                 c = GetChar();
715                                 if (idp <= &id_buf[IDFSIZE])
716                                         *idp++ = c;
717                         } while (in_idf(c));
718                         *--idp = '\0';
719
720                         if (blank) {
721                                 blank = 0;
722                                 add2repl(repl, ' ');
723                         }
724                         /* construct the formal parameter mark or identifier */
725                         if (n = find_name(id_buf, formals))
726                             add2repl(repl, FORMALP | (char) n);
727                         else {
728                             idp = id_buf;
729                             while (*idp) add2repl(repl, *idp++);
730                         }
731                 } else if (class(c) == STNUM) {
732                         if (blank) {
733                                 blank = 0;
734                                 add2repl(repl, ' ');
735                         }
736                         add2repl(repl, c);
737                         if (c == '.') {
738                                 c = GetChar();
739                                 if (class(c) != STNUM) {
740                                         continue;
741                                 }
742                                 add2repl(repl, c);
743                         }
744                         c = GetChar();
745                         while(in_idf(c) || c == '.') {
746                                 add2repl(repl, c);
747                                 if((c = GetChar()) == 'e' || c == 'E') {
748                                         add2repl(repl, c);
749                                         c = GetChar();
750                                         if (c == '+' || c == '-') {
751                                                 add2repl(repl, c);
752                                                 c = GetChar();
753                                         }
754                                 }
755                         }
756                 } else {
757                         if (blank) {
758                                 blank = 0;
759                                 add2repl(repl, ' ');
760                         }
761                         add2repl(repl, c);
762                         c = GetChar();
763                 }
764         }
765         *length = repl->r_ptr - repl->r_text;
766         return Realloc(repl->r_text, (unsigned)(repl->r_ptr - repl->r_text +1));
767 }
768
769 /*      macroeq() decides whether two macro replacement texts are
770         identical.  This version compares the texts, which occur
771         as strings, without taking care of the leading and trailing
772         blanks (spaces and tabs).
773 */
774 macroeq(s, t)
775         register char *s, *t;
776 {
777
778         /* skip leading spaces  */
779         while (BLANK(*s)) s++;
780         while (BLANK(*t)) t++;
781         /* first non-blank encountered in both strings  */
782         /* The actual comparison loop:                  */
783         while (*s && *s == *t)
784                 s++, t++;
785         /* two cases are possible when arrived here:    */
786         if (*s == '\0') {       /* *s == '\0'           */
787                 while (BLANK(*t)) t++;
788                 return *t == '\0';
789         }
790         else    {               /* *s != *t             */
791                 while (BLANK(*s)) s++;
792                 while (BLANK(*t)) t++;
793                 return (*s == '\0') && (*t == '\0');
794         }
795 }
796 #else /* NOPP */
797
798 struct idf *
799 GetIdentifier(skiponerr)
800         int skiponerr;          /* skip the rest of the line on error */
801 {
802         /*      returns a pointer to the descriptor of the identifier that is
803                 read from the input stream. When the input does not contain
804                 an identifier, the rest of the line is skipped when
805                 skiponerr is on, and a null-pointer is returned.
806                 The substitution of macros is disabled.
807         */
808         int tok;
809         struct token tk;
810
811         tok = GetToken(&tk);
812         if (tok != IDENTIFIER) {
813                 if (skiponerr && tok != EOI) SkipToNewLine();
814                 return (struct idf *)0;
815         }
816         return tk.tk_idf;
817 }
818
819 domacro()
820 {
821         int tok;
822         struct token tk;
823
824         EoiForNewline = 1;
825         if ((tok = GetToken(&tk)) == IDENTIFIER) {
826                 if (! strcmp(tk.tk_idf->id_text, "pragma")) {
827                         do_pragma();
828                         EoiForNewline = 0;
829                         return;
830                 }
831         } else if (tok == INTEGER) {
832                 do_line((unsigned int) tk.tk_ival);
833                 EoiForNewline = 0;
834                 return;
835         }
836         lexerror("illegal # line");
837         EoiForNewline = 0;
838         SkipToNewLine();
839 }
840 #endif /* NOPP */
841
842
843 do_line(l)
844         unsigned int l;
845 {
846         struct token tk;
847         int t = GetToken(&tk);
848
849         if (t != EOI) SkipToNewLine();
850         LineNumber = l;         /* the number of the next input line */
851         if (t == STRING) {      /* is there a filespecifier? */
852                 /*
853                  * Do not attempt to free the old string, since it might
854                  * be used in a def structure.
855                  */
856 #ifdef DBSYMTAB
857                 if (options['g'] && strcmp(FileName, tk.tk_bts) != 0) {
858                         C_ms_std(tk.tk_bts, N_SOL, 0);
859                 }
860 #endif /* DBSYMTAB */
861                 FileName = tk.tk_bts;
862         }
863 }