/*
 * 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/filename.c,v 1.11 2005/11/04 09:08:13 stefanf Exp $
 */

#include <ctype.h>

#include "config.h"
#include "cstring.h"
#include "fmm.h"
#include "msgcat.h"

#include "filename.h"
#include "list.h"
#include "external.h"
#include "flags.h"
#include "startup.h"
#include "suffix.h"
#include "utility.h"


/*
 *    CASE SENSITIVITY FLAG
 *
 *    This flag may be set to true to make tcc ignore case in filename
 *    suffixes.
 */

boolean case_insensitive = 0;


/*
 *    CONVERT A STRING TO LOWER CASE
 *
 *    This routine converts the string s to lower case.
 */

static void
to_lower_case(char *s)
{
	while (*s != '\0') {
		*s = tolower ((unsigned char)*s);
		s++;
	}
	return;
}


/*
 *    SUFFIX OVERRIDES
 *
 *    This table contains the strings which are used when the suffix
 *    overrides are set from the command line.  Initially, it is
 *    empty.  This table needs to be kept in step with Table 1.
 */

char *suffixes [TYPE_ARRAY_SIZE] = {
	null,	/* C_SOURCE */
	null,	/* PREPROC_C */
	"cpp",	/* CPP_SOURCE */
	null,	/* PREPROC_CPP */
	null,	/* INDEP_TDF */
	null,	/* DEP_TDF */
	null,	/* AS_SOURCE */
	null,	/* PREPROC_AS */
	null,	/* BINARY_OBJ */
	null,	/* EXECUTABLE */
	null,	/* PRETTY_TDF */
	null,	/* PL_TDF */
	null,	/* TDF_ARCHIVE */
	null,	/* MIPS_G_FILE */
	null,	/* MIPS_T_FILE */
	null,	/* C_SPEC */
	null,	/* CPP_SPEC */
	null,  	/* ERROR_FILE */
	null,	/* STARTUP_FILE */
	null,	/* UNKNOWN_TYPE */
	null,	/* INDEP_TDF_COMPLEX (dummy type) */
	null,	/* C_SPEC_1 (dummy type) */
	null,	/* C_SPEC_2 (dummy type) */
	null,	/* INDEP_TDF_AUX (dummy type) */
	null	/* BINARY_OBJ_AUX (dummy type) */
};


/*
 *    FILE STORAGE LOCATIONS
 *
 *    Output files may be stored either in the temporary directory, tempdir,
 *    or the work directory, workdir.
 */

char *tempdir = null;
char *workdir = null;


/*
 *    FIND THE BASE NAME OF A FILE
 *
 *    This routine returns the basename of the file name s.
 */

char *
find_basename(char *s)
{
	char *r = strrchr (s, '/');
	if (r != NULL)
		return (r + 1);
	return (s);
}


/*
 *    FIND THE FULL NAME OF A FILE
 *
 *    This routine returns the full name of the file name s.
 */

char *
find_fullname(char *s)
{
	static char *pwd = null;
	if (*s == '/') return (s);
	if (pwd == null) {
		if (get_cwd (buffer, buffer_size)) {
			pwd = string_concat (buffer, "/");
		} else {
			MSG_cant_determine_current_working_directory ();
			pwd = "";
		}
	}
	return (string_concat (pwd, s));
}


/*
 *    SPLIT OFF THE SUFFIX OF A FILE NAME
 *
 *    This routine splits the file name s into two part, the base part and
 *    the file suffix, and returns the latter.
 */

static char *
split_name(char *s)
{
	char *p = strrchr (s, '.');
	if (p != NULL) {
		*p = '\0';
		if (case_insensitive) {
			/* Allow for case insensitive systems */
			to_lower_case (p + 1);
		}
		return (p + 1);
	}
	return ("");
}


/*
 *    CREATE A NEW FILENAME
 *
 *    This routine allocates a new filename structure.
 */

static filename *
new_filename(void)
{
	return (xalloc (sizeof (filename)));
}


/*
 *    ADD A FILENAME TO A LIST
 *
 *    This routine joins the two filename lists, p and q, returning the
 *    result.
 */

