OpenSolaris_b135/lib/libxcurses/src/tic/ticparse.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1996, by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *	ticparse.c		
 *
 *	Terminal Information Compiler
 *
 *	Copyright 1990, 1992 by Mortice Kern Systems Inc.  All rights reserved.
 *
 *	Portions of this code Copyright 1982 by Pavel Curtis.
 */

#ifdef M_RCSID
#ifndef lint
static char rcsID[] = "$Header: /rd/src/tic/rcs/ticparse.c 1.22 1995/06/27 14:56:46 ant Exp $";
#endif
#endif

#include "tic.h"
#include <ctype.h>
#include <sys/stat.h>
#include <errno.h>

extern int get_token ANSI((void));	/* from ticscan.c */

char *string_table;
int next_free;		/* next free character in string_table */
int table_size = 0; 	/* current string_table size */
int term_names;		/* string table offset - current terminal */
int part2 = 0;		/* set to allow old compiled defns to be used */
int complete = 0;	/* 1 if entry done with no forward uses */

struct use_item {
	long	offset;
	struct use_item	*fptr, *bptr;
};

struct use_header {
	struct use_item	*head, *tail;
};

struct use_header	use_list = {NULL, NULL};
int			use_count = 0;

/*
 *  The use_list is a doubly-linked list with NULLs terminating the lists:
 *
 *	   use_item    use_item    use_item
 *	  ---------   ---------   ---------
 *	  |       |   |       |   |       |   offset
 *        |-------|   |-------|   |-------|
 *	  |   ----+-->|   ----+-->|  NULL |   fptr
 *	  |-------|   |-------|   |-------|
 *	  |  NULL |<--+----   |<--+----   |   bptr
 *	  ---------   ---------   ---------
 *	  ^                       ^
 *	  |  ------------------   |
 *	  |  |       |        |   |
 *	  +--+----   |    ----+---+
 *	     |       |        |
 *	     ------------------
 *	       head     tail
 *	          use_list
 *
 */

char bad_start[] = m_textstr(
	3107, "File does not start with terminal names in column one", "E"
);
char not_names[] = m_textstr(3108, "Token after a seek not NAMES", "E");
char use_links[] = m_textstr(3109, "\
\n\
Error in following up use-links.  Either there is\n\
a loop in the links or they reference non-existant\n\
terminals.  The following is a list of the entries\n\
involved:\n\n\
", "E");
char nomem_use_list[] = m_textstr(
	3110, "Not enough memory for use_list element", "E"
);
char long_path[] = m_textstr(3111, "Pathname \"%c/%s\" too long.", "W char term");
char more_than_one[] = m_textstr(
	3112, "More than one entry defined for \"%s\".\n","W term"
);
char fail_open[] = m_textstr(3113, "Failed to open \"%s\".\n", "E filename");
char write_err[] = m_textstr(3114, "Error in writing \"%s\".\n", "E filename");
char synonym[] = m_textstr(3115, "Terminal \"%s\" is a synonym for itself.\n", "W term");
char fail_link[] = m_textstr(3116, "Failed to link \"%s\" to \"%s\".\n", "E file1 file2");
char name_check[] = m_textstr(3117, "\
compile: Line %d: Illegal terminal name - '%s'\n\
Terminal names must start with lowercase or digit.\n\
", "E line_num term");
char nomem[] = m_textstr(3118, "Failed to allocated memory.\n", "E");
char unknown_term[] = m_textstr(202, "Unknown terminal \"%s\".\n", "E term"); 
char no_terminfo[] = m_textstr(203, "No terminfo database.\n", "E"); 
char unknown_cap[] = m_textstr(3119, "Unknown capability '%s'.", "E action");
char unknown_token[] = m_textstr(3120, "Unknown token type.", "W");
char wrong_type[] = m_textstr(3121, "Wrong type used for capability \"%s\".", "W type");


/*f
 * debugging routine to dump list
 */
