Subversion Repositories tendra.SVN

Rev

Rev 5 | Details | Compare with Previous | Last modification | View Log | RSS feed

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