1 /******************************************************************************
2 Copyright (c) 1999 Daniel Stenberg
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 ******************************************************************************/
22 #include <stdio.h>
23 #include <ctype.h>
24
25 #include "cppdef.h"
26 #include "cpp.h"
27
28 #ifdef _AMIGA
29 #include <proto/dos.h>
30 #endif
31
32 FILE_LOCAL void fpp_dump_line(struct Global *, int *);
33 FILE_LOCAL ReturnCode fpp_doif(struct Global *, int);
34 INLINE FILE_LOCAL ReturnCode fpp_doinclude(struct Global *);
35 INLINE FILE_LOCAL int fpp_hasdirectory(char *, char *);
36
37
38 /*
39 * Generate (by hand-inspection) a set of unique values for each control
40 * operator. Note that this is not guaranteed to work for non-Ascii
41 * machines. CPP won't compile if there are hash conflicts.
42 */
43
44 #define L_assert ('a' + ('s' << 1))
45 #define L_define ('d' + ('f' << 1))
46 #define L_elif ('e' + ('i' << 1))
47 #define L_else ('e' + ('s' << 1))
48 #define L_endif ('e' + ('d' << 1))
49 #define L_error ('e' + ('r' << 1))
50 #define L_if ('i' + (EOS << 1))
51 #define L_ifdef ('i' + ('d' << 1))
52 #define L_ifndef ('i' + ('n' << 1))
53 #define L_include ('i' + ('c' << 1))
54 #define L_line ('l' + ('n' << 1))
55 #define L_nogood (EOS + (EOS << 1)) /* To catch #i */
56 #define L_pragma ('p' + ('a' << 1))
57 #define L_undef ('u' + ('d' << 1))
58
fpp_control(struct Global * global,int * counter)59 ReturnCode fpp_control( struct Global *global,
60 int *counter ) /* Pending newline counter */
61 {
62 /*
63 * Process #control lines. Simple commands are processed inline,
64 * while complex commands have their own subroutines.
65 *
66 * The counter is used to force out a newline before #line, and
67 * #pragma commands. This prevents these commands from ending up at
68 * the end of the previous line if cpp is invoked with the -C option.
69 */
70
71 int c;
72 char *tp;
73 int hash;
74 char *ep;
75 ReturnCode ret;
76
77 c = fpp_skipws( global );
78
79 if( c == '\n' || c == EOF_CHAR )
80 {
81 (*counter)++;
82
83 return(FPP_OK);
84 }
85
86 if( !isdigit(c) )
87 fpp_scanid( global, c ); /* Get #word to tokenbuf */
88 else
89 {
90 fpp_unget( global ); /* Hack -- allow #123 as a */
91
92 strcpy( global->tokenbuf, "line" ); /* synonym for #line 123 */
93 }
94
95 hash = (global->tokenbuf[1] == EOS) ? L_nogood : (global->tokenbuf[0] + (global->tokenbuf[2] << 1));
96
97 switch( hash )
98 {
99 case L_assert:
100 tp = "assert";
101 break;
102 case L_define:
103 tp = "define";
104 break;
105 case L_elif:
106 tp = "elif";
107 break;
108 case L_else:
109 tp = "else";
110 break;
111 case L_endif:
112 tp = "endif";
113 break;
114 case L_error:
115 tp = "error";
116 break;
117 case L_if:
118 tp = "if";
119 break;
120 case L_ifdef:
121 tp = "ifdef";
122 break;
123 case L_ifndef:
124 tp = "ifndef";
125 break;
126 case L_include:
127 tp = "include";
128 break;
129 case L_line:
130 tp = "line";
131 break;
132 case L_pragma:
133 tp = "pragma";
134 break;
135 case L_undef:
136 tp = "undef";
137 break;
138 default:
139 hash = L_nogood;
140 case L_nogood:
141 tp = "";
142 break;
143 }
144
145 if( !streq( tp, global->tokenbuf ) )
146 hash = L_nogood;
147
148 /*
149 * hash is set to a unique value corresponding to the
150 * control keyword (or L_nogood if we think it's nonsense).
151 */
152 if( global->infile->fp == NULL )
153 fpp_cwarn( global, WARN_CONTROL_LINE_IN_MACRO, global->tokenbuf );
154
155 if( !compiling )
156 { /* Not compiling now */
157 switch( hash )
158 {
159 case L_if: /* These can't turn */
160 case L_ifdef: /* compilation on, but */
161 case L_ifndef: /* we must nest #if's */
162 if( ++global->ifptr >= &global->ifstack[BLK_NEST] )
163 {
164 fpp_cfatal( global, FATAL_TOO_MANY_NESTINGS, global->tokenbuf );
165
166 return( FPP_TOO_MANY_NESTED_STATEMENTS );
167 }
168
169 *global->ifptr = 0; /* !WAS_COMPILING */
170
171 case L_line: /* Many */
172 /*
173 * Are pragma's always processed?
174 */
175 case L_pragma: /* options */
176 case L_include: /* are uninteresting */
177 case L_define: /* if we */
178 case L_undef: /* aren't */
179 case L_assert: /* compiling. */
180 case L_error:
181 fpp_dump_line( global, counter ); /* Ignore rest of line */
182 return(FPP_OK);
183 }
184 }
185 /*
186 * Make sure that #line and #pragma are output on a fresh line.
187 */
188 if( *counter > 0 && (hash == L_line || hash == L_pragma) )
189 {
190 fpp_Putchar( global, '\n' );
191
192 (*counter)--;
193 }
194
195 switch( hash )
196 {
197 case L_line:
198 /*
199 * Parse the line to update the line number and "progname"
200 * field and line number for the next input line.
201 * Set wrongline to force it out later.
202 */
203 c = fpp_skipws( global );
204
205 global->workp = global->work; /* Save name in work */
206
207 while( c != '\n' && c != EOF_CHAR )
208 {
209 if( (ret = fpp_save( global, c )) )
210 return(ret);
211
212 c = fpp_get( global );
213 }
214
215 fpp_unget( global );
216
217 if( (ret = fpp_save( global, EOS )) )
218 return(ret);
219
220 /*
221 * Split #line argument into <line-number> and <name>
222 * We subtract 1 as we want the number of the next line.
223 */
224 global->line = atoi(global->work) - 1; /* Reset line number */
225
226 for( tp = global->work; isdigit(*tp) || type[(unsigned)*tp] == SPA; tp++)
227 ; /* Skip over digits */
228
229 if( *tp != EOS )
230 {
231 /* Got a filename, so: */
232
233 if( *tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL )
234 {
235 tp++; /* Skip over left quote */
236
237 *ep = EOS; /* And ignore right one */
238 }
239
240 if( global->infile->progname != NULL )
241 /* Give up the old name if it's allocated. */
242 free( global->infile->progname );
243
244 global->infile->progname = fpp_savestring( global, tp );
245 }
246
247 global->wrongline = FPP_TRUE; /* Force output later */
248 break;
249
250 case L_include:
251 ret = fpp_doinclude( global );
252 if( ret )
253 return(ret);
254 break;
255
256 case L_define:
257 ret = fpp_dodefine( global );
258 if( ret )
259 return(ret);
260 break;
261
262 case L_undef:
263 fpp_doundef( global );
264 break;
265
266 case L_else:
267 if( global->ifptr == &global->ifstack[0] )
268 {
269 fpp_cerror( global, ERROR_STRING_MUST_BE_IF, global->tokenbuf );
270
271 fpp_dump_line( global, counter );
272
273 return( FPP_OK );
274 }
275 else if( (*global->ifptr & ELSE_SEEN) != 0 )
276 {
277 fpp_cerror( global, ERROR_STRING_MAY_NOT_FOLLOW_ELSE, global->tokenbuf );
278
279 fpp_dump_line( global, counter );
280
281 return( FPP_OK );
282 }
283
284 *global->ifptr |= ELSE_SEEN;
285
286 if( (*global->ifptr & WAS_COMPILING) != 0 )
287 {
288 if( compiling || (*global->ifptr & FPP_TRUE_SEEN) != 0 )
289 compiling = FPP_FALSE;
290 else
291 {
292 compiling = FPP_TRUE;
293 }
294 }
295 break;
296
297 case L_elif:
298 if( global->ifptr == &global->ifstack[0] )
299 {
300 fpp_cerror( global, ERROR_STRING_MUST_BE_IF, global->tokenbuf );
301
302 fpp_dump_line( global, counter );
303
304 return( FPP_OK );
305 }
306 else if( (*global->ifptr & ELSE_SEEN) != 0 )
307 {
308 fpp_cerror( global, ERROR_STRING_MAY_NOT_FOLLOW_ELSE, global->tokenbuf );
309
310 fpp_dump_line( global, counter );
311
312 return( FPP_OK );
313 }
314
315 if( (*global->ifptr & (WAS_COMPILING | FPP_TRUE_SEEN)) != WAS_COMPILING )
316 {
317 compiling = FPP_FALSE; /* Done compiling stuff */
318
319 fpp_dump_line( global, counter ); /* Skip this clause */
320
321 return( FPP_OK );
322 }
323
324 ret = fpp_doif( global, L_if );
325
326 if( ret )
327 return(ret);
328
329 break;
330
331 case L_error:
332 fpp_cerror(global, ERROR_ERROR);
333 break;
334
335 case L_if:
336 case L_ifdef:
337 case L_ifndef:
338 if( ++global->ifptr < &global->ifstack[BLK_NEST] )
339 {
340 *global->ifptr = WAS_COMPILING;
341
342 ret = fpp_doif( global, hash );
343
344 if( ret )
345 return(ret);
346
347 break;
348 }
349
350 fpp_cfatal( global, FATAL_TOO_MANY_NESTINGS, global->tokenbuf );
351
352 return( FPP_TOO_MANY_NESTED_STATEMENTS );
353
354 case L_endif:
355 if( global->ifptr == &global->ifstack[0] )
356 {
357 fpp_cerror( global, ERROR_STRING_MUST_BE_IF, global->tokenbuf );
358
359 fpp_dump_line( global, counter );
360
361 return(FPP_OK);
362 }
363
364 if( !compiling && (*global->ifptr & WAS_COMPILING) != 0 )
365 global->wrongline = FPP_TRUE;
366
367 compiling = ((*global->ifptr & WAS_COMPILING) != 0);
368
369 --global->ifptr;
370
371 break;
372
373 case L_assert:
374 {
375 int result;
376
377 ret = fpp_eval( global, &result );
378
379 if(ret)
380 return(ret);
381
382 if( result == 0 )
383 fpp_cerror( global, ERROR_PREPROC_FAILURE );
384 }
385 break;
386
387 case L_pragma:
388 /*
389 * #pragma is provided to pass "options" to later
390 * passes of the compiler. cpp doesn't have any yet.
391 */
392 fpp_Putstring( global, "#pragma " );
393
394 while( (c = fpp_get( global ) ) != '\n' && c != EOF_CHAR )
395 fpp_Putchar( global, c );
396
397 fpp_unget( global );
398
399 fpp_Putchar( global, '\n' );
400
401 break;
402
403 default:
404 /*
405 * Undefined #control keyword.
406 * Note: the correct behavior may be to warn and
407 * pass the line to a subsequent compiler pass.
408 * This would allow #asm or similar extensions.
409 */
410 if( global->warnillegalcpp )
411 fpp_cwarn( global, WARN_ILLEGAL_COMMAND, global->tokenbuf );
412
413 fpp_Putchar( global, '#' );
414 fpp_Putstring( global, global->tokenbuf );
415 fpp_Putchar( global, ' ' );
416
417 while( (c = fpp_get( global ) ) != '\n' && c != EOF_CHAR )
418 fpp_Putchar( global, c );
419
420 fpp_unget( global );
421
422 fpp_Putchar( global, '\n' );
423
424 break;
425 }
426
427 if( hash != L_include )
428 {
429 #if OLD_PREPROCESSOR
430 /*
431 * Ignore the rest of the #control line so you can write
432 * #if foo
433 * #endif foo
434 */
435 fpp_dump_line( global, counter ); /* Take common exit */
436
437 return( FPP_OK );
438 #else
439 if( fpp_skipws( global ) != '\n' )
440 {
441 fpp_cwarn( global, WARN_UNEXPECTED_TEXT_IGNORED );
442
443 fpp_skipnl( global );
444 }
445 #endif
446 }
447
448 (*counter)++;
449
450 return( FPP_OK );
451 }
452
453 FILE_LOCAL
fpp_dump_line(struct Global * global,int * counter)454 void fpp_dump_line(struct Global *global, int *counter)
455 {
456 fpp_skipnl( global ); /* Ignore rest of line */
457
458 (*counter)++;
459 }
460
461 FILE_LOCAL
fpp_doif(struct Global * global,int hash)462 ReturnCode fpp_doif(struct Global *global, int hash)
463 {
464 /*
465 * Process an #if, #ifdef, or #ifndef. The latter two are straightforward,
466 * while #if needs a subroutine of its own to evaluate the expression.
467 *
468 * fpp_doif() is called only if compiling is FPP_TRUE. If false, compilation
469 * is always supressed, so we don't need to evaluate anything. This
470 * supresses unnecessary warnings.
471 */
472
473 int c;
474 int found;
475 ReturnCode ret;
476
477 if( (c = fpp_skipws( global ) ) == '\n' || c == EOF_CHAR )
478 {
479 fpp_unget( global );
480
481 fpp_cerror( global, ERROR_MISSING_ARGUMENT );
482
483 #if !OLD_PREPROCESSOR
484 fpp_skipnl( global ); /* Prevent an extra */
485
486 fpp_unget( global ); /* Error message */
487 #endif
488
489 return(FPP_OK);
490 }
491
492 if( hash == L_if )
493 {
494 fpp_unget( global );
495
496 ret = fpp_eval( global, &found );
497
498 if( ret )
499 return( ret );
500
501 found = (found != 0); /* Evaluate expr, != 0 is FPP_TRUE */
502
503 hash = L_ifdef; /* #if is now like #ifdef */
504 }
505 else
506 {
507 if( type[c] != LET )
508 { /* Next non-blank isn't letter */
509 /* ... is an error */
510 fpp_cerror( global, ERROR_MISSING_ARGUMENT );
511
512 #if !OLD_PREPROCESSOR
513 fpp_skipnl( global ); /* Prevent an extra */
514
515 fpp_unget( global ); /* Error message */
516 #endif
517
518 return(FPP_OK);
519 }
520
521 found = ( fpp_lookid( global, c ) != NULL ); /* Look for it in symbol table */
522 }
523
524 if( found == (hash == L_ifdef) )
525 {
526 compiling = FPP_TRUE;
527
528 *global->ifptr |= FPP_TRUE_SEEN;
529 }
530 else
531 compiling = FPP_FALSE;
532
533 return(FPP_OK);
534 }
535
536 INLINE FILE_LOCAL
fpp_doinclude(struct Global * global)537 ReturnCode fpp_doinclude( struct Global *global )
538 {
539 /*
540 * Process the #include control line.
541 * There are three variations:
542 *
543 * #include "file" search somewhere relative to the
544 * current source file, if not found,
545 * treat as #include <file>.
546 *
547 * #include <file> Search in an implementation-dependent
548 * list of places.
549 *
550 * #include token Expand the token, it must be one of
551 * "file" or <file>, process as such.
552 *
553 * Note: the November 12 draft forbids '>' in the #include <file> format.
554 * This restriction is unnecessary and not implemented.
555 */
556
557 int c;
558 int delim;
559 ReturnCode ret;
560
561 delim = fpp_skipws( global );
562
563 if( (ret = fpp_macroid( global, &delim )) )
564 return(ret);
565
566 if( delim != '<' && delim != '"' )
567 {
568 fpp_cerror( global, ERROR_INCLUDE_SYNTAX );
569
570 return( FPP_OK );
571 }
572
573 if( delim == '<' )
574 delim = '>';
575
576 global->workp = global->work;
577
578 while( (c = fpp_get(global)) != '\n' && c != EOF_CHAR )
579 if( (ret = fpp_save( global, c )) ) /* Put it away. */
580 return( ret );
581
582 fpp_unget( global ); /* Force nl after include */
583
584 /*
585 * The draft is unclear if the following should be done.
586 */
587 while( --global->workp >= global->work &&
588 (*global->workp == ' ' || *global->workp == '\t') )
589 ; /* Trim blanks from filename */
590
591 if( *global->workp != delim )
592 {
593 fpp_cerror( global, ERROR_INCLUDE_SYNTAX );
594
595 return(FPP_OK);
596 }
597
598 *global->workp = EOS; /* Terminate filename */
599
600 ret = fpp_openinclude( global, global->work, (delim == '"') );
601
602 if( ret && global->warnnoinclude )
603 {
604 /*
605 * Warn if #include file isn't there.
606 */
607 fpp_cwarn( global, WARN_CANNOT_OPEN_INCLUDE, global->work );
608 }
609
610 return( FPP_OK );
611 }
612
613 #ifdef _AMIGA
614 ReturnCode MultiAssignLoad( struct Global *global, char *incptr, char *filename, char *tmpname );
615 #endif
616
fpp_openinclude(struct Global * global,char * filename,int searchlocal)617 ReturnCode fpp_openinclude( struct Global *global,
618 char *filename, /* Input file name */
619 int searchlocal ) /* FPP_TRUE if #include "file" */
620 {
621 /*
622 * Actually open an include file. This routine is only called from
623 * fpp_doinclude() above, but was written as a separate subroutine for
624 * programmer convenience. It searches the list of directories
625 * and actually opens the file, linking it into the list of
626 * active files. Returns ReturnCode. No error message is printed.
627 */
628
629 char **incptr;
630 char tmpname[NWORK]; /* Filename work area */
631 size_t len;
632
633 if( filename[0] == '/' )
634 {
635 if( ! fpp_openfile( global, filename ) )
636 return(FPP_OK);
637 }
638
639 if( searchlocal && global->allowincludelocal )
640 {
641 /*
642 * Look in local directory first.
643 * Try to open filename relative to the directory of the current
644 * source file (as opposed to the current directory). (ARF, SCK).
645 * Note that the fully qualified pathname is always built by
646 * discarding the last pathname component of the source file
647 * name then tacking on the #include argument.
648 */
649 if( fpp_hasdirectory( global->infile->filename, tmpname ) )
650 strcat( tmpname, filename );
651 else
652 strcpy( tmpname, filename );
653
654 if( ! fpp_openfile( global, tmpname ) )
655 return(FPP_OK);
656 }
657
658 /*
659 * Look in any directories specified by -I command line
660 * arguments, then in the builtin search list.
661 */
662 for( incptr = global->incdir; incptr < global->incend; incptr++ )
663 {
664 len = strlen(*incptr);
665
666 if( len + strlen(filename) >= sizeof(tmpname) )
667 {
668 fpp_cfatal( global, FATAL_FILENAME_BUFFER_OVERFLOW );
669
670 return( FPP_FILENAME_BUFFER_OVERFLOW );
671 }
672 else
673 {
674 if( (*incptr)[len-1] != '/' )
675 sprintf( tmpname, "%s/%s", *incptr, filename );
676 else
677 sprintf( tmpname, "%s%s", *incptr, filename );
678
679 if( !fpp_openfile( global, tmpname ) )
680 return(FPP_OK);
681 }
682 }
683
684 return( FPP_NO_INCLUDE );
685 }
686
687 INLINE FILE_LOCAL
fpp_hasdirectory(char * source,char * result)688 int fpp_hasdirectory( char *source, /* Directory to examine */
689 char *result ) /* Put directory stuff here */
690 {
691 /*
692 * If a device or directory is found in the source filename string, the
693 * node/device/directory part of the string is copied to result and
694 * fpp_hasdirectory returns FPP_TRUE. Else, nothing is copied and it returns FPP_FALSE.
695 */
696
697 char *tp2;
698
699 if( (tp2 = strrchr( source, '/' ) ) == NULL )
700 return(FPP_FALSE);
701
702 strncpy( result, source, tp2 - source + 1 );
703
704 result[tp2 - source + 1] = EOS;
705
706 return( FPP_TRUE );
707 }
708
709 #ifdef _AMIGA
710 //
711 // amp July 9, 1997
712 //
713 // Use the OS Luke...
714 //
715 // We do the sneaky version and let the OS do all
716 // the hard work so we don't have to mess around
717 // a lot ;)
718 //
MultiAssignLoad(struct Global * global,char * incptr,char * filename,char * tmpname)719 ReturnCode MultiAssignLoad( struct Global *global, char *incptr, char *filename, char *tmpname )
720
721 { /* MultiAssignLoad */
722
723 struct MsgPort *FSTask;
724 struct DevProc *DevProc = NULL;
725 LONG RtnCode = FPP_NO_INCLUDE;
726
727 FSTask = GetFileSysTask();
728
729 do
730 {
731 //
732 // This should not bring up a requester.
733 // check to see if cpp does in fact tweek
734 // the process WindowPtr.
735 //
736 DevProc = GetDeviceProc( incptr, DevProc );
737
738 if( DevProc )
739 {
740 SetFileSysTask( DevProc->dvp_Port );
741
742 //
743 // Normally we would pass the lock and filename
744 // to the Load() routine, which would CD to the
745 // directory and Open(filename), but in order to
746 // satisfy the exisiting fpp_openfile() function, we
747 // bite the bullet and build the complete pathspec
748 // rather than add the standard Load() routine.
749 //
750 if( NameFromLock( DevProc->dvp_Lock, tmpname, NWORK ) )
751 {
752 AddPart( tmpname, filename, NWORK );
753
754 RtnCode = fpp_openfile( global, tmpname );
755
756 if( ! RtnCode )
757 break;
758 }
759 }
760
761 } while ( RtnCode &&
762 DevProc &&
763 (DevProc->dvp_Flags & DVPF_ASSIGN) &&
764 IoErr() == ERROR_OBJECT_NOT_FOUND); /* repeat if multi-assign */
765
766 SetFileSysTask( FSTask );
767
768 if( DevProc )
769 FreeDeviceProc( DevProc );
770
771 return RtnCode;
772
773 } /* MultiAssignLoad */
774 #endif //_AMIGA
775