STATIC int
dump_list(str)
char *str;
{
	struct use_item *ptr;
	char line[512];

	fprintf(stderr, "dump_list %s\n", str);
	for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr)
	{
		fseek(stdin, ptr->offset, 0);
		fgets(line, 1024, stdin);
		fprintf(stderr, "ptr %x off %d bptr %x fptr %x str %s",
		ptr, ptr->offset, ptr->bptr, ptr->fptr, line);
	}
	fprintf(stderr, "\n");
}


/*f
 *	Generate an error message if given name does not begin with a
 *	digit or lower-case letter.
 */
STATIC int
check_name(name)
char	*name;
{
	if (!isdigit(name[0]) && !isalpha(name[0])) {
		fprintf(stderr, m_strmsg(name_check), curr_line, name);
		exit(1);
	}
}

/*f
 *	Test whether this machine will need byte-swapping
 */
STATIC int
must_swap()
{
	union {
		short num;
		char byte[2];
	} test;
	test.num = 1;
	return (test.byte[1]);
}


/*f
 *      Put a record of the given offset onto the use-list.
 */
STATIC int
enqueue(offset)
long	offset;
{
	struct use_item	*item;

	item = (struct use_item *) malloc(sizeof(struct use_item));

	if (item == NULL)
	    syserr_abort(m_strmsg(nomem_use_list));

	item->offset = offset;

	if (use_list.head != NULL)
	{
	    item->bptr = use_list.tail;
	    use_list.tail->fptr = item;
	    item->fptr = NULL;
	    use_list.tail = item;
	}
	else
	{
	    use_list.tail = use_list.head = item;
	    item->fptr = item->bptr = NULL;
	}

	use_count ++;
}



/*f
 *	remove the pointed-to item from the use_list
 */
STATIC int
dequeue(ptr)
struct use_item	*ptr;
{
	if (ptr->fptr == NULL)
	    use_list.tail = ptr->bptr;
	else
	    (ptr->fptr)->bptr = ptr->bptr;

	if (ptr->bptr == NULL)
	    use_list.head = ptr->fptr;
	else
	    (ptr->bptr)->fptr = ptr->fptr;
	
	use_count --;
}



/*f
 *	Write out the compiled entry to the given file.
 *	Return 0 if OK or -1 if not.
 */
STATIC int
write_object(fp)
FILE *fp;
{
	int i, tlength;
    	__t_term_header header;
	char *tnames, zero = '\0';

	tnames = string_table + term_names;
	tlength = strlen(tnames) + 1;
	if (TERM_NAMES_LENGTH < tlength) 
		tlength = TERM_NAMES_LENGTH;
	if (must_swap()) {
		header.magic = swap(TERMINFO_MAGIC);
		header.name_size = swap(tlength);
		header.bool_count = swap(BOOLCOUNT);
		header.num_count = swap(NUMCOUNT);
		header.str_count = swap(STRCOUNT);
		header.str_size = swap(next_free);
	} else {
		header.magic = TERMINFO_MAGIC;
		header.name_size = tlength;
		header.bool_count = BOOLCOUNT;
		header.num_count = NUMCOUNT;
		header.str_count = STRCOUNT;
		header.str_size = next_free;
	}

	if (fwrite(&header, sizeof (header), 1, fp) != 1
	|| fwrite(tnames, sizeof (char), tlength, fp) != tlength 
	|| fwrite(boolean, sizeof (char), BOOLCOUNT, fp) != BOOLCOUNT)
		return (-1);
	
	if ((tlength+BOOLCOUNT) % 2 != 0 
	&& fwrite(&zero, sizeof (char), 1, fp) != 1)
		return (-1);

	if (must_swap()) {
		for (i = 0; i < NUMCOUNT; ++i)
			number[i] = swap(number[i]);
		for (i = 0; i < STRCOUNT; ++i)
			string[i] = swap(string[i]);
	}

	if (fwrite(number, sizeof (short), NUMCOUNT, fp) != NUMCOUNT
	|| fwrite(string, sizeof (short), STRCOUNT, fp) != STRCOUNT
	|| fwrite(string_table, sizeof (char), next_free, fp) != next_free)
		return (-1);
	return (0);
}