filename *
add_filename(filename *p, filename *q)
{
	filename *r;
	if (p == null) return (q);
	if (q == null) return (p);
	for (r = p; r->next != null; r = r->next) /* empty */;
	r->next = q;
	return (p);
}


/*
 *    CONVERT A KEY LETTER TO A FILE TYPE
 *
 *    This routine converts the letter s, which can be a file suffix (if
 *    suff is true), or a stage identifier (otherwise), to a file type.
 *    This routine needs to be kept in step with Table 1 and Table 2.
 */

int
find_type(int s, int suff)
{
	switch (s) {
	case C_SOURCE_KEY : return (C_SOURCE);
	case PREPROC_C_KEY : return (PREPROC_C);
	case CPP_SOURCE_KEY : return (CPP_SOURCE);
	case PREPROC_CPP_KEY : return (PREPROC_CPP);
	case AS_SOURCE_KEY : return (AS_SOURCE);
	case PREPROC_AS_KEY : return (PREPROC_AS);
	case BINARY_OBJ_KEY : return (BINARY_OBJ);
	case C_SPEC_KEY : return (C_SPEC);
	case CPP_SPEC_KEY : return (CPP_SPEC);
	}
	if (!checker) {
		switch (s) {
		case INDEP_TDF_KEY : return (INDEP_TDF);
		case DEP_TDF_KEY : return (DEP_TDF);
		case PRETTY_TDF_KEY : return (PRETTY_TDF);
		}
	}
	if (suff) return (DEFAULT_TYPE);
	switch (s) {
	case MIPS_G_FILE_KEY : return (MIPS_G_FILE);
	case MIPS_T_FILE_KEY : return (MIPS_T_FILE);
	case STARTUP_FILE_KEY : return (STARTUP_FILE);
	case ALL_KEY : return (ALL_TYPES);
	}
	if (!checker) {
		switch (s) {
		case PL_TDF_KEY : return (PL_TDF);
		case TDF_ARCHIVE_KEY : return (TDF_ARCHIVE);
		}
	}
	MSG_unknown_file_type (s);
	return (UNKNOWN_TYPE);
}


/*
 *    FIND A FILE SUFFIX
 *
 *    This routine converts a file type, t, into the corresponding file
 *    suffix.  It needs to be kept in step with Table 1 and Table 2.
 */

static char *
file_suffix(int t)
{
	static char suff [3];
	suff [0] = 0;
	suff [1] = 0;
	suff [2] = 0;
	switch (t) {
	case C_SOURCE : suff [0] = C_SOURCE_KEY; break;
	case PREPROC_C : suff [0] = PREPROC_C_KEY; break;
	case CPP_SOURCE : suff [0] = CPP_SOURCE_KEY; break;
	case PREPROC_CPP : suff [0] = PREPROC_CPP_KEY; break;
	case INDEP_TDF : suff [0] = INDEP_TDF_KEY; break;
	case INDEP_TDF_AUX : {
		suff [0] = INDEP_TDF_KEY;
		suff [1] = EXTRA_KEY;
		break;
	}
	case DEP_TDF : suff [0] = DEP_TDF_KEY; break;
	case AS_SOURCE : suff [0] = AS_SOURCE_KEY; break;
	case PREPROC_AS : suff [0] = PREPROC_AS_KEY; break;
	case BINARY_OBJ : suff [0] = BINARY_OBJ_KEY; break;
	case BINARY_OBJ_AUX : {
		if ((use_sparc_cc == 1) && use_system_cc) {
			suff [0] = '.';
			suff [1] = BINARY_OBJ_KEY;
		} else {
			suff [0] = BINARY_OBJ_AUX_KEY;
		}
		break;
	}
	case PRETTY_TDF : suff [0] = PRETTY_TDF_KEY; break;
	case PL_TDF : suff [0] = PL_TDF_KEY; break;
	case MIPS_G_FILE : suff [0] = MIPS_G_FILE_KEY; break;
	case MIPS_T_FILE : suff [0] = MIPS_T_FILE_KEY; break;
	case C_SPEC : suff [0] = C_SPEC_KEY; break;
	case CPP_SPEC : suff [0] = CPP_SPEC_KEY; break;
	}
	if (suff [0]) {
		if (case_insensitive && isupper ((unsigned char) suff [0])) {
			/* Make allowances for case insensitive systems */
			to_lower_case (suff);
			suff [1] = suff [0];
		}
		return (suff);
	}
	MSG_illegal_file_type ();
	return (file_suffix (DEFAULT_TYPE));
}


/*
 *    NEXT FILENAME IS ACTUALLY AN INPUT OPTION
 *
 *    Some command-line options, for example, system libraries, are treated
 *    like input files.  This flag is set to indicate that the next input
 *    file is actually an input option.
 */

boolean option_next = 0;


/*
 *    COUNT OF NUMBER OF INPUT FILES
 *
 *    The number of calls to find_filename is recorded.  Input options are
 *    excluded from this count.
 */

int no_input_files = 0;


/*
 *    ALLOCATE A NEW UNIQUE IDENTIFIER
 *
 *    Each file is assigned a unique number which identifies it and all the
 *    files derived from it.  This macro assigns a new unique number.
 */

static int uniq_no = 0;
#define new_unique()	(uniq_no++)


/*
 *    CREATE A FILENAME FROM A PATHNAME
 *
 *    This routine creates a new filename structure, corresponding to the
 *    file with name s and type t.  If t is UNKNOWN_TYPE then the actual
 *    file type is deduced from the file suffix.  This routine needs to
 *    be kept in step with Table 1, Table 2 and Table 3.
 */

filename *
find_filename(char *s, int t)
{
	filename *p = new_filename ();
	char *b = string_copy (find_basename (s));
	char *e = split_name (b);
	int i;

	/* Find the file type */
	if (suffix_overrides && t == UNKNOWN_TYPE) {
		for (i = 0; i < TYPE_ARRAY_SIZE; i++) {
			if (suffixes [i] != null && streq (e, suffixes [i])) {
				if (checker) {
					if (i == PL_TDF || i == TDF_ARCHIVE) continue;
				}
				t = i;
				break;
			}
		}
	}
	if (t == UNKNOWN_TYPE) {
		if (e [0]) {
			if (e [1]) {
				if (e [2]) {
					/* Length >= 3 */
					if (streq (e, CPP_2_SUFFIX)) {
						t = CPP_SOURCE;
					} else if (streq (e, PREPROC_CPP_2_SUFFIX)) {
						t = PREPROC_CPP;
					} else if (streq (e, AS_2_SUFFIX)) {
						t = AS_SOURCE;
					} else {
						t = DEFAULT_TYPE;
					}
				} else {
					/* Length == 2 */
					if (e [1] == EXTRA_KEY) {
						t = find_type (e [0], 1);
					} else if (streq (e, CPP_1_SUFFIX)) {
						t = CPP_SOURCE;
					} else if (streq (e, PREPROC_CPP_1_SUFFIX)) {
						t = PREPROC_CPP;
					} else if (streq (e, CPP_SPEC_1_SUFFIX)) {
						t = CPP_SPEC;
					} else if (checker) {
						t = DEFAULT_TYPE;
					} else if (streq (e, PL_TDF_SUFFIX)) {
						t = PL_TDF;
					} else if (streq (e, TDF_ARCHIVE_SUFFIX)) {
						t = TDF_ARCHIVE;
					} else {
						t = DEFAULT_TYPE;
					}
				}
			} else {
				/* Length == 1 */
				t = find_type (e [0], 1);
			}
		} else {
			/* Length == 0 */
			t = DEFAULT_TYPE;
		}
	}

	/* Return the result */
	p->name = s;
	p->bname = b;
	p->uniq = new_unique ();
	p->type = t;
	if (option_next) {
		p->storage = INPUT_OPTION;
		option_next = 0;
	} else {
		p->storage = INPUT_FILE;
		no_input_files++;
	}
	p->final = 0;
	p->aux = null;
	p->next = null;
	return (p);
}


