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