/*f
 *	Save the compiled version of a description in the filesystem.
 *
 *	make a copy of the name-list
 *	break it up into first-name and all-but-last-name
 *	creat(first-name)
 *	write object information to first-name
 *	close(first-name)
 *      for each name in all-but-last-name
 *	    link to first-name
 *
 */
STATIC void
dump_structure()
{
	FILE *fp;
	struct stat sb;
	char *p, *q, *first, *fn, *long_name, dir[2], tname[TERM_NAMES_LENGTH];

	/* Bag copy of terminal name list.  Parse off the last name,
	 * which should be the terminal's long name.  Parse off the 
	 * first name to be used for the terminal filename. 
	 */
	(void) strncpy(tname, string_table + term_names, TERM_NAMES_LENGTH);
	DEBUG(7, "Terminal names are \"%s\".\n", tname);
	for (p = tname + strlen(tname); tname < p; --p) {
		if (*p == '|') {
			long_name = ++p;
			break;
		}
	}
	if (tname == p)
		long_name = tname;
	for (p = tname; p < long_name; ++p) {
		if (*p == '|') {
			if (tname < p)
				*p++ = '\0';
			break;	
		}
	}
	if (check_only) {
		DEBUG(1, "Checked \"%s\".\n", tname);
		return;
	}
	DEBUG(7, "Terminfo file name is \"%s\".\n", tname);
	DEBUG(7, "Terminal's long name is \"%s\".\n", long_name);

	/* Create terminfo object file. */
	check_name(tname);
	*dir = tolower(*tname);
	dir[1] = '\0';
	first = m_pathcat(dir, tname);
	if (first == NULL)
		err_abort(m_strmsg(long_path), *tname, tname);
	if (0 <= stat(first, &sb) && start_time <= sb.st_mtime)
		warning(m_strmsg(more_than_one), tname);
	if (access(first, W_OK) == -1 && errno != ENOENT) {
		perror(first);
		err_abort(m_strmsg(write_err), first);
	}
	(void) unlink(first);
	if ((fp = fopen(first, "w")) == NULL)
		err_abort(m_strmsg(fail_open), first);
	DEBUG(1, "Created \"%s\".\n", first);
	if (write_object(fp) < 0)
		err_abort(m_strmsg(write_err), first);
	(void) fclose(fp);

	/* Create links for alternate names. */
	while (p < long_name) {
		for (q = p; p < long_name; ++p) {
			if (*p == '|') {
				*p++ = '\0';
				break;
			}
		}
		check_name(q);
		*dir = tolower(*q);
		dir[1] = '\0';
		fn = m_pathcat(dir, q);
		if (fn == NULL) {
			warning(m_strmsg(long_path), *q, q);
			continue;
		}
		if (strcmp(q, tname) == 0) {
			warning(m_strmsg(synonym), tname);
			continue;
		}
		if (0 <= stat(fn, &sb) && start_time <= sb.st_mtime) {
			warning(m_strmsg(more_than_one), q);
			continue;
		}
		if (access(fn, W_OK) == -1 && errno != ENOENT) {
			err_abort(m_strmsg(write_err), fn);
		}
		(void) unlink(fn);
		if (link(first, fn) < 0) {
			if ((fp = fopen(fn, "w")) == NULL)
				err_abort(m_strmsg(fail_open), fn);
			DEBUG(1, "Created \"%s\".\n", fn);
			if (write_object(fp) < 0)
				err_abort(m_strmsg(write_err), fn);
			(void) fclose(fp);
		} else {
			DEBUG(1, "Linked \"%s\".\n", fn);
		}
		free(fn);
	}
	free(first);
}


/*f
 *	copy string into next free part of string_table, doing a realloc()
 *	if necessary.  return offset of beginning of string from start of
 *	string_table.
 */
