bump product version to 5.0.4.1
[LibreOffice.git] / rsc / source / rscpp / cpp2.c
blob2c2e55094c617fde55be769011b2bc1d5b2b5fde
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <stdio.h>
21 #include <ctype.h>
22 #include "cppdef.h"
23 #include "cpp.h"
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 */
48 #endif
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)
61 int c;
62 char* tp;
63 int hash;
64 char* ep;
66 c = skipws();
67 if (c == '\n' || c == EOF_CHAR)
68 return counter + 1;
69 if (!isdigit(c))
70 scanid(c); /* Get #word to token[] */
71 else
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));
77 switch (hash)
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;
95 #endif
96 default: hash = L_nogood;
97 /*fall-through*/
98 case L_nogood: tp = ""; break;
100 if (!streq(tp, token))
101 hash = L_nogood;
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);
108 if (!compiling)
109 { /* Not compiling now */
110 switch (hash)
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])
116 goto if_nest_err;
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 */
129 return counter + 1;
133 * Make sure that #line and #pragma are output on a fresh line.
135 if (counter > 0 && (hash == L_line || hash == L_pragma))
137 PUTCHAR('\n');
138 counter--;
141 switch (hash)
143 case L_line:
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.
149 c = skipws();
150 workp = work; /* Save name in work */
151 while (c != '\n' && c != EOF_CHAR)
153 save(c);
154 c = get();
156 unget();
157 save(EOS);
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 */
177 break;
179 case L_include:
180 doinclude();
181 break;
183 case L_define:
184 dodefine();
185 break;
187 case L_undef:
188 doundef();
189 break;
191 case L_else:
192 if (ifptr == &ifstack[0])
193 goto nest_err;
194 else if ((*ifptr & ELSE_SEEN) != 0)
195 goto else_seen_err;
196 *ifptr |= ELSE_SEEN;
197 if ((*ifptr & WAS_COMPILING) != 0)
199 if (compiling || (*ifptr & TRUE_SEEN) != 0)
200 compiling = FALSE;
201 else
203 compiling = TRUE;
206 break;
208 case L_elif:
209 if (ifptr == &ifstack[0])
210 goto nest_err;
211 else if ((*ifptr & ELSE_SEEN) != 0)
213 else_seen_err: cerror("#%s may not follow #else", token);
214 goto dump_line;
216 if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING)
218 compiling = FALSE; /* Done compiling stuff */
219 goto dump_line; /* Skip this clause */
221 doif(L_if);
222 break;
224 case L_if:
225 case L_ifdef:
226 case L_ifndef:
227 if (++ifptr >= &ifstack[BLK_NEST])
228 if_nest_err: cfatal("Too many nested #%s statements", token);
229 *ifptr = WAS_COMPILING;
230 doif(hash);
231 break;
233 case L_endif:
234 if (ifptr == &ifstack[0])
236 nest_err: cerror("#%s must be in an #if", token);
237 goto dump_line;
239 if (!compiling && (*ifptr & WAS_COMPILING) != 0)
240 wrongline = TRUE;
241 compiling = ((*ifptr & WAS_COMPILING) != 0);
242 --ifptr;
243 break;
245 case L_assert:
246 if (eval() == 0)
247 cerror("Preprocessor assertion failure", NULLST);
248 break;
250 case L_pragma:
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)
257 cput(c);
258 unget();
259 break;
261 #if OSL_DEBUG_LEVEL > 1
262 case L_debug:
263 if (debug == 0)
264 dumpdef("debug set on");
265 debug++;
266 break;
268 case L_nodebug:
269 debug--;
270 break;
271 #endif
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)
275 cput(c);
276 fprintf( pCppOut, "\n" );
277 exit( 1 );
279 default:
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);
287 break;
289 if (hash != L_include)
291 if (skipws() != '\n')
293 cwarn("Unexpected text in #control line ignored", NULLST);
294 skipnl();
297 return counter + 1;
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)
310 int c;
311 int found;
313 if ((c = skipws()) == '\n' || c == EOF_CHAR)
315 unget();
316 goto badif;
318 if (hash == L_if)
320 unget();
321 found = (eval() != 0); /* Evaluate expr, != 0 is TRUE */
322 hash = L_ifdef; /* #if is now like #ifdef */
324 else
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))
332 compiling = TRUE;
333 *ifptr |= TRUE_SEEN;
335 else
337 compiling = FALSE;
339 return;
341 badif: cerror("#if, #ifdef, or #ifndef without an argument", NULLST);
342 skipnl(); /* Prevent an extra */
343 unget(); /* Error message */
344 return;
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.
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()
363 int c;
364 int delim;
366 delim = macroid(skipws());
367 if (delim != '<' && delim != '"')
368 goto incerr;
369 if (delim == '<')
370 delim = '>';
371 workp = work;
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 */
382 if (*workp != delim)
383 goto incerr;
384 #else
385 while ((c = get()) != delim && c != EOF_CHAR)
386 save(c);
387 #endif
388 *workp = EOS; /* Terminate filename */
389 instring = FALSE;
390 if (openinclude(work, (delim == '"')))
391 return;
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);
398 return;
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)
411 char** incptr;
412 char tmpname[NFWORK]; /* Filename work area */
414 if (searchlocal)
417 * Look in local directory first
419 #if HOST == SYS_UNIX
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;
434 else
436 cfatal("Filename work buffer overflow", NULLST);
439 else
441 int len = strlen(filename);
442 if(len < NFWORK)
444 memcpy(tmpname, filename, len);
445 tmpname[len] = 0;
447 else
449 cfatal("Filename work buffer overflow", NULLST);
452 #else
453 if (!hasdirectory(filename, tmpname, NFWORK) &&
454 hasdirectory(infile->filename, tmpname, NFWORK))
456 strcat(tmpname, filename);
458 else
460 strcpy(tmpname, filename);
462 #endif
463 if (openfile(tmpname))
464 return TRUE;
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);
474 else
476 #if HOST == SYS_UNIX
477 if (filename[0] == '/')
478 strcpy(tmpname, filename);
479 else
480 sprintf(tmpname, "%s/%s", *incptr, filename);
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 if (openfile(tmpname))
492 return TRUE;
495 return FALSE;
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)
505 #if HOST == SYS_UNIX
506 char* tp;
508 if ((tp = strrchr(source, '/')) == NULL)
509 return FALSE;
510 else
512 int len = (int)(tp - source);
513 if(len < max)
515 memcpy(result, source, len);
516 result[len] = 0;
518 else
520 cfatal("Filename work buffer overflow", NULLST);
522 return TRUE;
524 #else
526 * Random DEC operating system (RSTS/E)
528 char* tp;
530 (void)max;
532 if ((tp = strrchr(source, ']')) == NULL &&
533 (tp = strrchr(source, ':')) == NULL)
535 return FALSE;
537 else
539 strncpy(result, source, tp - source + 1);
540 result[tp - source + 1] = EOS;
541 return TRUE;
543 #endif
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */