tic_parse.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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-1999 by Sun Microsystems, Inc.
* All rights reserved.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* *******************************************************************
* COPYRIGHT NOTICE *
* ********************************************************************
* This software is copyright (C) 1982 by Pavel Curtis *
* *
* Permission is granted to reproduce and distribute *
* this file by any means so long as no fee is charged *
* above a nominal handling fee and so long as this *
* notice is always included in the copies. *
* *
* Other rights are reserved except as explicitly granted *
* by written permission of the author. *
* Pavel Curtis *
* Computer Science Dept. *
* 405 Upson Hall *
* Cornell University *
* Ithaca, NY 14853 *
* *
* Ph- (607) 256-4934 *
* *
* Pavel.Cornell@Udel-Relay (ARPAnet) *
* decvax!cornell!pavel (UUCPnet) *
* ********************************************************************
*/
/*
* comp_parse.c -- The high-level (ha!) parts of the compiler,
* that is, the routines which drive the scanner,
* etc.
*
* $Log: RCS/comp_parse.v $
* Revision 2.1 82/10/25 14:45:43 pavel
* Added Copyright Notice
*
* Revision 2.0 82/10/24 15:16:39 pavel
* Beta-one Test Release
*
* Revision 1.3 82/08/23 22:29:39 pavel
* The REAL Alpha-one Release Version
*
* Revision 1.2 82/08/19 19:09:53 pavel
* Alpha Test Release One
*
* Revision 1.1 82/08/12 18:37:12 pavel
* Initial revision
*
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include "curses_inc.h"
#include "compiler.h"
#include "object.h"
extern char check_only;
extern char *progname;
char *string_table;
int next_free; /* next free character in string_table */
unsigned int table_size = 0; /* current string_table size */
short 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
*
*/
/*
* compile()
*
* 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(
"File does not start with terminal names in column one");
while (token_type != EOF)
token_type = do_entry((struct use_item *)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(
"Token after a seek 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(
"Token after a seek 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 && !check_only) {
fprintf(stderr,
"\nError in following up use-links. Either there is\n");
fprintf(stderr,
"a loop in the links or they reference non-existant\n");
fprintf(stderr,
"terminals. The following is a list of the entries\n");
fprintf(stderr, "involved:\n\n");
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);
}
}
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");
}
/*
* int
* do_entry(item_ptr)
*
* 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
* dump compiled entry into filesystem
*
*/
int
do_entry(item_ptr)
struct use_item *item_ptr;
{
long entry_offset;
register int token_type;
register struct name_table_entry *entry_ptr;
int found_forward_use = FALSE;
short Booleans[MAXBOOLS],
Numbers[MAXNUMS],
Strings[MAXSTRINGS];
init_structure(Booleans, Numbers, Strings);
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)
/* do nothing */;
else if (strcmp(curr_token.tk_name, "use") == 0) {
if (handle_use(item_ptr, entry_offset,
Booleans, Numbers, Strings) < 0)
found_forward_use = TRUE;
} else {
entry_ptr = find_entry(curr_token.tk_name);
if (entry_ptr == NOTFOUND) {
warning("Unknown Capability - '%s'",
curr_token.tk_name);
continue;
}
if (token_type != CANCEL &&
entry_ptr->nte_type != token_type)
warning("Wrong type used for capability '%s'",
curr_token.tk_name);
switch (token_type) {
case CANCEL:
switch (entry_ptr->nte_type) {
case BOOLEAN:
Booleans[entry_ptr->nte_index] = -2;
break;
case NUMBER:
Numbers[entry_ptr->nte_index] = -2;
break;
case STRING:
Strings[entry_ptr->nte_index] = -2;
break;
}
break;
case BOOLEAN:
if (Booleans[entry_ptr->nte_index] == 0)
Booleans[entry_ptr->nte_index] = TRUE;
break;
case NUMBER:
if (Numbers[entry_ptr->nte_index] == -1)
Numbers[entry_ptr->nte_index] =
curr_token.tk_valnumber;
break;
case STRING:
if (Strings[entry_ptr->nte_index] == -1)
Strings[entry_ptr->nte_index] =
save_str(curr_token.tk_valstring);
break;
default:
warning("Unknown token type");
panic_mode(',');
continue;
}
} /* end else cur_token.name != "use" */
} /* endwhile (not EOF and not NAMES) */
if (found_forward_use)
return (token_type);
dump_structure(Booleans, Numbers, Strings);
complete = 1;
return (token_type);
}
/*
Change all cancellations to a non-entry.
For booleans, @ -> false
For nums, @ -> -1
For strings, @ -> -1
This only has to be done for entries which
have to be compatible with the pre-Vr3 format.
*/
#ifndef NOCANCELCOMPAT
elim_cancellations(Booleans, Numbers, Strings)
short Booleans[];
short Numbers[];
short Strings[];
{
register int i;
for (i = 0; i < BoolCount; i++) {
if (Booleans[i] == -2)
Booleans[i] = FALSE;
}
for (i = 0; i < NumCount; i++) {
if (Numbers[i] == -2)
Numbers[i] = -1;
}
for (i = 0; i < StrCount; i++) {
if (Strings[i] == -2)
Strings[i] = -1;
}
}
#endif /* NOCANCELCOMPAT */
/*
Change the cancellation signal from the -2 used internally to
the 2 used within the binary.
*/
change_cancellations(Booleans)
short Booleans[];
{
register int i;
for (i = 0; i < BoolCount; i++) {
if (Booleans[i] == -2)
Booleans[i] = 2;
}
}
/*
* enqueue(offset)
*
* Put a record of the given offset onto the use-list.
*
*/
enqueue(offset)
long offset;
{
struct use_item *item;
item = (struct use_item *)malloc(sizeof (struct use_item));
if (item == NULL)
syserr_abort("Not enough memory for use_list element");
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 ++;
}
/*
* dequeue(ptr)
*
* remove the pointed-to item from the use_list
*
*/
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 --;
}
/*
* invalid_term_name(name)
*
* Look for invalid characters in a term name. These include
* space, tab and '/'.
*
* Generate an error message if given name does not begin with a
* digit or letter, then exit.
*
* return TRUE if name is invalid.
*
*/
static int invalid_term_name(name)
register char *name;
{
int error = 0;
if (! isdigit(*name) && ! islower(*name) && ! isupper(*name))
error++;
for (; *name; name++)
if (isalnum(*name))
continue;
else if (isspace(*name) || (*name == '/'))
return (1);
if (error) {
fprintf(stderr, "%s: Line %d: Illegal terminal name - '%s'\n",
progname, curr_line, name);
fprintf(stderr,
"Terminal names must start with a letter or digit\n");
exit(1);
}
return (0);
}
/*
* dump_structure()
*
* 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
* if necessary
* clear CANCELS out of the structure
* creat(first-name)
* write object information to first-name
* close(first-name)
* for each valid name
* link to first-name
*
*/
dump_structure(Booleans, Numbers, Strings)
short Booleans[];
short Numbers[];
short Strings[];
{
struct stat64 statbuf;
FILE *fp;
char name_list[1024];
register char *first_name, *other_names, *cur_name;
char filename[128 + 2 + 1];
char linkname[128 + 2 + 1];
int len;
int alphastart = 0;
extern char *strchr(), *strrchr();
strcpy(name_list, term_names + string_table);
DEBUG(7, "Name list = '%s'\n", name_list);
first_name = name_list;
/* Set othernames to 1 past first '|' in the list. */
/* Null out that '|' in the process. */
other_names = strchr(first_name, '|');
if (other_names)
*other_names++ = '\0';
if (invalid_term_name(first_name))
warning("'%s': bad first term name.", first_name);
DEBUG(7, "First name = '%s'\n", first_name);
DEBUG(7, "Other names = '%s'\n", other_names ? other_names : "NULL");
if ((len = strlen(first_name)) > 128)
warning("'%s': terminal name too long.", first_name);
else if (len == 1)
warning("'%s': terminal name too short.", first_name);
check_dir(first_name[0]);
sprintf(filename, "%c/%s", first_name[0], first_name);
if (strlen(filename) > 16)
warning("'%s' filename too long, truncating to '%.16s'\n",
filename, filename);
if (stat64(filename, &statbuf) >= 0 && statbuf.st_mtime >= start_time) {
warning("'%s' defined in more than one entry.", first_name);
fprintf(stderr, "Entry being used is '%s'.\n",
(unsigned)term_names + string_table);
}
if (!check_only) {
unlink(filename);
fp = fopen(filename, "w");
if (fp == NULL) {
perror(filename);
syserr_abort("Can't open %s/%s\n",
destination, filename);
}
DEBUG(1, "Created %.16s\n", filename);
} else DEBUG(1, "Would have created %.16s\n", filename);
#ifndef NOCANCELCOMPAT
/* if there is no '+' in the name, eliminate */
/* cancellation markings. */
if (strchr(first_name, '+') == 0)
elim_cancellations(Booleans, Numbers, Strings);
else
#endif /* NOCANCELCOMPAT */
change_cancellations(Booleans);
if (!check_only) {
if (write_object(fp, Booleans, Numbers, Strings) < 0) {
syserr_abort("Error in writing %s/%s",
destination, filename);
}
fclose(fp);
}
alphastart = isalpha(first_name[0]);
while (other_names) {
cur_name = other_names;
other_names = strchr(cur_name, '|');
if (other_names)
*other_names++ = '\0';
if (*cur_name == '\0')
continue;
if ((len = strlen(cur_name)) > 128) {
warning("'%s': terminal name too long.", cur_name);
continue;
} else if (len == 1) {
warning("'%s': terminal name too short.", first_name);
continue;
}
if (invalid_term_name(cur_name)) {
if (other_names)
warning("'%s': bad term name found in list.",
cur_name);
continue;
}
check_dir(cur_name[0]);
sprintf(linkname, "%c/%s", cur_name[0], cur_name);
if (strlen(linkname) > 16) {
if (other_names) {
warning(
"'%s' linkname too long, truncating to '%.16s'\n", linkname, linkname);
} else {
continue;
}
}
alphastart |= isalpha(cur_name[0]);
if (strcmp(first_name, cur_name) == 0) {
warning("Terminal name '%s' synonym for itself",
first_name);
} else {
if (!check_only) {
if (stat64(linkname, &statbuf) >= 0 &&
statbuf.st_mtime >= start_time) {
warning(
"'%s' defined in more than one entry.", cur_name);
fprintf(stderr,
"Entry being used is '%s'.\n",
(unsigned)term_names +
string_table);
}
unlink(linkname);
if (link(filename, linkname) < 0)
syserr_abort("Can't link %s to %s",
filename, linkname);
DEBUG(1, "Linked %.16s\n", linkname);
} else DEBUG(1, "Would have linked %.16s\n", linkname);
}
}
if (!alphastart) {
warning("At least one synonym should begin with a letter.");
}
}
/*
* int
* write_object(fp, Booleans, Numbers, Strings)
*
* Write out the compiled entry to the given file.
* Return 0 if OK or -1 if not.
*
*/
#define swap(x) (((x >> 8) & 0377) + 256 * (x & 0377))
#define might_swap(x) (must_swap() ? swap(x) : (x))
int
write_object(fp, Booleans, Numbers, Strings)
FILE *fp;
short Booleans[];
short Numbers[];
short Strings[];
{
struct header header;
char *namelist;
short namelen;
char zero = '\0';
register int i;
char cBooleans[MAXBOOLS];
register int l_next_free;
namelist = term_names + string_table;
namelen = strlen(namelist) + 1;
l_next_free = next_free;
if (l_next_free % 256 == 255)
l_next_free++;
if (must_swap()) {
header.magic = swap(MAGIC);
header.name_size = swap(namelen);
header.bool_count = swap(BoolCount);
header.num_count = swap(NumCount);
header.str_count = swap(StrCount);
header.str_size = swap(l_next_free);
} else {
header.magic = MAGIC;
header.name_size = namelen;
header.bool_count = BoolCount;
header.num_count = NumCount;
header.str_count = StrCount;
header.str_size = l_next_free;
}
for (i = 0; i < BoolCount; i++)
cBooleans[i] = Booleans[i];
if (fwrite(&header, sizeof (header), 1, fp) != 1 ||
fwrite(namelist, sizeof (char), namelen, fp) != namelen ||
fwrite(cBooleans, sizeof (char), BoolCount, fp) !=
BoolCount)
return (-1);
if ((namelen+BoolCount) % 2 != 0 &&
fwrite(&zero, sizeof (char), 1, fp) != 1)
return (-1);
if (must_swap()) {
for (i = 0; i < NumCount; i++)
Numbers[i] = swap(Numbers[i]);
for (i = 0; i < StrCount; i++)
Strings[i] = swap(Strings[i]);
}
if (fwrite((char *)Numbers, sizeof (short), NumCount, fp) != NumCount ||
fwrite((char *)Strings, sizeof (short), StrCount, fp)
!= StrCount ||
fwrite(string_table, sizeof (char), l_next_free, fp)
!= l_next_free)
return (-1);
return (0);
}
/*
* int
* save_str(string)
*
* 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.
*
*/
int
save_str(string)
char *string;
{
int old_next_free;
/* Do not let an offset be 255. It reads as -1 in Vr2 binaries. */
if (next_free % 256 == 255)
next_free++;
old_next_free = next_free;
if (table_size == 0) {
if ((string_table = malloc(1024)) == NULL)
syserr_abort("Out of memory");
table_size = 1024;
DEBUG(5, "Made initial string table allocation. Size is %u\n",
table_size);
}
while (table_size <= next_free + strlen(string)) {
if ((string_table = realloc(string_table, table_size + 1024))
== NULL)
syserr_abort("Out of memory");
table_size += 1024;
DEBUG(5, "Extended string table. Size now %u\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);
}
/*
* init_structure(Booleans, Numbers, Strings)
*
* Initialise the given arrays
* Reset the next_free counter to zero.
*
*/
init_structure(Booleans, Numbers, Strings)
short Booleans[];
short Numbers[], Strings[];
{
int i;
for (i = 0; i < BoolCount; i++)
Booleans[i] = FALSE;
for (i = 0; i < NumCount; i++)
Numbers[i] = -1;
for (i = 0; i < StrCount; i++)
Strings[i] = -1;
next_free = 0;
}
/*
* int
* handle_use(item_ptr, entry_offset, Booleans, Numbers, Strings)
*
* 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.
*/
int
handle_use(item_ptr, entry_offset, Booleans, Numbers, Strings)
long entry_offset;
struct use_item *item_ptr;
short Booleans[];
short Numbers[];
short Strings[];
{
struct _bool_struct use_bools;
struct _num_struct use_nums;
struct _str_struct use_strs;
struct stat64 statbuf;
char filename[50];
int i;
char *UB = &use_bools._auto_left_margin; /* first bool */
short *UN = &use_nums._columns; /* first num */
char **US = &use_strs.strs._back_tab; /* first str */
if (invalid_term_name(curr_token.tk_valstring))
warning("%s: bad term name", curr_token.tk_valstring);
sprintf(filename, "%c/%s", curr_token.tk_valstring[0],
curr_token.tk_valstring);
if (stat64(filename, &statbuf) < 0 ||
part2 == 0 && statbuf.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", "");
return (-1);
} else {
DEBUG(2, "Backward USE to %s\n", curr_token.tk_valstring);
if (read_entry(filename, &use_bools, &use_nums, &use_strs) < 0)
syserr_abort("Error in re-reading compiled file %s",
filename);
for (i = 0; i < BoolCount; i++) {
if (Booleans[i] == FALSE)
if (UB[i] == TRUE) /* now true */
Booleans[i] = TRUE;
else if (UB[i] > TRUE) /* cancelled */
Booleans[i] = -2;
}
for (i = 0; i < NumCount; i++) {
if (Numbers[i] == -1)
Numbers[i] = UN[i];
}
for (i = 0; i < StrCount; i++) {
if (Strings[i] == -1)
if (US[i] == (char *)-1)
Strings[i] = -2;
else if (US[i] != (char *)0)
Strings[i] = save_str(US[i]);
}
}
return (0);
}