STATIC int
save_str(string)
char	*string;
{
	int	old_next_free = next_free;

	if (table_size == 0)
	{
	    if ((string_table = malloc(1024)) == NULL)
		syserr_abort(m_strmsg(nomem));
	    table_size = 1024;
	    DEBUG(5, "Made initial string table allocation.  Size is %d\n",
								    table_size);
	}

	while (table_size < next_free + strlen(string))
	{
	    if ((string_table = realloc(string_table, table_size + 1024))
									== NULL)
		syserr_abort(m_strmsg(nomem));
	    table_size += 1024;
	    DEBUG(5, "Extended string table.  Size now %d\n", table_size);
	}

	strcpy(&string_table[next_free], string);
	DEBUG(7, "Saved string '%s' ", string);
	DEBUG(7, "at location %d\n", next_free);
	next_free += strlen(string) + 1;

	return (old_next_free);
}

/*f
 *	Merge the compiled file whose name is in cur_token.valstring
 *	with the current entry.
 *
 *		if it's a forward use-link
 *	    	    if item_ptr == NULL
 *		        queue it up for later handling
 *	            else
 *		        ignore it (we're already going through the queue)
 *	        else it's a backward use-link
 *	            read in the object file for that terminal
 *	            merge contents with current structure
 *
 *	Returned value is 0 if it was a backward link and we
 *	successfully read it in, -1 if a forward link.
 */
STATIC int
handle_use(item_ptr, entry_offset)
struct use_item	*item_ptr;
long entry_offset;
{
        int i, err;
	struct stat sb;
	char *filename, dir[2];

	check_name(curr_token.tk_valstring);
	*dir = tolower(*curr_token.tk_valstring);
	dir[1] = '\0';
	filename = m_pathcat(dir, curr_token.tk_valstring);
	if (filename == NULL) {
		err_abort(
			m_strmsg(long_path), 
			*curr_token.tk_valstring, curr_token.tk_valstring
		);
	}
	if (stat(filename, &sb) < 0 
	|| (part2 == 0 && sb.st_mtime < start_time)) {
		DEBUG(2, "Forward USE to %s", curr_token.tk_valstring);
		if (item_ptr == NULL) {
			DEBUG(2, " (enqueued)\n", "");
			enqueue(entry_offset);
		} else {
			DEBUG(2, " (skipped)\n", "");
		}
		free(filename);
		return (-1);
	}
	DEBUG(2, "Backward USE to %s\n", curr_token.tk_valstring);
	(void) setupterm(curr_token.tk_valstring, STDOUT_FILENO, &err);
	switch (err) {
	case 1:
		for (i = 0; i < BOOLCOUNT; ++i) {
			if (boolean[i] == 0 && cur_term->Booleans[i])
				boolean[i] = 1;
		}
		for (i = 0; i < NUMCOUNT; ++i) {
			if (number[i] == -1 && cur_term->Numbers[i] != -1)
				number[i] = cur_term->Numbers[i];
		}
		for (i = 0; i < STRCOUNT; ++i) {
			if (string[i] == -1 && cur_term->Strings[i] != NULL)
				string[i] = save_str(cur_term->Strings[i]);
		}
		(void) del_curterm(cur_term);
		free(filename);
		break;
	case 0:
		err_abort(m_strmsg(unknown_term), filename); 
		exit(BAD_TERMINAL);
	case -1:
		err_abort(m_strmsg(no_terminfo)); 
		exit(BAD_TERMINAL);
	}
	return (0);
}



/*f
 *	Compile one entry.  During the first pass, item_ptr is NULL.  In pass
 *	two, item_ptr points to the current entry in the use_list.
 *
 *	found-forward-use = FALSE
 *	re-initialise internal arrays
 *	save names in string_table
 *	get_token()
 *	while (not EOF and not NAMES)
 *	    if found-forward-use
 *		do nothing
 *	    else if 'use'
 *		if handle_use() < 0
 *		    found-forward-use = TRUE
 *          else
 *	        check for existance and type-correctness
 *	        enter cap into structure
 *	        if STRING
 *	            save string in string_table
 *	    get_token()
 *      if ! found-forward-use
 *	    clear CANCELS out of the structure
 *	    dump compiled entry into filesystem
 */
