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 :
25 : /*
26 : * Generate (by hand-inspection) a set of unique values for each control
27 : * operator. Note that this is not guaranteed to work for non-Ascii
28 : * machines. CPP won't compile if there are hash conflicts.
29 : */
30 :
31 : #define L_assert ('a' + ('s' << 1))
32 : #define L_define ('d' + ('f' << 1))
33 : #define L_elif ('e' + ('i' << 1))
34 : #define L_else ('e' + ('s' << 1))
35 : #define L_endif ('e' + ('d' << 1))
36 : #define L_if ('i' + (EOS << 1))
37 : #define L_ifdef ('i' + ('d' << 1))
38 : #define L_ifndef ('i' + ('n' << 1))
39 : #define L_include ('i' + ('c' << 1))
40 : #define L_line ('l' + ('n' << 1))
41 : #define L_nogood (EOS + (EOS << 1)) /* To catch #i */
42 : #define L_pragma ('p' + ('a' << 1))
43 : #define L_undef ('u' + ('d' << 1))
44 : #define L_error ('e' + ('r' << 1)) /* BP 5.3.92, #error */
45 : #if OSL_DEBUG_LEVEL > 1
46 : #define L_debug ('d' + ('b' << 1)) /* #debug */
47 : #define L_nodebug ('n' + ('d' << 1)) /* #nodebug */
48 : #endif
49 :
50 :
51 : /*
52 : * Process #control lines. Simple commands are processed inline,
53 : * while complex commands have their own subroutines.
54 : *
55 : * The counter is used to force out a newline before #line, and
56 : * #pragma commands. This prevents these commands from ending up at
57 : * the end of the previous line if cpp is invoked with the -C option.
58 : */
59 714315 : int control(int counter)
60 : {
61 : int c;
62 : char* tp;
63 : int hash;
64 : char* ep;
65 :
66 714315 : c = skipws();
67 714315 : if (c == '\n' || c == EOF_CHAR)
68 0 : return counter + 1;
69 714315 : if (!isdigit(c))
70 714315 : scanid(c); /* Get #word to token[] */
71 : else
72 : {
73 0 : unget(); /* Hack -- allow #123 as a */
74 0 : strcpy(token, "line"); /* synonym for #line 123 */
75 : }
76 714315 : hash = (token[1] == EOS) ? L_nogood : (token[0] + (token[2] << 1));
77 714315 : switch (hash)
78 : {
79 0 : case L_assert: tp = "assert"; break;
80 695411 : case L_define: tp = "define"; break;
81 40 : case L_elif: tp = "elif"; break;
82 22 : case L_else: tp = "else"; break;
83 6547 : case L_endif: tp = "endif"; break;
84 453 : case L_if: tp = "if"; break;
85 158 : case L_ifdef: tp = "ifdef"; break;
86 5936 : case L_ifndef: tp = "ifndef"; break;
87 5230 : case L_include: tp = "include"; break;
88 0 : case L_line: tp = "line"; break;
89 0 : case L_pragma: tp = "pragma"; break;
90 142 : case L_undef: tp = "undef"; break;
91 376 : case L_error: tp = "error"; break;
92 : #if OSL_DEBUG_LEVEL > 1
93 : case L_debug: tp = "debug"; break;
94 : case L_nodebug: tp = "nodebug"; break;
95 : #endif
96 0 : default: hash = L_nogood;
97 : /*fall-through*/
98 0 : case L_nogood: tp = ""; break;
99 : }
100 714315 : if (!streq(tp, token))
101 0 : hash = L_nogood;
102 : /*
103 : * hash is set to a unique value corresponding to the
104 : * control keyword (or L_nogood if we think it's nonsense).
105 : */
106 714315 : if (infile->fp == NULL)
107 0 : cwarn("Control line \"%s\" within macro expansion", token);
108 714315 : if (!compiling)
109 : { /* Not compiling now */
110 213133 : switch (hash)
111 : {
112 : case L_if: /* These can't turn */
113 : case L_ifdef: /* compilation on, but */
114 : case L_ifndef: /* we must nest #if's */
115 17 : if (++ifptr >= &ifstack[BLK_NEST])
116 0 : goto if_nest_err;
117 17 : *ifptr = 0; /* !WAS_COMPILING */
118 : case L_line: /* Many */
119 : /*
120 : * Are pragma's always processed?
121 : */
122 : case L_pragma: /* options */
123 : case L_include: /* are uninteresting */
124 : case L_define: /* if we */
125 : case L_undef: /* aren't */
126 : case L_assert: /* compiling. */
127 : case L_error: /* BP 5.3.92, #error */
128 210174 : dump_line: skipnl(); /* Ignore rest of line */
129 210174 : return counter + 1;
130 : }
131 : }
132 : /*
133 : * Make sure that #line and #pragma are output on a fresh line.
134 : */
135 504161 : if (counter > 0 && (hash == L_line || hash == L_pragma))
136 : {
137 0 : PUTCHAR('\n');
138 0 : counter--;
139 : }
140 :
141 504161 : switch (hash)
142 : {
143 : case L_line:
144 : /*
145 : * Parse the line to update the line number and "progname"
146 : * field and line number for the next input line.
147 : * Set wrongline to force it out later.
148 : */
149 0 : c = skipws();
150 0 : workp = work; /* Save name in work */
151 0 : while (c != '\n' && c != EOF_CHAR)
152 : {
153 0 : save(c);
154 0 : c = get();
155 : }
156 0 : unget();
157 0 : save(EOS);
158 : /*
159 : * Split #line argument into <line-number> and <name>
160 : * We subtract 1 as we want the number of the next line.
161 : */
162 0 : line = atoi(work) - 1; /* Reset line number */
163 0 : for (tp = work; isdigit(*tp) || type[(int)*tp] == SPA; tp++)
164 : ; /* Skip over digits */
165 0 : if (*tp != EOS) /* Got a filename, so: */
166 : {
167 0 : if (*tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL)
168 : {
169 0 : tp++; /* Skip over left quote */
170 0 : *ep = EOS; /* And ignore right one */
171 : }
172 0 : if (infile->progname != NULL) /* Give up the old name */
173 0 : free(infile->progname); /* if it's allocated. */
174 0 : infile->progname = savestring(tp);
175 : }
176 0 : wrongline = TRUE; /* Force output later */
177 0 : break;
178 :
179 : case L_include:
180 4148 : doinclude();
181 4148 : break;
182 :
183 : case L_define:
184 486845 : dodefine();
185 486845 : break;
186 :
187 : case L_undef:
188 29 : doundef();
189 29 : break;
190 :
191 : case L_else:
192 22 : if (ifptr == &ifstack[0])
193 0 : goto nest_err;
194 22 : else if ((*ifptr & ELSE_SEEN) != 0)
195 0 : goto else_seen_err;
196 22 : *ifptr |= ELSE_SEEN;
197 22 : if ((*ifptr & WAS_COMPILING) != 0)
198 : {
199 21 : if (compiling || (*ifptr & TRUE_SEEN) != 0)
200 19 : compiling = FALSE;
201 : else
202 : {
203 2 : compiling = TRUE;
204 : }
205 : }
206 22 : break;
207 :
208 : case L_elif:
209 40 : if (ifptr == &ifstack[0])
210 0 : goto nest_err;
211 40 : else if ((*ifptr & ELSE_SEEN) != 0)
212 : {
213 0 : else_seen_err: cerror("#%s may not follow #else", token);
214 0 : goto dump_line;
215 : }
216 40 : if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING)
217 : {
218 20 : compiling = FALSE; /* Done compiling stuff */
219 20 : goto dump_line; /* Skip this clause */
220 : }
221 20 : doif(L_if);
222 20 : break;
223 :
224 : case L_if:
225 : case L_ifdef:
226 : case L_ifndef:
227 6530 : if (++ifptr >= &ifstack[BLK_NEST])
228 0 : if_nest_err: cfatal("Too many nested #%s statements", token);
229 6530 : *ifptr = WAS_COMPILING;
230 6530 : doif(hash);
231 6530 : break;
232 :
233 : case L_endif:
234 6547 : if (ifptr == &ifstack[0])
235 : {
236 0 : nest_err: cerror("#%s must be in an #if", token);
237 0 : goto dump_line;
238 : }
239 6547 : if (!compiling && (*ifptr & WAS_COMPILING) != 0)
240 2939 : wrongline = TRUE;
241 6547 : compiling = ((*ifptr & WAS_COMPILING) != 0);
242 6547 : --ifptr;
243 6547 : break;
244 :
245 : case L_assert:
246 0 : if (eval() == 0)
247 0 : cerror("Preprocessor assertion failure", NULLST);
248 0 : break;
249 :
250 : case L_pragma:
251 : /*
252 : * #pragma is provided to pass "options" to later
253 : * passes of the compiler. cpp doesn't have any yet.
254 : */
255 0 : fprintf( pCppOut, "#pragma ");
256 0 : while ((c = get()) != '\n' && c != EOF_CHAR)
257 0 : cput(c);
258 0 : unget();
259 0 : break;
260 :
261 : #if OSL_DEBUG_LEVEL > 1
262 : case L_debug:
263 : if (debug == 0)
264 : dumpdef("debug set on");
265 : debug++;
266 : break;
267 :
268 : case L_nodebug:
269 : debug--;
270 : break;
271 : #endif
272 : case L_error: /* BP 5.3.92, #error */
273 0 : fprintf( pCppOut, "cpp: line %d, Error directive: ", line );
274 0 : while ((c = get()) != '\n' && c != EOF_CHAR)
275 0 : cput(c);
276 0 : fprintf( pCppOut, "\n" );
277 0 : exit( 1 );
278 :
279 : default:
280 : /*
281 : * Undefined #control keyword.
282 : * Note: the correct behavior may be to warn and
283 : * pass the line to a subsequent compiler pass.
284 : * This would allow #asm or similar extensions.
285 : */
286 0 : cerror("Illegal # command \"%s\"", token);
287 0 : break;
288 : }
289 504141 : if (hash != L_include)
290 : {
291 499993 : if (skipws() != '\n')
292 : {
293 0 : cwarn("Unexpected text in #control line ignored", NULLST);
294 0 : skipnl();
295 : }
296 : }
297 504141 : return counter + 1;
298 : }
299 :
300 : /*
301 : * Process an #if, #ifdef, or #ifndef. The latter two are straightforward,
302 : * while #if needs a subroutine of its own to evaluate the expression.
303 : *
304 : * doif() is called only if compiling is TRUE. If false, compilation
305 : * is always suppressed, so we don't need to evaluate anything. This
306 : * suppresses unnecessary warnings.
307 : */
308 6550 : FILE_LOCAL void doif(int hash)
309 : {
310 : int c;
311 : int found;
312 :
313 6550 : if ((c = skipws()) == '\n' || c == EOF_CHAR)
314 : {
315 0 : unget();
316 0 : goto badif;
317 : }
318 6550 : if (hash == L_if)
319 : {
320 466 : unget();
321 466 : found = (eval() != 0); /* Evaluate expr, != 0 is TRUE */
322 466 : hash = L_ifdef; /* #if is now like #ifdef */
323 : }
324 : else
325 : {
326 6084 : if (type[c] != LET) /* Next non-blank isn't letter */
327 0 : goto badif; /* ... is an error */
328 6084 : found = (lookid(c) != NULL); /* Look for it in symbol table */
329 : }
330 6550 : if (found == (hash == L_ifdef))
331 : {
332 3628 : compiling = TRUE;
333 3628 : *ifptr |= TRUE_SEEN;
334 : }
335 : else
336 : {
337 2922 : compiling = FALSE;
338 : }
339 6550 : return;
340 :
341 0 : badif: cerror("#if, #ifdef, or #ifndef without an argument", NULLST);
342 0 : skipnl(); /* Prevent an extra */
343 0 : unget(); /* Error message */
344 0 : return;
345 : }
346 :
347 : /*
348 : * Process the #include control line.
349 : * There are three variations:
350 : * #include "file" search somewhere relative to the
351 : * current source file, if not found,
352 : * treat as #include <file>.
353 : * #include <file> Search in an implementation-dependent
354 : * list of places.
355 : * #include token Expand the token, it must be one of
356 : * "file" or <file>, process as such.
357 : *
358 : * Note: the November 12 draft forbids '>' in the #include <file> format.
359 : * This restriction is unnecessary and not implemented.
360 : */
361 4148 : FILE_LOCAL void doinclude()
362 : {
363 : int c;
364 : int delim;
365 :
366 4148 : delim = macroid(skipws());
367 4148 : if (delim != '<' && delim != '"')
368 0 : goto incerr;
369 4148 : if (delim == '<')
370 3258 : delim = '>';
371 4148 : workp = work;
372 4148 : instring = TRUE; /* Accept all characters */
373 : #ifdef CONTROL_COMMENTS_NOT_ALLOWED
374 : while ((c = get()) != '\n' && c != EOF_CHAR)
375 : save(c); /* Put it away. */
376 : unget(); /* Force nl after includee */
377 : /*
378 : * The draft is unclear if the following should be done.
379 : */
380 : while (--workp >= work && *workp == ' ')
381 : ; /* Trim blanks from filename */
382 : if (*workp != delim)
383 : goto incerr;
384 : #else
385 67176 : while ((c = get()) != delim && c != EOF_CHAR)
386 58880 : save(c);
387 : #endif
388 4148 : *workp = EOS; /* Terminate filename */
389 4148 : instring = FALSE;
390 4148 : if (openinclude(work, (delim == '"')))
391 4148 : return;
392 : /*
393 : * No sense continuing if #include file isn't there.
394 : */
395 0 : cfatal("Cannot open include file \"%s\"", work);
396 :
397 0 : incerr: cerror("#include syntax error", NULLST);
398 0 : return;
399 : }
400 :
401 : /*
402 : * Actually open an include file. This routine is only called from
403 : * doinclude() above, but was written as a separate subroutine for
404 : * programmer convenience. It searches the list of directories
405 : * and actually opens the file, linking it into the list of
406 : * active files. Returns TRUE if the file was opened, FALSE
407 : * if openinclude() fails. No error message is printed.
408 : */
409 4148 : FILE_LOCAL int openinclude(char* filename, int searchlocal)
410 : {
411 : char** incptr;
412 : char tmpname[NFWORK]; /* Filename work area */
413 :
414 4148 : if (searchlocal)
415 : {
416 : /*
417 : * Look in local directory first
418 : */
419 : #if HOST == SYS_UNIX
420 : /*
421 : * Try to open filename relative to the directory of the current
422 : * source file (as opposed to the current directory). (ARF, SCK).
423 : */
424 1780 : if (filename[0] != '/' &&
425 890 : hasdirectory(infile->filename, tmpname, NFWORK))
426 890 : {
427 890 : int len = strlen(tmpname);
428 890 : int len2 = strlen(filename);
429 890 : if(len + len2 < NFWORK)
430 : {
431 890 : memcpy(tmpname + len, filename, len2);
432 890 : tmpname[len + len2] = 0;
433 : }
434 : else
435 : {
436 0 : cfatal("Filename work buffer overflow", NULLST);
437 : }
438 : }
439 : else
440 : {
441 0 : int len = strlen(filename);
442 0 : if(len < NFWORK)
443 : {
444 0 : memcpy(tmpname, filename, len);
445 0 : tmpname[len] = 0;
446 : }
447 : else
448 : {
449 0 : cfatal("Filename work buffer overflow", NULLST);
450 : }
451 : }
452 : #else
453 : if (!hasdirectory(filename, tmpname, NFWORK) &&
454 : hasdirectory(infile->filename, tmpname, NFWORK))
455 : {
456 : strcat(tmpname, filename);
457 : }
458 : else
459 : {
460 : strcpy(tmpname, filename);
461 : }
462 : #endif
463 890 : if (openfile(tmpname))
464 8 : return TRUE;
465 : }
466 : /*
467 : * Look in any directories specified by -I command line
468 : * arguments, then in the builtin search list.
469 : */
470 12736 : for (incptr = incdir; incptr < incend; incptr++)
471 : {
472 12736 : if (strlen(*incptr) + strlen(filename) >= (NFWORK - 1))
473 0 : cfatal("Filename work buffer overflow", NULLST);
474 : else
475 : {
476 : #if HOST == SYS_UNIX
477 12736 : if (filename[0] == '/')
478 0 : strcpy(tmpname, filename);
479 : else
480 12736 : sprintf(tmpname, "%s/%s", *incptr, filename);
481 :
482 : #elif HOST == SYS_UNKNOWN
483 : if (filename[0] == '\\')
484 : strcpy(tmpname, filename);
485 : else
486 : sprintf(tmpname, "%s\\%s", *incptr, filename);
487 : #else
488 : if (!hasdirectory(filename, tmpname, NFWORK))
489 : sprintf(tmpname, "%s%s", *incptr, filename);
490 : #endif
491 12736 : if (openfile(tmpname))
492 4140 : return TRUE;
493 : }
494 : }
495 0 : return FALSE;
496 : }
497 :
498 : /*
499 : * If a device or directory is found in the source filename string, the
500 : * node/device/directory part of the string is copied to result and
501 : * hasdirectory returns TRUE. Else, nothing is copied and it returns FALSE.
502 : */
503 890 : FILE_LOCAL int hasdirectory(char* source, char* result, int max)
504 : {
505 : #if HOST == SYS_UNIX
506 : char* tp;
507 :
508 890 : if ((tp = strrchr(source, '/')) == NULL)
509 0 : return FALSE;
510 : else
511 : {
512 890 : int len = (int)(tp - source);
513 890 : if(len < max)
514 : {
515 890 : memcpy(result, source, len);
516 890 : result[len] = 0;
517 : }
518 : else
519 : {
520 0 : cfatal("Filename work buffer overflow", NULLST);
521 : }
522 890 : return TRUE;
523 : }
524 : #else
525 : /*
526 : * Random DEC operating system (RSTS/E)
527 : */
528 : char* tp;
529 :
530 : (void)max;
531 :
532 : if ((tp = strrchr(source, ']')) == NULL &&
533 : (tp = strrchr(source, ':')) == NULL)
534 : {
535 : return FALSE;
536 : }
537 : else
538 : {
539 : strncpy(result, source, tp - source + 1);
540 : result[tp - source + 1] = EOS;
541 : return TRUE;
542 : }
543 : #endif
544 : }
545 :
546 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|