Subversion Repositories tendra.SVN

Rev

Rev 2 | Go to most recent revision | Details | Compare with Previous | 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 "object.h"
33
#include "hash.h"
34
#include "lex.h"
35
#include "name.h"
36
#include "syntax.h"
37
#include "type.h"
38
#include "utility.h"
39
 
40
 
41
/*
42
    CREATE A KEYWORD
43
 
44
    This routine creates a keyword nm with lexical token value t.
45
*/
46
 
47
static void make_keyword
48
    PROTO_N ( ( nm, t ) )
49
    PROTO_T ( char *nm X int t )
50
{
51
    object *p = make_object ( nm, OBJ_KEYWORD ) ;
52
    p->u.u_num = t ;
53
    IGNORE add_hash ( keywords, p, no_version ) ;
54
    return ;
55
}
56
 
57
 
58
/*
59
    INITIALISE KEYWORDS
60
 
61
    This routine initialises the hash table of keywords.
62
*/
63
 
64
void init_keywords
65
    PROTO_Z ()
66
{
67
#define MAKE_KEYWORD( NAME, LEX )\
68
    make_keyword ( NAME, LEX )
69
#include "keyword.h"
70
    return ;
71
}
72
 
73
 
74
/*
75
    CURRENT LEXICAL TOKEN
76
 
77
    These variables are used to store the value of the current lexical
78
    token.
79
*/
80
 
81
int crt_lex_token = lex_unknown ;
82
int saved_lex_token = lex_unknown ;
83
char *token_value = null ;
84
 
85
 
86
/*
87
    INPUT FILE
88
 
89
    These variable input_file gives the file from which the input is read.
90
    The input_pending variable is used to unread one character.
91
*/
92
 
93
FILE *input_file ;
94
int input_pending = LEX_EOF ;
95
 
96
 
97
/*
98
    READ A CHARACTER FROM THE INPUT FILE
99
 
100
    This routine reads the next character from the input file.
101
*/
102
 
103
static int read_char
104
    PROTO_Z ()
105
{
106
    int c = input_pending ;
107
    if ( c == LEX_EOF ) {
108
	c = fgetc ( input_file ) ;
109
	if ( c == '\n' ) line_no++ ;
110
	if ( c == EOF ) return ( LEX_EOF ) ;
111
	c &= 0xff ;
112
    } else {
113
	input_pending = LEX_EOF ;
114
    }
115
    return ( c ) ;
116
}
117
 
118
 
119
/*
120
    MAPPINGS OF LEXICAL ANALYSER ROUTINES
121
 
122
    These macros give the mappings from the lexical analyser to the
123
    routines defined in this module.
124
*/
125
 
126
static int read_identifier PROTO_S ( ( int, int, int ) ) ;
127
static int read_number PROTO_S ( ( int, int ) ) ;
128
static int read_string PROTO_S ( ( int ) ) ;
129
static int read_insert PROTO_S ( ( int ) ) ;
130
static int read_c_comment PROTO_S ( ( int ) ) ;
131
static int read_comment PROTO_S ( ( int ) ) ;
132
 
133
#define unread_char( A )	input_pending = ( A )
134
#define get_global( A )		read_identifier ( 0, ( A ), 0 )
135
#define get_local( A, B )	read_identifier ( ( A ), ( B ), 0 )
136
#define get_command( A, B )	read_identifier ( ( A ), ( B ), 0 )
137
#define get_variable( A, B )	read_identifier ( ( A ), ( B ), 0 )
138
#define get_number( A )		read_number ( ( A ), 0 )
139
#define get_string( A )		read_string ( 0 )
140
#define get_comment( A )	read_comment ( 0 )
141
#define get_c_comment( A, B )	read_c_comment ( 0 )
142
#define get_text( A, B )	read_insert ( 0 )
143
#define unknown_token( A )	lex_unknown
144
 
145
 
