1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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.
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 */
52 * Process #control lines. Simple commands are processed inline,
53 * while complex commands have their own subroutines.
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.
59 int control(int counter
)
67 if (c
== '\n' || c
== EOF_CHAR
)
70 scanid(c
); /* Get #word to token[] */
73 unget(); /* Hack -- allow #123 as a */
74 strcpy(token
, "line"); /* synonym for #line 123 */
76 hash
= (token
[1] == EOS
) ? L_nogood
: (token
[0] + (token
[2] << 1));
79 case L_assert
: tp
= "assert"; break;
80 case L_define
: tp
= "define"; break;
81 case L_elif
: tp
= "elif"; break;
82 case L_else
: tp
= "else"; break;
83 case L_endif
: tp
= "endif"; break;
84 case L_if
: tp
= "if"; break;
85 case L_ifdef
: tp
= "ifdef"; break;
86 case L_ifndef
: tp
= "ifndef"; break;
87 case L_include
: tp
= "include"; break;
88 case L_line
: tp
= "line"; break;
89 case L_pragma
: tp
= "pragma"; break;
90 case L_undef
: tp
= "undef"; break;
91 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;
96 default: hash
= L_nogood
;
98 case L_nogood
: tp
= ""; break;
100 if (!streq(tp
, token
))
103 * hash is set to a unique value corresponding to the
104 * control keyword (or L_nogood if we think it's nonsense).
106 if (infile
->fp
== NULL
)
107 cwarn("Control line \"%s\" within macro expansion", token
);
109 { /* Not compiling now */
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 if (++ifptr
>= &ifstack
[BLK_NEST
])
117 *ifptr
= 0; /* !WAS_COMPILING */
118 case L_line
: /* Many */
120 * Are pragma's always processed?
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 dump_line
: skipnl(); /* Ignore rest of line */
133 * Make sure that #line and #pragma are output on a fresh line.
135 if (counter
> 0 && (hash
== L_line
|| hash
== L_pragma
))
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.
150 workp
= work
; /* Save name in work */
151 while (c
!= '\n' && c
!= EOF_CHAR
)
159 * Split #line argument into <line-number> and <name>
160 * We subtract 1 as we want the number of the next line.
162 line
= atoi(work
) - 1; /* Reset line number */
163 for (tp
= work
; isdigit(*tp
) || type
[(int)*tp
] == SPA
; tp
++)
164 ; /* Skip over digits */
165 if (*tp
!= EOS
) /* Got a filename, so: */
167 if (*tp
== '"' && (ep
= strrchr(tp
+ 1, '"')) != NULL
)
169 tp
++; /* Skip over left quote */
170 *ep
= EOS
; /* And ignore right one */
172 if (infile
->progname
!= NULL
) /* Give up the old name */
173 free(infile
->progname
); /* if it's allocated. */
174 infile
->progname
= savestring(tp
);
176 wrongline
= TRUE
; /* Force output later */
192 if (ifptr
== &ifstack
[0])
194 else if ((*ifptr
& ELSE_SEEN
) != 0)
197 if ((*ifptr
& WAS_COMPILING
) != 0)
199 if (compiling
|| (*ifptr
& TRUE_SEEN
) != 0)
209 if (ifptr
== &ifstack
[0])
211 else if ((*ifptr
& ELSE_SEEN
) != 0)
213 else_seen_err
: cerror("#%s may not follow #else", token
);
216 if ((*ifptr
& (WAS_COMPILING
| TRUE_SEEN
)) != WAS_COMPILING
)
218 compiling
= FALSE
; /* Done compiling stuff */
219 goto dump_line
; /* Skip this clause */
227 if (++ifptr
>= &ifstack
[BLK_NEST
])
228 if_nest_err
: cfatal("Too many nested #%s statements", token
);
229 *ifptr
= WAS_COMPILING
;
234 if (ifptr
== &ifstack
[0])
236 nest_err
: cerror("#%s must be in an #if", token
);
239 if (!compiling
&& (*ifptr
& WAS_COMPILING
) != 0)
241 compiling
= ((*ifptr
& WAS_COMPILING
) != 0);
247 cerror("Preprocessor assertion failure", NULLST
);
252 * #pragma is provided to pass "options" to later
253 * passes of the compiler. cpp doesn't have any yet.
255 fprintf( pCppOut
, "#pragma ");
256 while ((c
= get()) != '\n' && c
!= EOF_CHAR
)
261 #if OSL_DEBUG_LEVEL > 1
264 dumpdef("debug set on");
272 case L_error
: /* BP 5.3.92, #error */
273 fprintf( pCppOut
, "cpp: line %d, Error directive: ", line
);
274 while ((c
= get()) != '\n' && c
!= EOF_CHAR
)
276 fprintf( pCppOut
, "\n" );
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.
286 cerror("Illegal # command \"%s\"", token
);
289 if (hash
!= L_include
)
291 if (skipws() != '\n')
293 cwarn("Unexpected text in #control line ignored", NULLST
);
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.
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.
308 FILE_LOCAL
void doif(int hash
)
313 if ((c
= skipws()) == '\n' || c
== EOF_CHAR
)
321 found
= (eval() != 0); /* Evaluate expr, != 0 is TRUE */
322 hash
= L_ifdef
; /* #if is now like #ifdef */
326 if (type
[c
] != LET
) /* Next non-blank isn't letter */
327 goto badif
; /* ... is an error */
328 found
= (lookid(c
) != NULL
); /* Look for it in symbol table */
330 if (found
== (hash
== L_ifdef
))
341 badif
: cerror("#if, #ifdef, or #ifndef without an argument", NULLST
);
342 skipnl(); /* Prevent an extra */
343 unget(); /* Error message */
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
355 * #include token Expand the token, it must be one of
356 * "file" or <file>, process as such.
358 * Note: the November 12 draft forbids '>' in the #include <file> format.
359 * This restriction is unnecessary and not implemented.
361 FILE_LOCAL
void doinclude()
366 delim
= macroid(skipws());
367 if (delim
!= '<' && delim
!= '"')
372 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 */
378 * The draft is unclear if the following should be done.
380 while (--workp
>= work
&& *workp
== ' ')
381 ; /* Trim blanks from filename */
385 while ((c
= get()) != delim
&& c
!= EOF_CHAR
)
388 *workp
= EOS
; /* Terminate filename */
390 if (openinclude(work
, (delim
== '"')))
393 * No sense continuing if #include file isn't there.
395 cfatal("Cannot open include file \"%s\"", work
);
397 incerr
: cerror("#include syntax error", NULLST
);
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.
409 FILE_LOCAL
int openinclude(char* filename
, int searchlocal
)
412 char tmpname
[NFWORK
]; /* Filename work area */
417 * Look in local directory first
421 * Try to open filename relative to the directory of the current
422 * source file (as opposed to the current directory). (ARF, SCK).
424 if (filename
[0] != '/' &&
425 hasdirectory(infile
->filename
, tmpname
, NFWORK
))
427 int len
= strlen(tmpname
);
428 int len2
= strlen(filename
);
429 if(len
+ len2
< NFWORK
)
431 memcpy(tmpname
+ len
, filename
, len2
);
432 tmpname
[len
+ len2
] = 0;
436 cfatal("Filename work buffer overflow", NULLST
);
441 int len
= strlen(filename
);
444 memcpy(tmpname
, filename
, len
);
449 cfatal("Filename work buffer overflow", NULLST
);
453 if (!hasdirectory(filename
, tmpname
, NFWORK
) &&
454 hasdirectory(infile
->filename
, tmpname
, NFWORK
))
456 strcat(tmpname
, filename
);
460 strcpy(tmpname
, filename
);
463 if (openfile(tmpname
))
467 * Look in any directories specified by -I command line
468 * arguments, then in the builtin search list.
470 for (incptr
= incdir
; incptr
< incend
; incptr
++)
472 if (strlen(*incptr
) + strlen(filename
) >= (NFWORK
- 1))
473 cfatal("Filename work buffer overflow", NULLST
);
477 if (filename
[0] == '/')
478 strcpy(tmpname
, filename
);
480 sprintf(tmpname
, "%s/%s", *incptr
, filename
);
482 #elif HOST == SYS_UNKNOWN
483 if (filename
[0] == '\\')
484 strcpy(tmpname
, filename
);
486 sprintf(tmpname
, "%s\\%s", *incptr
, filename
);
488 if (!hasdirectory(filename
, tmpname
, NFWORK
))
489 sprintf(tmpname
, "%s%s", *incptr
, filename
);
491 if (openfile(tmpname
))
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.
503 FILE_LOCAL
int hasdirectory(char* source
, char* result
, int max
)
508 if ((tp
= strrchr(source
, '/')) == NULL
)
512 int len
= (int)(tp
- source
);
515 memcpy(result
, source
, len
);
520 cfatal("Filename work buffer overflow", NULLST
);
526 * Random DEC operating system (RSTS/E)
532 if ((tp
= strrchr(source
, ']')) == NULL
&&
533 (tp
= strrchr(source
, ':')) == NULL
)
539 strncpy(result
, source
, tp
- source
+ 1);
540 result
[tp
- source
+ 1] = EOS
;
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */