File:  [MW Coherent from dump] / coherent / b / STREAMS / conf_79 / mklink.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:36 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * Create a compile/link manifest for the system kernel based on the
 * configured devices and the presence of files in the configuration
 * directories.
 */

/*
 *-IMPORTS:
 *	<sys/compat.h>
 *		CONST
 *		USE_PROTO
 *		ARGS ()
 *	<stdio.h>
 *		NULL
 *		stdin
 *		stdout
 *		fopen ()
 *		fclose ()
 *		fputs ()
 *		fputc ()
 *	<stdlib.h>
 *		getenv ()
 *	<time.h>
 *		time_t
 *		localtime ()
 *		time ()
 *	<string.h>
 *		strerror ()
 *		strftime ()
 *	"ehand.h"
 *		ehand_t
 *		PUSH_HANDLER ()
 *		POP_HANDLER ()
 *		CHAIN_ERROR ()
 *		throw_error ()
 *	"buildobj.h"
 *		build_t
 *		builder_alloc ()
 *		builder_free ()
 *		build_begin ()
 *		build_add ()
 *		build_end ()
 *		build_release ()
 *		build_error ()
 *	"mdev.h"
 *		MD_ENABLED
 *		mdev_t
 *		mdevices ()
 *	"symbol.h"
 *		symbol_t
 *	"read.h"
 *		READ_EOF
 *		input_t
 *		read_char ()
 *		read_error ()
 *		read_close ()
 *	"mkinput.h"
 *		make_file_input ()
 */

#include <sys/compat.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include "ehand.h"
#include "buildobj.h"
#include "mdev.h"
#include "symbol.h"
#include "read.h"
#include "mkinput.h"

#include "mkconf.h"

/*
 * For entries that are OS-specific, wrap up the choice in a macro.
 */

#ifdef	__MSDOS__

#define	DOS_OR_UNIX(dos,unix)	dos

#else

#define	DOS_OR_UNIX(dos, unix)	unix

#endif


/*
 * Testing for the existence of a file may seem simple, but there are some
 * subtleties that can trip the unwary, such as which function to use to test
 * for the existence of the object?
 *
 *  + fopen () is in ISO C and checks for (a) existence of the named object,
 *    (b) whether it is readable. It may return a useful value in 'errno'
 *    which can be used to (i) issue diagnostics in the user's local language,
 *    (ii) cope with specific kinds of error gracefully, since support for an
 *    E... code defined in an extension to C library behaviour such as POSIX.1
 *    can be tested for with the #ifdef directive.
 *  - fopen () can be slow.
 *
 *  + access () can test quickly for readability and existence, is present in
 *    POSIX.1.
 *  - access () isn't in ISO C, and some older Unix systems do things rather
 *    differently than POSIX specifies.
 *
 *  + fstat () returns all the information that access () does, and more. It
 *    is older and more likely to be present as a local extension to C in non-
 *    POSIX systems. fstat () can distinguish between real files and file-like
 *    objects such as named pipes.
 *  - It is usually possible to fstat () objects that cannot be accessed,
 *    which means extra complexity such as getuid () if restrictive
 *    permissions are set up. Testing for unusual objects such as pipes may
 *    reduce portability to systems which don't possess such objects.
 *
 * Additional considerations when using a system that may involve a large
 * directory tree is whether to walk the tree with chdir () rather than
 * building large pathnames which require expensive rescanning by the
 * operating system, and so forth.
 *
 * This file uses fopen () and simple macro-substitution in pathnames, since
 * performance is a lesser concern than portability. As for permissions, the
 * user of this program should be highly priveleged in order to be able to
 * specify the contents of the kernel code; the user has to be able to read
 * the files we scan for in order to compile or link them during the actual
 * kernel build phase.
 */

struct macro_spec {
	char		ms_macrochar;	/* character after '%' sign */
	CONST char    *	ms_envname;	/* environment variable to override */
	CONST char    *	ms_default;
	CONST char    *	ms_value;		/* Actual value */
} macros [] = {
	{ 'p' },				/* built at run-time */
	{ 'P', "CONFPATH", DOS_OR_UNIX (".", ".") },
	{ 'T', "TMP", DOS_OR_UNIX (".", ".") },
	{ 'o', "OBJEXT", DOS_OR_UNIX ("obj", "o") },
	{ 'O', "LIBEXT", DOS_OR_UNIX ("lib", "a") },
	{ 'c', "CEXT", DOS_OR_UNIX ("c", "c") },
	{ 'C', "CPPEXT", DOS_OR_UNIX ("cpp", "cc") }
};


/*
 * Since all the entries in the "make_spec" are strings which we want to treat
 * in a uniform way, we arrange things as an array rather than a structure.
 */

enum {
	TEST_FILE,
	LINK_ENTRY,
	COMPILE_RULES,
	CLEAN_ENTRY,

	MAKE_MAX
};


/*
 * Special entries for use to mean "*exactly* the same as" some previously
 * generated entry. This save generating multiple copies of the macroexpanded
 * text to save memory.
 */

CONST char SAME_AS [MAKE_MAX] [1];


/*
 * Structure of make specifications. The macroexpanded versions are kept
 * around so that the strings can be emitted in appropriate phases of the
 * make-file generation.
 */

typedef	struct make_spec	make_t;

struct make_spec {
	CONST char    *	m_specs [MAKE_MAX];
	make_t	      *	m_next;
};

make_t config_commands [] = {
	{ { "%P/%p/Driver.%O", "%P/%p/Driver.%O ", NULL, NULL } },
	{ { "%P/%p/Driver.%o", "%P/%p/Driver.%o ", NULL, NULL } },
	{ { "%P/%p/Space.%c", "%T/%p.%o ",
		"%2: %1\n\t$(CC) $(CFLAGS) -o"
			DOS_OR_UNIX ("", " ") "%2 -c %1\n\n",
		SAME_AS [LINK_ENTRY] } },
	{ { "%P/%p/Space.%C", "%T/%p.%o",
		"%2: %1\n\t$(CC) $(CFLAGS) -o"
			DOS_OR_UNIX ("", " ") "%2 -c %1\n\n",
		SAME_AS [LINK_ENTRY] } }
};

make_t stub_commands [] = {
	{ { "%P/%p/Stub.%o", "%P/%p/Stub.%o ", NULL, NULL } }
};

#define	ARRAY_LENGTH(a)		(sizeof (a) / sizeof (* a))


/*
 * Check the named file for existence.
 */

#ifdef	USE_PROTO
LOCAL int file_exists (CONST char * name)
#else
LOCAL int
file_exists ARGS ((name))
CONST char    *	name;
#endif
{
	FILE	      *	temp;

	if ((temp = fopen (name, "r")) == NULL)
		return 0;

	fclose (temp);
	return 1;
}


/*
 * Set up macros, checking environment variables and selecting defaults. This
 * function also sets up the 'prefix' macro variable.
 */

#ifdef	USE_PROTO
LOCAL void (init_macros) (CONST char * prefix)
#else
LOCAL void
init_macros ARGS ((prefix))
CONST char    *	prefix;
#endif
{
	int		i;

	for (i = 0 ; i < ARRAY_LENGTH (macros) ; i ++) {

		if (macros [i].ms_envname == NULL)
			macros [i].ms_value = prefix;

		if (macros [i].ms_value == NULL &&
		    (macros [i].ms_value =
				getenv (macros [i].ms_envname)) == NULL)
			macros [i].ms_value = macros [i].ms_default;
	}
}


/*
 * Macroexpand a single entry in a make specification.
 */

#ifdef	USE_PROTO
LOCAL void (expand_line) (make_t * spec, make_t * target, build_t * heap,
			  int line)
