Subversion Repositories tendra.SVN

Rev

Rev 40 | 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 "filename.h"
63
#include "list.h"
64
#include "environ.h"
65
#include "flags.h"
66
#include "options.h"
67
#include "suffix.h"
68
#include "utility.h"
69
 
70
 
71
/*
6 7u83 72
 * THE CURRENT ENVIRONMENTS PATH
73
 *
74
 * The environment path is a colon-separated list of directories which are
75
 * searched for tcc environments.
76
 */
2 7u83 77
 
6 7u83 78
static char *envpath = ".";
2 7u83 79
 
80
 
81
/*
6 7u83 82
 * UPDATE THE ENVIRONMENTS PATH
83
 *
84
 * This routine initialises and updates the environments path. This is given by
85
 * the contents of the system variable TCCENV, plus the default directory
86
 * (environ_dir), plus the current directory.
87
 */
2 7u83 88
 
6 7u83 89
void
90
find_envpath(void)
2 7u83 91
{
6 7u83 92
	char *p = buffer;
93
	char *tcc_env = getenv(TCCENV_VAR);
94
	if (tcc_env) {
95
		IGNORE sprintf(p, "%s:", tcc_env);
96
		p += strlen(p);
97
	}
98
	IGNORE sprintf(p, "%s:.", environ_dir);
99
	if (!streq(buffer, envpath)) {
100
		envpath = string_copy(buffer);
101
	}
102
	return;
2 7u83 103
}
104
 
105
 
106
/*
6 7u83 107
 * PRINT THE ENVIRONMENTS PATH
108
 *
109
 * This routine prints the environment path.
110
 */
2 7u83 111
 
6 7u83 112
void
113
show_envpath(void)
2 7u83 114
{
6 7u83 115
	find_envpath();
116
	error(INFO, "Environment path is '%s'", envpath);
117
	return;
2 7u83 118
}
119
 
120
 
121
/*
6 7u83 122
 * READ AN ENVIRONMENT - AUXILIARY ROUTINE
123
 *
124
 * This routine reads the environment named nm, returning zero if it is
125
 * successful. A return value of 1 indicates that the environment could not be
126
 * found, otherwise 2 is returned.
127
 *
128
 * In the revision to this function, the strategy is to minimize copying chars.
129
 * This routine opens the file, and examines each env file a line at a time.
130
 * Line analysis scans past the key, until the first whitespace is found. Then,
131
 * the routine scans forward to the start of the value, and cut the line buffer
132
 * in half by making the whitespace a '\0' character. The routine continues
133
 * scanning the value, performing appropriate substitutions for <vars>.
134
 *
135
 * TODO: The two resulting strings in the buffer (key and value) must be
136
 * strdup'd. A future revision to this function may just read entire file into
137
 * memory, and convert key whitespace to NULL characters, thereby breaking up
138
 * the lines into appropriate tokens, and avoiding copying strings around in
139
 * memory.
2 7u83 140
*/
141
 