STATIC int
do_entry(item_ptr)
struct use_item	*item_ptr;
{
	void *array;
	long entry_offset;
	int i, index;
	register int token_type;
	int found_forward_use = 0;

	reset();
	next_free = 0;

	complete = 0;
	term_names = save_str(curr_token.tk_name);
	DEBUG(2, "Starting '%s'\n", curr_token.tk_name);
	entry_offset = curr_file_pos;

	for (token_type = get_token(); 
	token_type != EOF && token_type != NAMES;
	token_type = get_token()) {
		if (found_forward_use) {
			;
		} else if (strcmp(curr_token.tk_name, "use") == 0) {
			if (handle_use(item_ptr, entry_offset) < 0)
				found_forward_use = 1;
		} else {
			if (find(curr_token.tk_name, &array, &index) < 0) {
				warning(
					m_strmsg(unknown_cap), 
					curr_token.tk_name
				);
				continue;
			}
			switch (token_type) {
			case CANCEL:
				if (array == boolean)
					boolean[index] = 2;
				else
					((short*) array)[index] = -2;
				continue;
			case BOOLEAN:
				if (array == boolean) {
					boolean[index] = 1;
					continue;
				}
				break;
			case NUMBER:
				if (array == number) {
					number[index] = curr_token.tk_valnumber;
					continue;
				}
				break;
			case STRING:
				if (array == string) {
					string[index] = save_str(
						curr_token.tk_valstring
					);
					continue;
				}
				break;
			default:
				warning(m_strmsg(unknown_token));
				panic_mode(',');
				continue;
			}
			warning(m_strmsg(wrong_type), curr_token.tk_name);
		} 
	} 
	if (found_forward_use)
		return (token_type);

	/* Changed canceled values into in-active values. */
	for (i = 0; i < BOOLCOUNT; ++i)
		if (boolean[i] == 2)
			boolean[i] = 0;
	for (i = 0; i < NUMCOUNT; ++i)
		if (number[i] == -2)
			number[i] = -1;
	for (i = 0; i < STRCOUNT; ++i)
		if (string[i] == -2)
			string[i] = -1;
	dump_structure();
	complete = 1;
	return (token_type);
}



/*f
 *	Main loop of the compiler.
 *
 *	get_token()
 *	if curr_token != NAMES
 *	    err_abort()
 *	while (not at end of file)
 *	    do an entry
 */
void
compile()
{
	char			line[1024];
	int			token_type;
	struct use_item	*ptr;
	int			old_use_count;

	token_type = get_token();

	if (token_type != NAMES)
		err_abort(m_strmsg(bad_start));
	
	while (token_type != EOF)
	    token_type = do_entry(NULL);

	DEBUG(2, "Starting handling of forward USE's\n", "");

	for (part2=0; part2<2; part2++) {
	    old_use_count = -1;

	    DEBUG(2, "\n\nPART %d\n\n", part2);

	    while (use_list.head != NULL && old_use_count != use_count)
	    {
		old_use_count = use_count;
		for (ptr = use_list.tail; ptr != NULL; ptr = ptr->bptr)
		{
		    fseek(stdin, ptr->offset, 0);
		    reset_input();
		    if ((token_type = get_token()) != NAMES)
			syserr_abort(m_strmsg(not_names));			
		    (void) do_entry(ptr);
		    if (complete)
			dequeue(ptr);
		}

		for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr)
		{
		    fseek(stdin, ptr->offset, 0);
		    reset_input();
		    if ((token_type = get_token()) != NAMES)
			syserr_abort(m_strmsg(not_names));			
		    (void) do_entry(ptr);
		    if (complete)
			dequeue(ptr);
		}
		
		DEBUG(2,"Finished a pass through enqueued forward USE's\n","");
	    }
	}

	if (use_list.head != NULL) {
		fprintf(stderr, use_links);
		for (ptr = use_list.head; ptr != NULL; ptr = ptr->fptr) {
			fseek(stdin, ptr->offset, 0);
			fgets(line, 1024, stdin);
			fprintf(stderr, "%s", line);
		}
		exit(1);
	}
}