xref: /trunk/main/rsc/source/rscpp/cpp2.c (revision 86e1cf34)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include        <stdio.h>
25 #include        <ctype.h>
26 #include        "cppdef.h"
27 #include        "cpp.h"
28 #if HOST == SYS_VMS
29 /*
30  * Include the rms stuff.  (We can't just include rms.h as it uses the
31  * VaxC-specific library include syntax that Decus CPP doesn't support.
32  * By including things by hand, we can CPP ourself.)
33  */
34 #include        <nam.h>
35 #include        <fab.h>
36 #include        <rab.h>
37 #include        <rmsdef.h>
38 #endif
39 
40 /*
41  * Generate (by hand-inspection) a set of unique values for each control
42  * operator.  Note that this is not guaranteed to work for non-Ascii
43  * machines.  CPP won't compile if there are hash conflicts.
44  */
45 
46 #define L_assert        ('a' + ('s' << 1))
47 #define L_define        ('d' + ('f' << 1))
48 #define L_elif          ('e' + ('i' << 1))
49 #define L_else          ('e' + ('s' << 1))
50 #define L_endif         ('e' + ('d' << 1))
51 #define L_if            ('i' + (EOS << 1))
52 #define L_ifdef         ('i' + ('d' << 1))
53 #define L_ifndef        ('i' + ('n' << 1))
54 #define L_include       ('i' + ('c' << 1))
55 #define L_line          ('l' + ('n' << 1))
56 #define L_nogood        (EOS + (EOS << 1))      /* To catch #i          */
57 #define L_pragma        ('p' + ('a' << 1))
58 #define L_undef         ('u' + ('d' << 1))
59 #define L_error         ('e' + ('r' << 1))      /* BP 5.3.92, #error */
60 #define MAXLINE 80                              /* BP 5.3.92, #error */
61 #if OSL_DEBUG_LEVEL > 1
62 #define L_debug         ('d' + ('b' << 1))      /* #debug               */
63 #define L_nodebug       ('n' + ('d' << 1))      /* #nodebug             */
64 #endif
65 
66 
InitCpp2()67 void InitCpp2()
68 {
69 
70 }
71 
72 
73 int
control(int counter)74 control(int counter)
75 /*
76  * Process #control lines.  Simple commands are processed inline,
77  * while complex commands have their own subroutines.
78  *
79  * The counter is used to force out a newline before #line, and
80  * #pragma commands.  This prevents these commands from ending up at
81  * the end of the previous line if cpp is invoked with the -C option.
82  */
83 {
84         register int            c;
85         register char           *tp;
86         register int            hash;
87         char                    *ep;
88 
89         c = skipws();
90         if (c == '\n' || c == EOF_CHAR)
91             return (counter + 1);
92         if (!isdigit(c))
93             scanid(c);                  /* Get #word to token[]         */
94         else {
95             unget();                    /* Hack -- allow #123 as a      */
96             strcpy(token, "line");      /* synonym for #line 123        */
97         }
98         hash = (token[1] == EOS) ? L_nogood : (token[0] + (token[2] << 1));
99         switch (hash) {
100         case L_assert:  tp = "assert";          break;
101         case L_define:  tp = "define";          break;
102         case L_elif:    tp = "elif";            break;
103         case L_else:    tp = "else";            break;
104         case L_endif:   tp = "endif";           break;
105         case L_if:      tp = "if";              break;
106         case L_ifdef:   tp = "ifdef";           break;
107         case L_ifndef:  tp = "ifndef";          break;
108         case L_include: tp = "include";         break;
109         case L_line:    tp = "line";            break;
110         case L_pragma:  tp = "pragma";          break;
111         case L_undef:   tp = "undef";           break;
112         case L_error:   tp = "error";           break;
113 #if OSL_DEBUG_LEVEL > 1
114         case L_debug:   tp = "debug";           break;
115         case L_nodebug: tp = "nodebug";         break;
116 #endif
117         default:        hash = L_nogood;
118         case L_nogood:  tp = "";                break;
119         }
120         if (!streq(tp, token))
121             hash = L_nogood;
122         /*
123          * hash is set to a unique value corresponding to the
124          * control keyword (or L_nogood if we think it's nonsense).
125          */
126         if (infile->fp == NULL)
127             cwarn("Control line \"%s\" within macro expansion", token);
128         if (!compiling) {                       /* Not compiling now    */
129             switch (hash) {
130             case L_if:                          /* These can't turn     */
131             case L_ifdef:                       /*  compilation on, but */
132             case L_ifndef:                      /*   we must nest #if's */
133                 if (++ifptr >= &ifstack[BLK_NEST])
134                     goto if_nest_err;
135                 *ifptr = 0;                     /* !WAS_COMPILING       */
136             case L_line:                        /* Many                 */
137             /*
138              * Are pragma's always processed?
139              */
140             case L_pragma:                      /*  options             */
141             case L_include:                     /*   are uninteresting  */
142             case L_define:                      /*    if we             */
143             case L_undef:                       /*     aren't           */
144             case L_assert:                      /*      compiling.      */
145             case L_error:                       /* BP 5.3.92, #error */
146 dump_line:      skipnl();                       /* Ignore rest of line  */
147                 return (counter + 1);
148             }
149         }
150         /*
151          * Make sure that #line and #pragma are output on a fresh line.
152          */
153         if (counter > 0 && (hash == L_line || hash == L_pragma)) {
154             PUTCHAR('\n');
155             counter--;
156         }
157         switch (hash) {
158         case L_line:
159             /*
160              * Parse the line to update the line number and "progname"
161              * field and line number for the next input line.
162              * Set wrongline to force it out later.
163              */
164             c = skipws();
165             workp = work;                       /* Save name in work    */
166             while (c != '\n' && c != EOF_CHAR) {
167                 save(c);
168                 c = get();
169             }
170             unget();
171             save(EOS);
172             /*
173              * Split #line argument into <line-number> and <name>
174              * We subtract 1 as we want the number of the next line.
175              */
176             line = atoi(work) - 1;              /* Reset line number    */
177             for (tp = work; isdigit(*tp) || type[(int)*tp] == SPA; tp++)
178                 ;                               /* Skip over digits     */
179             if (*tp != EOS) {                   /* Got a filename, so:  */
180                 if (*tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL) {
181                     tp++;                       /* Skip over left quote */
182                     *ep = EOS;                  /* And ignore right one */
183                 }
184                 if (infile->progname != NULL)   /* Give up the old name */
185                     free(infile->progname);     /* if it's allocated.   */
186                 infile->progname = savestring(tp);
187             }
188             wrongline = TRUE;                   /* Force output later   */
189             break;
190 
191         case L_include:
192             doinclude();
193             break;
194 
195         case L_define:
196             dodefine();
197             break;
198 
199         case L_undef:
200             doundef();
201             break;
202 
203         case L_else:
204             if (ifptr == &ifstack[0])
205                 goto nest_err;
206             else if ((*ifptr & ELSE_SEEN) != 0)
207                 goto else_seen_err;
208             *ifptr |= ELSE_SEEN;
209             if ((*ifptr & WAS_COMPILING) != 0) {
210                 if (compiling || (*ifptr & TRUE_SEEN) != 0)
211                     compiling = FALSE;
212                 else {
213                     compiling = TRUE;
214                 }
215             }
216             break;
217 
218         case L_elif:
219             if (ifptr == &ifstack[0])
220                 goto nest_err;
221             else if ((*ifptr & ELSE_SEEN) != 0) {
222 else_seen_err:  cerror("#%s may not follow #else", token);
223                 goto dump_line;
224             }
225             if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING) {
226                 compiling = FALSE;              /* Done compiling stuff */
227                 goto dump_line;                 /* Skip this clause     */
228             }
229             doif(L_if);
230             break;
231 
232         case L_if:
233         case L_ifdef:
234         case L_ifndef:
235             if (++ifptr >= &ifstack[BLK_NEST])
236 if_nest_err:    cfatal("Too many nested #%s statements", token);
237             *ifptr = WAS_COMPILING;
238             doif(hash);
239             break;
240 
241         case L_endif:
242             if (ifptr == &ifstack[0]) {
243 nest_err:       cerror("#%s must be in an #if", token);
244                 goto dump_line;
245             }
246             if (!compiling && (*ifptr & WAS_COMPILING) != 0)
247                 wrongline = TRUE;
248             compiling = ((*ifptr & WAS_COMPILING) != 0);
249             --ifptr;
250             break;
251 
252         case L_assert:
253             if (eval() == 0)
254                 cerror("Preprocessor assertion failure", NULLST);
255             break;
256 
257         case L_pragma:
258             /*
259              * #pragma is provided to pass "options" to later
260              * passes of the compiler.  cpp doesn't have any yet.
261              */
262             fprintf( pCppOut, "#pragma ");
263             while ((c = get()) != '\n' && c != EOF_CHAR)
264                 cput(c);
265             unget();
266             break;
267 
268 #if OSL_DEBUG_LEVEL > 1
269         case L_debug:
270             if (debug == 0)
271                 dumpdef("debug set on");
272             debug++;
273             break;
274 
275         case L_nodebug:
276             debug--;
277             break;
278 #endif
279         case L_error:                       /* BP 5.3.92, #error */
280         {
281             fprintf( pCppOut, "cpp: line %u, Error directive: ", line );
282             while ((c = get()) != '\n' && c != EOF_CHAR)
283                 cput(c);
284             fprintf( pCppOut, "\n" );
285             exit( 1 );
286             break;
287         }
288         default:
289             /*
290              * Undefined #control keyword.
291              * Note: the correct behavior may be to warn and
292              * pass the line to a subsequent compiler pass.
293              * This would allow #asm or similar extensions.
294              */
295             cerror("Illegal # command \"%s\"", token);
296             break;
297         }
298         if (hash != L_include) {
299 #if OLD_PREPROCESSOR
300             /*
301              * Ignore the rest of the #control line so you can write
302              *          #if     foo
303              *          #endif  foo
304              */
305             goto dump_line;                     /* Take common exit     */
306 #else
307             if (skipws() != '\n') {
308                 cwarn("Unexpected text in #control line ignored", NULLST);
309                 skipnl();
310             }
311 #endif
312         }
313         return (counter + 1);
314 }
315 
316 FILE_LOCAL
doif(int hash)317 void doif(int hash)
318 /*
319  * Process an #if, #ifdef, or #ifndef.  The latter two are straightforward,
320  * while #if needs a subroutine of its own to evaluate the expression.
321  *
322  * doif() is called only if compiling is TRUE.  If false, compilation
323  * is always suppressed, so we don't need to evaluate anything.  This
324  * suppresses unnecessary warnings.
325  */
326 {
327         register int            c;
328         register int            found;
329 
330         if ((c = skipws()) == '\n' || c == EOF_CHAR) {
331             unget();
332             goto badif;
333         }
334         if (hash == L_if) {
335             unget();
336             found = (eval() != 0);      /* Evaluate expr, != 0 is  TRUE */
337             hash = L_ifdef;             /* #if is now like #ifdef       */
338         }
339         else {
340             if (type[c] != LET)         /* Next non-blank isn't letter  */
341                 goto badif;             /* ... is an error              */
342             found = (lookid(c) != NULL); /* Look for it in symbol table */
343         }
344         if (found == (hash == L_ifdef)) {
345             compiling = TRUE;
346             *ifptr |= TRUE_SEEN;
347         }
348         else {
349             compiling = FALSE;
350         }
351         return;
352 
353 badif:  cerror("#if, #ifdef, or #ifndef without an argument", NULLST);
354 #if !OLD_PREPROCESSOR
355         skipnl();                               /* Prevent an extra     */
356         unget();                                /* Error message        */
357 #endif
358         return;
359 }
360 
361 FILE_LOCAL
doinclude()362 void doinclude()
363 /*
364  * Process the #include control line.
365  * There are three variations:
366  *      #include "file"         search somewhere relative to the
367  *                              current source file, if not found,
368  *                              treat as #include <file>.
369  *      #include <file>         Search in an implementation-dependent
370  *                              list of places.
371  *      #include token          Expand the token, it must be one of
372  *                              "file" or <file>, process as such.
373  *
374  * Note: the November 12 draft forbids '>' in the #include <file> format.
375  * This restriction is unnecessary and not implemented.
376  */
377 {
378         register int            c;
379         register int            delim;
380 #if HOST == SYS_VMS
381         char                    def_filename[NAM$C_MAXRSS + 1];
382 #endif
383 
384         delim = macroid(skipws());
385         if (delim != '<' && delim != '"')
386             goto incerr;
387         if (delim == '<')
388             delim = '>';
389         workp = work;
390         instring = TRUE;                /* Accept all characters        */
391 #ifdef CONTROL_COMMENTS_NOT_ALLOWED
392         while ((c = get()) != '\n' && c != EOF_CHAR)
393             save(c);                    /* Put it away.                 */
394         unget();                        /* Force nl after includee      */
395         /*
396          * The draft is unclear if the following should be done.
397          */
398         while (--workp >= work && *workp == ' ')
399             ;                           /* Trim blanks from filename    */
400         if (*workp != delim)
401             goto incerr;
402 #else
403         while ((c = get()) != delim && c != EOF_CHAR)
404             save(c);
405 #endif
406         *workp = EOS;                   /* Terminate filename           */
407         instring = FALSE;
408 #if HOST == SYS_VMS
409         /*
410          * Assume the default .h filetype.
411          */
412         if (!vmsparse(work, ".H", def_filename)) {
413             perror(work);               /* Oops.                        */
414             goto incerr;
415         }
416         else if (openinclude(def_filename, (delim == '"')))
417             return;
418 #else
419         if (openinclude(work, (delim == '"')))
420             return;
421 #endif
422         /*
423          * No sense continuing if #include file isn't there.
424          */
425         cfatal("Cannot open include file \"%s\"", work);
426 
427 incerr: cerror("#include syntax error", NULLST);
428         return;
429 }
430 
431 FILE_LOCAL int
openinclude(char * filename,int searchlocal)432 openinclude(char* filename, int searchlocal)
433 /*
434  * Actually open an include file.  This routine is only called from
435  * doinclude() above, but was written as a separate subroutine for
436  * programmer convenience.  It searches the list of directories
437  * and actually opens the file, linking it into the list of
438  * active files.  Returns TRUE if the file was opened, FALSE
439  * if openinclude() fails.  No error message is printed.
440  */
441 {
442         register char           **incptr;
443 #if HOST == SYS_VMS
444 #if NFWORK < (NAM$C_MAXRSS + 1)
445     << error, NFWORK is not greater than NAM$C_MAXRSS >>
446 #endif
447 #endif
448         char                    tmpname[NFWORK]; /* Filename work area   */
449 
450         if (searchlocal) {
451             /*
452              * Look in local directory first
453              */
454 #if HOST == SYS_UNIX
455             /*
456              * Try to open filename relative to the directory of the current
457              * source file (as opposed to the current directory). (ARF, SCK).
458              */
459             if (filename[0] != '/'
460              && hasdirectory(infile->filename, tmpname))
461                 strcat(tmpname, filename);
462             else {
463                 strcpy(tmpname, filename);
464             }
465 #else
466             if (!hasdirectory(filename, tmpname)
467              && hasdirectory(infile->filename, tmpname))
468                 strcat(tmpname, filename);
469             else {
470                 strcpy(tmpname, filename);
471             }
472 #endif
473             if (openfile(tmpname))
474                 return (TRUE);
475         }
476         /*
477          * Look in any directories specified by -I command line
478          * arguments, then in the builtin search list.
479          */
480         for (incptr = incdir; incptr < incend; incptr++) {
481             if (strlen(*incptr) + strlen(filename) >= (NFWORK - 1))
482                 cfatal("Filename work buffer overflow", NULLST);
483             else {
484 #if HOST == SYS_UNIX
485                 if (filename[0] == '/')
486                     strcpy(tmpname, filename);
487                 else {
488                     sprintf(tmpname, "%s/%s", *incptr, filename);
489                 }
490 #elif HOST == SYS_UNKNOWN
491                 if (filename[0] == '\\')
492                     strcpy(tmpname, filename);
493                 else {
494                     sprintf(tmpname, "%s\\%s", *incptr, filename);
495                 }
496 #else
497                 if (!hasdirectory(filename, tmpname))
498                     sprintf(tmpname, "%s%s", *incptr, filename);
499 #endif
500                 if (openfile(tmpname))
501                     return (TRUE);
502             }
503         }
504         return (FALSE);
505 }
506 
507 FILE_LOCAL int
hasdirectory(char * source,char * result)508 hasdirectory(char* source, char* result)
509 /*
510  * If a device or directory is found in the source filename string, the
511  * node/device/directory part of the string is copied to result and
512  * hasdirectory returns TRUE.  Else, nothing is copied and it returns FALSE.
513  */
514 {
515 #if HOST == SYS_UNIX
516         register char           *tp;
517 
518         if ((tp = strrchr(source, '/')) == NULL)
519             return (FALSE);
520         else {
521             strncpy(result, source, tp - source + 1);
522             result[tp - source + 1] = EOS;
523             return (TRUE);
524         }
525 #else
526 #if HOST == SYS_VMS
527         if (vmsparse(source, NULLST, result)
528          && result[0] != EOS)
529             return (TRUE);
530         else {
531             return (FALSE);
532         }
533 #else
534         /*
535          * Random DEC operating system (RSX, RT11, RSTS/E)
536          */
537         register char           *tp;
538 
539         if ((tp = strrchr(source, ']')) == NULL
540          && (tp = strrchr(source, ':')) == NULL)
541             return (FALSE);
542         else {
543             strncpy(result, source, tp - source + 1);
544             result[tp - source + 1] = EOS;
545             return (TRUE);
546         }
547 #endif
548 #endif
549 }
550 
551 #if HOST == SYS_VMS
552 
553 /*
554  * EXP_DEV is set if a device was specified, EXP_DIR if a directory
555  * is specified.  (Both set indicate a file-logical, but EXP_DEV
556  * would be set by itself if you are reading, say, SYS$INPUT:)
557  */
558 #define DEVDIR (NAM$M_EXP_DEV | NAM$M_EXP_DIR)
559 
560 FILE_LOCAL int
vmsparse(source,defstring,result)561 vmsparse(source, defstring, result)
562 char            *source;
563 char            *defstring;     /* non-NULL -> default string.          */
564 char            *result;        /* Size is at least NAM$C_MAXRSS + 1    */
565 /*
566  * Parse the source string, applying the default (properly, using
567  * the system parse routine), storing it in result.
568  * TRUE if it parsed, FALSE on error.
569  *
570  * If defstring is NULL, there are no defaults and result gets
571  * (just) the node::[directory] part of the string (possibly "")
572  */
573 {
574         struct FAB      fab = cc$rms_fab;       /* File access block    */
575         struct NAM      nam = cc$rms_nam;       /* File name block      */
576         char            fullname[NAM$C_MAXRSS + 1];
577         register char   *rp;                    /* Result pointer       */
578 
579         fab.fab$l_nam = &nam;                   /* fab -> nam           */
580         fab.fab$l_fna = source;                 /* Source filename      */
581         fab.fab$b_fns = strlen(source);         /* Size of source       */
582         fab.fab$l_dna = defstring;              /* Default string       */
583         if (defstring != NULLST)
584             fab.fab$b_dns = strlen(defstring);  /* Size of default      */
585         nam.nam$l_esa = fullname;               /* Expanded filename    */
586         nam.nam$b_ess = NAM$C_MAXRSS;           /* Expanded name size   */
587         if (sys$parse(&fab) == RMS$_NORMAL) {   /* Parse away           */
588             fullname[nam.nam$b_esl] = EOS;      /* Terminate string     */
589             result[0] = EOS;                    /* Just in case         */
590             rp = &result[0];
591             /*
592              * Remove stuff added implicitly, accepting node names and
593              * dev:[directory] strings (but not process-permanent files).
594              */
595             if ((nam.nam$l_fnb & NAM$M_PPF) == 0) {
596                 if ((nam.nam$l_fnb & NAM$M_NODE) != 0) {
597                     strncpy(result, nam.nam$l_node, nam.nam$b_node);
598                     rp += nam.nam$b_node;
599                     *rp = EOS;
600                 }
601                 if ((nam.nam$l_fnb & DEVDIR) == DEVDIR) {
602                     strncpy(rp, nam.nam$l_dev, nam.nam$b_dev + nam.nam$b_dir);
603                     rp += nam.nam$b_dev + nam.nam$b_dir;
604                     *rp = EOS;
605                 }
606             }
607             if (defstring != NULLST) {
608                 strncpy(rp, nam.nam$l_name, nam.nam$b_name + nam.nam$b_type);
609                 rp += nam.nam$b_name + nam.nam$b_type;
610                 *rp = EOS;
611                 if ((nam.nam$l_fnb & NAM$M_EXP_VER) != 0) {
612                     strncpy(rp, nam.nam$l_ver, nam.nam$b_ver);
613                     rp[nam.nam$b_ver] = EOS;
614                 }
615             }
616             return (TRUE);
617         }
618         return (FALSE);
619 }
620 #endif
621 
622