6 7u83 142
int
143
read_env_aux(char *nm, hashtable *ht)
2 7u83 144
{
145
    /* Find the environment */
6 7u83 146
    FILE *f;
147
    char *p, *q;
148
    int   line_num;
149
 
150
    if (*nm == 0) {
151
	return (1);
152
    } else if (*nm == '/') {
153
	f = fopen(nm, "r");
2 7u83 154
    } else {
6 7u83 155
	p = envpath;
2 7u83 156
	do {
6 7u83 157
	    q = buffer;
158
	    while (*p && *p != ':') *(q++) = *(p++);
159
	    *(q++) = '/';
160
	    IGNORE strcpy(q, nm);
161
	    f = fopen(buffer, "r");
162
	} while (f == null && *(p++));
2 7u83 163
    }
6 7u83 164
    if (f == null) {
165
	    return (1);
166
    }
2 7u83 167
 
6 7u83 168
    /*
169
     * Parse each line of the environment file
170
     */
171
    line_num = 0;
172
 
173
    while (fgets(buffer, buffer_size, f) != null) {
174
	char	 c;	     /* temporary character */
175
	char	*p;          /* current pointer to scan buffer */
176
	char	*key_start;  /* points to +, <, > start of key */
177
	int	 key_length; /* length of key */
178
	char	*val_start;  /* start of value associated with key */
179
	char	*val_end;    /* end of value */
180
	char	*esc_start;  /* start of substituion field, always a '<' */
181
	char	*esc_end;    /* end of susbtitution field, always a '>' */
40 7u83 182
	size_t	 esc_len;    /* number of chars to escape over */
6 7u83 183
	char	*sub;	     /* character substitution for escape sequences */
40 7u83 184
	size_t	 count;      /* counter to stop scan at buffer_size */
185
	size_t	 line_len;   /* length of this buffer */
6 7u83 186
	char	*end = NULL; /* end of line */
187
	char	*cmd;	     /* final command string being built */
188
	list	 dummy;	     /* final command */
189
	htnode	*hn;	     /* wrapper for command storage */
190
 
191
	line_len = strlen(buffer);
192
	count = 1;
193
	p = buffer;
194
	c = *p++;
195
	line_num++;
196
 
197
	if (c == '<' || c == '>' || c == '+' || c == '?') {
198
	    key_start = (p - 1);
199
	    key_length = 0;
200
	    while (c = *p++, is_alphanum(c)) {
201
		if (count++ == buffer_size)
202
		    error(FATAL, "%s: line %d: Exceeded max line size", nm,
203
			  line_num);
204
		key_length++;
2 7u83 205
	    }
6 7u83 206
 
207
	    /* mark off key from val */
208
	    *(p - 1) = '\0';
209
 
210
	    /* skip over spacing between key and value */
211
	    while (c == ' ' || c == '\t') {
212
		c = *p++;
213
		if (count++ == buffer_size) {
214
		    error(FATAL, "%s: line %d: Exceeded max line size", nm,
215
			  line_num);
216
		}
2 7u83 217
	    }
6 7u83 218
 
219
	    /* sanity check */
220
	    if (c == '\0') {
221
		error(WARNING, "%s: line %d: No value assigned to key %s",
222
		      nm, line_num, key_start);
223
		continue;
224
	    }
225
 
226
	    /* All values assigned to a key must be in quotes */
227
	    if (c != '"') {
228
		error(WARNING, "%s: line %d: Value assigned to key %s"
229
		       " must be quoted", nm, line_num, key_start);
230
		continue;
231
	    }
232
 
233
	    val_start = p;
234
 
235
	    /* remove leading quotation mark from val */
236
	    *(val_start - 1) = ' ';
237
 
238
	    /* read the value, until the matching close quote */
239
	    while (c = *p++, (c != '"' && c != '\n' && c != '\0')) {
240
		if (count++ == buffer_size) {
241
		    error(FATAL, "%s: line %d: Exceeded max line size", nm,
242
			  line_num);
2 7u83 243
		}
6 7u83 244
 
245
		if (c == '<') {
40 7u83 246
		    size_t sub_len;    /* length of substitution */
247
		    size_t diff;       /* difference between two lengths */
248
		    size_t delta;      /* direction of growth */
249
		    size_t cnt;        /* counter */
250
		    size_t shift_max;  /* amount to move */
6 7u83 251
		    char *pivot;    /* where to start shifting */
252
 
253
		    /* mark start of <> sequence */
254
		    esc_start = (p - 1);
255
		    esc_len = 2; /* accounts for <, > */
256
 
257
		    /* expand quote */
258
		    while (c = *p++, c != '>') {
259
			esc_len++;
260
			if (count++ == buffer_size) {
261
			    error(FATAL, "%s: line %d: Exceeded max line size",
262
				  nm, line_num);
263
			}
264
 
265
			if (c == '\n' || c == '\0') {
266
			    error(FATAL, "%s: line %d: Unmatched escape"
267
				   " sequence, missing >", nm, line_num);
268
			}
269
 
270
			if (c == '<') {
271
			    error(FATAL, "%s: line %d: Nested < > escape "
272
				  " sequences prohibited",
273
				  nm, line_num);
274
			    continue;
275
			}
276
		    }
277
 
278
		    /* mark end of <> sequence */
279
		    esc_end = (p - 1);
280
 
281
		    /*
282
		     * find a substitution; all error handling done in function
283
		     */
284
		    sub = dereference_var(esc_start + 1, esc_end, ht, nm,
285
					  line_num);
286
 
287
		    /* find length of substitution */
288
		    sub_len = strlen(sub);
289
 
290
		    /* do we grow or shrink */
291
		    diff = (sub_len - esc_len);
292
 
293
		    /* find the number of characters that must be moved */
294
		    shift_max = strlen(esc_end);
295
 
296
		    if (!end) {
297
			end = (buffer + line_len);
298
		    }
299
 
300
		    if (diff > 0) {
301
			/* grow */
302
			pivot = end;
303
			delta = -1;
304
		    } else {
305
			/* shrink */
306
			delta = 1;
307
			pivot = esc_end + 1;
308
		    }
309
 
310
		    /* adjust end pointers and length counters */
311
		    end += diff;
312
		    line_len += diff;
313
		    count += diff;
314
		    if (count == buffer_size) {
315
			error(FATAL, "%s: line %d: Exceeded max line size",
316
			      nm, line_num);
317
		    }
318
 
319
		    /* make room for the substitution */
320
		    for (cnt = 0; cnt < shift_max; cnt++){
321
			*(pivot + (cnt * delta) + diff) =
322
			    *(pivot + (cnt * delta));
323
		    }
324
 
325
		    /* perform subsitution on resized line */
326
		    for (cnt = 0; cnt < sub_len; cnt++) {
327
			*(esc_start + cnt) = sub[cnt];
328
		    }
329
 
330
		    /* advance our scanning pointer */
331
		    p = esc_end + diff;
332
		} /* if escape '<' sequence */
333
	    } /* while *p != "" */
334
 
335
	    /* did we end the val scan on new line or EOF? */
336
	    if (c == '\n' || c == '\0') {
337
		error(WARNING, "%s: line %d: Value assigned to key %s"
338
		       " not terminated with end quote",
339
		       nm, line_num, key_start);
340
		continue;
2 7u83 341
	    }
6 7u83 342
 
343
	    /* mark end of the value */
344
	    val_end = (p - 1);
345
 
346
	    /* set close quote to null */
347
	    *(val_end) = '\0';
348
 
349
	    /* build the command string */
350
	    cmd = string_append(key_start, val_start, ' ');
351
	    key_start = string_copy(key_start);
352
	    val_start = (cmd + key_length + 2);
353
 
354
	    /*
355
	     * If the key/value pair is a tccenv variable, it's a finished
356
	     * command, and should be executed.
357
	     */
358
	    hn = lookup_table(ht, key_start);
359
	    if (hn && (hn->flag & TCCENV)) {
360
		/* process the command */
361
		dummy.item = cmd;
362
		dummy.next = null;
363
		process_options(&dummy, environ_optmap, 1);
2 7u83 364
	    }
6 7u83 365
 
366
	    /* update hashtable with new key/value pair*/
367
	    hn = update_table(ht, key_start, val_start, USR, nm, line_num);
368
	} /* if the line is a +, >, < env action command */
369
    } /* for each line in the env file */
370
 
371
    return (0);
372
} /* read_env_aux() */
373
 