146
/*
147
    INCLUDE THE LEXICAL ANALYSER
148
 
149
    The automatically generated lexical analyser is included at this
150
    point.  It defines the routine read_token which reads the next
151
    lexical token from the input file.
152
*/
153
 
154
#include "lexer.h"
155
 
156
 
157
/*
158
    READ AN IDENTIFIER NAME
159
 
160
    This routine reads an identifier name from the input file.  It is
161
    entered after the first character, b, has been read.  a gives the
162
    identifier prefix, '+' for commands, '$' for variables, '~' for
163
    local identifiers, and 0 for normal identifiers.
164
*/
165
 
166
static int read_identifier
167
    PROTO_N ( ( a, b, pp ) )
168
    PROTO_T ( int a X int b X int pp )
169
{
170
    int c ;
171
    object *p ;
172
    int i = 0 ;
173
    char *s = buffer ;
174
    if ( a ) s [ i++ ] = ( char ) a ;
175
    s [ i++ ] = ( char ) b ;
176
    for ( ; ; ) {
177
	c = read_char () ;
178
	if ( !is_alphanum ( lookup_char ( c ) ) ) break ;
179
	s [i] = ( char ) c ;
180
	if ( ++i >= buffsize ) {
181
	    error ( ERR_SERIOUS, "Identifier too long" ) ;
182
	    i = 1 ;
183
	}
184
    }
185
    unread_char ( c ) ;
186
    s [i] = 0 ;
187
    p = search_hash ( keywords, s, no_version ) ;
188
    if ( p ) return ( p->u.u_num ) ;
189
    token_value = s ;
190
    if ( a == 0 ) {
191
	if ( !pp ) token_value = string_copy ( s ) ;
192
	return ( lex_name ) ;
193
    }
194
    if ( a == '$' ) {
195
	if ( !pp ) token_value = string_copy ( s ) ;
196
	return ( lex_variable ) ;
197
    }
198
    if ( a == '+' ) {
199
	/* Commands */
200
	if ( !pp ) token_value = string_copy ( s ) ;
201
	error ( ERR_SERIOUS, "Unknown command, '%s'", s ) ;
202
	return ( lex_name ) ;
203
    }
204
    token_value = string_concat ( HIDDEN_NAME, s + 1 ) ;
205
    return ( lex_name ) ;
206
}
207
 
208
 
209
/*
210
    READ A NUMBER
211
 
212
    This routine reads a number from the input file.  It is entered after
213
    the initial character, a, has been read.
214
*/
215
 
216
static int read_number
217
    PROTO_N ( ( a, pp ) )
218
    PROTO_T ( int a X int pp )
219
{
220
    int c ;
221
    int i = 0 ;
222
    char *s = buffer ;
223
    s [ i++ ] = ( char ) a ;
224
    for ( ; ; ) {
225
	c = read_char () ;
226
	if ( !is_digit ( lookup_char ( c ) ) ) break ;
227
	s [i] = ( char ) c ;
228
	if ( ++i >= buffsize ) {
229
	    error ( ERR_SERIOUS, "Number too long" ) ;
230
	    i = 0 ;
231
	}
232
    }
233
    unread_char ( c ) ;
234
    s [i] = 0 ;
235
    if ( pp ) {
236
	token_value = s ;
237
    } else {
238
	token_value = string_copy ( s ) ;
239
    }
240
    return ( lex_number ) ;
241
}
242
 
243
 
244
/*
245
    READ A STRING
246
 
247
    This routine reads a string from the input file.  It is entered after
248
    the initial quote has been read.
249
*/
250
 
251
static int read_string
252
    PROTO_N ( ( pp ) )
253
    PROTO_T ( int pp )