#else
LOCAL void
expand_line ARGS ((spec, target, heap, line))
make_t	      *	spec;
make_t	      *	target;
build_t	      *	heap;
int		line;
#endif
{
	int		i;
	CONST char    *	scan;

	/*
	 * Check to see if the spec wants to be identical to a
	 * previous entry.
	 */

	scan = spec->m_specs [line];

	for (i = 0 ; i < line ; i ++)
		if (scan == SAME_AS [i]) {

			target->m_specs [line] = target->m_specs [i];
			return;
		}

	/*
	 * We will need to build a new entry in the heap.
	 */

	if ((i = build_begin (heap, 0, NULL)) != 0)
		throw_error ("Cannot begin make line, error %s",
			     build_error (i));

	for (;;) {
		CONST char    *	start;
		char		ch;

		/*
		 * Begin by moving any constant stuff to the output.
		 */

		start = scan;

		while ((ch = * scan ++) != 0 && ch != '%')
			;	/* DO NOTHING */

		/*
		 * If we have hit the last character in the source string,
		 * add that into the section we copy to the output, since it
		 * make manipulating the strings simpler :-).
		 */

		if ((i = scan - start - (ch != 0)) > 0 &&
		    (i == build_add (heap, i, start)) != 0)
			throw_error ("Unable to add to make macro, error %s",
				     build_error (i));

		if (ch == 0) {
			/*
			 * End the macro generation and record the start
			 * address of the generated text in the output record.
			 */

			if ((target->m_specs [line] =
					build_end (heap, NULL)) == NULL)
				throw_error ("Error ending make macro");
			return;
		}


		/*
		 * To get here we must have hit a '%'-sign, so deal with it.
		 *
		 * Our test for the numeric macros assumes that the digits
		 * have consecutive character codes in the executuion
		 * environment.
		 */

		ch = * scan ++;

		if (ch == 0)
			throw_error ("Bad macro specification in expand_line ()");
		else if (ch > '0' && ch - '1' < line)
			start = target->m_specs [ch - '1'];
		else if (ch == '%')
			start = "%";
		else {
			/*
			 * Scan the macro table for a match on 'ch'.
			 */

			start = NULL;

			for (i = 0 ; i < ARRAY_LENGTH (macros) ; i ++)
				if (macros [i].ms_macrochar == ch) {

					start = macros [i].ms_value;
					break;
				}
		}


		/*
		 * Copy the macro expansion to the destination.
		 */

		if (start != NULL &&
		    (i = build_add (heap, strlen (start), start)) != 0)
			throw_error ("Unable to add to make macro, error %s",
				     build_error (i));
	}
}


/*
 * Macroexpand the make specification template, building the target macros in
 * a build heap. If the file named by the first line does not exist, then
 * discard that name and return, otherwise expand the rest of the macros and
 * return an indication that the caller should record this entry.
 */

#ifdef	USE_PROTO
LOCAL int (expand_spec) (make_t * spec, make_t * target, build_t * heap)
#else
LOCAL int
expand_spec ARGS ((spec, target, heap))
make_t	      *	spec;
make_t	      *	target;
build_t	      *	heap;
#endif
{
	int		i;

	expand_line (spec, target, heap, TEST_FILE);

	if (! file_exists (target->m_specs [TEST_FILE])) {

		if (build_release (heap, target->m_specs [TEST_FILE]) != 0)
			throw_error ("Cannot release macro memory in expand_spec ()");

		return 0;
	}

	for (i = TEST_FILE + 1 ; i < MAKE_MAX ; i ++) {

		if (spec->m_specs [i] == NULL) {

			target->m_specs [i] = NULL;
			continue;
		}

		expand_line (spec, target, heap, i);
	}

	return 1;
}


/*
 * This function builds up part of a makefile specification.
 */

#ifdef	USE_PROTO
LOCAL make_t * (make_make) (build_t * heap, CONST char * prefix,
			    make_t * specs, int nspecs, make_t * makelist)
#else
LOCAL make_t *
make_make ARGS ((heap, prefix, specs, nspecs, makelist))
build_t	      *	heap;
CONST char    *	prefix;
make_t	      *	specs;
int		nspecs;
make_t	      *	makelist;
#endif
{
	int		i;
	make_t		temp;
	make_t	      *	scan;

	init_macros (prefix);

	for (i = 0 ; i < nspecs ; i ++) {
		/*
		 * Expand the specifications, and then test to see if the file
		 * named by the first part of the expanded specification
		 * exists.
		 */

		if (expand_spec (specs + i, & temp, heap)) {
			/*
			 * Add the specification to the list of items to be
			 * included in the makefile. We do a quick scan for
			 * duplicate file-name strings, just for paranoia.
			 */

			for (scan = makelist ; scan != NULL ;
			     scan = scan->m_next) {

				if (strcmp (scan->m_specs [TEST_FILE],
					    temp.m_specs [TEST_FILE]) == 0)
					continue;
			}

			if ((scan = (make_t *)
					build_malloc (heap,
						      sizeof (temp))) == NULL)
				throw_error ("Out of memory in make_make ()");

			* scan = temp;
			scan->m_next = makelist;
			makelist = scan;
		}
	}

	return makelist;
}


