|
|
1.1 root 1: /* @(#) tic.c: 1.1 10/15/83 (1.15 2/23/83) */
2:
3: #ifdef pdp11
4: /* Has to be this small to fit, even split I/D, with several use='s */
5: # define CAPSIZ 2048
6: #else
7: # define CAPSIZ 4096
8: #endif
9:
10: #define MAXHOP 32 /* max number of use= indirections */
11:
12: #define COMMENT '#'
13: #define SEPARATE ','
14: #define NUMBER '#'
15: #define STRING '='
16: #define CANCEL '@'
17:
18: #include <stdio.h>
19: #include <ctype.h>
20: #include <sys/types.h>
21: #include <sys/stat.h>
22:
23: #include "../local/uparm.h"
24: #ifndef E_TERMINFO
25: #define E_TERMINFO "terminfo.src"
26: #endif
27:
28: #ifndef termpath
29: #define termpath(name) "/usr/lib/terminfo/name"
30: #endif
31:
32: /*
33: * L_ctermid is only defined on USG.
34: * We use it here since we don't include curses.h.
35: */
36: #ifdef L_ctermid
37: #define index strchr
38: #endif
39:
40: /*
41: * compile: program to compile a source terminfo file into object files
42: */
43:
44: static char *tbuf;
45: static int hopcount; /* detect infinite loops in terminfo, init 0 */
46: long starttime;
47: int verbose;
48: char *terminfo;
49: char *tskip();
50: char *tgetstr();
51: char *tdecode();
52: char *getenv();
53:
54: char *sourcefile = E_TERMINFO;
55:
56: main(argc, argv)
57: char **argv;
58: {
59: int i;
60:
61: time(&starttime);
62: while (argc > 1 && argv[1][0] == '-') {
63: switch(argv[1][1]) {
64: case 'v':
65: if (argv[1][2])
66: verbose = argv[1][2] - '0';
67: else
68: verbose++;
69: break;
70: default:
71: fprintf(stderr, "Usage: compile [-v] [files...]\n");
72: exit(1);
73: }
74: argc--; argv++;
75: }
76:
77: terminfo = getenv("TERMINFO");
78:
79: if (argc == 1) {
80: compfile(stdin, "stdin");
81: }
82: else for (i=1; i<argc; i++) {
83: compfile(fopen(argv[i], "r"), argv[i]);
84: }
85: }
86:
87: /*
88: * Compile a file. This is very similar to the
89: * code in tgetstr but it passes through the whole file.
90: */
91: compfile(tf, fname)
92: FILE *tf;
93: char *fname;
94: {
95: register char *cp;
96: register int c;
97: register int i = 0, cnt = 0;
98: char bp[CAPSIZ];
99: char ibuf[CAPSIZ];
100: char *cp2;
101:
102: if (tf == NULL) {
103: perror(fname);
104: return (-1);
105: }
106: ibuf[0] = 0;
107: for (;;) {
108: tbuf = bp;
109: strcpy(bp, ibuf);
110: for (;;) {
111: if (fgets(ibuf, sizeof ibuf, tf) == NULL) {
112: fclose(tf);
113: if (tnchkuse(fname))
114: store(bp);
115: return 0;
116: }
117: /* comment or blank line */
118: if (ibuf[0] == COMMENT || ibuf[0] == '\n')
119: continue;
120: cp = &ibuf[strlen(ibuf)-3];
121: /* Allow and ignore old style backslashes */
122: if (*cp == SEPARATE && cp[1] == '\\')
123: cp[1] = 0;
124: cp[2] = 0; /* get rid of newline */
125: /* lines with leading white space are continuation */
126: if (!isspace(ibuf[0]) && *bp)
127: break;
128: if (strlen(bp) + strlen(ibuf) >= CAPSIZ) {
129: fprintf(stdout, "Terminfo entry too long:\n");
130: fprintf(stdout, "%s", bp);
131: }
132: else {
133: cp = ibuf;
134: while (isspace(*cp))
135: cp++;
136: strcat(bp, cp);
137: }
138: }
139:
140: /*
141: * We have it, now do something with it.
142: */
143: if (tnchkuse(fname))
144: store(bp);
145: }
146: }
147:
148: /*
149: * Get an entry for terminal name in buffer bp,
150: * from the terminfo file. Parse is very rudimentary;
151: * we just notice escaped newlines.
152: */
153: tgetent(bp, name, fname)
154: char *bp, *name, *fname;
155: {
156: register char *cp;
157: register int c;
158: register int i = 0, cnt = 0;
159: char ibuf[CAPSIZ];
160: char *cp2;
161: FILE *tf;
162:
163: ibuf[0] = 0;
164: tf = fopen(fname, "r");
165: if (tf == NULL)
166: return (-1);
167: tbuf = bp;
168: for (;;) {
169: strcpy(bp, ibuf);
170: for (;;) {
171: if (fgets(ibuf, sizeof ibuf, tf) == NULL) {
172: fclose(tf);
173: if (tnamatch(name))
174: return(tnchkuse(fname));
175: return 0;
176: }
177: if (ibuf[0] == COMMENT) /* comment */
178: continue;
179: cp = &ibuf[strlen(ibuf)-3];
180: /* Allow and ignore old style backslashes */
181: if (*cp == SEPARATE && cp[1] == '\\')
182: cp[1] = 0;
183: cp[2] = 0; /* get rid of newline */
184: /* lines with leading white space are continuation */
185: if (!isspace(ibuf[0]) && *bp)
186: break;
187: if (strlen(bp) + strlen(ibuf) >= CAPSIZ) {
188: fprintf(stdout, "Terminfo entry too long:\n");
189: fprintf(stdout, "%s", bp);
190: }
191: else {
192: cp = ibuf;
193: while (isspace(*cp))
194: cp++;
195: strcat(bp, cp);
196: }
197: }
198:
199: /*
200: * The real work for the match.
201: */
202: if (tnamatch(name)) {
203: fclose(tf);
204: return(tnchkuse(fname));
205: }
206: }
207: }
208:
209: /*
210: * tnchkuse: check the last entry, see if it's use=xxx. If so,
211: * recursively find xxx and append that entry (minus the names)
212: * to take the place of the use=xxx entry. This allows terminfo
213: * entries to say "like an HP2621 but doesn't turn on the labels".
214: * Note that this works because of the left to right scan.
215: */
216: tnchkuse(fname)
217: char *fname;
218: {
219: register char *p, *q;
220: char tcname[16]; /* name of similar terminal */
221: char tcbuf[CAPSIZ];
222: char restbuf[CAPSIZ];
223: char *holdtbuf = tbuf;
224: char *beg_use, *beg_next;
225: char *strchr();
226: int l;
227:
228: p = tbuf;
229: if (++hopcount > MAXHOP) {
230: fprintf(stdout, "Infinite use= loop '%s'\n", tbuf);
231: return (0);
232: }
233: for (;;) {
234: p = strchr(p, 'u');
235: if (p == NULL) {
236: tbuf = holdtbuf;
237: return 1;
238: }
239: beg_use = p;
240: if (*++p != 's' || *++p != 'e' || *++p != '=')
241: continue;
242: strncpy(tcname, ++p, sizeof tcname);
243: q = strchr(tcname, SEPARATE);
244: if (q)
245: *q = 0;
246: /* try local file ... */
247: if (tgetent(tcbuf, tcname, fname) != 1) {
248: /* ... and master */
249: if (tgetent(tcbuf, tcname, E_TERMINFO) != 1) {
250: printf("Cannot find term %s\n", tcname);
251: return(0);
252: }
253: }
254:
255: /* Find the end of the use= spec */
256: for(beg_next=beg_use;
257: *beg_next && *beg_next!=SEPARATE;
258: beg_next++)
259: ;
260: beg_next++;
261: while (isspace(*beg_next++))
262: ;
263: beg_next--;
264:
265: /* Now shuffle string around. */
266: strcpy(restbuf, beg_next);
267: p = strchr(tcbuf, SEPARATE);
268: if (p == NULL)
269: p = tcbuf;
270: else
271: p++;
272: strcpy(beg_use, p);
273: p = strchr(beg_use, '\0');
274: strcpy(p, restbuf);
275: }
276: }
277:
278: /*
279: * Tnamatch deals with name matching. The first field of the terminfo
280: * entry is a sequence of names separated by |'s, so we compare
281: * against each such name. The normal : terminator after the last
282: * name (before the first field) stops us.
283: */
284: tnamatch(np)
285: char *np;
286: {
287: register char *Np, *Bp;
288:
289: /* printf("tnamatch, np '%s', tbuf '%s'\n", np, tbuf); */
290: Bp = tbuf;
291: if (*Bp == COMMENT)
292: return(0);
293: for (;;) {
294: for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
295: ;
296: if (*Np == 0 && (*Bp == '|' || *Bp == SEPARATE || *Bp == 0))
297: return (1);
298: while (*Bp && *Bp != SEPARATE && *Bp != '|')
299: Bp++;
300: if (*Bp == 0 || *Bp == SEPARATE)
301: return (0);
302: Bp++;
303: }
304: }
305:
306: /*
307: * Skip to the next SEPARATE delimited field.
308: */
309: static char *
310: tskip(bp)
311: register char *bp;
312: {
313:
314: while (*bp && *bp != SEPARATE)
315: bp++;
316: if (*bp == 0)
317: return bp;
318: bp++;
319: while (isspace(*bp) || *bp == SEPARATE)
320: bp++;
321: return (bp);
322: }
323:
324: /*
325: * Return the (numeric) option id.
326: * Numeric options look like
327: * li#80
328: * i.e. the option string is separated from the numeric value by
329: * a # character. If the option is not found we return -1.
330: * Note that we handle octal numbers beginning with 0.
331: */
332: tgetnum(id)
333: char *id;
334: {
335: register int i, base;
336: register char *bp = tbuf;
337: int idl = strlen(id);
338: int sign = 1;
339:
340: for (;;) {
341: bp = tskip(bp);
342: if (*bp == 0)
343: return (-1);
344: if (strncmp(id, bp, idl))
345: continue;
346: bp += idl;
347: if (*bp == CANCEL)
348: return(-1);
349: if (*bp != NUMBER && *bp != STRING)
350: continue;
351: bp++;
352: if (*bp == '-') {
353: sign = -1;
354: bp++;
355: }
356: base = 10;
357: if (*bp == '0')
358: base = 8;
359: i = 0;
360: while (isdigit(*bp))
361: i *= base, i += *bp++ - '0';
362: i *= sign;
363: return (i);
364: }
365: }
366:
367: /*
368: * Handle a flag option.
369: * Flag options are given "naked", i.e. followed by a : or the end
370: * of the buffer. Return 1 if we find the option, or 0 if it is
371: * not given.
372: */
373: tgetflag(id)
374: char *id;
375: {
376: register char *bp = tbuf;
377: int idl = strlen(id);
378:
379: for (;;) {
380: bp = tskip(bp);
381: if (!*bp)
382: return (0);
383: if (strncmp(bp, id, idl) == 0) {
384: bp += idl;
385: if (!*bp || *bp == SEPARATE)
386: return (1);
387: else if (*bp == CANCEL)
388: return(0);
389: }
390: }
391: }
392:
393: /*
394: * Get a string valued option.
395: * These are given as
396: * cl=^Z
397: * Much decoding is done on the strings, and the strings are
398: * placed in area, which is a ref parameter which is updated.
399: * No checking on area overflow.
400: */
401: char *
402: tgetstr(id, area)
403: char *id, **area;
404: {
405: register char *bp = tbuf;
406: int idl = strlen(id);
407:
408: for (;;) {
409: bp = tskip(bp);
410: if (!*bp)
411: return (0);
412: if (strncmp(id, bp, idl))
413: continue;
414: bp += idl;
415: if (*bp == CANCEL)
416: return(0);
417: if (*bp != STRING)
418: continue;
419: bp++;
420: return (tdecode(bp, area));
421: }
422: }
423:
424: /*
425: * Tdecode does the grung work to decode the
426: * string capability escapes.
427: */
428: static char *
429: tdecode(str, area)
430: register char *str;
431: char **area;
432: {
433: register char *cp;
434: register int c;
435: register char *dp;
436: int i;
437:
438: cp = *area;
439: while ((c = *str++) && c != SEPARATE) {
440: switch (c) {
441:
442: case '^':
443: c = *str++ & 037;
444: break;
445:
446: case '\\':
447: /*
448: * \x escapes understood:
449: * \e escape
450: * \E escape
451: * \^ ^
452: * \\ \
453: * \, ,
454: * \: :
455: * \l linefeed
456: * \n newline (=linefeed)
457: * \r return
458: * \t tab
459: * \b backspace
460: * \f formfeed
461: * \s space
462: * \0 null
463: * \### octal ###
464: */
465: dp = "e\033E\033^^\\\\,,::l\012n\nr\rt\tb\bf\fs ";
466: c = *str++;
467: nextc:
468: if (*dp++ == c) {
469: c = *dp++;
470: break;
471: }
472: dp++;
473: if (*dp)
474: goto nextc;
475: if (isdigit(c)) {
476: c -= '0', i = 2;
477: do {
478: if (!isdigit(*str))
479: break;
480: c <<= 3;
481: c |= *str++ - '0';
482: } while (--i);
483: if (c == 0)
484: c = 0200; /* don't term. str. */
485: }
486: break;
487: }
488: *cp++ = c;
489: }
490: *cp++ = 0;
491: str = *area;
492: *area = cp;
493: return (str);
494: }
495:
496: extern char *boolnames[], *numnames[], *strnames[];
497: extern char *boolcodes[], *numcodes[], *strcodes[];
498:
499: char *malloc();
500: char *tgetstr();
501:
502: #define TIMAGNUM 0432
503:
504: store(cap)
505: char *cap;
506: {
507: register char *cp;
508: register int i;
509: register char **pp, **np;
510: char tcpbuf[1024];
511: char *tcp = tcpbuf;
512: char *tname = cap;
513: char *tnp;
514: char tnbuf[256], names[256];
515: char fnbuf[64], lnbuf[64];
516: char strtab[4096];
517: register char *strtabptr;
518: FILE *fd;
519: int sname, sbool;
520:
521: while (*cap != SEPARATE) /* skip over names */
522: cap++;
523: *cap = 0;
524: strcpy(tnbuf, tname);
525: strcpy(names, tname);
526: *cap = SEPARATE;
527:
528: for (tnp=tnbuf; *tnp && *tnp != '|' && *tnp != SEPARATE; tnp++)
529: ;
530: if (*tnp)
531: *tnp++ = 0;
532: if (terminfo) {
533: strcpy(fnbuf, terminfo);
534: strcat(fnbuf, "/");
535: } else {
536: strcpy(fnbuf, termpath(/));
537: }
538: strcat(fnbuf, tnbuf);
539: checkon(fnbuf);
540: if (verbose)
541: printf("create '%s'\n", fnbuf);
542: fd = fopen(fnbuf, "w");
543: if (fd == NULL) {
544: perror(fnbuf);
545: return;
546: }
547:
548: putsh(TIMAGNUM, fd);
549: sname = strlen(names)+1;
550: putsh(sname, fd);
551: sbool = listlen(boolcodes);
552: putsh(sbool, fd);
553: putsh(listlen(numcodes), fd);
554: putsh(listlen(strcodes), fd);
555: putsh(0, fd); /* length of string table */
556:
557: /* Write out various terminal names to file, null terminated. */
558: for (cp=names; *cp; cp++)
559: putc(*cp, fd);
560: putc(0, fd);
561:
562: /* Write out the booleans: flag */
563: for (pp=boolnames, np=boolcodes; *np; pp++,np++) {
564: i = tgetflag(*pp);
565: putc(i, fd);
566: if (verbose > 2)
567: printf("bool cap %s code %s val %d\n", *pp, *np, i);
568: }
569: if ((sname + sbool) & 1)
570: putc(0, fd);
571:
572: /* Numbers: highbyte, lowbyte. 0377,0377 means -1 (missing) */
573: for (pp=numnames, np=numcodes; *np; pp++,np++) {
574: i = tgetnum(*pp);
575: putsh(i, fd);
576: if (verbose > 1)
577: printf("num cap %s code %s val %d\n", *pp, *np, i);
578: }
579:
580: /* Strings: offset into string table. If cap is missing, -1 is used */
581: strtabptr = strtab;
582: for (pp=strnames, np=strcodes; *np; pp++,np++) {
583: cp = tgetstr(*pp, &tcp);
584: if (verbose > 3)
585: if (cp)
586: printf("str %s code %s val %s\n", *pp, *np, cp);
587: else
588: printf("str %s code %s val NULL\n", *pp, *np);
589: if (cp) {
590: putsh(strtabptr-strtab, fd);
591: while (*strtabptr++ = *cp++)
592: ;
593: } else {
594: putsh(-1, fd);
595: }
596: }
597: fwrite(strtab, 1, strtabptr-strtab, fd);
598: fseek(fd, 10L, 0); /* Back to string table size in header */
599: putsh(strtabptr-strtab, fd);
600: fclose(fd);
601: hopcount = 0;
602:
603: while (*tnp) {
604: i = 0;
605: for (tname=tnp; *tnp && *tnp != '|' && *tnp != SEPARATE; tnp++)
606: if (isspace(*tnp))
607: i = 1;
608: if (*tnp)
609: *tnp++ = 0;
610: if (i)
611: continue;
612: if (terminfo) {
613: strcpy(lnbuf, terminfo);
614: strcat(lnbuf, "/");
615: } else {
616: strcpy(lnbuf, termpath(/));
617: }
618: strcat(lnbuf, tname);
619: checkon(lnbuf);
620: link(fnbuf, lnbuf);
621: if (verbose)
622: printf("link '%s' '%s'\n", fnbuf, lnbuf);
623: }
624: }
625:
626: /*
627: * Write a short out to the file in machine-independent format.
628: */
629: putsh(val, fd)
630: register val;
631: FILE *fd;
632: {
633: if (val != -1) {
634: putc(val&0377, fd);
635: putc((val>>8)&0377, fd);
636: } else {
637: /* Write -1 as two 0377's. */
638: putc(0377, fd);
639: putc(0377, fd);
640: }
641: }
642:
643: listlen(list)
644: register char **list;
645: {
646: register int rv = 0;
647:
648: while (*list) {
649: list++;
650: rv++;
651: }
652: return rv;
653: }
654:
655: /*
656: * Do various processing on a file name we are about to create.
657: * If it already exists, and it's older than we started, unlink it.
658: * Also insert a / after the 2nd char of the tail, and make sure
659: * that directory exists.
660: */
661: checkon(fn)
662: char *fn;
663: {
664: struct stat stbuf;
665: char *fp, *cp;
666: char nbuf[64];
667: char cmdbuf[64];
668:
669: /* Find last / */
670: for (cp=fn; *cp; cp++)
671: if (*cp == '/')
672: fp = cp;
673: if (cp-fp > 2) {
674: cp = fp+2;
675: strcpy(nbuf, fp+1);
676: *cp = 0;
677: if (stat(fn, &stbuf) < 0) {
678: sprintf(cmdbuf, "mkdir %s", fn);
679: if (verbose)
680: printf("%s\n", cmdbuf);
681: system(cmdbuf);
682: }
683: *cp++ = '/';
684: strcpy(cp, nbuf);
685: }
686: else
687: printf("%s: terminal name too short\n", fp+1);
688: if (stat(fn, &stbuf) < 0)
689: return;
690: if (stbuf.st_mtime < starttime) {
691: if (verbose > 1)
692: printf("unlink %s\n", fn);
693: unlink(fn);
694: }
695: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.