254
{
255
    int c ;
256
    int i = 0 ;
257
    char *s = buffer ;
258
    for ( ; ; ) {
259
	c = read_char () ;
260
	if ( c == '"' ) {
261
	    /* End of string */
262
	    break ;
263
	} else if ( c == '\\' ) {
264
	    /* Deal with escaped characters */
265
	    c = read_char () ;
266
	    if ( c == '\n' || c == LEX_EOF ) goto new_line ;
267
	    if ( pp ) {
268
		/* Preserve escapes when preprocessing */
269
		s [i] = '\\' ;
270
		i++ ;
271
	    } else {
272
		/* Examine escape sequence */
273
		switch ( c ) {
274
		    case 'n' : c = '\n' ; break ;
275
		    case 'r' : c = '\r' ; break ;
276
		    case 't' : c = '\t' ; break ;
277
		}
278
	    }
279
	} else if ( c == '\n' || c == LEX_EOF ) {
280
	    /* Deal with new lines */
281
	    new_line : {
282
		error ( ERR_SERIOUS, "New line in string" ) ;
283
		s [i] = 0 ;
284
		return ( lex_string ) ;
285
	    }
286
	}
287
	s [i] = ( char ) c ;
288
	if ( ++i >= buffsize ) {
289
	    error ( ERR_SERIOUS, "String too long" ) ;
290
	    i = 0 ;
291
	}
292
    }
293
    s [i] = 0 ;
294
    if ( pp ) {
295
	token_value = s ;
296
    } else {
297
	token_value = string_copy ( s ) ;
298
    }
299
    return ( lex_string ) ;
300
}
301
 
302
 
303
/*
304
    READ A SECTION OF QUOTED TEXT
305
 
306
    This routine reads a section of quoted text (indicated by enclosure
307
    in a number of percent signs) into the buffer.  On entry two percents
308
    have already been read.  Firstly any further percents are read, then
309
    the text is read until an equal number of percents are encountered.
310
    Any leading or trailing whitespace is ignored if pp is false.
311
*/
312
 
313
static int read_insert
314
    PROTO_N ( ( pp ) )
315
    PROTO_T ( int pp )
316
{
317
    int c ;
318
    int i = 0 ;
319
    int p = 0 ;
320
    int percents = 2 ;
321
    char *s = buffer ;
322
    while ( c = read_char (), c == '%' ) percents++ ;
323
    unread_char ( c ) ;
324
    if ( pp ) {
325
	/* Preserve percents when preprocessing */
326
	if ( percents < buffsize ) {
327
	    for ( i = 0 ; i < percents ; i++ ) s [i] = '%' ;
328
	} else {
329
	    error ( ERR_SERIOUS, "Insert too long" ) ;
330
	}
331
    }
332
    do {
333
	c = read_char () ;
334
	if ( c == '%' ) {
335
	    p++ ;
336
	} else {
337
	    if ( c == LEX_EOF ) {
338
		error ( ERR_SERIOUS, "End of file in quoted text" ) ;
339
		return ( lex_eof ) ;
340
	    }
341
	    p = 0 ;
342
	}
343
	s [i] = ( char ) c ;
344
	if ( ++i >= buffsize ) {
345
	    error ( ERR_SERIOUS, "Insert too long" ) ;
346
	    i = 0 ;
347
	}
348
    } while ( p != percents ) ;
349
    if ( pp ) {
350
	/* Preserve percents when preprocessing */
351
	s [i] = 0 ;
352
	token_value = s ;
353
    } else {
354
	/* Strip out initial and final white space */
355
	if ( i >= p ) i -= p ;
356
	s [i] = 0 ;
357
	while ( --i >= 0 ) {
358
	    int a = ( int ) s [i] ;
359
	    int t = lookup_char ( a & 0xff ) ;
360
	    if ( !is_white ( t ) ) break ;
361
	    s [i] = 0 ;
362
	}
363
	i = 0 ;
364
	for ( ; ; ) {
365
	    int a = ( int ) s [i] ;
366
	    int t = lookup_char ( a & 0xff ) ;
367
	    if ( !is_white ( t ) ) break ;
368
	    i++ ;
369
	}
370
	token_value = string_copy ( s + i ) ;
371
    }
372
    return ( percents % 2 ? lex_build_Hinsert : lex_insert ) ;
373
}
374
 