374
 
375
/*
376
 * Lookup value for tccenv(5) variables. This function takes in an escape
377
 * sequence from an env file, and attempts to find a definition. For example,
378
 * <TENDRA_BASEDIR> may be passed in, and be mapped to the value supplied
379
 * previously by -y arguments.
380
 *
381
 * The function looks up TENDRA_* variables first, since they are so common.
382
 * The TENDRA_* variable resolution also consults the shell environment, if no
383
 * -y argument or +TENDRA_* declaration was given in an env file. Failing that,
384
 * the function consults the hash table of tccenv key/value mappings. This
385
 * function performs all error handling; it will return a valid char *, or
386
 * fail.
387
 */
388
char *
389
dereference_var(char *esc_start, char *esc_end, hashtable *ht, char *nm,
390
		int line_num)
391
{
392
	htnode* hn;
393
	char *sub = NULL;
394
	/* temporarily replace '>' with '\0' to facilitate lookup */
395
	char tmp = *esc_end;
396
	*esc_end = '\0';
397
 
398
	/*
399
	 * Attempt to match TENDRA_* env arguments, which are most likely to
400
	 * occur.
401
	 */
402
	if (!strncmp("TENDRA", esc_start, 6)) {
403
		sub = find_path_subst(esc_start);
2 7u83 404
	}
6 7u83 405
 
406
	/* If we fail to find a TENDRA_* env match, look
407
	   up in hashtable */
408
	if (!sub) {
409
		hn = lookup_table(ht, esc_start);
410
		if (hn == NULL) {
411
			*esc_end = tmp;
412
			error(FATAL, "Undefined variable <%s> in %s line %d",
413
			      esc_start, nm, line_num);
414
		}
415
		sub = hn->val;
416
	}
417
 
418
	*esc_end = tmp;
419
	return sub;
2 7u83 420
}
421
 
