Subversion Repositories tendra.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 7u83 1
/*
2
    		 Crown Copyright (c) 1997
3
 
4
    This TenDRA(r) Computer Program is subject to Copyright
5
    owned by the United Kingdom Secretary of State for Defence
6
    acting through the Defence Evaluation and Research Agency
7
    (DERA).  It is made available to Recipients with a
8
    royalty-free licence for its use, reproduction, transfer
9
    to other parties and amendment for any purpose not excluding
10
    product development provided that any such use et cetera
11
    shall be deemed to be acceptance of the following conditions:-
12
 
13
        (1) Its Recipients shall ensure that this Notice is
14
        reproduced upon any copies or amended versions of it;
15
 
16
        (2) Any amended version of it shall be clearly marked to
17
        show both the nature of and the organisation responsible
18
        for the relevant amendment or amendments;
19
 
20
        (3) Its onward transfer from a recipient to another
21
        party shall be deemed to be that party's acceptance of
22
        these conditions;
23
 
24
        (4) DERA gives no warranty or assurance as to its
25
        quality or suitability for any purpose and DERA accepts
26
        no liability whatsoever in relation to any use to which
27
        it may be put.
28
*/
29
 
30
 
31
#include "config.h"
32
#include "system.h"
33
#include "c_types.h"
34
#include "loc_ext.h"
35
#include "error.h"
36
#include "catalog.h"
37
#include "option.h"
38
#include "file.h"
39
#include "buffer.h"
40
#include "char.h"
41
#include "dump.h"
42
#include "lex.h"
43
#include "preproc.h"
44
#include "syntax.h"
45
#include "token.h"
46
#include "ustring.h"
47
#include "xalloc.h"
48
 
49
 
50
/*
51
    INPUT AND OUTPUT FILES
52
 
53
    These are the files from which the lexical routines read their input
54
    and to which the output routines write.
55
*/
56
 
57
FILE *input_file = NULL ;
58
FILE *output_file [ OUTPUT_FILES ] = { NULL, NULL, NULL, NULL, NULL } ;
59
 
60
 
61
/*
62
    CURRENT FILE NAMES
63
 
64
    The variable input_name is used to hold the name of the current input
65
    file name.  This remains constant and is not, for example, changed by
66
    #line directives.  Similarly output_name holds the names of the output
67
    files.
68
*/
69
 
70
string input_name = NULL ;
71
string output_name [ OUTPUT_FILES ] = { NULL, NULL, NULL, NULL, NULL } ;
72
 
73
 
74
/*
75
    STANDARD FILE NAME
76
 
77
    The macro std_file_name checks whether the file name A is the special
78
    string "-" used to indicate the standard input or output.  The other
79
    macros give the corresponding names used in error reports.
80
*/
81
 
82
#define std_file_name( A )	ustrseq ( ( A ), "-" )
83
#define stdin_name		ustrlit ( "<stdin>" )
84
#define stdout_name		ustrlit ( "<stdout>" )
85
 
86
 
87
/*
88
    INTERNAL FILE BUFFER
89
 
90
    This buffer is used as the internal file buffer which is used to hold
91
    any preprocessing directives arising from command-line options.  It
92
    has an associated dummy file and file name.
93
*/
94
 
95
BUFFER internal_buff = NULL_buff ;
96
static FILE *internal_file = NULL ;
97
static string internal_name = NULL ;
98
 
99
 
100
/*
101
    INCLUDE FILE BUFFER
102
 
103
    This buffer is used to build up file names when processing #include
104
    directives.
105
*/
106
 
107
BUFFER incl_buff = NULL_buff ;
108
 
109
 
110
/*
111
    INPUT FLAGS
112
 
113
    These flags are used to record changes in the current file location.
114
*/
115
 
116
unsigned long crt_spaces = 0 ;
117
unsigned long tab_width = 8 ;
118
int crt_line_changed = 0 ;
119
int crt_file_changed = 0 ;
120
int crt_col_changed = 0 ;
121
int crt_file_type = 0 ;
122
int bad_crt_loc = 0 ;
123
 
124
 
125
/*
126
    INPUT BUFFERS
127
 
128
    These variables describe the buffers used in reading the input file.
129
    Each buffer consists of an array of characters, preceded by space
130
    for the storage of unread characters, and followed by an overflow
131
    area for repeated end of file markers.  The position within the
132
    buffer is indicated by the pointer input_posn.  Other pointers give
133
    the end of the current buffer and the end of the last buffer.
134
    Characters beyond the end of the buffer hold char_end.
135
*/
136
 
137
#define NO_BUFFER		4
138
#define CHAR_SZ			sizeof ( character )
139
#define BUFF_SZ			( ( size_t ) ( BUFSIZ ) )
140
#define PENDING_SZ		16
141
#define OVERFLOW_SZ		16
142
#define TOTAL_SZ		( PENDING_SZ + BUFF_SZ + OVERFLOW_SZ )
143
 
144
typedef struct {
145
    string buff ;
146
    string posn ;
147
    string end ;
148
    string eof ;
149
    long bytes ;
150
} INPUT_BUFFER ;
151
 
152
static INPUT_BUFFER input_buff [ NO_BUFFER ] = {
153
    { NULL, NULL, NULL, NULL, 0 },
154
    { NULL, NULL, NULL, NULL, 0 },
155
    { NULL, NULL, NULL, NULL, 0 },
156
    { NULL, NULL, NULL, NULL, 0 }
157
} ;
158
 
159
string input_start = NULL ;
160
string input_posn = NULL ;
161
string input_end = NULL ;
162
string input_eof = NULL ;
163
string input_crt = NULL ;
164
static long input_bytes = 0 ;
165
static int input_special = 0 ;
166
static int started_buff = 0 ;
167
unsigned long crt_buff_no = 0 ;
168
 
169
 
170
/*
171
    IS A FILE NAME A FULL PATHNAME?
172
 
173
    This routine checks whether the file name nm represents a full
174
    pathname.
175
*/
176
 
177
int is_full_pathname
178
    PROTO_N ( ( nm ) )
179
    PROTO_T ( string nm )