/*
 * Iterate over all the mdevice entries and run over either the 'config' or
 * 'stub' make-file specifications.
 */

#ifdef	USE_PROTO
LOCAL void (write_makefile) (FILE * out, input_t * example, build_t * heap)
#else
LOCAL void
write_makefile ARGS ((out, example, heap))
FILE	      *	out;
input_t	      *	example;
build_t	      *	heap;
#endif
{
	mdev_t	      *	mdevp;
	make_t	      *	spec = NULL;
	int		ch;

	for (mdevp = mdevices () ; mdevp != NULL ; mdevp = mdevp->md_next) {
		int		config = mdevp->md_configure == MD_ENABLED;

		spec = make_make (heap, mdevp->md_prefix->s_data,
				  config ? config_commands : stub_commands,
				  config ? ARRAY_LENGTH (config_commands) :
					   ARRAY_LENGTH (stub_commands),
				  spec);
	}


	/*
	 * Actually generate the output from a template file, expanding '%'-
	 * commands in the template into lists of strings as generated above.
	 */

	while ((ch = read_char (example)) != READ_EOF) {
		make_t	      *	scan;

		switch (ch) {

		case '%':
			switch (ch = read_char (example)) {

			case '%':
				fputc (ch, out);
				break;

			case 'T':
				ch = TEST_FILE;
				goto write_list;

			case 'L':
				ch = LINK_ENTRY;
				goto write_list;

			case 'C':
				ch = COMPILE_RULES;
				goto write_list;

			case 'R':

				ch = CLEAN_ENTRY;
write_list:
				for (scan = spec ; scan != NULL ;
				     scan = scan->m_next) {

					if (scan->m_specs [ch] == NULL)
						continue;

					fputs (scan->m_specs [ch], out);
				}

				break;

			default:
				read_error (example);
				throw_error ("Bad %%-entry in template file");
			}
			break;

		default:
			fputc (ch, out);
			break;
		}
	}
}


/*
 * Write out a detailed set of compile/link commands which will set up a new
 * kernel. It searches the appropriate directories where driver object files
 * are expected to be installed and determines what files should be linked
 * based on the presence of files with appropriate names.
 */

#ifdef	USE_PROTO
int (write_link) (CONST char * outname, CONST char * inname)
#else
int
write_link ARGS ((outname, inname))
CONST char    *	outname;
CONST char    *	inname;
#endif
{
	time_t		gentime;
	char		timebuf [70];
	FILE	      *	out;
	input_t	      *	example;
	ehand_t		err;
	build_t	      *	heap;

	if ((heap = builder_alloc (256, 1)) == NULL)
		throw_error ("Unable to allocate temporary heap in write_link ()");

	if (inname == NULL)
		example = make_file_input (stdin, "template", '#');
	else if ((out = fopen (inname, "r")) == NULL)
		throw_error ("Unable to open template file in write_link ()");
	else
		example = make_file_input (out, inname, '#');

	if (example == NULL)
		throw_error ("Out of memory in write_link ()");

	if (outname == NULL)
		out = stdout;
	else if ((out = fopen (outname, "w")) == NULL)
		throw_error ("Unable to open output file for writing in write_link ()");

	if (PUSH_HANDLER (err) == 0) {
		time (& gentime);

		fprintf (out, "# Kernel makefile\n");
		fprintf (out, "# This makefile was automatically generated."
			      " Do not hand-modify\n");

#ifdef	__COHERENT__
		strncpy (timebuf, asctime (localtime (& gentime)),
			 sizeof (timebuf) - 1);
		timebuf [sizeof (timebuf) - 1] = 0;
#else
		strftime (timebuf, sizeof (timebuf) - 1, "%x %X %Z",
			  localtime (& gentime));
#endif

		fprintf (out, "# Generated at %s\n", timebuf);

		write_makefile (out, example, heap);

		if (out != stdout)
			fclose (out);

		if (inname != NULL)
			read_close (example);

		builder_free (heap);

	} else {
		if (out != stdout)
			fclose (out);

		if (inname != NULL)
			read_close (example);

		builder_free (heap);

		CHAIN_ERROR (err);
	}

	POP_HANDLER (err);
	return 0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.