375
 
376
/*
377
    READ A C COMMENT
378
 
379
    This routine reads a C-style comment into the buffer.  The routine is
380
    entered just after the initial / * has been read, and continues until
381
    the corresponding * /.
382
*/
383
 
384
static int read_c_comment
385
    PROTO_N ( ( pp ) )
386
    PROTO_T ( int pp )
387
{
388
    int c ;
389
    int i = 2 ;
390
    int p = 0 ;
391
    char *s = buffer ;
392
    s [0] = '/' ;
393
    s [1] = '*' ;
394
    do {
395
	c = read_char () ;
396
	if ( c == '*' && p == 0 ) {
397
	    p = 1 ;
398
	} else if ( c == '/' && p == 1 ) {
399
	    p = 2 ;
400
	} else {
401
	    p = 0 ;
402
	}
403
	if ( c == LEX_EOF ) {
404
	    error ( ERR_SERIOUS, "End of file in comment" ) ;
405
	    return ( lex_eof ) ;
406
	}
407
	s [i] = ( char ) c ;
408
	if ( ++i >= buffsize ) {
409
	    error ( ERR_SERIOUS, "Comment too long" ) ;
410
	    i = 2 ;
411
	}
412
    } while ( p != 2 ) ;
413
    s [i] = 0 ;
414
    if ( pp ) {
415
	token_value = s ;
416
    } else {
417
	token_value = string_copy ( s ) ;
418
    }
419
    return ( lex_comment ) ;
420
}
421
 
422
 
423
/*
424
    READ A TSPEC COMMENT
425
 
426
    This routine steps over a tspec comment.  It is entered after the
427
    initial '#' has been read and skips to the end of the line.  If pp
428
    is false then the next token is returned.
429
*/
430
 
431
static int read_comment
432
    PROTO_N ( ( pp ) )
433
    PROTO_T ( int pp )
434
{
435
    int c ;
436
    while ( c = read_char (), c != '\n' ) {
437
	if ( c == LEX_EOF ) {
438
	    error ( ERR_SERIOUS, "End of file in comment" ) ;
439
	    return ( lex_eof ) ;
440
	}
441
    }
442
    if ( pp ) return ( lex_unknown ) ;
443
    return ( read_token () ) ;
444
}
445
 
446
 
447
/*
448
    READ A PREPROCESSING TOKEN
449
 
450
    This routine is a stripped down version of read_token which is used
451
    in preprocessing.  Initial white space is skipped if w is true.
452
    The token read is always stored in the buffer.
453
*/
454
 
455
static int read_pptoken
456
    PROTO_N ( ( w ) )
457
    PROTO_T ( int w )
458
{
459
    int c ;
460
    int t = lex_unknown ;
461
    do {
462
	c = read_char () ;
463
    } while ( w && is_white ( lookup_char ( c ) ) ) ;
464
    switch ( c ) {
465
	case '"' : {
466
	    return ( read_string ( 1 ) ) ;
467
	}
468
	case '#' : {
469
	    IGNORE read_comment ( 1 ) ;
470
	    if ( w ) return ( read_pptoken ( w ) ) ;
471
	    c = '\n' ;
472
	    break ;
473
	}
474
	case '%' : {
475
	    int a = read_char () ;
476
	    if ( a == '%' ) return ( read_insert ( 1 ) ) ;
477
	    unread_char ( a ) ;
478
	    break ;
479
	}
480
	case '+' : {
481
	    int a = read_char () ;
482
	    if ( is_alpha ( lookup_char ( a ) ) ) {
483
		return ( read_identifier ( c, a, 1 ) ) ;
484
	    }
485
	    unread_char ( a ) ;
486
	    break ;
487
	}
488
	case '/' : {
489
	    int a = read_char () ;
490
	    if ( a == '*' ) return ( read_c_comment ( 1 ) ) ;
491
	    unread_char ( a ) ;
492
	    break ;
493
	}
494
	case ':' : {
495
	    int a = read_char () ;
496
	    if ( a == '=' ) {
497
		buffer [0] = ( char ) c ;
498
		buffer [1] = ( char ) a ;
499
		buffer [2] = 0 ;
500
		return ( lex_assign ) ;
501
	    }
502
	    unread_char ( a ) ;
503
	    break ;
504
	}
505
	case '(' : t = lex_open_Hround ; break ;
506
	case ')' : t = lex_close_Hround ; break ;
507
	case '{' : t = lex_open_Hbrace ; break ;
508
	case '}' : t = lex_close_Hbrace ; break ;
509
	case ';' : t = lex_semicolon ; break ;
510
	case ',' : t = lex_comma ; break ;
511
	case LEX_EOF : t = lex_eof ; break ;
512
    }
513
    buffer [0] = ( char ) c ;
514
    buffer [1] = 0 ;
515
    return ( t ) ;
516
}
517
 