180
{
181
    character c = nm [0] ;
182
    character q = ( character ) drive_sep ;
183
    if ( c == char_slash ) return ( 1 ) ;
184
    if ( c && q && nm [1] == q ) {
185
	/* Allow for DOS drive letters */
186
	return ( is_alpha_char ( ( unsigned long ) c ) ) ;
187
    }
188
    return ( 0 ) ;
189
}
190
 
191
 
192
/*
193
    CONVERT A FILE NAME TO STANDARD FORMAT
194
 
195
    This routine modifies the file name nm to the standard format with
196
    sensible forward slashes rather backslashes.
197
*/
198
 
199
string make_pathname
200
    PROTO_N ( ( nm ) )
201
    PROTO_T ( string nm )
202
{
203
    character q = ( character ) file_sep ;
204
    if ( q != char_slash ) {
205
	string s ;
206
	nm = xustrcpy ( nm ) ;
207
	for ( s = nm ; *s ; s++ ) {
208
	    if ( *s == q ) *s = char_slash ;
209
	}
210
    }
211
    return ( nm ) ;
212
}
213
 
214
 
215
/*
216
    NORMALISE A FILE NAME
217
 
218
    This routine normalises the file name s by removing any . or ..
219
    components.  The result is only used in the printing of error
220
    messages so it doesn't matter too much if the result isn't quite
221
    right.
222
*/
223
 
224
static string normalise_pathname
225
    PROTO_N ( ( s ) )
226
    PROTO_T ( string s )
227
{
228
    character c ;
229
    string p = s ;
230
    int depth = 0 ;
231
    int changed = 0 ;
232
    BUFFER *bf = clear_buffer ( &incl_buff, NIL ( FILE ) ) ;
233
    while ( c = *( p++ ), c != 0 ) {
234
	if ( c == char_slash ) {
235
	    if ( p [0] == char_dot ) {
236
		if ( p [1] == char_slash ) {
237
		    /* Have '/./' */
238
		    p++ ;
239
		    changed = 1 ;
240
		} else if ( p [1] == char_dot && p [2] == char_slash ) {
241
		    /* Have '/../' */
242
		    string q = bf->posn ;
243
		    if ( depth > 0 ) {
244
			string q0 = bf->start ;
245
			*q = 0 ;
246
			q = ustrrchr ( q0, char_slash ) ;
247
			if ( q && q != q0 ) {
248
			    bf->posn = q ;
249
			    p += 2 ;
250
			    changed = 1 ;
251
			    c = 0 ;
252
			}
253
		    }
254
		    if ( c ) bfputc ( bf, ( int ) c ) ;
255
		    depth-- ;
256
		} else {
257
		    bfputc ( bf, ( int ) c ) ;
258
		    depth++ ;
259
		}
260
	    } else if ( p [0] == char_slash ) {
261
		/* Have '//' */
262
		p++ ;
263
		changed = 1 ;
264
	    } else {
265
		bfputc ( bf, ( int ) c ) ;
266
		depth++ ;
267
	    }
268
	} else {
269
	    bfputc ( bf, ( int ) c ) ;
270
	}
271
    }
272
    bfputc ( bf, 0 ) ;
273
    if ( changed ) s = xustrcpy ( bf->start ) ;
274
    return ( s ) ;
275
}
276
 
277
 
278
/*
279
    SET CURRENT LOCATION
280
 
281
    This routine sets the current location from the filename nm.  It returns
282
    the result of applying make_pathname to nm.
283
*/
284
 
285
string set_crt_loc
286
    PROTO_N ( ( nm, special ) )
287
    PROTO_T ( string nm X int special )
288
{
289
    string en ;
290
    unsigned long date ;
291
    if ( special ) {
292
	/* Standard input */
293
	en = ustrlit ( "" ) ;
294
	date = 0 ;
295
    } else {
296
	/* Simple file */
297
	STAT_TYPE *fs ;
298
	STAT_TYPE fstr ;
299
	nm = make_pathname ( nm ) ;
300
	en = nm ;
301
	fs = stat_func ( strlit ( nm ), &fstr ) ;
302
	date = stat_date ( fs ) ;
303
    }
304
    CREATE_loc ( nm, en, nm, NULL, NULL_ptr ( LOCATION ), date, crt_loc ) ;
305
    return ( nm ) ;
306
}
307
 
308
 
309
/*
310
    OPEN INPUT FILE
311
 
312
    This routine opens the input file, using input_name as the name of the
313
    file to be opened.  If this is the null string then the standard input
314
    is used.  The file is opened in binary mode if bin is true.  The routine
315
    also allocates space for each of the buffers above.  The routine returns
316
    1 if the file is opened successfully.
317
*/
318
 
319
int open_input
320
    PROTO_N ( ( bin ) )
321
    PROTO_T ( int bin )
322
{
323
    if ( input_file == NULL ) {
324
	string nm = input_name ;
325
	if ( nm == NULL || std_file_name ( nm ) ) {
326
	    nm = set_crt_loc ( stdin_name, 1 ) ;
327
	    input_special = 1 ;
328
	    if ( !bin ) input_file = stdin ;
329
	} else {
330
	    CONST char *mode = ( bin ? "rb" : "r" ) ;
331
	    nm = set_crt_loc ( nm, 0 ) ;
332
	    input_special = 0 ;
333
	    input_file = fopen ( strlit ( nm ), mode ) ;
334
	}
335
	input_name = nm ;
336
	if ( input_file == NULL ) return ( 0 ) ;
337
	if ( !started_buff ) {
338
	    unsigned i ;
339
	    for ( i = 0 ; i < NO_BUFFER ; i++ ) {
340
		string buff = xmalloc_nof ( character, TOTAL_SZ ) ;
341
		input_buff [i].buff = buff ;
342
	    }
343
	    started_buff = 1 ;
344
	}
345
	crt_file_changed = 1 ;
346
	crt_line_changed = 1 ;
347
	crt_spaces = 0 ;
348
    }
349
    return ( 1 ) ;
350
}
351
 
352
 
353
/*
354
    FREE SPACE ALLOCATED FOR INPUT BUFFERS
355
 
356
    This routine frees the memory allocated for the input buffers.
357
*/
358
 
