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