xref: /trunk/main/rsc/source/rscpp/cpp3.c (revision 926379c8)
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 #include        <stdio.h>
24 #ifdef UNX
25 #include        <stdlib.h>
26 #endif
27 #include        <ctype.h>
28 #include        "cppdef.h"
29 #include        "cpp.h"
30 
31 #include        "time.h" /* BP */
32 
33 #ifndef _STRING_H
34 #include <string.h>
35 #endif
36 
37 #ifndef _NO_PROTO
38 int AddInclude( char *pIncStr );  /* BP, 11.09.91, Forward-Deklaration */
39 #endif
40 
41 #if (OSL_DEBUG_LEVEL > 1) && (HOST == SYS_VMS || HOST == SYS_UNIX)
42 #include        <signal.h>
43 #endif
44 
InitCpp3()45 void InitCpp3()
46 {
47 }
48 
49 
50 int
openfile(char * filename)51 openfile(char* filename)
52 /*
53  * Open a file, add it to the linked list of open files.
54  * This is called only from openfile() above.
55  */
56 {
57         register FILE           *fp;
58 
59         if ((fp = fopen(filename, "r")) == NULL) {
60 #if OSL_DEBUG_LEVEL > 1
61 			if ( debug || !bDumpDefs )
62 	            perror(filename);
63 #endif
64             return (FALSE);
65         }
66 #if OSL_DEBUG_LEVEL > 1
67         if (debug)
68             fprintf(stderr, "Reading from \"%s\"\n", filename);
69 #endif
70         addfile(fp, filename);
71         return (TRUE);
72 }
73 
addfile(FILE * fp,char * filename)74 void addfile(FILE* fp, char* filename)
75 /*
76  * Initialize tables for this open file.  This is called from openfile()
77  * above (for #include files), and from the entry to cpp to open the main
78  * input file.  It calls a common routine, getfile() to build the FILEINFO
79  * structure which is used to read characters.  (getfile() is also called
80  * to setup a macro replacement.)
81  */
82 {
83         register FILEINFO       *file;
84 /* #ifndef _NO_PROTO */
85                 extern FILEINFO         *getfile( int bufsize, char *filename ); /* BP */
86 /* #endif */
87         file = getfile(NBUFF, filename);
88         file->fp = fp;                  /* Better remember FILE *       */
89         file->buffer[0] = EOS;          /* Initialize for first read    */
90         line = 1;                       /* Working on line 1 now        */
91         wrongline = TRUE;               /* Force out initial #line      */
92 }
93 
setincdirs()94 void setincdirs()
95 /*
96  * Append system-specific directories to the include directory list.
97  * Called only when cpp is started.
98  */
99 {
100 
101 #ifdef  CPP_INCLUDE
102         *incend++ = CPP_INCLUDE;
103 #define IS_INCLUDE      1
104 #else
105 #define IS_INCLUDE      0
106 #endif
107 
108 #if HOST == SYS_UNIX
109         *incend++ = "/usr/include";
110 #define MAXINCLUDE      (NINCLUDE - 1 - IS_INCLUDE)
111 #endif
112 
113 #if HOST == SYS_VMS
114         extern char     *getenv();
115 
116         if (getenv("C$LIBRARY") != NULL)
117             *incend++ = "C$LIBRARY:";
118         *incend++ = "SYS$LIBRARY:";
119 #define MAXINCLUDE      (NINCLUDE - 2 - IS_INCLUDE)
120 #endif
121 
122 #if HOST == SYS_RSX
123         extern int      $$rsts;                 /* TRUE on RSTS/E       */
124         extern int      $$pos;                  /* TRUE on PRO-350 P/OS */
125         extern int      $$vms;                  /* TRUE on VMS compat.  */
126 
127         if ($$pos) {                            /* P/OS?                */
128             *incend++ = "SY:[ZZDECUSC]";        /* C #includes          */
129             *incend++ = "LB:[1,5]";             /* RSX library          */
130         }
131         else if ($$rsts) {                      /* RSTS/E?              */
132             *incend++ = "SY:@";                 /* User-defined account */
133             *incend++ = "C:";                   /* Decus-C library      */
134             *incend++ = "LB:[1,1]";             /* RSX library          */
135         }
136         else if ($$vms) {                       /* VMS compatibility?   */
137             *incend++ = "C:";
138         }
139         else {                                  /* Plain old RSX/IAS    */
140             *incend++ = "LB:[1,1]";
141         }
142 #define MAXINCLUDE      (NINCLUDE - 3 - IS_INCLUDE)
143 #endif
144 
145 #if HOST == SYS_RT11
146         extern int      $$rsts;                 /* RSTS/E emulation?    */
147 
148         if ($$rsts)
149             *incend++ = "SY:@";                 /* User-defined account */
150         *incend++ = "C:";                       /* Decus-C library disk */
151         *incend++ = "SY:";                      /* System (boot) disk   */
152 #define MAXINCLUDE      (NINCLUDE - 3 - IS_INCLUDE)
153 #endif
154 
155 #if HOST == SYS_UNKNOWN
156 /*
157  * BP: 25.07.91, Kontext: GenMake
158  * Unter DOS wird nun auch die Environment-Variable INCLUDE ausgewetet.
159  * Es kommt erschwerend hinzu, dass alle Eintraege, die mit ';' getrennt
160  * sind, mit in die Liste aufenommen werden muessen.
161  * Dies wird mit der Funktion strtok() realisiert.
162  * Vorsicht bei der Benutzung von malloc !!!
163  * In savestring wird naemlich getmem() verwendet. Vermutlich kommen sich
164  * die beiden Funktion in die Quere. Als ich malloc statt savestring
165  * verwendete knallte es in strcpy() !
166  */
167 
168 #if !defined( ZTC ) && !defined( WNT ) && !defined(BLC) && ! defined UNX && ! defined OS2
169         extern   char     *getenv( char *pStr ); /* BP */
170 #endif
171                  char     *pIncGetEnv = NULL;    /* Pointer auf INCLUDE   */
172 
173         if ( ( pIncGetEnv = getenv("INCLUDE") ) != NULL )
174             AddInclude( pIncGetEnv );
175 
176 #define MAXINCLUDE      (NINCLUDE - 3 - IS_INCLUDE)
177 #endif
178 
179 
180 }
181 
182 /* BP: 11.09.91, Kontext: Erweiterung des INCLUDE-Services
183  * Bislang konnte der cpp keine Include-Angaben in der Kommandozeile
184  * vertragen, bei denen die directries mit ';' getrennt wurden.
185  * Dies ist auch verstaendlich, da dieses cpp fuer UNIX-Systeme
186  * massgeschneidert wurde und in UNI die ';' als Zeichen zum Abschluss
187  * von Kommandos gilt.
188  */
189 
AddInclude(char * pIncStr)190 int AddInclude( char* pIncStr )
191 {
192     char     *pIncEnv    = NULL;    /* Kopie des INCLUDE     */
193     char     *pIncPos;              /* wandert zum naechsten */
194 
195     pIncEnv = savestring( pIncStr );
196     pIncPos = strtok( pIncEnv, ";" );
197 
198     while( pIncPos != NULL )
199     {
200         if (incend >= &incdir[MAXINCLUDE])
201             cfatal("Too many include directories", NULLST);
202         *incend++ = pIncPos;
203         pIncPos   = strtok( NULL, ";" );
204     }
205     return( 1 );
206 }
207 
208 
209 
210 
211 int
dooptions(int argc,char ** argv)212 dooptions(int argc, char** argv)
213 /*
214  * dooptions is called to process command line arguments (-Detc).
215  * It is called only at cpp startup.
216  */
217 {
218         register char           *ap;
219         register DEFBUF         *dp;
220         register int            c;
221         int                     i, j;
222         char                    *arg;
223         SIZES                   *sizp;          /* For -S               */
224         int                     size;           /* For -S               */
225         int                     isdatum;        /* FALSE for -S*        */
226         int                     endtest;        /* For -S               */
227 
228         for (i = j = 1; i < argc; i++) {
229             arg = ap = argv[i];
230 
231 			if (*ap++ != '-' || *ap == EOS)
232 			{
233 					argv[j++] = argv[i];
234 			}
235 			else {
236                 c = *ap++;                      /* Option byte          */
237                 if (islower(c))                 /* Normalize case       */
238                     c = toupper(c);
239                 switch (c) {                    /* Command character    */
240                 case 'C':                       /* Keep comments        */
241                     cflag = TRUE;
242                     keepcomments = TRUE;
243                     break;
244 
245                 case 'D':                       /* Define symbol        */
246 #if HOST != SYS_UNIX
247 /*                   zap_uc(ap); */             /* Force define to U.C. */
248 #endif
249                     /*
250                      * If the option is just "-Dfoo", make it -Dfoo=1
251                      */
252                     while (*ap != EOS && *ap != '=')
253                         ap++;
254                     if (*ap == EOS)
255                         ap = "1";
256                     else
257                         *ap++ = EOS;
258                     /*
259                      * Now, save the word and its definition.
260                      */
261                     dp = defendel(argv[i] + 2, FALSE);
262                     dp->repl = savestring(ap);
263                     dp->nargs = DEF_NOARGS;
264                     break;
265 
266                 case 'E':                       /* Ignore non-fatal     */
267                     eflag = TRUE;               /* errors.              */
268                     break;
269 
270                 case 'I':                       /* Include directory    */
271                     AddInclude( ap );           /* BP, 11.09.91 */
272                     break;
273 
274                 case 'N':                       /* No predefineds       */
275                     nflag++;                    /* Repeat to undefine   */
276                     break;                      /* __LINE__, etc.       */
277 
278                 case 'S':
279                     sizp = size_table;
280                     if (0 != (isdatum = (*ap != '*'))) /* If it's just -S,     */
281                         endtest = T_FPTR;       /* Stop here            */
282                     else {                      /* But if it's -S*      */
283                         ap++;                   /* Step over '*'        */
284                         endtest = 0;            /* Stop at end marker   */
285                     }
286                     while (sizp->bits != endtest && *ap != EOS) {
287                         if (!isdigit(*ap)) {    /* Skip to next digit   */
288                             ap++;
289                             continue;
290                         }
291                         size = 0;               /* Compile the value    */
292                         while (isdigit(*ap)) {
293                             size *= 10;
294                             size += (*ap++ - '0');
295                         }
296                         if (isdatum)
297                             sizp->size = size;  /* Datum size           */
298                         else
299                             sizp->psize = size; /* Pointer size         */
300                         sizp++;
301                     }
302                     if (sizp->bits != endtest)
303                         cwarn("-S, too few values specified in %s", argv[i]);
304                     else if (*ap != EOS)
305                         cwarn("-S, too many values, \"%s\" unused", ap);
306                     break;
307 
308                 case 'U':                       /* Undefine symbol      */
309 #if HOST != SYS_UNIX
310 /*                    zap_uc(ap);*/
311 #endif
312                     if (defendel(ap, TRUE) == NULL)
313                         cwarn("\"%s\" wasn't defined", ap);
314                     break;
315 
316 #if OSL_DEBUG_LEVEL > 1
317                 case 'X':                       /* Debug                */
318                     debug = (isdigit(*ap)) ? atoi(ap) : 1;
319 #if (HOST == SYS_VMS || HOST == SYS_UNIX)
320                     signal(SIGINT, (void (*)(int)) abort); /* Trap "interrupt" */
321 #endif
322                     fprintf(stderr, "Debug set to %d\n", debug);
323                     break;
324 #endif
325 
326 #if OSL_DEBUG_LEVEL > 1
327                 case 'P':                       /* #define's dump       */
328                     bDumpDefs = 1;
329                     fprintf(stderr, "Dump #define's is on\n");
330                     break;
331 #endif
332 
333                 default:                        /* What is this one?    */
334                     cwarn("Unknown option \"%s\"", arg);
335                     fprintf(stderr, "The following options are valid:\n\
336   -C\t\t\tWrite source file comments to output\n\
337   -Dsymbol=value\tDefine a symbol with the given (optional) value\n\
338   -Idirectory\t\tAdd a directory to the #include search list\n\
339   -N\t\t\tDon't predefine target-specific names\n\
340   -Stext\t\tSpecify sizes for #if sizeof\n\
341   -Usymbol\t\tUndefine symbol\n");
342 #if OSL_DEBUG_LEVEL > 1
343                     fprintf(stderr, "  -Xvalue\t\tSet internal debug flag\n");
344                     fprintf(stderr, "  -P\t\t\tdump #define's\n");
345 #endif
346                     break;
347                 }                       /* Switch on all options        */
348             }                           /* If it's a -option            */
349         }                               /* For all arguments            */
350 #if OSL_DEBUG_LEVEL > 1
351 		if ( (bDumpDefs ? j > 4 : j > 3) ) {
352 #else
353         if (j > 3) {
354 #endif
355             cerror(
356                 "Too many file arguments.  Usage: cpp [input [output]]",
357                 NULLST);
358         }
359 		return (j);                     /* Return new argc              */
360 }
361 
362 int
363 readoptions(char* filename, char*** pfargv)
364 {
365         FILE           *fp;
366 		int c;
367         int bInQuotes = 0;
368 		char optbuff[1024], *poptbuff;
369 		int fargc=0, back;
370 		char *fargv[PARALIMIT], **pfa;
371 
372 		pfa=*pfargv=malloc(sizeof(fargv));
373 
374 		poptbuff=&optbuff[0];
375 		filename++;
376 		if ((fp = fopen(filename, "r")) == NULL) {
377 #if OSL_DEBUG_LEVEL > 1
378 			if ( debug || !bDumpDefs )
379 	            perror(filename);
380 #endif
381             return (FALSE);
382         }
383 		do
384 		{
385             /*
386              *  #i27914# double ticks '"' now have a duplicate function:
387              *  1. they define a string ( e.g. -DFOO="baz" )
388              *  2. a string can contain spaces, so -DFOO="baz zum" defines one
389              *  argument no two !
390              */
391 			c=fgetc(fp);
392 			if ( c != ' ' && c != CR && c != NL && c != HT && c != EOF)
393 			{
394 				*poptbuff++=(char)c;
395                 if( c == '"' )
396                     bInQuotes = ~bInQuotes;
397 			}
398 			else
399 			{
400                 if( c != EOF && bInQuotes )
401                     *poptbuff++=(char)c;
402                 else
403                 {
404                     *poptbuff=EOS;
405                     if (strlen(optbuff)>0)
406                     {
407                         pfa[fargc+1]=malloc(strlen(optbuff)+1);
408                         strcpy(pfa[fargc+1],optbuff);
409                         fargc++;
410                         pfa[fargc+1]=NULL;
411                         poptbuff=&optbuff[0];
412                     }
413                 }
414 			}
415 		}
416 		while ( c != EOF );
417 
418 		fclose(fp);
419 		back=dooptions(fargc+1,pfa);
420 
421 		return (back);
422 }
423 
424 
425 
426 #if HOST != SYS_UNIX
427 FILE_LOCAL void
428 zap_uc(char* ap)
429 /*
430  * Dec operating systems mangle upper-lower case in command lines.
431  * This routine forces the -D and -U arguments to uppercase.
432  * It is called only on cpp startup by dooptions().
433  */
434 {
435         while (*ap != EOS) {
436             /*
437              * Don't use islower() here so it works with Multinational
438              */
439             if (*ap >= 'a' && *ap <= 'z')
440                 *ap = (char)toupper(*ap);
441             ap++;
442         }
443 }
444 #endif
445 
446 void initdefines()
447 /*
448  * Initialize the built-in #define's.  There are two flavors:
449  *      #define decus   1               (static definitions)
450  *      #define __FILE__ ??             (dynamic, evaluated by magic)
451  * Called only on cpp startup.
452  *
453  * Note: the built-in static definitions are suppressed by the -N option.
454  * __LINE__, __FILE__, and __DATE__ are always present.
455  */
456 {
457         register char           **pp;
458         register char           *tp;
459         register DEFBUF         *dp;
460         int                     i;
461         time_t                  tvec;
462 
463 #if !defined( ZTC ) && !defined( WNT ) && !defined(BLC) && !defined(G3)
464         extern char             *ctime();
465 #endif
466 
467         /*
468          * Predefine the built-in symbols.  Allow the
469          * implementor to pre-define a symbol as "" to
470          * eliminate it.
471          */
472         if (nflag == 0) {
473             for (pp = preset; *pp != NULL; pp++) {
474                 if (*pp[0] != EOS) {
475                     dp = defendel(*pp, FALSE);
476                     dp->repl = savestring("1");
477                     dp->nargs = DEF_NOARGS;
478                 }
479             }
480         }
481         /*
482          * The magic pre-defines (__FILE__ and __LINE__ are
483          * initialized with negative argument counts.  expand()
484          * notices this and calls the appropriate routine.
485          * DEF_NOARGS is one greater than the first "magic" definition.
486          */
487         if (nflag < 2) {
488             for (pp = magic, i = DEF_NOARGS; *pp != NULL; pp++) {
489                 dp = defendel(*pp, FALSE);
490                 dp->nargs = --i;
491             }
492 #if OK_DATE
493             /*
494              * Define __DATE__ as today's date.
495              */
496             dp = defendel("__DATE__", FALSE);
497             dp->repl = tp = getmem(27);
498             dp->nargs = DEF_NOARGS;
499             time(&tvec);
500             *tp++ = '"';
501             strcpy(tp, ctime(&tvec));
502             tp[24] = '"';                       /* Overwrite newline    */
503 #endif
504         }
505 }
506 
507 #if HOST == SYS_VMS
508 /*
509  * getredirection() is intended to aid in porting C programs
510  * to VMS (Vax-11 C) which does not support '>' and '<'
511  * I/O redirection.  With suitable modification, it may
512  * useful for other portability problems as well.
513  */
514 
515 int
516 getredirection(argc, argv)
517 int             argc;
518 char            **argv;
519 /*
520  * Process vms redirection arg's.  Exit if any error is seen.
521  * If getredirection() processes an argument, it is erased
522  * from the vector.  getredirection() returns a new argc value.
523  *
524  * Warning: do not try to simplify the code for vms.  The code
525  * presupposes that getredirection() is called before any data is
526  * read from stdin or written to stdout.
527  *
528  * Normal usage is as follows:
529  *
530  *      main(argc, argv)
531  *      int             argc;
532  *      char            *argv[];
533  *      {
534  *              argc = getredirection(argc, argv);
535  *      }
536  */
537 {
538         register char           *ap;    /* Argument pointer     */
539         int                     i;      /* argv[] index         */
540         int                     j;      /* Output index         */
541         int                     file;   /* File_descriptor      */
542         extern int              errno;  /* Last vms i/o error   */
543 
544         for (j = i = 1; i < argc; i++) {   /* Do all arguments  */
545             switch (*(ap = argv[i])) {
546             case '<':                   /* <file                */
547                 if (freopen(++ap, "r", stdin) == NULL) {
548                     perror(ap);         /* Can't find file      */
549                     exit(errno);        /* Is a fatal error     */
550                 }
551                 break;
552 
553             case '>':                   /* >file or >>file      */
554                 if (*++ap == '>') {     /* >>file               */
555                     /*
556                      * If the file exists, and is writable by us,
557                      * call freopen to append to the file (using the
558                      * file's current attributes).  Otherwise, create
559                      * a new file with "vanilla" attributes as if the
560                      * argument was given as ">filename".
561                      * access(name, 2) returns zero if we can write on
562                      * the specified file.
563                      */
564                     if (access(++ap, 2) == 0) {
565                         if (freopen(ap, "a", stdout) != NULL)
566                             break;      /* Exit case statement  */
567                         perror(ap);     /* Error, can't append  */
568                         exit(errno);    /* After access test    */
569                     }                   /* If file accessible   */
570                 }
571                 /*
572                  * On vms, we want to create the file using "standard"
573                  * record attributes.  creat(...) creates the file
574                  * using the caller's default protection mask and
575                  * "variable length, implied carriage return"
576                  * attributes. dup2() associates the file with stdout.
577                  */
578                 if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
579                  || dup2(file, fileno(stdout)) == -1) {
580                     perror(ap);         /* Can't create file    */
581                     exit(errno);        /* is a fatal error     */
582                 }                       /* If '>' creation      */
583                 break;                  /* Exit case test       */
584 
585             default:
586                 argv[j++] = ap;         /* Not a redirector     */
587                 break;                  /* Exit case test       */
588             }
589         }                               /* For all arguments    */
590         argv[j] = NULL;                 /* Terminate argv[]     */
591         return (j);                     /* Return new argc      */
592 }
593 #endif
594