359
void term_input
360
    PROTO_Z ()
361
{
362
    free_buffer ( &incl_buff ) ;
363
    free_buffer ( &internal_buff ) ;
364
    if ( started_buff ) {
365
	unsigned i ;
366
	for ( i = 0 ; i < NO_BUFFER ; i++ ) {
367
	    xfree_nof ( input_buff [i].buff ) ;
368
	    input_buff [i].buff = NULL ;
369
	}
370
	started_buff = 0 ;
371
    }
372
    input_start = NULL ;
373
    input_bytes = 0 ;
374
    return ;
375
}
376
 
377
 
378
/*
379
    OPEN OUTPUT FILE
380
 
381
    This routine opens the nth output file, using output_name as the name
382
    of the file to be opened.  If this is the null string then the standard
383
    output is used.  The file is opened in binary mode if bin is true.  The
384
    routine returns 1 if the file is opened successfully.
385
*/
386
 
387
int open_output
388
    PROTO_N ( ( n, bin ) )
389
    PROTO_T ( int n X int bin )
390
{
391
    if ( output_file [n] == NULL ) {
392
	string nm = output_name [n] ;
393
	if ( nm == NULL || std_file_name ( nm ) ) {
394
	    nm = stdout_name ;
395
	    output_name [n] = nm ;
396
	    output_file [n] = stdout ;
397
	    if ( bin ) return ( 0 ) ;
398
	} else {
399
	    CONST char *mode = ( bin ? "wb" : "w" ) ;
400
	    nm = make_pathname ( nm ) ;
401
	    output_name [n] = nm ;
402
	    output_file [n] = fopen ( strlit ( nm ), mode ) ;
403
	    if ( output_file [n] == NULL ) return ( 0 ) ;
404
	}
405
    }
406
    return ( 1 ) ;
407
}
408
 
409
 
410
/*
411
    CLOSE INPUT FILE
412
 
413
    This routine closes the input file.
414
*/
415
 
416
void close_input
417
    PROTO_Z ()
418
{
419
    FILE *fin = input_file ;
420
    if ( fin && !input_special ) {
421
	if ( ferror ( fin ) || fclose ( fin ) ) {
422
	    char *nm = strlit ( input_name ) ;
423
	    error ( ERROR_INTERNAL, "Reading error in '%s'", nm ) ;
424
	}
425
    }
426
    input_file = NULL ;
427
    return ;
428
}
429
 
430
 
431
/*
432
    CLOSE OUTPUT FILE
433
 
434
    This routine closes the nth output file.
435
*/
436
 
437
void close_output
438
    PROTO_N ( ( n ) )
439
    PROTO_T ( int n )
440
{
441
    FILE *fout = output_file [n] ;
442
    if ( fout && fout != stdout && fout != stderr ) {
443
	if ( ferror ( fout ) || fclose ( fout ) ) {
444
	    char *nm = strlit ( output_name [n] ) ;
445
	    error ( ERROR_INTERNAL, "Writing error in '%s'", nm ) ;
446
	}
447
    }
448
    output_file [n] = NULL ;
449
    return ;
450
}
451
 
452
 
453
/*
454
    FILL THE INPUT BUFFER
455
 
456
    This routine fills the current input buffer from the input file, setting
457
    up the associated buffer pointers.  It returns NULL to indicate that no
458
    bytes were read.
459
*/
460
 
461
static string fill_buffer
462
    PROTO_Z ()
463
{
464
    size_t i, n ;
465
    size_t m = TOTAL_SZ ;
466
    FILE *f = input_file ;
467
    string p = input_start ;
468
 
469
    /* Fill the buffer from the input file */
470
    if ( f == internal_file ) {
471
	n = ( size_t ) bfread ( &internal_buff, p, ( gen_size ) BUFF_SZ ) ;
472
	if ( n < BUFF_SZ ) m = n ;
473
    } else if ( f ) {
474
	n = fread ( ( gen_ptr ) p, CHAR_SZ, BUFF_SZ, f ) ;
475
	if ( n < BUFF_SZ ) m = n ;
476
    } else {
477
	n = 0 ;
478
	m = 0 ;
479
    }
480
    input_posn = p ;
481
    input_end = p + n ;
482
    input_eof = p + m ;
483
    input_crt = p ;
484
    input_bytes += ( long ) n ;
485
 
486
    /* Fill the overflow area with char_end's */
487
    for ( i = n ; i < n + OVERFLOW_SZ ; i++ ) p [i] = char_end ;
488
    if ( n == 0 ) p = NULL ;
489
    return ( p ) ;
490
}
491
 
492
 
493
/*
494
    INITIALISE BUFFER
495
 
496
    This routine initialises buffer number i and makes it into the current
497
    buffer.  It returns NULL to indicate an empty file.
498
*/
499
 
500
string init_buffer
501
    PROTO_N ( ( i ) )
502
    PROTO_T ( unsigned long i )
503
{
504
    crt_buff_no = i ;
505
    input_start = input_buff [i].buff + PENDING_SZ ;
506
    input_bytes = 0 ;
507
    return ( fill_buffer () ) ;
508
}
509
 
510
 
511
/*
512
    RESUME BUFFER
513
 
514
    This routine makes buffer number i into the current buffer by restoring
515
    the main values from those stored in the buffer.
516
*/
517
 
518
static void resume_buffer
519
    PROTO_N ( ( i ) )
520
    PROTO_T ( unsigned long i )
521
{
522
    INPUT_BUFFER *p = input_buff + i ;
523
    input_start = p->buff + PENDING_SZ ;
524
    input_posn = p->posn ;
525
    input_end = p->end ;
526
    input_eof = p->eof ;
527
    input_crt = p->posn ;
528
    input_bytes = p->bytes ;
529
    return ;
530
}
531
 
532
 
533
/*
534
    FIND CURRENT FILE POSITION
535
 
536
    This routine finds the current file position and updates the pointers
537
    in buffer i (which should be the current buffer number).
538
*/
539
 
540
long tell_buffer
541
    PROTO_N ( ( i ) )
542
    PROTO_T ( unsigned long i )