518
 
519
/*
520
    READ A STRING
521
 
522
    This routine reads a string plus one other character from the input
523
    file, storing the string in str and returning the other character.
524
    b is set to true if the string is enclosed in brackets.
525
*/
526
 
527
static int read_pp_string
528
    PROTO_N ( ( str, b ) )
529
    PROTO_T ( char **str X int *b )
530
{
531
    int c = read_pptoken ( 1 ) ;
532
    if ( c == lex_open_Hround ) {
533
	*b = 1 ;
534
	c = read_pptoken ( 1 ) ;
535
    }
536
    if ( c != lex_string ) {
537
	error ( ERR_SERIOUS, "Syntax error - string expected" ) ;
538
	*str = "???" ;
539
	return ( c ) ;
540
    }
541
    *str = string_copy ( buffer ) ;
542
    c = read_pptoken ( 1 ) ;
543
    if ( *b ) {
544
	if ( c != lex_close_Hround ) {
545
	    error ( ERR_SERIOUS, "Syntax error - ')' expected" ) ;
546
	}
547
	c = read_pptoken ( 1 ) ;
548
    }
549
    return ( c ) ;
550
}
551
 
552
 
553
/*
554
    PRINT A SUBSET NAME
555
 
556
    This routine prints the command cmd "api", "file", "subset" to the
557
    file output.
558
*/
559
 
560
static void print_subset_name
561
    PROTO_N ( ( output, cmd, api, file, subset, b ) )
562
    PROTO_T ( FILE *output X char *cmd X
563
	      char *api X char *file X char *subset X int b )
564
{
565
    if ( b ) {
566
	IGNORE fprintf ( output, "%s ( \"%s\" )", cmd, api ) ;
567
    } else {
568
	IGNORE fprintf ( output, "%s \"%s\"", cmd, api ) ;
569
    }
570
    if ( file ) IGNORE fprintf ( output, ", \"%s\"", file ) ;
571
    if ( subset ) {
572
	if ( file == null ) IGNORE fputs ( ", \"\"", output ) ;
573
	IGNORE fprintf ( output, ", \"%s\"", subset ) ;
574
    }
575
    return ;
576
}
577
 
578
 
579
/*
580
    PRINT THE CURRENT FILE POSITION
581
 
582
    This routine prints file name and line number directives to the file
583
    output.
584
*/
585
 
586
static void print_posn
587
    PROTO_N ( ( output ) )
588
    PROTO_T ( FILE *output )
589
{
590
    static char *last_filename = "" ;
591
    if ( !streq ( filename, last_filename ) ) {
592
	IGNORE fprintf ( output, "$FILE = \"%s\" ;\n", filename ) ;
593
	last_filename = filename ;
594
    }
595
    IGNORE fprintf ( output, "$LINE = %d ;\n", line_no - 1 ) ;
596
    return ;
597
}
598
 