/*
 *    FIND WHERE TO STORE FILES OF A GIVEN TYPE
 *
 *    This routine returns the storage class for files of type t.  Note
 *    that this may be PRESERVED_FILE which is only a legal storage type
 *    when it is passed to make_filename (which turns it into OUTPUT_FILE).
 */

int
where(int t)
{
	if (!keeps [t]) return (TEMP_FILE);
	if (!stops [t]) return (PRESERVED_FILE);
	return (OUTPUT_FILE);
}


/*
 *    CREATE A FILENAME FROM ANOTHER FILENAME
 *
 *    This routine creates a new filename structure by forming the file
 *    derived from p which has type t and storage s.  This routine needs
 *    to be kept in step with Table 1 and Table 2.
 */

filename *
make_filename(filename *p, int t, int s)
{
	boolean f = 0;
	char *b, *d, *e;
	char *nm = null;
	filename *q = new_filename ();

	/* Examine the storage class */
	switch (s) {
	case INPUT_FILE :
	case INPUT_OPTION : {
		/* This shouldn't occur */
		d = null;
		break;
	}
	case OUTPUT_FILE : {
		/* Check output file name */
		if (final_name) {
			static boolean used_final_name = 0;
			if (used_final_name) {
				MSG_can_only_name_one_file_with_o ();
			} else {
				nm = final_name;
				b = find_basename (nm);
#ifdef EXECUTABLE_SUFFIX
				if (t == EXECUTABLE && strchr (b, '.') == null) {
					/* Add '.exe' suffix if necessary */
					nm = string_concat (nm, EXECUTABLE_SUFFIX);
					b = string_concat (b, EXECUTABLE_SUFFIX);
				}
#endif
				used_final_name = 1;
				f = 1;
			}
		}
		d = workdir;
		break;
	}
	case PRESERVED_FILE : {
		/* Preserved files are turned into output files */
		d = workdir;
		s = OUTPUT_FILE;
		break;
	}
	case TEMP_FILE : {
		/* Temporary files */
		d = tempdir;
		break;
	}
	default : {
		MSG_illegal_storage_type ();
		d = null;
		break;
	}
	}

	/* Find the file name */
	if (nm == null) {
		if (p != null && p->type == t) {
			nm = find_basename (p->name);
			if (d != null) {
				IGNORE sprintf (buffer, "%s/%s", d, nm);
				nm = string_copy (buffer);
			}
			b = p->bname;
		} else if (s == TEMP_FILE && p != null && !verbose) {
			switch (t) {
			case BINARY_OBJ_AUX :
			case BINARY_OBJ : break;
			case C_SPEC : break;
			case CPP_SPEC : break;
			case INDEP_TDF_AUX :
			case INDEP_TDF : {
				if (make_archive || make_complex || make_pretty ||
					tokdef_name) {
					break;
				}
				goto default_lab;
			}
			default :
				default_lab : {
					b = p->bname;
					e = file_suffix (t);
					IGNORE sprintf (buffer, "%s/%s.%s", d, TEMP_NAME, e);
					nm = string_copy (buffer);
					break;
				}
			}
		}
	}

	/* Find the file name */
	if (nm == null) {
		if (p == null || make_up_names) {
			static int seq = 0;
			IGNORE sprintf (buffer, MADE_UP_NAME, seq++);
			b = string_copy (buffer);
		} else {
			b = p->bname;
		}
		e = file_suffix (t);
		if (d == null) {
			IGNORE sprintf (buffer, "%s.%s", b, e);
		} else {
			IGNORE sprintf (buffer, "%s/%s.%s", d, b, e);
		}
		nm = string_copy (buffer);
	}

	/* Fill in the fields of the result */
	SET (b);
	q->name = nm;
	q->bname = b;
	q->uniq = (p ? p->uniq : new_unique ());
	q->type = t;
	q->storage = s;
	q->final = f;
	q->aux = null;
	q->next = null;
	return (q);
}


syntax highlighted by Code2HTML, v. 0.9.1