|
|
researchv10 Norman
/* @(#) tic.c: 1.1 10/15/83 (1.15 2/23/83) */
#ifdef pdp11
/* Has to be this small to fit, even split I/D, with several use='s */
# define CAPSIZ 2048
#else
# define CAPSIZ 4096
#endif
#define MAXHOP 32 /* max number of use= indirections */
#define COMMENT '#'
#define SEPARATE ','
#define NUMBER '#'
#define STRING '='
#define CANCEL '@'
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "../local/uparm.h"
#ifndef E_TERMINFO
#define E_TERMINFO "terminfo.src"
#endif
#ifndef termpath
#define termpath(name) "/usr/lib/terminfo/name"
#endif
/*
* L_ctermid is only defined on USG.
* We use it here since we don't include curses.h.
*/
#ifdef L_ctermid
#define index strchr
#endif
/*
* compile: program to compile a source terminfo file into object files
*/
static char *tbuf;
static int hopcount; /* detect infinite loops in terminfo, init 0 */
long starttime;
int verbose;
char *terminfo;
char *tskip();
char *tgetstr();
char *tdecode();
char *getenv();
char *sourcefile = E_TERMINFO;
main(argc, argv)
char **argv;
{
int i;
time(&starttime);
while (argc > 1 && argv[1][0] == '-') {
switch(argv[1][1]) {
case 'v':
if (argv[1][2])
verbose = argv[1][2] - '0';
else
verbose++;
break;
default:
fprintf(stderr, "Usage: compile [-v] [files...]\n");
exit(1);
}
argc--; argv++;
}
terminfo = getenv("TERMINFO");
if (argc == 1) {
compfile(stdin, "stdin");
}
else for (i=1; i<argc; i++) {
compfile(fopen(argv[i], "r"), argv[i]);
}
}
/*
* Compile a file. This is very similar to the
* code in tgetstr but it passes through the whole file.
*/
compfile(tf, fname)
FILE *tf;
char *fname;
{
register char *cp;
register int c;
register int i = 0, cnt = 0;
char bp[CAPSIZ];
char ibuf[CAPSIZ];
char *cp2;
if (tf == NULL) {
perror(fname);
return (-1);
}
ibuf[0] = 0;
for (;;) {
tbuf = bp;
strcpy(bp, ibuf);
for (;;) {
if (fgets(ibuf, sizeof ibuf, tf) == NULL) {
fclose(tf);
if (tnchkuse(fname))
store(bp);
return 0;
}
/* comment or blank line */
if (ibuf[0] == COMMENT || ibuf[0] == '\n')
continue;
cp = &ibuf[strlen(ibuf)-3];
/* Allow and ignore old style backslashes */
if (*cp == SEPARATE && cp[1] == '\\')
cp[1] = 0;
cp[2] = 0; /* get rid of newline */
/* lines with leading white space are continuation */
if (!isspace(ibuf[0]) && *bp)
break;
if (strlen(bp) + strlen(ibuf) >= CAPSIZ) {
fprintf(stdout, "Terminfo entry too long:\n");
fprintf(stdout, "%s", bp);
}
else {
cp = ibuf;
while (isspace(*cp))
cp++;
strcat(bp, cp);
}
}
/*
* We have it, now do something with it.
*/
if (tnchkuse(fname))
store(bp);
}
}
/*
* Get an entry for terminal name in buffer bp,
* from the terminfo file. Parse is very rudimentary;
* we just notice escaped newlines.
*/
tgetent(bp, name, fname)
char *bp, *name, *fname;
{
register char *cp;
register int c;
register int i = 0, cnt = 0;
char ibuf[CAPSIZ];
char *cp2;
FILE *tf;
ibuf[0] = 0;
tf = fopen(fname, "r");
if (tf == NULL)
return (-1);
tbuf = bp;
for (;;) {
strcpy(bp, ibuf);
for (;;) {
if (fgets(ibuf, sizeof ibuf, tf) == NULL) {
fclose(tf);
if (tnamatch(name))
return(tnchkuse(fname));
return 0;
}
if (ibuf[0] == COMMENT) /* comment */
continue;
cp = &ibuf[strlen(ibuf)-3];
/* Allow and ignore old style backslashes */
if (*cp == SEPARATE && cp[1] == '\\')
cp[1] = 0;
cp[2] = 0; /* get rid of newline */
/* lines with leading white space are continuation */
if (!isspace(ibuf[0]) && *bp)
break;
if (strlen(bp) + strlen(ibuf) >= CAPSIZ) {
fprintf(stdout, "Terminfo entry too long:\n");
fprintf(stdout, "%s", bp);
}
else {
cp = ibuf;
while (isspace(*cp))
cp++;
strcat(bp, cp);
}
}
/*
* The real work for the match.
*/
if (tnamatch(name)) {
fclose(tf);
return(tnchkuse(fname));
}
}
}
/*
* tnchkuse: check the last entry, see if it's use=xxx. If so,
* recursively find xxx and append that entry (minus the names)
* to take the place of the use=xxx entry. This allows terminfo
* entries to say "like an HP2621 but doesn't turn on the labels".
* Note that this works because of the left to right scan.
*/
tnchkuse(fname)
char *fname;
{
register char *p, *q;
char tcname[16]; /* name of similar terminal */
char tcbuf[CAPSIZ];
char restbuf[CAPSIZ];
char *holdtbuf = tbuf;
char *beg_use, *beg_next;
char *strchr();
int l;
p = tbuf;
if (++hopcount > MAXHOP) {
fprintf(stdout, "Infinite use= loop '%s'\n", tbuf);
return (0);
}
for (;;) {
p = strchr(p, 'u');
if (p == NULL) {
tbuf = holdtbuf;
return 1;
}
beg_use = p;
if (*++p != 's' || *++p != 'e' || *++p != '=')
continue;
strncpy(tcname, ++p, sizeof tcname);
q = strchr(tcname, SEPARATE);
if (q)
*q = 0;
/* try local file ... */
if (tgetent(tcbuf, tcname, fname) != 1) {
/* ... and master */
if (tgetent(tcbuf, tcname, E_TERMINFO) != 1) {
printf("Cannot find term %s\n", tcname);
return(0);
}
}
/* Find the end of the use= spec */
for(beg_next=beg_use;
*beg_next && *beg_next!=SEPARATE;
beg_next++)
;
beg_next++;
while (isspace(*beg_next++))
;
beg_next--;
/* Now shuffle string around. */
strcpy(restbuf, beg_next);
p = strchr(tcbuf, SEPARATE);
if (p == NULL)
p = tcbuf;
else
p++;
strcpy(beg_use, p);
p = strchr(beg_use, '\0');
strcpy(p, restbuf);
}
}
/*
* Tnamatch deals with name matching. The first field of the terminfo
* entry is a sequence of names separated by |'s, so we compare
* against each such name. The normal : terminator after the last
* name (before the first field) stops us.
*/
tnamatch(np)
char *np;
{
register char *Np, *Bp;
/* printf("tnamatch, np '%s', tbuf '%s'\n", np, tbuf); */
Bp = tbuf;
if (*Bp == COMMENT)
return(0);
for (;;) {
for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
;
if (*Np == 0 && (*Bp == '|' || *Bp == SEPARATE || *Bp == 0))
return (1);
while (*Bp && *Bp != SEPARATE && *Bp != '|')
Bp++;
if (*Bp == 0 || *Bp == SEPARATE)
return (0);
Bp++;
}
}
/*
* Skip to the next SEPARATE delimited field.
*/
static char *
tskip(bp)
register char *bp;
{
while (*bp && *bp != SEPARATE)
bp++;
if (*bp == 0)
return bp;
bp++;
while (isspace(*bp) || *bp == SEPARATE)
bp++;
return (bp);
}
/*
* Return the (numeric) option id.
* Numeric options look like
* li#80
* i.e. the option string is separated from the numeric value by
* a # character. If the option is not found we return -1.
* Note that we handle octal numbers beginning with 0.
*/
tgetnum(id)
char *id;
{
register int i, base;
register char *bp = tbuf;
int idl = strlen(id);
int sign = 1;
for (;;) {
bp = tskip(bp);
if (*bp == 0)
return (-1);
if (strncmp(id, bp, idl))
continue;
bp += idl;
if (*bp == CANCEL)
return(-1);
if (*bp != NUMBER && *bp != STRING)
continue;
bp++;
if (*bp == '-') {
sign = -1;
bp++;
}
base = 10;
if (*bp == '0')
base = 8;
i = 0;
while (isdigit(*bp))
i *= base, i += *bp++ - '0';
i *= sign;
return (i);
}
}
/*
* Handle a flag option.
* Flag options are given "naked", i.e. followed by a : or the end
* of the buffer. Return 1 if we find the option, or 0 if it is
* not given.
*/
tgetflag(id)
char *id;
{
register char *bp = tbuf;
int idl = strlen(id);
for (;;) {
bp = tskip(bp);
if (!*bp)
return (0);
if (strncmp(bp, id, idl) == 0) {
bp += idl;
if (!*bp || *bp == SEPARATE)
return (1);
else if (*bp == CANCEL)
return(0);
}
}
}
/*
* Get a string valued option.
* These are given as
* cl=^Z
* Much decoding is done on the strings, and the strings are
* placed in area, which is a ref parameter which is updated.
* No checking on area overflow.
*/
char *
tgetstr(id, area)
char *id, **area;
{
register char *bp = tbuf;
int idl = strlen(id);
for (;;) {
bp = tskip(bp);
if (!*bp)
return (0);
if (strncmp(id, bp, idl))
continue;
bp += idl;
if (*bp == CANCEL)
return(0);
if (*bp != STRING)
continue;
bp++;
return (tdecode(bp, area));
}
}
/*
* Tdecode does the grung work to decode the
* string capability escapes.
*/
static char *
tdecode(str, area)
register char *str;
char **area;
{
register char *cp;
register int c;
register char *dp;
int i;
cp = *area;
while ((c = *str++) && c != SEPARATE) {
switch (c) {
case '^':
c = *str++ & 037;
break;
case '\\':
/*
* \x escapes understood:
* \e escape
* \E escape
* \^ ^
* \\ \
* \, ,
* \: :
* \l linefeed
* \n newline (=linefeed)
* \r return
* \t tab
* \b backspace
* \f formfeed
* \s space
* \0 null
* \### octal ###
*/
dp = "e\033E\033^^\\\\,,::l\012n\nr\rt\tb\bf\fs ";
c = *str++;
nextc:
if (*dp++ == c) {
c = *dp++;
break;
}
dp++;
if (*dp)
goto nextc;
if (isdigit(c)) {
c -= '0', i = 2;
do {
if (!isdigit(*str))
break;
c <<= 3;
c |= *str++ - '0';
} while (--i);
if (c == 0)
c = 0200; /* don't term. str. */
}
break;
}
*cp++ = c;
}
*cp++ = 0;
str = *area;
*area = cp;
return (str);
}
extern char *boolnames[], *numnames[], *strnames[];
extern char *boolcodes[], *numcodes[], *strcodes[];
char *malloc();
char *tgetstr();
#define TIMAGNUM 0432
store(cap)
char *cap;
{
register char *cp;
register int i;
register char **pp, **np;
char tcpbuf[1024];
char *tcp = tcpbuf;
char *tname = cap;
char *tnp;
char tnbuf[256], names[256];
char fnbuf[64], lnbuf[64];
char strtab[4096];
register char *strtabptr;
FILE *fd;
int sname, sbool;
while (*cap != SEPARATE) /* skip over names */
cap++;
*cap = 0;
strcpy(tnbuf, tname);
strcpy(names, tname);
*cap = SEPARATE;
for (tnp=tnbuf; *tnp && *tnp != '|' && *tnp != SEPARATE; tnp++)
;
if (*tnp)
*tnp++ = 0;
if (terminfo) {
strcpy(fnbuf, terminfo);
strcat(fnbuf, "/");
} else {
strcpy(fnbuf, termpath(/));
}
strcat(fnbuf, tnbuf);
checkon(fnbuf);
if (verbose)
printf("create '%s'\n", fnbuf);
fd = fopen(fnbuf, "w");
if (fd == NULL) {
perror(fnbuf);
return;
}
putsh(TIMAGNUM, fd);
sname = strlen(names)+1;
putsh(sname, fd);
sbool = listlen(boolcodes);
putsh(sbool, fd);
putsh(listlen(numcodes), fd);
putsh(listlen(strcodes), fd);
putsh(0, fd); /* length of string table */
/* Write out various terminal names to file, null terminated. */
for (cp=names; *cp; cp++)
putc(*cp, fd);
putc(0, fd);
/* Write out the booleans: flag */
for (pp=boolnames, np=boolcodes; *np; pp++,np++) {
i = tgetflag(*pp);
putc(i, fd);
if (verbose > 2)
printf("bool cap %s code %s val %d\n", *pp, *np, i);
}
if ((sname + sbool) & 1)
putc(0, fd);
/* Numbers: highbyte, lowbyte. 0377,0377 means -1 (missing) */
for (pp=numnames, np=numcodes; *np; pp++,np++) {
i = tgetnum(*pp);
putsh(i, fd);
if (verbose > 1)
printf("num cap %s code %s val %d\n", *pp, *np, i);
}
/* Strings: offset into string table. If cap is missing, -1 is used */
strtabptr = strtab;
for (pp=strnames, np=strcodes; *np; pp++,np++) {
cp = tgetstr(*pp, &tcp);
if (verbose > 3)
if (cp)
printf("str %s code %s val %s\n", *pp, *np, cp);
else
printf("str %s code %s val NULL\n", *pp, *np);
if (cp) {
putsh(strtabptr-strtab, fd);
while (*strtabptr++ = *cp++)
;
} else {
putsh(-1, fd);
}
}
fwrite(strtab, 1, strtabptr-strtab, fd);
fseek(fd, 10L, 0); /* Back to string table size in header */
putsh(strtabptr-strtab, fd);
fclose(fd);
hopcount = 0;
while (*tnp) {
i = 0;
for (tname=tnp; *tnp && *tnp != '|' && *tnp != SEPARATE; tnp++)
if (isspace(*tnp))
i = 1;
if (*tnp)
*tnp++ = 0;
if (i)
continue;
if (terminfo) {
strcpy(lnbuf, terminfo);
strcat(lnbuf, "/");
} else {
strcpy(lnbuf, termpath(/));
}
strcat(lnbuf, tname);
checkon(lnbuf);
link(fnbuf, lnbuf);
if (verbose)
printf("link '%s' '%s'\n", fnbuf, lnbuf);
}
}
/*
* Write a short out to the file in machine-independent format.
*/
putsh(val, fd)
register val;
FILE *fd;
{
if (val != -1) {
putc(val&0377, fd);
putc((val>>8)&0377, fd);
} else {
/* Write -1 as two 0377's. */
putc(0377, fd);
putc(0377, fd);
}
}
listlen(list)
register char **list;
{
register int rv = 0;
while (*list) {
list++;
rv++;
}
return rv;
}
/*
* Do various processing on a file name we are about to create.
* If it already exists, and it's older than we started, unlink it.
* Also insert a / after the 2nd char of the tail, and make sure
* that directory exists.
*/
checkon(fn)
char *fn;
{
struct stat stbuf;
char *fp, *cp;
char nbuf[64];
char cmdbuf[64];
/* Find last / */
for (cp=fn; *cp; cp++)
if (*cp == '/')
fp = cp;
if (cp-fp > 2) {
cp = fp+2;
strcpy(nbuf, fp+1);
*cp = 0;
if (stat(fn, &stbuf) < 0) {
sprintf(cmdbuf, "mkdir %s", fn);
if (verbose)
printf("%s\n", cmdbuf);
system(cmdbuf);
}
*cp++ = '/';
strcpy(cp, nbuf);
}
else
printf("%s: terminal name too short\n", fp+1);
if (stat(fn, &stbuf) < 0)
return;
if (stbuf.st_mtime < starttime) {
if (verbose > 1)
printf("unlink %s\n", fn);
unlink(fn);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.