543
{
544
    long bytes_left ;
545
    INPUT_BUFFER *p = input_buff + i ;
546
    p->posn = input_posn ;
547
    p->end = input_end ;
548
    p->eof = input_eof ;
549
    p->bytes = input_bytes ;
550
    bytes_left = ( long ) ( input_end - input_posn ) ;
551
    if ( bytes_left < 0 ) bytes_left = 0 ;
552
    return ( input_bytes - bytes_left ) ;
553
}
554
 
555
 
556
/*
557
    SET CURRENT FILE POSITION
558
 
559
    This routine sets the current file position to n and the current buffer
560
    to number i.  started is false if the file has just been opened.
561
*/
562
 
563
void seek_buffer
564
    PROTO_N ( ( i, n, started ) )
565
    PROTO_T ( unsigned long i X long n X int started )
566
{
567
    int s ;
568
    FILE *f = input_file ;
569
    if ( f == NULL ) return ;
570
    if ( f == internal_file ) {
571
	if ( started ) {
572
	    /* Reset position to start of buffer */
573
	    internal_buff.posn = internal_buff.start ;
574
	    started = 0 ;
575
	}
576
	s = 0 ;
577
    } else {
578
	s = file_seek ( f, n ) ;
579
    }
580
    if ( s == 0 ) {
581
	/* Perform seek by hand */
582
	string p ;
583
	if ( started ) {
584
	    /* Rewind to start of file */
585
	    IGNORE file_seek ( f, ( long ) 0 ) ;
586
	}
587
	p = init_buffer ( i ) ;
588
	while ( input_bytes < n ) {
589
	    if ( p == NULL ) {
590
		char *nm = strlit ( input_name ) ;
591
		CONST char *msg = "Internal seek error in '%s'" ;
592
		error ( ERROR_INTERNAL, msg, nm ) ;
593
		return ;
594
	    }
595
	    p = fill_buffer () ;
596
	}
597
	input_posn = input_end - ( input_bytes - n ) ;
598
	input_crt = input_posn ;
599
    } else {
600
	if ( s == -1 ) {
601
	    char *nm = strlit ( input_name ) ;
602
	    CONST char *msg = "Internal seek error in '%s'" ;
603
	    error ( ERROR_INTERNAL, msg, nm ) ;
604
	}
605
	input_start = input_buff [i].buff + PENDING_SZ ;
606
	input_bytes = n ;
607
	IGNORE fill_buffer () ;
608
    }
609
    return ;
610
}
611
 
612
 
613
/*
614
    UPDATE THE CURRENT COLUMN POSITION
615
 
616
    The current column position is only updated at convenient junctures.
617
    The variable input_crt is used to keep track of the last such location.
618
*/
619
 
620
void update_column
621
    PROTO_Z ()
622
{
623
    string p = input_posn ;
624
    if ( p ) {
625
	unsigned long n = ( unsigned long ) ( p - input_crt ) ;
626
	if ( n ) {
627
	    crt_loc.column += n ;
628
	    input_crt = p ;
629
	}
630
    }
631
    return ;
632
}
633
 
634
 
635
/*
636
    REFILL THE INPUT BUFFER
637
 
638
    This routine refills the input buffer, returning the first character.
639
    It is called whenever the next character in the buffer is char_end.
640
    It is possible that the character is really char_end, in which case
641
    this is returned.  Otherwise the buffer is refilled.  Note that in order
642
    for unread_char to work correctly with char_eof, ( char_eof & 0xff )
643
    must equal char_end.
644
*/
645
 
646
int refill_char
647
    PROTO_Z ()
648
{
649
    int c ;
650
    update_column () ;
651
    do {
652
	string p = input_posn ;
653
	if ( p <= input_end ) return ( char_end ) ;
654
	if ( p > input_eof ) return ( char_eof ) ;
655
	crt_loc.column += ( unsigned long ) ( p - input_crt ) ;
656
	IGNORE fill_buffer () ;
657
	c = next_char () ;
658
    } while ( c == char_end ) ;
659
    input_crt = input_posn ;
660
    return ( c ) ;
661
}
662
 
663
 
664
/*
665
    INCLUDE FILE SEARCH PATH
666
 
667
    The variable dir_path gives the list of directories searched for
668
    #include'd files.  The variable crt_dir_path is set after each #include
669
    directive to the position in the path after that at which the included
670
    file was found.
671
*/
672
 
673
INCL_DIR *dir_path = NULL ;
674
static INCL_DIR *crt_dir_path = NULL ;
675
static INCL_DIR *crt_found_path = NULL ;
676
 
677
 
678
/*
679
    FIND A NAMED DIRECTORY
680
 
681
    This routine looks up a named directory called nm.  It returns the
682
    corresponding directory structure, or the null pointer if nm is not
683
    defined.
684
*/
685
 
686
static INCL_DIR *find_directory
687
    PROTO_N ( ( nm ) )
688
    PROTO_T ( string nm )
689
{
690
    INCL_DIR *p = dir_path ;
691
    while ( p != NULL ) {
692
	string s = p->name ;
693
	if ( s && ustreq ( s, nm ) ) return ( p ) ;
694
	p = p->next ;
695
    }
696
    return ( NULL ) ;
697
}
698
 
699
 
700
/*
701
    ADD A DIRECTORY TO THE SEARCH PATH
702
 
703
    This routine adds the directory dir with associated name nm to the
704
    include file search path.
705
*/
706
 
707
void add_directory
708
    PROTO_N ( ( dir, nm ) )
709
    PROTO_T ( string dir X string nm )
710
{
711
    INCL_DIR *p = dir_path ;
712
    INCL_DIR *q = xmalloc_one ( INCL_DIR ) ;
713
    if ( nm && find_directory ( nm ) ) {
714
	char *s = strlit ( nm ) ;
715
	error ( ERROR_WARNING, "Directory '%s' already defined", s ) ;
716
	nm = NULL ;
717
    }
718
    q->path = make_pathname ( dir ) ;
719
    q->name = nm ;
720
    q->mode = NULL ;
721
    q->no = LINK_NONE ;
722
    q->next = NULL ;
723
    if ( p == NULL ) {
724
	dir_path = q ;
725
	crt_dir_path = q ;
726
    } else {
727
	while ( p->next ) p = p->next ;
728
	p->next = q ;
729
    }
730
    return ;
731
}
732
 