599
 
600
/*
601
    PREPROCESS A SUBFILE
602
 
603
    This routine reads a +IMPLEMENT or +USE directive (indicated by n)
604
    from the input file to output.
605
*/
606
 
607
static void preproc_subfile
608
    PROTO_N ( ( output, cmd ) )
609
    PROTO_T ( FILE *output X char *cmd )
610
{
611
    int c ;
612
    int txt ;
613
    int b = 0 ;
614
    char *api = null ;
615
    char *file = null ;
616
    char *subset = null ;
617
    c = read_pp_string ( &api, &b ) ;
618
    if ( c == lex_comma ) {
619
	int d = 0 ;
620
	c = read_pp_string ( &file, &d ) ;
621
	if ( d ) {
622
	    error ( ERR_SERIOUS, "Illegally bracketed string" ) ;
623
	    d = 0 ;
624
	}
625
	if ( c == lex_comma ) {
626
	    c = read_pp_string ( &subset, &d ) ;
627
	    if ( d ) error ( ERR_SERIOUS, "Illegally bracketed string" ) ;
628
	}
629
	if ( *file == 0 ) file = null ;
630
    }
631
    if ( c == lex_semicolon ) {
632
	txt = ';' ;
633
    } else if ( c == lex_open_Hround ) {
634
	txt = '(' ;
635
    } else {
636
	error ( ERR_SERIOUS, "Syntax error - ';' or '(' expected" ) ;
637
	txt = ';' ;
638
    }
639
    preproc ( output, api, file, subset ) ;
640
    print_posn ( output ) ;
641
    print_subset_name ( output, cmd, api, file, subset, b ) ;
642
    IGNORE fputc ( ' ', output ) ;
643
    IGNORE fputc ( txt, output ) ;
644
    return ;
645
}
646
 
647
 
648
/*
649
    PREPROCESS A FILE
650
 
651
    This routine preprocesses the subset api:file:subset into output.
652
*/
653
 
654
void preproc
655
    PROTO_N ( ( output, api, file, subset ) )
656
    PROTO_T ( FILE *output X char *api X char *file X char *subset )
