/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Crown Copyright (c) 1997
*
* This TenDRA(r) Computer Program is subject to Copyright
* owned by the United Kingdom Secretary of State for Defence
* acting through the Defence Evaluation and Research Agency
* (DERA). It is made available to Recipients with a
* royalty-free licence for its use, reproduction, transfer
* to other parties and amendment for any purpose not excluding
* product development provided that any such use et cetera
* shall be deemed to be acceptance of the following conditions:-
*
* (1) Its Recipients shall ensure that this Notice is
* reproduced upon any copies or amended versions of it;
*
* (2) Any amended version of it shall be clearly marked to
* show both the nature of and the organisation responsible
* for the relevant amendment or amendments;
*
* (3) Its onward transfer from a recipient to another
* party shall be deemed to be that party's acceptance of
* these conditions;
*
* (4) DERA gives no warranty or assurance as to its
* quality or suitability for any purpose and DERA accepts
* no liability whatsoever in relation to any use to which
* it may be put.
*
* $TenDRA: tendra/src/tools/tcc/environ.c,v 1.24 2005/10/22 21:13:57 stefanf Exp $
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "config.h"
#include "cstring.h"
#include "msgcat.h"
#include "filename.h"
#include "list.h"
#include "environ.h"
#include "flags.h"
#include "options.h"
#include "suffix.h"
#include "utility.h"
/*
* THE CURRENT ENVIRONMENTS PATH
*
* The environment path is a colon-separated list of directories which
* are searched for tcc environments.
*/
static char *envpath = ".";
/*
* UPDATE THE ENVIRONMENTS PATH
*
* This routine initialises and updates the environments path. This is
* given by the contents of the system variable TCCENV, plus the default
* directory (environ_dir), plus the current directory.
*/
void
find_envpath(void)
{
char *p = buffer;
char *tcc_env = getenv (TCCENV_VAR);
if (tcc_env) {
IGNORE sprintf (p, "%s:", tcc_env);
p += strlen (p);
}
IGNORE sprintf (p, "%s:.", environ_dir);
if (!streq (buffer, envpath))
envpath = string_copy (buffer);
return;
}
/*
* PRINT THE ENVIRONMENTS PATH
*
* This routine prints the environment path.
*/
void
show_envpath(char *dummy)
{
UNUSED (dummy);
find_envpath ();
MSG_environment_path_is (envpath);
return;
}
/*
* READ AN ENVIRONMENT - AUXILIARY ROUTINE
*
* This routine reads the environment named nm, returning zero if it
* is successful. A return value of 1 indicates that the environment
* could not be found, otherwise 2 is returned.
*
* In the revision to this function, the strategy is to minimize
* copying chars. This routine opens the file, and examines each env
* file a line at a time. Line analysis scans past the key, until
* the first whitespace is found. Then, the routine scans forward to
* the start of the value, and cut the line buffer in half by making
* the whitespace a '\0' character. The routine continues scanning
* the value, performing appropriate substitutions for <vars>.
*
* TODO: The two resulting strings in the buffer (key and value) must
* be strdup'd. A future revision to this function may just read
* entire file into memory, and convert key whitespace to NULL
* characters, thereby breaking up the lines into appropriate tokens,
* and avoiding copying strings around in memory.
*/
static int
read_env_aux(char *nm, hashtable *ht)
{
/* Find the environment */
FILE *f;
char *p, *q;
int line_num;
if (*nm == 0) {
return (1);
} else if (*nm == '/') {
f = fopen (nm, "r");
} else {
p = envpath;
do {
q = buffer;
while (*p && *p != ':') {
*(q++) = *(p++);
}
*(q++) = '/';
IGNORE strcpy (q, nm);
f = fopen (buffer, "r");
} while (f == null && *(p++));
}
if (f == null)
return (1);
/*
* Parse each line of the env file
*/
line_num = 0;
while (fgets (buffer, buffer_size, f) != null) {
char c; /* temp char */
char *p; /* current pointer to scan buffer */
char *key_start; /* points to +, <, > start of key */
int key_length; /* length of key */
char *val_start; /* start of value associated with key */
char *val_end; /* end of value */
char *esc_start; /* start of substitution field, always a '<' */
char *esc_end; /* end of substitution field, always a '>' */
int esc_len; /* number of chars to escape over */
char *sub; /* character substitution for escape
sequences */
int count; /* counter to stop scan at buffer_size */
int line_len; /* length of this buffer */
char *end = NULL; /* end of line */
char *cmd; /* final command string being built */
list dummy; /* final command */
htnode* hn; /* wrapper for command storage */
line_len = strlen(buffer);
count = 1;
p = buffer;
c = *p++;
line_num++;
if (c == ACTION_PREPEND || c == ACTION_APPEND ||
c == ACTION_REPLACE || c == ACTION_QUERY) {
key_start = (p-1);
key_length = 0;
while (c = *p++, is_alphanum(c)) {
if (count++ == buffer_size)
MSG_exceeded_max_line_size (nm, line_num);
key_length++;
}
/* mark off key from val */
*(p-1) = '\0';
/* skip over spacing between key and value */
while (c==' ' || c == '\t') {
c = *p++;
if (count++ == buffer_size)
MSG_exceeded_max_line_size (nm, line_num);
}
/* sanity check */
if (c== '\0') {
MSG_no_value_assigned_to_key (nm, line_num, key_start);
continue;
}
/* All values assigned to a key must be in quotes */
if (c != '"') {
MSG_value_assigned_to_key_must_be_quoted (nm, line_num, key_start);
continue;
}
val_start = p;
/* remove leading quotation mark from val */
*(val_start-1) = ' ';
/* read the value, until the matching close quote */
while (c = *p++, (c != '"' && c != '\n' && c != '\0')) {
if (count++ == buffer_size)
MSG_exceeded_max_line_size (nm, line_num);
if (c=='<') {
int sub_len; /* length of substitution */
int diff; /* difference between two lengths */
int delta; /* direction of growth */
int cnt; /* counter */
int shift_max; /* amount to move */
char *pivot; /* where to start shifting */
/* mark start of <> sequence */
esc_start = (p-1);
esc_len = 2; /* accounts for <, > */
/* expand quote */
while (c = *p++, c!= '>') {
esc_len++;
if (count++ == buffer_size)
MSG_exceeded_max_line_size (nm, line_num);
if (c== '\n' || c== '\0') {
MSG_unmatched_escape_sequence_missing_ra (nm, line_num);
}
if (c== '<') {
MSG_nested_la_ra_escape_sequences_prohibited (nm, line_num);
continue;
}
}
/* mark end of <> sequence */
esc_end = (p-1);
/* find a substitution; all error handling done in
function */
sub = dereference_var(esc_start+1, esc_end, ht,
nm, line_num);
/* find length of substitution */
sub_len = strlen(sub);
/* do we grow or shrink */
diff = (sub_len - esc_len);
/* find the number of characters that must be
moved */
shift_max = strlen (esc_end);
if (!end)
end = (buffer + line_len);
if (diff > 0) { /* grow */
pivot = end;
delta = -1;
}
else { /* shrink */
delta = 1;
pivot = esc_end + 1;
}
/* adjust end pointers and length counters */
end += diff;
line_len += diff;
count += diff;
if (count == buffer_size)
MSG_exceeded_max_line_size (nm, line_num);
/* make room for the substitution */
for (cnt = 0; cnt < shift_max; cnt++) {
*(pivot + (cnt * delta) + diff) =
*(pivot + (cnt * delta));
}
/* perform substitution on resized line */
for (cnt = 0; cnt < sub_len; cnt++)
*(esc_start + cnt) = sub[cnt];
/* advance our scanning pointer */
p = esc_end + diff;
} /* if escape '<' sequence */
} /* while *p != '"' */
/* did we end the val scan on new line or EOF? */
if (c=='\n' || c=='\0') {
MSG_value_assigned_to_key_not_terminated_with_end_quote
(nm, line_num, key_start);
continue;
}
/* mark end of the value */
val_end = (p-1);
/* set close quote to null */
*(val_end) = '\0';
/* build the command string */
cmd = string_append (key_start, val_start, ' ');
key_start = string_copy (key_start);
val_start = (cmd + key_length + 2);
/* if the key/value pair is a tccenv variable, it's
a finished command, and should be executed */
hn = lookup_table(ht, key_start);
if (hn && (hn->flag & TCCENV)) {
/* process the command */
dummy.item = cmd;
dummy.next = null;
process_options (&dummy, environ_optmap, 1);
}
if (*key_start != ACTION_QUERY) {
/* update hashtable with new key/value pair*/
hn = update_table(ht, key_start, val_start,
USR, nm, line_num);
}
}
} /* for each line in the env file */
return (0);
} /* read_env_aux() */
/*
* Lookup value for tccenv(5) variables. This function takes in an
* escape sequence from an env file, and attempts to find a
* definition. For example, <TENDRA_BASEDIR> may be passed in, and
* be mapped to the value supplied previously by -y arguments.
*
* The function looks up TENDRA_* variables first, since they are so
* common. The TENDRA_* variable resolution also consults the shell
* environment, if no -y argument or +TENDRA_* declaration was given
* in an env file. Failing that, the function consults the hash
* table of tccenv key/value mappings. This function performs
* all error handling; it will return a valid char *, or fail.
*/
char *
dereference_var(char *esc_start, char *esc_end, hashtable *ht,
char *nm, int line_num)
{
htnode* hn;
char *sub = NULL;
/* temporarily replace '>' with '\0' to facilitate lookup */
char tmp = *esc_end;
*esc_end = '\0';
/* Attempt to match TENDRA_* env arguments,
which are most likely to occur. */
if (strneq ("TENDRA", esc_start, 6)) {
sub = get_tendra_var(esc_start);
}
/* If we fail to find a TENDRA_* env match, look
up in hashtable */
if (!sub) {
hn = lookup_table(ht, esc_start);
if (hn == NULL) {
MSG_undefined_variable_in_file (esc_start, nm, line_num);
}
sub = hn->val;
}
*esc_end = tmp;
return sub;
}
int environ_count;
hashtable *environ_hashtable;
/*
* Reconcile the table of user-defined env options. At present this
* function just makes sure that non-tccenv(5) variables declared by
* the user were used in the env files. If not, it's likely a
* subtle bug or typo, and a warning issues if the version -v switch
* is used.
*
* Future revisions may also attempt to supply definitions to hash
* keys that were not found during the O(N) initial pass through the
* env files. (That is, the env reading would be O(2N), and attempt
* to finding all possible definitions, including those out of
* order.)
*/
void
reconcile_envopts(void)
{
int i;
htnode *hn;
/* If no -Y args were given whatsoever, give a warning, since a
* mysterious internal error ("tcc: Internal: The tool
* 'C_producer' is not available'") is about to follow during the
* execute stage. This mistake is so fundamental, we give a
* warning even without verbose being set.
*/
if (environ_count == 0) {
MSG_not_invoked_with_any_Yenv_arguments ();
}
/* All subsequent warnings require a verbose flag */
if (!verbose)
return;
/* If the global env table is NULL, no -Y args succeeded, or none
were given */
if (!environ_hashtable) {
/* -Y args given, but failed */
if (environ_count > 0)
MSG_failed_to_load_any_environment_files ();
return;
}
for (i = 0; i < TCC_TBLSZE; i++) {
hn = environ_hashtable->node[i];
if (hn && (hn->flag & USR) &&
!(hn->flag & READ))
MSG_environment_option_declared_but_never_used (hn->file, hn->line_num, hn->key);
}
}
/*
* READ AN ENVIRONMENT
*
* This routine reads the environment named nm, reporting an error if
* it is unsuccessful.
*/
void
read_env(char *nm)
{
int e;
static hashtable *ht;
/* note attempt to load -Y env file */
environ_count++;
if (ht == NULL) {
ht = init_table (TCC_TBLSZE, TCC_KEYSZE, &hash);
environ_hashtable = ht; /* hack */
}
e = read_env_aux(nm, ht);
if (e == 1)
MSG_cant_find_environment (nm);
return;
}
syntax highlighted by Code2HTML, v. 0.9.1