733
 
734
/*
735
    SET A DIRECTORY COMPILATION MODE
736
 
737
    This routine sets the compilation mode for the directory named nm
738
    to be p.
739
*/
740
 
741
void directory_mode
742
    PROTO_N ( ( nm, p ) )
743
    PROTO_T ( string nm X OPTIONS *p )
744
{
745
    INCL_DIR *q = find_directory ( nm ) ;
746
    if ( q ) {
747
	if ( q->mode ) {
748
	    report ( preproc_loc, ERR_pragma_dir_mode ( nm ) ) ;
749
	}
750
	if ( p ) q->mode = p ;
751
    } else {
752
	report ( preproc_loc, ERR_pragma_dir_undef ( nm ) ) ;
753
    }
754
    return ;
755
}
756
 
757
 
758
/*
759
    LISTS OF START-UP AND END-UP FILES
760
 
761
    These variables give the lists of start-up and end-up files.  These
762
    are equivalent to #include "file" directives at respectively the start
763
    and the end of the main include file.
764
*/
765
 
766
LIST ( string ) startup_files = NULL_list ( string ) ;
767
LIST ( string ) endup_files = NULL_list ( string ) ;
768
 
769
 
770
/*
771
    SET UP INTERNAL START-UP FILE
772
 
773
    This routine sets up the built-in internal start-up file.
774
*/
775
 
776
void builtin_startup
777
    PROTO_Z ()
778
{
779
    BUFFER *bf = &internal_buff ;
780
    internal_name = DEREF_string ( posn_file ( crt_loc.posn ) ) ;
781
    internal_file = xmalloc_one ( FILE ) ;
782
    if ( bf->posn != bf->start ) {
783
	/* Add to list of start-up files if necessary */
784
	CONS_string ( internal_name, startup_files, startup_files ) ;
785
	bf->end = bf->posn ;
786
	bf->posn = bf->start ;
787
    }
788
    return ;
789
}
790
 
791
 
792
/*
793
    OPEN NEXT START-UP FILE
794
 
795
    This routine opens the next start-up file.  It continues trying until
796
    a start-up file is successfully opened or there are no start-up files
797
    left.
798
*/
799
 
800
void open_startup
801
    PROTO_Z ()
802
{
803
    LIST ( string ) p = startup_files ;
804
    while ( !IS_NULL_list ( p ) ) {
805
	string fn = DEREF_string ( HEAD_list ( p ) ) ;
806
	p = TAIL_list ( p ) ;
807
	startup_files = p ;
808
	preproc_loc = crt_loc ;
809
	crt_file_type = 1 ;
810
	if ( start_include ( fn, char_quote, 2, 0 ) ) return ;
811
    }
812
    crt_file_type = 0 ;
813
    return ;
814
}
815
 
816
 
817
/*
818
    LIST OF INCLUDED FILES
819
 
820
    This list is used to record all the files included plus the position
821
    of the current file within this list.
822
*/
823
 
824
typedef struct incl_file_tag {
825
    string name ;
826
    int imported ;
827
    HASHID macro ;
828
    unsigned test ;
829
    int state ;
830
    PTR ( LOCATION ) from ;
831
    STAT_TYPE *data ;
832
    STAT_TYPE data_ref ;
833
    struct incl_file_tag *next ;
834
} INCL_FILE ;
835
 
836
static INCL_FILE *included_files = NULL ;
837
static INCL_FILE *crt_included_file = NULL ;
838
 
839
 
840
/*
841
    TABLE OF INCLUSIONS
842
 
843
    This table is used to hold the file positions for all the currently
844
    active #include directives.  Note that this is done as a finite
845
    (but hopefully sufficiently large) array to detect recursive
846
    inclusions.
847
*/
848
 
849
typedef struct {
850
    string name ;
851
    FILE *fileptr ;
852
    long offset ;
853
    int special ;
854
    int startup ;
855
    int interface ;
856
    OPTIONS *mode ;
857
    INCL_DIR *path ;
858
    INCL_DIR *found ;
859
    INCL_FILE *incl ;
860
} INCL_BUFF ;
861
 
862
#define MAX_INCL_DEPTH		256
863
 
864
static INCL_BUFF position_array [ MAX_INCL_DEPTH ] ;
865
static INCL_BUFF *position = position_array ;
866
static unsigned long position_size = MAX_INCL_DEPTH ;
867
 
868
 
869
/*
870
    SIMPLE INCLUSION DEPTH
871
 
872
    There are two approaches to suspending the current file - either
873
    leaving the file open or closing it and reopening it later.  The
874
    latter is more efficient, but is limited by the maximum number of
875
    files which can be opened at one time (FOPEN_MAX).  Therefore this
876
    strategy is only used for this number of files.  Note that FOPEN_MAX
877
    is at least 8 (including the 3 standard files).
878
*/
879
 
880
#define LAST_BUFFER_NO		( ( unsigned long ) ( NO_BUFFER - 1 ) )
881
#define SIMPLE_INCL_DEPTH	LAST_BUFFER_NO
882
 
883
 
884
/*
885
    SET MAXIMUM INCLUDE DEPTH
886
 
887
    This routine sets the maximum include file depth to n.
888
*/
889
 
890
void set_incl_depth
891
    PROTO_N ( ( n ) )
892
    PROTO_T ( unsigned long n )
893
{
894
    if ( n > 10000 ) n = 10000 ;
895
    if ( n > position_size ) {
896
	/* Allocate more space if necessary */
897
	unsigned long i, m ;
898
	INCL_BUFF *p = xmalloc_nof ( INCL_BUFF, n ) ;
899
	INCL_BUFF *q = position ;
900
	m = crt_option_value ( OPT_VAL_include_depth ) ;
901
	for ( i = 0 ; i < m ; i++ ) p [i] = q [i] ;
902
	position_size = n ;
903
	position = p ;
904
	if ( q != position_array ) xfree_nof ( q ) ;
905
    }
906
    option_value ( OPT_VAL_include_depth ) = n ;
907
    return ;
908
}
909
 
910
 