422
 
423
/*
6 7u83 424
 * Reconcile the table of user-defined env options. At present this function
425
 * just makes sure that non-tccenv(5) variables declared by the user were used
426
 * in the env files. If not, it's likely a subtle bug or typo, and a warning
427
 * issues if the version -v switch is used.
428
 *
429
 * Future revisions may also attempt to supply definitions to hash keys that
430
 * were not found during the O(N) initial pass through the env files. (That is,
431
 * the env reading would be O(2N), and attempt to finding all possible
432
 * definitions, including those out of order.)
433
 */
2 7u83 434
 
6 7u83 435
void
436
reconcile_envopts(void)
437
{
438
	int i;
439
	htnode *hn;
2 7u83 440
 
6 7u83 441
	/*
442
	 * If no -Y args were given whatsoever, give a warning, since a
443
	 * mysterious internal error ("tcc: Internal: The tool 'C_producer' is
444
	 * not available'") is about to follow during the execute stage. This
445
	 * mistake is so fundamental, we give a warning even without verbose
446
	 * being set.
447
	 */
448
	if (environ_count == 0) {
449
		error(WARNING, "not invoked with any -Y env arguments");
450
	}
451
 
452
	/* All subsequent warnings require a verbose flag */
453
	if (!verbose) {
454
		return;
455
	}
456
 
457
	/*
458
	 * If the global env table is NULL, no -Y args succeeded, or none were
459
	 * given.
460
	 */
461
	if (!environ_hashtable) {
462
		/* -Y args given, but failed */
463
 
464
		if (environ_count > 0) {
465
			error(WARNING, "failed to load any environment files");
466
			return;
467
		}
468
	}
469
 
470
	for (i = 0; i < TCC_TBLSIZE; i++) {
471
		hn = environ_hashtable->node[i];
472
 
473
		if (hn && (hn->flag & USR) && !(hn->flag & READ)) {
474
			error(WARNING,
475
			      "%s, line %d: environment option %s declared"
476
			      " but never used", hn->file, hn->line_num,
477
			      hn->key);
478
		}
479
	}
480
}
481
 
482
/*
483
 * READ AN ENVIRONMENT
484
 *
485
 * This routine reads the environment named nm, reporting an error if it is
486
 * unsuccessful.
487
 */
488
 
489
void
490
read_env(char *nm)
2 7u83 491
{
6 7u83 492
	int e;
493
	static hashtable *ht;
494
 
495
	/* note attempt to load -Y env file */
496
	environ_count++;
497
 
498
	if (ht == NULL) {
499
		ht = init_table(TCC_TBLSIZE, TCC_KEYSIZE, &hash);
500
		environ_hashtable = ht; /* hack */
501
	}
502
 
503
	e = read_env_aux(nm, ht);
504
	if (e == 1) {
505
		error(WARNING, "Can't find environment, '%s'", nm);
506
	}
507
	return;
2 7u83 508
}
6 7u83 509
 
510
void
511
dump_env(void)
512
{
513
	htnode *hn;
514
	int i;
515
 
516
	if (environ_hashtable == NULL) {
517
		error(FATAL, "No environment information found\n");
518
	}
519
 
40 7u83 520
	(void)printf("Environment dump:\n");
6 7u83 521
	/* Traverse the hash tree and print all data in it */
522
	for (i = 0; i < TCC_TBLSIZE; i++) {
523
		hn = environ_hashtable->node[i];
524
 
525
		if (hn) {
526
			printf("\t%s = %s\n", hn->key, hn->val);
527
		}
528
	}
529
}