657
{
658
    int c ;
659
    char *s ;
660
    object *p ;
661
    char *sn, *nm ;
662
    FILE *old_file ;
663
    int old_pending ;
664
    int old_line_no ;
665
    char *old_filename ;
666
    boolean found = 0 ;
667
    int brackets = 0 ;
668
    int end_brackets = 0 ;
669
    int if_depth = 0 ;
670
    int else_depth = 0 ;
671
    FILE *input = null ;
672
    boolean printing = ( boolean ) ( subset ? 0 : 1 ) ;
673
 
674
    /* Check for previous inclusion */
675
    sn = subset_name ( api, file, subset ) ;
676
    p = search_hash ( subsets, sn, no_version ) ;
677
    if ( p != null ) {
678
	if ( p->u.u_info == null ) {
679
	    error ( ERR_SERIOUS, "Recursive inclusion of '%s'", sn ) ;
680
	} else if ( p->u.u_info->implemented ) {
681
	    error ( ERR_SERIOUS, "Set '%s' not found", sn ) ;
682
	}
683
	return ;
684
    }
685
 
686
    /* Open the input file */
687
    nm = ( file ? file : MASTER_FILE ) ;
688
    if ( !streq ( api, LOCAL_API ) ) {
689
	nm = string_printf ( "%s/%s", api, nm ) ;
690
    }
691
    s = input_dir ;
692
    while ( s ) {
693
	char *t = strchr ( s, ':' ) ;
694
	if ( t == null ) {
695
	   IGNORE sprintf ( buffer, "%s/%s", s, nm ) ;
696
	   s = null ;
697
	} else {
698
	   IGNORE strcpy ( buffer, s ) ;
699
	   IGNORE sprintf ( buffer + ( t - s ), "/%s", nm ) ;
700
	   s = t + 1 ;
701
	}
702
	input = fopen ( buffer, "r" ) ;
703
	if ( input ) {
704
	    nm = string_copy ( buffer ) ;
705
	    break ;
706
	}
707
    }
708
    if ( input == null ) {
709
	input = fopen ( nm, "r" ) ;
710
	if ( input == null ) {
711
	    char *err = "Set '%s' not found (can't find file %s)" ;
712
	    error ( ERR_SERIOUS, err, sn, nm ) ;
713
	    p = make_object ( sn, OBJ_SUBSET ) ;
714
	    IGNORE add_hash ( subsets, p, no_version ) ;
715
	    p->u.u_info = make_info ( api, file, subset ) ;
716
	    p->u.u_info->implemented = 1 ;
717
	    return ;
718
	}
719
    }
720
    if ( verbose > 1 ) {
721
	if ( subset ) {
722
	    error ( ERR_INFO, "Preprocessing %s [%s] ...", nm, subset ) ;
723
	} else {
724
	    error ( ERR_INFO, "Preprocessing %s ...", nm ) ;
725
	}
726
    }
727
    old_filename = filename ;
728
    old_line_no = line_no ;
729
    old_file = input_file ;
730
    old_pending = input_pending ;
731
    filename = nm ;
732
    line_no = 1 ;
733
    input_file = input ;
734
    input_pending = LEX_EOF ;
735
    p = make_object ( sn, OBJ_SUBSET ) ;
736
    IGNORE add_hash ( subsets, p, no_version ) ;
737
 
738
    /* Print position identifier */
739
    print_subset_name ( output, "+SET", api, file, subset, 0 ) ;
740
    IGNORE fputs ( " := {\n", output ) ;
741
    if ( printing ) print_posn ( output ) ;
742
 
743
    /* Process the input */
744
    while ( c = read_pptoken ( 0 ), c != lex_eof ) {
745
	switch ( c ) {
746
 
747
	    case lex_subset : {
748
		/* Deal with subsets */
749
		int d = 0 ;
750
		c = read_pp_string ( &s, &d ) ;
751
		if ( d ) error ( ERR_SERIOUS, "Illegally bracketed string" ) ;
752
		if ( c != lex_assign ) {
753
		    error ( ERR_SERIOUS, "Syntax error - ':=' expected" ) ;
754
		}
755
		c = read_pptoken ( 1 ) ;
756
		if ( c != lex_open_Hbrace ) {
757
		    error ( ERR_SERIOUS, "Syntax error - '{' expected" ) ;
758
		}
759
		brackets++ ;
760
		if ( printing ) {
761
		    int b = brackets ;
762
		    char *cmd = "+IMPLEMENT" ;
763
		    preproc ( output, api, file, s ) ;
764
		    print_subset_name ( output, cmd, api, file, s, 0 ) ;
765
		    IGNORE fputs ( " ;\n", output ) ;
766
		    do {
767
			c = read_pptoken ( 0 ) ;
768
			if ( c == lex_open_Hbrace ) {
769
			    brackets++ ;
770
			} else if ( c == lex_close_Hbrace ) {
771
			    brackets-- ;
772
			} else if ( c == lex_eof ) {
773
			    char *err = "Can't find end of subset '%s'" ;
774
			    error ( ERR_SERIOUS, err, s ) ;
775
			    goto end_of_file ;
776
			}
777
		    } while ( brackets >= b ) ;
778
		    c = read_pptoken ( 1 ) ;
779
		    if ( c != lex_semicolon ) {
780
			error ( ERR_SERIOUS, "Syntax error - ';' expected" ) ;
781
		    }
782
		    print_posn ( output ) ;
783
		} else {
784
		    if ( streq ( s, subset ) ) {
785
			if ( found ) {
786
			    char *err = "Set '%s' already defined (line %d)" ;
787
			    error ( ERR_SERIOUS, err, sn, p->line_no ) ;
788
			} else {
789
			    found = 1 ;
790
			    printing = 1 ;
791
			    print_posn ( output ) ;
792
			    p->line_no = line_no ;
793
			    end_brackets = brackets ;
794
			}
795
		    }
796
		}
797
		break ;
798
	    }
799
 
800
	    case lex_implement : {
801
		/* Deal with subset uses */
802
		if ( printing ) preproc_subfile ( output, "+IMPLEMENT" ) ;
803
		break ;
804
	    }
805
 
806
	    case lex_use : {
807
		/* Deal with subset uses */
808
		if ( printing ) preproc_subfile ( output, "+USE" ) ;
809
		break ;
810
	    }
811
 
812
	    case lex_set : {
813
		/* Deal with sets */
814
		error ( ERR_SERIOUS, "+SET directive in preprocessor" ) ;
815
		goto default_lab ;
816
	    }
817
 
818
	    case lex_if :
819
	    case lex_ifdef :
820
	    case lex_ifndef : {
821
		if_depth++ ;
822
		else_depth = 0 ;
823
		goto default_lab ;
824
	    }
825
 
826
	    case lex_else : {
827
		if ( if_depth == 0 ) {
828
		    error ( ERR_SERIOUS, "+ELSE without +IF" ) ;
829
		} else {
830
		    if ( else_depth ) {
831
			error ( ERR_SERIOUS, "Duplicate +ELSE" ) ;
832
		    }
833
		    else_depth = 1 ;
834
		}
835
		goto default_lab ;
836
	    }
837
 
838
	    case lex_endif : {
839
		if ( if_depth == 0 ) {
840
		    error ( ERR_SERIOUS, "+ENDIF without +IF" ) ;
841
		} else {
842
		    if_depth-- ;
843
		}
844
		else_depth = 0 ;
845
		goto default_lab ;
846
	    }
847
 
848
	    case lex_string : {
849
		/* Deal with strings */
850
		if ( printing ) {
851
		    IGNORE fprintf ( output, "\"%s\"", buffer ) ;
852
		}
853
		break ;
854
	    }
855
 
856
	    case lex_open_Hbrace : {
857
		/* Start of subset */
858
		brackets++ ;
859
		goto default_lab ;
860
	    }
861
 
862
	    case lex_close_Hbrace : {
863
		/* End of subset */
864
		brackets-- ;
865
		if ( brackets < 0 ) {
866
		    error ( ERR_SERIOUS, "Unmatched '}'" ) ;
867
		    brackets = 0 ;
868
		}
869
		if ( subset && brackets < end_brackets ) {
870
		    printing = 0 ;
871
		}
872
		goto default_lab ;
873
	    }
874
 
875
	    default :
876
	    default_lab : {
877
		/* Deal with simple tokens */
878
		if ( printing ) IGNORE fputs ( buffer, output ) ;
879
		break ;
880
	    }
881
	}
882
    }
883
 
884
    /* End of file */
885
    end_of_file : {
886
	if ( brackets ) {
887
	    error ( ERR_SERIOUS, "Bracket imbalance of %d", brackets ) ;
888
	}
889
	while ( if_depth ) {
890
	    error ( ERR_SERIOUS, "+IF without +ENDIF" ) ;
891
	    if_depth-- ;
892
	}
893
	IGNORE fputs ( "} ;\n", output ) ;
894
	IGNORE fclose ( input ) ;
895
	p->u.u_info = make_info ( api, file, subset ) ;
896
	filename = old_filename ;
897
	line_no = old_line_no ;
898
	input_file = old_file ;
899
	input_pending = old_pending ;
900
	if ( subset && !found ) {
901
	    char *err = "Set '%s' not found (can't find subset '%s')" ;
902
	    error ( ERR_SERIOUS, err, sn, subset ) ;
903
	    p->u.u_info->implemented = 1 ;
904
	}
905
	return ;
906
    }
907
}