911
/*
912
    CHECK WHETHER A FILE HAS ALREADY BEEN INCLUDED
913
 
914
    This routine checks whether the file with pathname nm and file
915
    statistics fs has already been included and does not need to be
916
    included again.  It also sets crt_included_file.  st is as in
917
    start_include.
918
*/
919
 
920
int already_included
921
    PROTO_N ( ( nm, fs, st ) )
922
    PROTO_T ( string nm X STAT_TYPE *fs X int st )
923
{
924
    INCL_FILE *p = included_files ;
925
    while ( p != NULL ) {
926
	int ok ;
927
	if ( ustreq ( nm, p->name ) && st != 4 ) {
928
	    /* Check file names */
929
	    ok = 1 ;
930
	} else {
931
	    /* Check file statistics */
932
	    ok = stat_equal ( fs, p->data ) ;
933
	}
934
	if ( ok ) {
935
	    /* Check matching file */
936
	    if ( st == 4 ) {
937
		/* Simple enquiry */
938
		return ( 1 ) ;
939
	    }
940
	    crt_included_file = p ;
941
	    if ( st == 1 ) {
942
		/* Imported file */
943
		if ( p->imported == 1 ) return ( 1 ) ;
944
		p->imported = 1 ;
945
	    }
946
	    if ( p->state == 2 ) {
947
		/* Check protection macro */
948
		unsigned def = check_macro ( p->macro, 0 ) ;
949
		def &= PP_COND_MASK ;
950
		if ( def == p->test ) return ( 1 ) ;
951
	    }
952
	    return ( 0 ) ;
953
	}
954
	p = p->next ;
955
    }
956
 
957
    /* Create new imported file structure */
958
    p = xmalloc_one ( INCL_FILE ) ;
959
    if ( st != 4 ) crt_included_file = p ;
960
    p->name = nm ;
961
    p->imported = st ;
962
    p->macro = NULL_hashid ;
963
    p->state = 0 ;
964
    p->test = PP_TRUE ;
965
    p->from = NULL_ptr ( LOCATION ) ;
966
    if ( fs ) {
967
	/* File system information available */
968
	p->data = &( p->data_ref ) ;
969
	p->data_ref = *fs ;
970
    } else {
971
	p->data = NULL ;
972
    }
973
    p->next = included_files ;
974
    included_files = p ;
975
    return ( 0 ) ;
976
}
977
 
978
 
979
/*
980
    CHECK A FILE PROTECTION MACRO
981
 
982
    This routine checks whether the given macro identifier is a file
983
    protection macro for the current file, that is to say, whether the
984
    file has the form:
985
 
986
		#ifndef macro
987
		....
988
		#endif
989
 
990
    prev gives the previous preprocessing directive and dir gives the
991
    current preprocessing directive.
992
*/
993
 
994
void protection_macro
995
    PROTO_N ( ( macro, prev, dir ) )
996
    PROTO_T ( HASHID macro X int prev X int dir )
997
{
998
    INCL_FILE *incl = crt_included_file ;
999
    if ( incl ) {
1000
	if ( prev == lex_included ) {
1001
	    if ( incl->state == 0 ) {
1002
		if ( dir == lex_ifndef ) {
1003
		    /* Have '#ifndef macro' at start of file */
1004
		    incl->macro = macro ;
1005
		    incl->test = PP_TRUE ;
1006
		    incl->state = 1 ;
1007
		    return ;
1008
		}
1009
		if ( dir == lex_ifdef ) {
1010
		    /* Have '#ifdef macro' at start of file */
1011
		    incl->macro = macro ;
1012
		    incl->test = PP_FALSE ;
1013
		    incl->state = 1 ;
1014
		    return ;
1015
		}
1016
		if ( dir == lex_eof ) {
1017
		    /* Start and end of file coincide */
1018
		    incl->macro = NULL_hashid ;
1019
		    incl->test = PP_TRUE ;
1020
		    incl->state = 2 ;
1021
		    return ;
1022
		}
1023
	    }
1024
	}
1025
	if ( prev == lex_end_condition ) {
1026
	    if ( incl->state == 1 ) {
1027
		if ( dir == lex_eof ) {
1028
		    /* Have '#endif' at end of file */
1029
		    incl->state = 2 ;
1030
		    return ;
1031
		}
1032
	    }
1033
	}
1034
	incl->state = 0 ;
1035
    }
1036
    return ;
1037
}
1038
 
1039
 
1040
/*
1041
    CREATE A FILE NAME
1042
 
1043
    This routine forms a composite file name consisting of a directory
1044
    component d and a file component f.  The up argument is true if the
1045
    existing file component is to be removed from d.
1046
*/
1047
 
1048
static string add_pathname
1049
    PROTO_N ( ( d, f, up ) )
1050
    PROTO_T ( string d X string f X int up )
1051
{
1052
    if ( d ) {
1053
	BUFFER *bf = clear_buffer ( &incl_buff, NIL ( FILE ) ) ;
1054
	bfputs ( bf, d ) ;
1055
	if ( up ) {
1056
	    /* Remove file component */
1057
	    string s = ustrrchr ( bf->start, char_slash ) ;
1058
	    if ( s == NULL ) return ( f ) ;
1059
	    bf->posn = s ;
1060
	}
1061
	bfputc ( bf, char_slash ) ;
1062
	bfputs ( bf, f ) ;
1063
	return ( bf->start ) ;
1064
    }
1065
    return ( f ) ;
1066
}
1067
 
1068
 
1069
/*
1070
    FIND AN INCLUDE FILE
1071
 
1072
    This routine searches for, and opens, an included file named nm.  The
1073
    argument q equals '"' or '>', depending on the form of the #include
1074
    directive.  The argument st is 0 for normal inclusions, 1 for imported
1075
    inclusions, 2 for start-up files and 3 for end-up files.  A value of
1076
    4 is used for checking inclusion.  next is true if the search is to
1077
    restart at the current position in the directory path.  The routine
1078
    returns 1 to indicate that the file was opened successfully.
1079
*/
1080
 
1081
int start_include
1082
    PROTO_N ( ( nm, q, st, next ) )
1083
    PROTO_T ( string nm X int q X int st X int next )
