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