1084
{
1085
    FILE *g ;
1086
    FILE *f = NULL ;
1087
    int special = 0 ;
1088
    string file = nm ;
1089
    string dir = NULL ;
1090
    unsigned long c, m ;
1091
    string rfile = NULL ;
1092
    OPTIONS *mode = NULL ;
1093
    PTR ( LOCATION ) from ;
1094
    unsigned long date = 0 ;
1095
    INCL_DIR *found = NULL ;
1096
    INCL_DIR *path = dir_path ;
1097
    INCL_FILE *incl = crt_included_file ;
1098
 
1099
    /* Check for empty file name */
1100
    if ( nm [0] == 0 ) {
1101
	report ( preproc_loc, ERR_cpp_include_empty () ) ;
1102
	return ( 0 ) ;
1103
    }
1104
 
1105
    /* Search for included file */
1106
    if ( nm == internal_name ) {
1107
	/* Allow for command-line options */
1108
	rfile = ustrlit ( "" ) ;
1109
	f = internal_file ;
1110
	special = 1 ;
1111
 
1112
    } else if ( is_full_pathname ( nm ) ) {
1113
	/* Allow for full file names */
1114
	if ( st < 2 ) {
1115
	    report ( preproc_loc, ERR_cpp_include_full ( nm ) ) ;
1116
	}
1117
	f = fopen ( strlit ( file ), "r" ) ;
1118
 
1119
    } else if ( std_file_name ( nm ) ) {
1120
	/* Allow for standard input (extension) */
1121
	file = stdin_name ;
1122
	rfile = ustrlit ( "" ) ;
1123
	f = stdin ;
1124
	special = 1 ;
1125
 
1126
    } else {
1127
	/* Check quoted include directives */
1128
	if ( q == char_quote ) {
1129
	    file = add_pathname ( input_name, nm, 1 ) ;
1130
	    f = fopen ( strlit ( file ), "r" ) ;
1131
	    found = crt_found_path ;
1132
	}
1133
 
1134
	/* Search directory path */
1135
	if ( f == NULL ) {
1136
	    if ( next ) {
1137
		/* Start search at current position */
1138
		path = crt_dir_path ;
1139
	    }
1140
	    while ( f == NULL && path != NULL ) {
1141
		dir = path->path ;
1142
		file = add_pathname ( dir, nm, 0 ) ;
1143
		f = fopen ( strlit ( file ), "r" ) ;
1144
		found = path ;
1145
		mode = path->mode ;
1146
		path = path->next ;
1147
	    }
1148
	} else {
1149
	    path = crt_dir_path ;
1150
	    dir = DEREF_string ( posn_dir ( crt_loc.posn ) ) ;
1151
	}
1152
    }
1153
    if ( st == 4 ) {
1154
	/* Just testing ... */
1155
	if ( f == NULL ) return ( 0 ) ;
1156
	if ( !special ) fclose_v ( f ) ;
1157
	return ( 1 ) ;
1158
    }
1159
 
1160
    /* Report unfound files */
1161
    if ( f == NULL ) {
1162
	report ( preproc_loc, ERR_cpp_include_unknown ( nm ) ) ;
1163
	return ( 0 ) ;
1164
    }
1165
 
1166
    /* Check for multiple inclusions */
1167
    file = xustrcpy ( file ) ;
1168
    if ( special ) {
1169
	crt_included_file = NULL ;
1170
    } else {
1171
	STAT_TYPE fstr ;
1172
	STAT_TYPE *fs = stat_func ( strlit ( file ), &fstr ) ;
1173
	if ( already_included ( file, fs, st ) ) {
1174
	    /* Only read file if necessary */
1175
	    from = crt_included_file->from ;
1176
	    report ( preproc_loc, ERR_cpp_include_dup ( nm, from ) ) ;
1177
	    crt_included_file = incl ;
1178
	    fclose_v ( f ) ;
1179
	    return ( 0 ) ;
1180
	}
1181
	date = stat_date ( fs ) ;
1182
    }
1183
 
1184
    /* Store position of #include directive */
1185
    c = crt_option_value ( OPT_VAL_include_depth ) ;
1186
    if ( !incr_value ( OPT_VAL_include_depth ) ) {
1187
	/* Include depth too great */
1188
	crt_option_value ( OPT_VAL_include_depth ) = c ;
1189
	crt_included_file = incl ;
1190
	return ( 0 ) ;
1191
    }
1192
    g = input_file ;
1193
    position [c].name = input_name ;
1194
    position [c].special = input_special ;
1195
    position [c].startup = st ;
1196
    position [c].interface = crt_interface ;
1197
    position [c].mode = mode ;
1198
    position [c].path = crt_dir_path ;
1199
    position [c].found = crt_found_path ;
1200
    position [c].incl = incl ;
1201
    if ( c < SIMPLE_INCL_DEPTH || input_special ) {
1202
	/* Store open file */
1203
	m = c + 1 ;
1204
	position [c].fileptr = g ;
1205
	position [c].offset = tell_buffer ( c ) ;
1206
    } else {
1207
	/* Store position in closed file */
1208
	m = LAST_BUFFER_NO ;
1209
	position [c].fileptr = NULL ;
1210
	position [c].offset = tell_buffer ( m ) ;
1211
	if ( ferror ( g ) || fclose ( g ) ) {
1212
	    char *gnm = strlit ( input_name ) ;
1213
	    error ( ERROR_INTERNAL, "Reading error in '%s'", gnm ) ;
1214
	}
1215
    }
1216
    crt_found_path = found ;
1217
    crt_dir_path = path ;
1218
 
1219
    /* Set up new file */
1220
    input_name = file ;
1221
    input_file = f ;
1222
    input_special = special ;
1223
    nm = ( file + ustrlen ( file ) ) - ustrlen ( nm ) ;
1224
    if ( rfile == NULL ) {
1225
	rfile = file ;
1226
	file = normalise_pathname ( file ) ;
1227
    }
1228
    if ( option ( OPT_include_verbose ) ) {
1229
	report ( preproc_loc, ERR_cpp_include_open ( file ) ) ;
1230
    }
1231
    crt_loc.line-- ;
1232
    crt_loc.column = 0 ;
1233
    input_crt = input_posn ;
1234
    if ( do_header ) dump_include ( &crt_loc, nm, st, q ) ;
1235
    from = MAKE_ptr ( SIZE_loc ) ;
1236
    COPY_loc ( from, crt_loc ) ;
1237
    CREATE_loc ( file, rfile, nm, dir, from, date, crt_loc ) ;
1238
    if ( crt_included_file ) {
1239
	/* Set inclusion position */
1240
	crt_included_file->from = from ;
1241
    }
1242
    if ( do_header ) dump_start ( &crt_loc, found ) ;
1243
    IGNORE init_buffer ( m ) ;
1244
    start_preproc_if () ;
1245
    if ( mode ) {
1246
	/* Begin new checking scope if necessary */
1247
	begin_option ( NULL_id ) ;
1248
	use_mode ( mode, ERROR_SERIOUS ) ;
1249
    }
1250
    crt_file_changed = 2 ;
1251
    crt_line_changed = 1 ;
1252
    crt_spaces = 0 ;
1253
    return ( 1 ) ;
1254
}
1255
 
1256
 
1257
/*
1258
    END AN INCLUDE FILE
1259
 
1260
    This routine is called at the end of each source file.  prev is as
1261
    in protection_macro.  The routine returns 1 if the end of file causes
1262
    a reversion to a previous file via a #include directive, and 0 if this
1263
    is the main source file.  Note that start-up and end-up files are
1264
    spotted during this process (except the first start-up file which is
1265
    dealt with in process_file).  Start-up files are at depth 1, whereas
1266
    end-up files are at depth 0.
1267
*/
1268
 
1269
int end_include
1270
    PROTO_N ( ( prev ) )
1271
    PROTO_T ( int prev )
1272
{
1273
    unsigned long c ;
1274
    PTR ( LOCATION ) loc ;
1275
    FILE *f = input_file ;
1276
    string nm = input_name ;
1277
 
1278
    /* Check for protection macros */
1279
    if ( !clear_preproc_if () ) prev = lex_end_condition ;
1280
    protection_macro ( NULL_hashid, prev, lex_eof ) ;
1281
 
1282
    /* Tidy up the current file */
1283
    if ( f != NULL ) {
1284
	if ( input_special ) {
1285
	    if ( f == internal_file ) {
1286
		free_buffer ( &internal_buff ) ;
1287
	    }
1288
	} else {
1289
	    if ( ferror ( f ) || fclose ( f ) ) {
1290
		char *fnm = strlit ( nm ) ;
1291
		error ( ERROR_INTERNAL, "Reading error in '%s'", fnm ) ;
1292
	    }
1293
	}
1294
	input_file = NULL ;
1295
	if ( do_header ) dump_end ( &crt_loc ) ;
1296
    }
1297
 
1298
    /* Check for previous file */
1299
    c = crt_option_value ( OPT_VAL_include_depth ) ;
1300
    if ( c == 0 ) {
1301
	/* End of main file - deal with end-up files */
1302
	LIST ( string ) p = endup_files ;
1303
	while ( !IS_NULL_list ( p ) ) {
1304
	    string fn = DEREF_string ( HEAD_list ( p ) ) ;
1305
	    p = TAIL_list ( p ) ;
1306
	    endup_files = p ;
1307
	    preproc_loc = crt_loc ;
1308
	    crt_file_type = 1 ;
1309
	    if ( start_include ( fn, char_quote, 3, 0 ) ) return ( 1 ) ;
1310
	}
1311
	crt_file_type = 0 ;
1312
	return ( 0 ) ;
1313
    }
1314
    decr_value ( OPT_VAL_include_depth ) ;
1315
    c-- ;
1316
 
1317
    /* End checking scope if necessary */
1318
    if ( position [c].mode ) end_option ( 0 ) ;
1319
 
1320
    /* Restore previous file position (don't destroy old value) */
1321
    loc = DEREF_ptr ( posn_from ( crt_loc.posn ) ) ;
1322
    DEREF_loc ( loc, crt_loc ) ;
1323
    crt_file_changed = 2 ;
1324
    crt_line_changed = 1 ;
1325
    crt_spaces = 0 ;
1326
 
1327
    /* Reopen the previous buffer */
1328
    input_name = position [c].name ;
1329
    input_file = position [c].fileptr ;
1330
    input_special = position [c].special ;
1331
    crt_dir_path = position [c].path ;
1332
    crt_found_path = position [c].found ;
1333
    crt_included_file = position [c].incl ;
1334
    crt_interface = position [c].interface ;
1335
    if ( input_file == NULL ) {
1336
	/* Reopen old file */
1337
	char *str = strlit ( input_name ) ;
1338
	if ( str ) {
1339
	    input_file = fopen ( str, "r" ) ;
1340
	    if ( input_file == NULL ) {
1341
		CONST char *msg = "Internal file error in '%s'" ;
1342
		error ( ERROR_INTERNAL, msg, str ) ;
1343
		crt_loc.line++ ;
1344
		crt_loc.column = 0 ;
1345
		input_crt = input_posn ;
1346
		return ( end_include ( lex_ignore_token ) ) ;
1347
	    }
1348
	    seek_buffer ( LAST_BUFFER_NO, position [c].offset, 0 ) ;
1349
	}
1350
    } else {
1351
	/* Resume old file position */
1352
	resume_buffer ( c ) ;
1353
    }
1354
    if ( option ( OPT_include_verbose ) ) {
1355
	LOCATION ploc ;
1356
	int st = position [c].startup ;
1357
	ploc = crt_loc ;
1358
	if ( st >= 2 ) ploc.line++ ;
1359
	report ( ploc, ERR_cpp_include_close ( nm ) ) ;
1360
    }
1361
    if ( do_header ) dump_include ( &crt_loc, NULL_string, 4, 0 ) ;
1362
    crt_loc.line++ ;
1363
    crt_loc.column = 0 ;
1364
    input_crt = input_posn ;
1365
 
1366
    /* Could be the end of a start-up file - try the next one */
1367
    if ( c == 0 ) open_startup () ;
1368
    return ( 1 ) ;
1369
}