1 /* @(#)cap.c 1.60 21/08/20 Copyright 2000-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)cap.c 1.60 21/08/20 Copyright 2000-2021 J. Schilling";
6 #endif
7 /*
8 * termcap a TERMCAP compiler
9 *
10 * The termcap database is an ASCII representation of the data
11 * so people may believe that there is no need for a compiler.
12 * Syntax checks and unification however are a property of compilers.
13 * We check for correct data types, output all entries in a unique
14 * order and recode all strings with the same escape notation.
15 * This is needed in to compare two entries and it makes life easier.
16 *
17 * Copyright (c) 2000-2021 J. Schilling
18 */
19 /*
20 * The contents of this file are subject to the terms of the
21 * Common Development and Distribution License, Version 1.0 only
22 * (the "License"). You may not use this file except in compliance
23 * with the License.
24 *
25 * See the file CDDL.Schily.txt in this distribution for details.
26 * A copy of the CDDL is also available via the Internet at
27 * http://www.opensource.org/licenses/cddl1.txt
28 *
29 * When distributing Covered Code, include this CDDL HEADER in each
30 * file and include the License file CDDL.Schily.txt from this distribution.
31 */
32
33 #include <schily/stdio.h>
34 #include <schily/stdlib.h>
35 #include <schily/unistd.h>
36 #include <schily/standard.h>
37 #include <schily/fcntl.h>
38 #include <schily/string.h>
39 #include <schily/termcap.h>
40 #include <schily/getargs.h>
41 #define SCHILY_PRINT
42 #define GT_COMERR /* #define comerr gtcomerr */
43 #define GT_ERROR /* #define error gterror */
44 #include <schily/schily.h>
45 #include <schily/nlsdefs.h>
46
47 #define TBUF 2048
48
49 typedef struct {
50 char *tc_name; /* Termcap name */
51 char *tc_iname; /* Terminfo name */
52 char *tc_var; /* Curses Variable name */
53 char *tc_comment; /* Explanation */
54 int tc_flags;
55 } clist;
56
57 /*
58 * Definitions for tc_flags
59 */
60 #define C_BOOL 0x01 /* This is a boolean entry */
61 #define C_INT 0x02 /* This is a numeric entry */
62 #define C_STRING 0x04 /* This is a string entry */
63 #define C_TC 0x08 /* This is a tc= string entry */
64 #define C_PAD 0x10 /* This rentry requires padding */
65 #define C_PADN 0x20 /* Padding based on affect count */
66 #define C_PARM 0x40 /* Padding based on affect count */
67 #define C_OLD 0x100 /* This is an old termcap only entry */
68 #define C_CURIOUS 0x200 /* This is a curious termcap entry */
69
70 /*
71 * The list of capabilities for the termcap command.
72 * This contains the Termcap Name, the Terminfo Name and a Comment.
73 */
74 LOCAL clist caplist[] = {
75 #include "caplist.c"
76 };
77
78 LOCAL int ncaps = sizeof (caplist) / sizeof (caplist[0]);
79
80 LOCAL BOOL nodisabled = FALSE;
81 LOCAL BOOL nounknown = FALSE;
82 LOCAL BOOL nowarn = FALSE;
83 LOCAL BOOL dooctal = FALSE;
84 LOCAL BOOL docaret = FALSE;
85 LOCAL BOOL gnugoto = FALSE;
86 LOCAL BOOL oneline = FALSE;
87
88 #ifdef HAVE_SETVBUF
89 LOCAL char obuf[4096];
90 #else
91 #ifdef HAVE_SETVBUF
92 LOCAL char obuf[BUFSIZ];
93 #endif
94 #endif
95
96 LOCAL void init_clist __PR((void));
97 LOCAL char * tskip __PR((char *ep));
98 LOCAL char * tfind __PR((char *ep, char *ent));
99 LOCAL void dumplist __PR((void));
100 LOCAL void usage __PR((int ex));
101 EXPORT int main __PR((int ac, char **av));
102 LOCAL void checkentries __PR((char *tname, int *slenp));
103 LOCAL char *type2str __PR((int type));
104 LOCAL void checkbad __PR((char *tname, char *unknown, char *disabled));
105 LOCAL void outcap __PR((char *tname, char *unknown, char *disabled, BOOL obsolete_last));
106 LOCAL char * checkgoto __PR((char *tname, char *ent, char *cm, int col, int line));
107 LOCAL char * checkquote __PR((char *tname, char *s));
108 LOCAL char * quote __PR((char *s));
109 LOCAL char * requote __PR((char *s));
110 LOCAL void compile_ent __PR((char *tname, BOOL do_tc, BOOL obsolete_last));
111 LOCAL void read_names __PR((char *fname, BOOL do_tc, BOOL obsolete_last));
112
113 /*
114 * Initialize the tc_flags struct member in "caplist".
115 */
116 LOCAL void
init_clist()117 init_clist()
118 {
119 int i;
120 int flags = 0;
121 int flags2;
122
123 for (i = 0; i < ncaps; i++) {
124 flags2 = 0;
125
126 /*
127 * Process meta entries.
128 */
129 if (caplist[i].tc_name[0] == '-' &&
130 caplist[i].tc_name[1] == '-') {
131 }
132
133 if (streql(caplist[i].tc_var, "BOOL")) {
134
135 flags &= ~(C_BOOL|C_INT|C_STRING);
136 flags |= C_BOOL;
137 }
138 if (streql(caplist[i].tc_var, "INT")) {
139
140 flags &= ~(C_BOOL|C_INT|C_STRING);
141 flags |= C_INT;
142 }
143 if (streql(caplist[i].tc_var, "STRING")) {
144
145 flags &= ~(C_BOOL|C_INT|C_STRING);
146 flags |= C_STRING;
147 }
148 if (streql(caplist[i].tc_var, "TC")) {
149
150 flags |= C_TC;
151 }
152 if (streql(caplist[i].tc_var, "COMMENT")) {
153
154 flags &= ~(C_BOOL|C_INT|C_STRING);
155 flags &= ~C_OLD;
156 }
157 if (streql(caplist[i].tc_var, "OBSOLETE")) {
158 /*
159 * OBSOLETE is used together with BOOL, INT or STRING
160 */
161 flags |= C_OLD;
162 }
163 if (streql(caplist[i].tc_var, "CURIOUS")) {
164 /*
165 * CURIOUS is a special tag for the tc= entry.
166 */
167 flags &= ~C_OLD;
168 flags |= C_CURIOUS;
169 }
170 if (caplist[i].tc_comment[0] != '\0') {
171 char *p = caplist[i].tc_comment;
172
173 p += strlen(p) - 1;
174
175 if (*p == ')' && strchr("NP*", p[-1]) != NULL) {
176 while (strchr("NP*", *--p) != NULL) {
177 if (*p == 'N')
178 flags2 |= C_PARM;
179 if (*p == 'P')
180 flags2 |= C_PAD;
181 if (*p == '*')
182 flags2 |= C_PADN;
183 }
184 }
185 }
186 caplist[i].tc_flags = flags | flags2;
187 }
188 }
189
190
191 /*
192 * Skip past next ':'.
193 * If there are two consecutive ':', the returned pointer may point to ':'.
194 *
195 * A copy from the local function libxtermcap:tgetent.c:tskip()
196 */
197 LOCAL char *
tskip(ep)198 tskip(ep)
199 register char *ep;
200 {
201 while (*ep) {
202 if (*ep++ == ':')
203 return (ep);
204 }
205 return (ep);
206 }
207
208 /*
209 * A copy from the local function libxtermcap:tgetent.c:tfind()
210 */
211 LOCAL char *
tfind(ep,ent)212 tfind(ep, ent)
213 register char *ep;
214 char *ent;
215 {
216 register char e0 = ent[0];
217 register char e1 = ent[1];
218
219 for (;;) {
220 ep = tskip(ep);
221 if (*ep == '\0')
222 break;
223 if (*ep == ':')
224 continue;
225 if (e0 != *ep++)
226 continue;
227 if (*ep == '\0')
228 break;
229 if (e1 != *ep++)
230 continue;
231 return (ep);
232 }
233 return ((char *) NULL);
234 }
235
236 /*
237 * Dump the all entries from "caplist".
238 * Skip all special entries.
239 */
240 LOCAL void
dumplist()241 dumplist()
242 {
243 int i;
244 int j;
245 char parms[8];
246
247 for (i = 0; i < ncaps; i++) {
248 int l;
249 /*
250 * Skip meta entries.
251 */
252 if (caplist[i].tc_name[0] == '-' &&
253 caplist[i].tc_name[1] == '-')
254 continue;
255 if (caplist[i].tc_name[0] == '.' &&
256 caplist[i].tc_name[1] == '.')
257 continue;
258
259 parms[0] = '\0';
260 j = 0;
261 if (caplist[i].tc_flags & C_PARM)
262 parms[j++] = 'N';
263 if (caplist[i].tc_flags & C_PAD)
264 parms[j++] = 'P';
265 if (caplist[i].tc_flags & C_PADN)
266 parms[j++] = '*';
267 parms[j] = '\0';
268
269
270 l = strlen(caplist[i].tc_var);
271 printf("{\"%s\", \"%s\",%s\"%s\"},%s /*%c%c%-3s %s */\n",
272 caplist[i].tc_name,
273 caplist[i].tc_iname,
274 strlen(caplist[i].tc_iname) >= 5 ? "\t":"\t\t",
275 caplist[i].tc_var,
276 l >= 20 ? "":
277 l >= 12 ? "\t":
278 l >= 4 ? "\t\t": "\t\t\t",
279 (caplist[i].tc_flags & C_BOOL) ? 'B':
280 (caplist[i].tc_flags & C_INT) ? 'I':
281 (caplist[i].tc_flags & C_STRING) ? 'S': '?',
282 (caplist[i].tc_flags & C_OLD) ? 'O': ' ',
283 parms,
284 caplist[i].tc_comment);
285 }
286 }
287
288 LOCAL void
usage(ex)289 usage(ex)
290 int ex;
291 {
292 error("Usage: %s\n", get_progname());
293 error("Options:\n");
294 error("-e use termcap entry from TERMCAP environment if present\n");
295 error("-help print this help\n");
296 error("-version print version number\n");
297 error("-dumplist dump internal capability list\n");
298 error("-inorder print caps in order, else print outdated caps last\n");
299 error("-noinorder switch off -inorder, print unknown and outdated caps last\n");
300 error("-dooctal prefer '\\003' before '^C' when creating escaped strings\n");
301 error("-docaret prefer '^M' before '\\r' when creating escaped strings\n");
302 error("if=name input file for termcap compiling\n");
303 error("-gnugoto allow GNU tgoto() format extensions '%%C' and '%%m'.\n");
304 error("-nodisabled do not output disabled termcap entries\n");
305 error("-nounknown do not output unkonwn termcap entries\n");
306 error("-nowarn do not warn about problems that could be fixed\n");
307 error("-oneline output termcap entries in a single line\n");
308 error("-s Output commands to set and export TERM and TERMCAP.\n");
309 error("-tc follow tc= entries and generate cumulative output\n");
310 error("-v increase verbosity level\n");
311 error("With if= name, -inorder is default\n");
312 exit(ex);
313 }
314
315 EXPORT int
main(ac,av)316 main(ac, av)
317 int ac;
318 char *av[];
319 {
320 int cac;
321 char *const *cav;
322 char *tbuf; /* Termcap buffer */
323 char unknown[5*TBUF]; /* Buffer fuer "unknown" Entries */
324 char disabled[5*TBUF]; /* Buffer fuer :..xx: Entries */
325 char *tname = getenv("TERM");
326 char *tcap = getenv("TERMCAP");
327 int slen = 0;
328 int fullen;
329 int strippedlen;
330 BOOL help = FALSE;
331 BOOL prvers = FALSE;
332 BOOL dodump = FALSE;
333 BOOL inorder = FALSE;
334 BOOL noinorder = FALSE;
335 BOOL sflag = FALSE;
336 int verbose = 0;
337 BOOL do_tc = FALSE;
338 BOOL useenv = FALSE;
339 char *infile = NULL;
340
341 save_args(ac, av);
342
343 (void) setlocale(LC_ALL, "");
344
345 #ifdef USE_NLS
346 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
347 #define TEXT_DOMAIN "termcap" /* Use this only if it weren't */
348 #endif
349 { char *dir;
350 dir = searchfileinpath("share/locale", F_OK,
351 SIP_ANY_FILE|SIP_NO_PATH, NULL);
352 if (dir)
353 (void) bindtextdomain(TEXT_DOMAIN, dir);
354 else
355 #if defined(PROTOTYPES) && defined(INS_BASE)
356 (void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
357 #else
358 (void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
359 #endif
360 (void) textdomain(TEXT_DOMAIN);
361 }
362 #endif /* USE_NLS */
363
364 #ifdef HAVE_SETVBUF
365 setvbuf(stdout, obuf, _IOFBF, sizeof (obuf));
366 #else
367 #ifdef HAVE_SETVBUF
368 setbuf(stdout, obuf);
369 #endif
370 #endif
371 init_clist();
372
373 cac = ac;
374 cav = av;
375 cac--, cav++;
376 if (getallargs(&cac, &cav, "help,version,e,dumplist,inorder,noinorder,oneline,v+,s,tc,if*,nodisabled,nounknown,nowarn,dooctal,docaret,gnugoto",
377 &help, &prvers,
378 &useenv,
379 &dodump, &inorder, &noinorder,
380 &oneline,
381 &verbose,
382 &sflag,
383 &do_tc,
384 &infile,
385 &nodisabled, &nounknown,
386 &nowarn, &dooctal, &docaret,
387 &gnugoto) < 0) {
388 errmsgno(EX_BAD, "Bad option '%s'\n", cav[0]);
389 usage(EX_BAD);
390 }
391 if (help)
392 usage(0);
393 if (prvers) {
394 gtprintf("termcap %s %s (%s-%s-%s)\n\n", "1.60", "2021/08/20",
395 HOST_CPU, HOST_VENDOR, HOST_OS);
396 gtprintf("Copyright (C) 2000-2021 %s\n", _("J�rg Schilling"));
397 gtprintf("This is free software; see the source for copying conditions. There is NO\n");
398 gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
399 exit(0);
400 }
401
402 if (dodump) {
403 dumplist();
404 exit(0);
405 }
406
407 if (infile)
408 inorder = TRUE;
409 if (noinorder)
410 inorder = FALSE;
411
412
413 if (!useenv && tcap && *tcap != '/')
414 *tcap = '\0';
415
416 if (infile) {
417 char env[2048];
418
419 if (tcap)
420 *tcap = '\0';
421 if (tname)
422 *tname = '\0';
423 snprintf(env, sizeof (env), "TERMPATH=%s", infile);
424 putenv(env);
425 read_names(infile, do_tc, !inorder);
426 exit(0);
427 }
428
429 /*
430 * Check existence & unstripped Termcap len
431 *
432 * TCF_NO_TC Don't follow tc= entries
433 * TCF_NO_SIZE Don't get actual ttysize (li#/co#)
434 * TCF_NO_STRIP Don't strip down termcap buffer
435 *
436 * If called with the -s option, this is the only place where we
437 * retrieve the termcap entry, so use the default settings in this case.
438 */
439 if (!sflag)
440 tcsetflags((do_tc?0:TCF_NO_TC)|TCF_NO_SIZE|TCF_NO_STRIP);
441 if (tgetent(NULL, tname) != 1)
442 comerr("no term '%s' found\n", tname);
443 tbuf = tcgetbuf();
444 fullen = strlen(tbuf);
445
446 if (sflag) {
447 char *p = getenv("SHELL");
448 int is_csh = FALSE;
449
450 if (p) {
451 int len = strlen(p);
452
453 if (len >= 3 && streql(&p[len-3], "csh"))
454 is_csh = TRUE;
455 }
456 if (is_csh) {
457 /*
458 * csh
459 */
460 printf("set noglob;\n");
461 printf("setenv TERM %s;\n", tname);
462 printf("setenv TERMCAP '%s';\n", tbuf);
463 printf("unset noglob;\n");
464 } else {
465 /*
466 * Bourne Shell and compatible
467 */
468 printf("export TERMCAP TERM;\n");
469 printf("TERM=%s;\n", tname);
470 printf("TERMCAP='%s';\n", tbuf);
471 }
472 exit(0);
473 }
474
475 /*
476 * Get Stripped len
477 */
478 tcsetflags((do_tc?0:TCF_NO_TC)|TCF_NO_SIZE);
479 tgetent(NULL, tname);
480 tbuf = tcgetbuf();
481 strippedlen = strlen(tbuf);
482
483 if (verbose > 0)
484 checkentries(tname, &slen);
485
486 if (verbose > 1) {
487 printf("tbuf: '%s'\n", tbuf);
488 printf("full tbuf len: %d stripped tbuf len: %d\n", fullen, strippedlen);
489 printf("string length: %d\n", slen);
490 }
491
492 checkbad(tname, unknown, disabled);
493 outcap(tname, unknown, disabled, !inorder);
494
495 return (0);
496 }
497
498 /*
499 * Check entries for correct type and print them
500 */
501 LOCAL void
checkentries(tname,slenp)502 checkentries(tname, slenp)
503 char *tname;
504 int *slenp;
505 {
506 char stbuf[10*TBUF]; /* String buffer zum zaehlen */
507 char *sbp; /* String buffer Ende */
508 int i;
509 int b; /* Fuer bool/int Werte */
510 char *p;
511 char *p2;
512 char *pe;
513 char *tbuf = tcgetbuf();
514
515 /*
516 * Print first part of Termcap entry
517 */
518 p = strchr(tbuf, ':');
519 if (p)
520 i = p - tbuf;
521 else
522 i = strlen(tbuf);
523 printf("tbuf: '%.*s'\n", i, tbuf);
524
525 b = strlen(tbuf);
526 if (b > 2*TBUF) {
527 error("%s: termcap entry too long (%d bytes).\n", tname, b);
528 return;
529 }
530
531 sbp = stbuf;
532 p2 = tbuf;
533 for (i = 0; i < ncaps; i++) {
534 /*
535 * Skip meta entries.
536 */
537 if (caplist[i].tc_name[0] == '-' &&
538 caplist[i].tc_name[1] == '-')
539 continue;
540
541 if (!(pe = tfind(tbuf, caplist[i].tc_name)))
542 continue;
543
544 if ((caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING)) == 0)
545 continue;
546
547 if ((caplist[i].tc_flags & C_OLD) != 0) {
548 printf("OBSOLETE ");
549 }
550
551 if (*pe == '@') {
552 printf("'%s' -> %s", caplist[i].tc_name,
553 ((caplist[i].tc_flags & C_STRING) != 0)? "null @" :
554 (((caplist[i].tc_flags & C_INT) != 0)? "-1 @" :
555 (((caplist[i].tc_flags & C_BOOL) != 0)? "FALSE @" :
556 "unknown-@")));
557 printf(" %s", caplist[i].tc_comment);
558 printf("\n");
559 continue;
560 }
561 if (i == ncaps -1) {
562 p2 = tfind(p2, "tc");
563 if (p2 == NULL)
564 break;
565 if (*p2 == '=') {
566 char *n = tskip(p2);
567
568 p = &p2[1];
569 n--; /* Backstep from ':' */
570 if ((n - p) >= TBUF) {
571 error("NOTICE(%s). string '%.*s' too long.\n",
572 tname, (int)(n - p2)+2, p2-2);
573 break;
574 }
575 strlcpy(sbp, p, n-p+1);
576 p = sbp; /* The tc= parameter */
577 } else
578 break;
579 } else {
580 char *e = tfind(tbuf, caplist[i].tc_name);
581 char *n = tskip(e);
582
583 /*
584 * e and n are always != NULL.
585 */
586 p = NULL;
587 if (*e == '=' && (n - e) > 1024) {
588 /*
589 * Warn if the string is > 1024, but let up to
590 * 4096 bytes appear to be able to count the
591 * real length.
592 */
593 error("NOTICE(%s). string '%.*s' longer than %d.\n",
594 tname, (int)(n - e)+1, e-2, 1024);
595 }
596 if (*e != '=') {
597 /*
598 * Not a string type entry.
599 */
600 /* EMPTY */
601 ;
602 } else if ((n - e) > 4*TBUF) {
603 error("NOTICE(%s). string '%.*s' too long.\n",
604 tname, (int)(n - e)+1, e-2);
605 } else if ((sbp - stbuf) > 4*TBUF) {
606 error(
607 "NOTICE(%s). '%.*s' string table overflow.\n",
608 tname, (int)(n - e)+1, e-2);
609 } else {
610 p = tgetstr(caplist[i].tc_name, &sbp);
611 }
612 }
613 if (caplist[i].tc_name[0] == 'm' &&
614 caplist[i].tc_name[1] == 'a' &&
615 (caplist[i].tc_flags & C_STRING) == 0) {
616 /*
617 * The "ma" entry exists as numeric and string
618 * capability. We are currently not looking for
619 * the string capability, so ignore this entry.
620 */
621 /* EMPTY */
622 ;
623 } else if (p) {
624 /*
625 * XXX Option zum Deaktivieren des Quoten
626 */
627 p = quote(p);
628 printf("'%s' -> '%s'", caplist[i].tc_name, p);
629 printf(" %s", caplist[i].tc_comment);
630
631 if ((caplist[i].tc_flags & C_STRING) == 0)
632 printf(" -> WARNING: TYPE mismatch");
633 printf("\n");
634 if (i == ncaps -1)
635 i--;
636 continue;
637 }
638 b = tgetnum(caplist[i].tc_name);
639
640 if (caplist[i].tc_name[0] == 'm' &&
641 caplist[i].tc_name[1] == 'a' &&
642 (caplist[i].tc_flags & C_INT) == 0) {
643 /*
644 * The "ma" entry exists as numeric and string
645 * capability. We are currently not looking for
646 * the numeric capability, so ignore this entry.
647 */
648 /* EMPTY */
649 ;
650 } else if (b >= 0) {
651 printf("'%s' -> %d", caplist[i].tc_name, b);
652 printf(" %s", caplist[i].tc_comment);
653 if ((caplist[i].tc_flags & C_INT) == 0)
654 printf(" -> WARNING: TYPE mismatch");
655 printf("\n");
656 continue;
657 }
658
659 b = tgetflag(caplist[i].tc_name);
660 printf("'%s' -> %s", caplist[i].tc_name, b?"TRUE":"FALSE");
661 printf(" %s", caplist[i].tc_comment);
662 if ((caplist[i].tc_flags & C_BOOL) == 0)
663 printf(" -> WARNING: TYPE mismatch");
664 printf("\n");
665 continue;
666
667 }
668 if (slenp)
669 *slenp = sbp - stbuf;
670 }
671
672 LOCAL char *
type2str(type)673 type2str(type)
674 int type;
675 {
676 switch (type & 0x07) {
677
678 case C_INT: return ("INT");
679 case C_STRING: return ("STRING");
680 case C_BOOL: return ("BOOL");
681 default: return ("<unknown>");
682 }
683 }
684
685 /*
686 * Check for bad termcap entries
687 */
688 LOCAL void
checkbad(tname,unknown,disabled)689 checkbad(tname, unknown, disabled)
690 char *tname;
691 char *unknown;
692 char *disabled;
693 {
694 char ent[3]; /* Space to hold termcap entry */
695 char *up; /* Unknown buffer Ende */
696 char *dp; /* Disabled buffer Ende */
697 int rb; /* Fuer bool/int Werte */
698 char *rp; /* Fuer string Werte */
699 BOOL found; /* Correct type already found */
700 int i;
701 char *p;
702 char *p2;
703 char *xp;
704 char *tbuf = tcgetbuf();
705 BOOL out_tty = isatty(STDOUT_FILENO);
706
707 p = tskip(tbuf);
708
709 i = strlen(tbuf);
710 if (i > 2*TBUF) {
711 printf("# BAD(%s). Skipping long entry (%d bytes): '%s'\n",
712 tname,
713 i,
714 tbuf);
715 if (!out_tty)
716 error("BAD(%s). Skipping long entry (%d bytes): '%.*s'\n",
717 tname,
718 i,
719 (int)(p - tbuf - 1), tbuf);
720 return;
721 }
722
723 up = unknown;
724 dp = disabled;
725 while (*p) {
726 while (*p == ':')
727 p = tskip(p);
728 if (*p == '\0')
729 break;
730 /*
731 * Avoid to warn about bad termcap entries as long as we
732 * implement support for the terminfo escape '\:'.
733 */
734 if (p > (tbuf+1) && p[-1] == ':' && p[-2] == '\\') {
735 p = tskip(p);
736 continue;
737 }
738 if (p[1] == '\0' ||
739 p[1] == ':') {
740 if (p[0] == '\t' || /* This is an indented line */
741 p[0] == ' ') { /* also ignore single space */
742 p = tskip(p);
743 continue;
744 }
745 printf("# NOTICE(%s). Short entry (':%c%s') removed\n",
746 tname, p[0], p[1]?":":"");
747 if (!out_tty)
748 error("NOTICE(%s). Short entry (':%c%s') removed\n",
749 tname, p[0], p[1]?":":"");
750 p = tskip(p);
751 continue;
752 }
753 checkquote(tname, p);
754 if (p[2] != ':' && p[2] != '@' && p[2] != '#' && p[2] != '=') {
755 p2 = tskip(p);
756 if (p[0] == ' ' || p[0] == '\t') {
757 printf("# BAD(%s). Skipping %sblank entry: '%.*s'\n",
758 tname,
759 (p[1] == ' ' || p[1] == '\t')?
760 "":"partially ",
761 (int)(p2 - p - 1), p);
762 if (!out_tty)
763 error("BAD(%s). Skipping %sblank entry: '%.*s'\n",
764 tname,
765 (p[1] == ' ' || p[1] == '\t')?
766 "":"partially ",
767 (int)(p2 - p - 1), p);
768 p = tskip(p);
769 continue;
770 }
771 if (p[0] == '.') {
772 if (strncmp(p, "..DISABLED:", 11) == 0 ||
773 strncmp(p, "..OBSOLETE:", 11) == 0 ||
774 strncmp(p, "..UNKNOWN:", 10) == 0) {
775 p = tskip(p);
776 continue;
777 }
778 printf("# NOTICE(%s). Disabled entry: '%.*s'\n",
779 tname, (int)(p2 - p - 1), p);
780 if (!out_tty)
781 error("NOTICE(%s). Disabled entry: '%.*s'\n",
782 tname, (int)(p2 - p - 1), p);
783 if (1) {
784 strncpy(dp, p, p2 - p);
785 dp += p2 - p;
786 } else {
787 strncpy(dp, p, p2 - p);
788 dp[p2 - p] = '\0';
789 strcpy(dp, requote(dp));
790 dp += strlen(dp);
791 *dp++ = ':';
792 }
793 p = tskip(p);
794 continue;
795 }
796 xp = p;
797 if (xp > &tbuf[2])
798 xp = &p[-2];
799 while (xp > tbuf && *xp != ':')
800 --xp;
801 printf("# BAD(%s). Illegal entry (3rd char '%c' for ':%c%c%c'): '%.*s'\n",
802 tname, p[2], p[0], p[1], p[2], (int)(p2 - xp - 1), xp);
803 if (!out_tty)
804 error("BAD(%s). Illegal entry (3rd char '%c' for '%c%c%c'): '%.*s'\n",
805 tname, p[2], p[0], p[1], p[2], (int)(p2 - p - 1), p);
806 p = tskip(p);
807 continue;
808 }
809 if ((p[0] == ' ' || p[0] == '\t') &&
810 (p[1] == ' ' || p[1] == '\t')) {
811 p2 = tskip(p);
812 printf("# BAD(%s). Skipping blank entry: '%.*s'\n",
813 tname, (int)(p2 - p - 1), p);
814 if (!out_tty)
815 error("BAD(%s). Skipping blank entry: '%.*s'\n",
816 tname, (int)(p2 - p - 1), p);
817 p = tskip(p);
818 continue;
819 }
820 strncpy(ent, p, 2);
821 ent[2] = '\0';
822
823 for (i = 0; i < ncaps; i++) {
824 /*
825 * Skip meta entries.
826 */
827 if (caplist[i].tc_name[0] == '-' &&
828 caplist[i].tc_name[1] == '-')
829 continue;
830
831 if (caplist[i].tc_name[0] == 'm' &&
832 caplist[i].tc_name[1] == 'a' &&
833 (caplist[i].tc_flags & C_STRING) == 0) {
834 /*
835 * We found the "ma" entry in the numeric
836 * variant. If this was a string cap, continue
837 * searching for the matching caplist entry.
838 * The "ma=" entry is past "ma#" in our list.
839 */
840 if (p[2] == '=')
841 continue;
842 }
843
844 if (streql(ent, caplist[i].tc_name))
845 break;
846 }
847 if (i == ncaps) {
848 p2 = tskip(p);
849 printf("# NOTICE(%s). Unknown entry ('%s'): '%.*s'\n",
850 tname, ent, (int)(p2 - p - 1), p);
851 if (!out_tty)
852 error("NOTICE(%s). Unknown entry ('%s'): '%.*s'\n",
853 tname, ent, (int)(p2 - p - 1), p);
854 if (1) {
855 strncpy(up, p, p2 - p);
856 up += p2 - p;
857 } else {
858 strncpy(up, p, p2 - p);
859 up[p2 - p] = '\0';
860 strcpy(up, requote(up));
861 up += strlen(up);
862 *up++ = ':';
863 }
864 } else if ((caplist[i].tc_flags & (C_STRING|C_PARM)) == (C_STRING|C_PARM)) {
865
866 if (p[2] == '=') {
867 char buf[5*TBUF];
868 char *bp = buf;
869 char *val = tdecode(&p[3], &bp);
870
871 checkgoto(tname, ent, val, 0, 0);
872 }
873 }
874
875 if (i == ncaps) {
876 /*
877 * This is an unknown entry that has already been
878 * handled above. We cannot check the type for unknown
879 * entries, so continue to check the other entries.
880 */
881 p = tskip(p);
882 continue;
883 }
884 found = FALSE;
885 rp = tgetstr(caplist[i].tc_name, NULL);
886 if (rp)
887 found = TRUE;
888 if (caplist[i].tc_name[0] == 'm' &&
889 caplist[i].tc_name[1] == 'a' &&
890 (caplist[i].tc_flags & C_STRING) == 0) {
891 /* EMPTY */
892 ;
893 } else if (rp) {
894 if ((caplist[i].tc_flags & C_STRING) == 0) {
895 p2 = tskip(p);
896 printf("# BAD(%s). Type mismatch '%s' in '%.*s' is STRING should be %s\n",
897 tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
898 }
899 }
900 if (rp) { /* tgetstr() return was malloc()ed */
901 free(rp);
902 rp = NULL;
903 }
904 rb = tgetnum(caplist[i].tc_name);
905 if (rb >= 0)
906 found = TRUE;
907 if (caplist[i].tc_name[0] == 'm' &&
908 caplist[i].tc_name[1] == 'a' &&
909 (caplist[i].tc_flags & C_INT) == 0) {
910 /* EMPTY */
911 ;
912 } else if (rb >= 0) {
913 if ((caplist[i].tc_flags & C_INT) == 0) {
914 p2 = tskip(p);
915 printf("# BAD(%s). Type mismatch '%s' in '%.*s' is INT should be %s\n",
916 tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
917 }
918 }
919
920 if (!found && p[2] != '@') {
921 rb = tgetflag(caplist[i].tc_name);
922 if ((caplist[i].tc_flags & C_BOOL) == 0) {
923 p2 = tskip(p);
924 if (rb == 0 && p[2] != '@') {
925 printf("# NOTICE(%s). Canceled entry '%s@' followed by '%.*s' expected type %s\n",
926 tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
927 } else {
928 printf("# BAD(%s). Type mismatch '%s' in '%.*s' is BOOL should be %s\n",
929 tname, ent, (int)(p2 - p - 1), p, type2str(caplist[i].tc_flags));
930 }
931 }
932 }
933
934 p = tskip(p);
935 }
936 *up = '\0';
937 *dp = '\0';
938 if (unknown[0] != '\0') {
939 error("NOTICE(%s). Unknown: '%s'\n", tname, unknown);
940 }
941 if (disabled[0] != '\0') {
942 error("NOTICE(%s). Disabled: '%s'\n", tname, disabled);
943 }
944 }
945
946 LOCAL int itotype[6] = { 0, C_BOOL, C_INT, C_STRING, 0, C_TC|C_STRING };
947 #ifdef DEBUG
948 LOCAL char *itoname[6] = { "0", "C_BOOL", "C_INT", "C_STRING", "C_UNKNOWN", "C_TC" };
949 #endif
950
951 LOCAL void
outcap(tname,unknown,disabled,obsolete_last)952 outcap(tname, unknown, disabled, obsolete_last)
953 char *tname;
954 char *unknown;
955 char *disabled;
956 BOOL obsolete_last; /* obsolete_last == !inorder */
957 {
958 char stbuf[10*TBUF]; /* String buffer zum zaehlen */
959 char line[10*TBUF]; /* Fuer Einzelausgabe */
960 char *sbp; /* String buffer Ende */
961 int llen;
962 int curlen;
963 int i;
964 int j = 0;
965 int b; /* Fuer bool/int Werte */
966 int flags;
967 char *p;
968 char *p2;
969 char *pe;
970 char *tbuf = tcgetbuf();
971 BOOL didobsolete = FALSE;
972
973
974 b = strlen(tbuf);
975 if (b > 2*TBUF) {
976 return;
977 }
978
979 p = strchr(tbuf, ':');
980 if (p)
981 i = p - tbuf;
982 else
983 i = strlen(tbuf);
984 printf("%.*s:", i, tbuf); /* Print Terminal name/label */
985 llen = i + 1;
986
987 sbp = stbuf;
988 flags = 0;
989 p2 = tbuf;
990
991 /*
992 * If obsolete_last is FALSE, then this loop goes from j=1..5,
993 * else, there is only one loop with j=-1 as the loop stops with j==0.
994 */
995 for (j = obsolete_last ? -1:1; j != 0 && j <= 5; j++)
996 for (i = 0; i < ncaps; i++) {
997 #ifdef DEBUG
998 flush();
999 error("caplist[%d]->'%c%c' (%s)->'%.10s' j=%d\n",
1000 i, caplist[i].tc_name[0], caplist[i].tc_name[1],
1001 itoname[j], tfind(tbuf, caplist[i].tc_name), j);
1002 #endif
1003 /*
1004 * Skip meta entries.
1005 */
1006 if (caplist[i].tc_name[0] == '-' &&
1007 caplist[i].tc_name[1] == '-')
1008 continue;
1009
1010 /*
1011 * Print unknown entries in order at the end of the respective
1012 * block. Do not mark inline them and requote the strings.
1013 */
1014 if (j >= 1 && j <= 3 && i == ncaps -1) {
1015 char *px = unknown;
1016 int t = itotype[j];
1017 char *val = NULL; /* Keep GCC happy */
1018
1019 while (*px) {
1020 pe = px;
1021 px = tskip(pe);
1022
1023 if (t == C_BOOL) {
1024 if (pe[2] != ':' && pe[2] != '@')
1025 continue;
1026 } else if (t == C_INT) {
1027 if (pe[2] != '#')
1028 continue;
1029 } else {
1030 if (pe[2] != '=')
1031 continue;
1032 val = requote(&pe[3]);
1033 }
1034 p = pe;
1035 curlen = px - pe;
1036 if (p[2] == '=')
1037 curlen = strlen(val) + 4;
1038 if (curlen <= 0)
1039 break;
1040 if (flags != t && !oneline) {
1041 printf("\\\n\t:");
1042 llen = 9;
1043 flags = t;
1044 }
1045 if ((llen > 9) && ((llen + curlen) >= 79 &&
1046 !oneline)) {
1047 printf("\\\n\t:");
1048 llen = 9 + curlen;
1049 } else {
1050 llen += curlen;
1051 }
1052 if (p[2] == '=')
1053 printf("%.2s=%.*s:", p, curlen, val);
1054 else
1055 printf("%.*s", curlen, p);
1056 }
1057 }
1058 /*
1059 * If j < 0, sort order is order from caplist array
1060 * and thus obsolete entries appear past non obsolete entries.
1061 *
1062 * If j > 0, sort order is BOOL -> INT -> STRING
1063 */
1064 if (j > 0 && (caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING|C_TC)) != itotype[j])
1065 continue;
1066
1067 if ((caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING)) == 0) {
1068 if (streql(caplist[i].tc_var, "unknown")) {
1069 if (j > 0) /* Printed in order before */
1070 continue;
1071 if (unknown[0] == '\0')
1072 continue;
1073 if (nounknown)
1074 continue;
1075
1076 if ((llen > 9 || flags == 0) && !oneline)
1077 printf("\\\n\t:");
1078 if (!oneline) {
1079 printf("%s%s",
1080 "..UNKNOWN:\\\n\t:",
1081 unknown);
1082 }
1083 llen = 99;
1084 } else {
1085 if (disabled[0] == '\0')
1086 continue;
1087 if (nodisabled)
1088 continue;
1089
1090 if ((llen > 9 || flags == 0) && !oneline)
1091 printf("\\\n\t:");
1092 if (!oneline) {
1093 printf("%s%s",
1094 "..DISABLED:\\\n\t:",
1095 disabled);
1096 }
1097 llen = 99;
1098 }
1099 continue;
1100 }
1101
1102 if (!(pe = tfind(tbuf, caplist[i].tc_name)))
1103 continue;
1104
1105 if (*pe == '@') {
1106 curlen = sprintf(line, "%s@:", caplist[i].tc_name);
1107 goto printit;
1108 }
1109 if (i == ncaps -1) {
1110 /*
1111 * tfind() first calls tskip() but the last printed
1112 * entry may be just before the "tc=" enty and did
1113 * already call p2 = tskip(pe). In this case, we must
1114 * not call tfind().
1115 */
1116 if (strncmp(p2, "tc=", 3) == 0)
1117 p2 = &p2[2];
1118 else
1119 p2 = tfind(p2, "tc");
1120 if (p2 == NULL)
1121 break; /* End of loop */
1122 if (*p2 == '=') {
1123 p = &p2[1];
1124 strcpy(sbp, p);
1125 if ((p = strchr(sbp, ':')) != NULL)
1126 *p = '\0';
1127 p = sbp;
1128 } else {
1129 break; /* End of loop */
1130 }
1131 curlen = sprintf(line, "%s=%s:", caplist[i].tc_name, p);
1132 i--;
1133 goto printit;
1134 } else {
1135 p = tgetstr(caplist[i].tc_name, &sbp);
1136 }
1137
1138 if (p) {
1139 if (caplist[i].tc_flags & C_INT) { /* check type for "ma" */
1140 if (caplist[i].tc_name[0] == 'm' &&
1141 caplist[i].tc_name[1] == 'a') {
1142 /*
1143 * The "ma" entry exists as numeric and
1144 * string capability. We are currently
1145 * not looking for the string
1146 * capability, so try to check for int.
1147 */
1148 goto trynum;
1149 }
1150 }
1151
1152 if (caplist[i].tc_flags & C_STRING) { /* check type for "ma" */
1153 curlen = sprintf(line, "%s=%s:", caplist[i].tc_name, quote(p));
1154 goto printit;
1155 } else {
1156 p2 = tskip(pe);
1157 error("%s: Illegal entry '%s' '%.*s' (should not be a string)\n",
1158 tname, caplist[i].tc_name,
1159 (int)(p2 - pe - 1), pe);
1160 continue;
1161 }
1162 }
1163 trynum:
1164 b = tgetnum(caplist[i].tc_name);
1165 if (b >= 0) {
1166 if (caplist[i].tc_flags & C_INT) { /* check type for "ma" */
1167 curlen = sprintf(line, "%s#%d:", caplist[i].tc_name, b);
1168 goto printit;
1169 } else {
1170 p2 = tskip(pe);
1171 if (caplist[i].tc_name[0] == 'm' &&
1172 caplist[i].tc_name[1] == 'a') {
1173 /*
1174 * The "ma" entry exists as numeric and
1175 * string capability. We are currently
1176 * not looking for the string
1177 * capability. We will check the string
1178 * entry later in the caplist[i] loop.
1179 */
1180 continue;
1181 }
1182 error("%s: Illegal entry '%s' '%.*s' (should not be a number)\n",
1183 tname, caplist[i].tc_name,
1184 (int)(p2 - pe - 1), pe);
1185 continue;
1186 }
1187 }
1188
1189 b = tgetflag(caplist[i].tc_name);
1190 if (b != 0) {
1191 if (caplist[i].tc_flags & C_BOOL) {
1192 curlen = sprintf(line, "%s:", caplist[i].tc_name);
1193 goto printit;
1194 } else {
1195 p2 = tskip(pe);
1196 error("%s: Illegal entry '%s' '%.*s' (should not be a bool)\n",
1197 tname, caplist[i].tc_name,
1198 (int)(p2 - pe - 1), pe);
1199 continue;
1200 }
1201 }
1202 p2 = tskip(pe);
1203 if (caplist[i].tc_flags & C_INT) { /* check type for "ma" */
1204 if (caplist[i].tc_name[0] == 'm' &&
1205 caplist[i].tc_name[1] == 'a' &&
1206 *pe == '=') {
1207 /*
1208 * This entry will be checked later.
1209 * The "ma=" entry is past "ma#" in our list.
1210 */
1211 continue;
1212 }
1213 }
1214 error("%s: Illegal entry '%s' '%.*s'\n",
1215 tname, caplist[i].tc_name,
1216 (int)(p2 - pe - 1), pe);
1217 continue;
1218
1219 printit:
1220 if (!oneline &&
1221 flags != (caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING|C_TC))) {
1222 printf("\\\n\t:");
1223 llen = 9;
1224 flags = caplist[i].tc_flags & (C_BOOL|C_INT|C_STRING|C_TC);
1225 }
1226
1227 /*
1228 * If j > 0, sort order is BOOL -> INT -> STRING
1229 * Do not print the OBSOLETE header in this case.
1230 */
1231 if (!oneline &&
1232 j < 0 && (caplist[i].tc_flags & C_OLD) != 0) {
1233 if (!didobsolete) {
1234 if (llen > 9)
1235 printf("\\\n\t:");
1236 if (!oneline) {
1237 printf("..OBSOLETE:\\\n\t:");
1238 }
1239 llen = 9;
1240 didobsolete = TRUE;
1241 }
1242 }
1243
1244 /*error("line: '%s', llen: %d curlen: %d sum: %d\n", line, llen, curlen, llen + curlen);*/
1245 p = line;
1246 curlen = strlen(p);
1247 if ((llen > 9) && ((llen + curlen) >= 79) && !oneline) {
1248 printf("\\\n\t:");
1249 llen = 9 + curlen;
1250 } else {
1251 llen += curlen;
1252 }
1253 printf("%s", p);
1254 }
1255 printf("\n");
1256 flush();
1257 }
1258
1259
1260 #define OBUF_SIZE 80
1261
1262 /*
1263 * Perform string preparation/conversion for cursor addressing.
1264 * The string cm contains a format string.
1265 *
1266 * A copy from the local function libxtermcap:tgoto.c:tgoto()
1267 */
1268 LOCAL char *
checkgoto(tname,ent,cm,col,line)1269 checkgoto(tname, ent, cm, col, line)
1270 char *tname;
1271 char *ent;
1272 char *cm;
1273 int col;
1274 int line;
1275 {
1276 static char outbuf[OBUF_SIZE]; /* Where the output goes to */
1277 char xbuf[10]; /* for %. corrections */
1278 register char *op = outbuf;
1279 register char *p = cm;
1280 register int c;
1281 register int val = line;
1282 int usecol = 0;
1283 BOOL out_tty = isatty(STDOUT_FILENO);
1284 BOOL hadbad = FALSE;
1285
1286 if (p == 0) {
1287 badfmt:
1288 /*
1289 * Be compatible to 'vi' in case of bad format.
1290 */
1291 return ("OOPS");
1292 }
1293 xbuf[0] = 0;
1294 while ((c = *p++) != '\0') {
1295 if ((op + 5) >= &outbuf[OBUF_SIZE])
1296 goto overflow;
1297
1298 if (c != '%') {
1299 *op++ = c;
1300 continue;
1301 }
1302 switch (c = *p++) {
1303
1304 case '%': /* %% -> % */
1305 /* This is from BSD */
1306 *op++ = c;
1307 continue;
1308
1309 case 'd': /* output as printf("%d"... */
1310 /* This is from BSD (use val) */
1311 if (val < 10)
1312 goto onedigit;
1313 if (val < 100)
1314 goto twodigits;
1315 /*FALLTHROUGH*/
1316
1317 case '3': /* output as printf("%03d"... */
1318 /* This is from BSD (use val) */
1319 if (val >= 1000) {
1320 *op++ = '0' + (val / 1000);
1321 val %= 1000;
1322 }
1323 *op++ = '0' + (val / 100);
1324 val %= 100;
1325 /*FALLTHROUGH*/
1326
1327 case '2': /* output as printf("%02d"... */
1328 /* This is from BSD (use val) */
1329 twodigits:
1330 *op++ = '0' + val / 10;
1331 onedigit:
1332 *op++ = '0' + val % 10;
1333 nextparam:
1334 usecol ^= 1;
1335 setval:
1336 val = usecol ? col : line;
1337 continue;
1338
1339 case 'C': /* For c-100: print quotient of */
1340 /* value by 96, if nonzero, */
1341 /* then do like %+. */
1342 /* This is from GNU (use val) */
1343 if (!gnugoto)
1344 goto badchar;
1345 if (val >= 96) {
1346 *op++ = val / 96;
1347 val %= 96;
1348 }
1349 /*FALLTHROUGH*/
1350
1351 case '+': /* %+x like %c but add x before */
1352 /* This is from BSD (use val) */
1353 val += *p++;
1354 /*FALLTHROUGH*/
1355
1356 case '.': /* output as printf("%c" but... */
1357 /* This is from BSD (use val) */
1358 if (usecol || UP) {
1359 /*
1360 * We assume that backspace works and we don't
1361 * need to test for BC too.
1362 *
1363 * If you did not call stty tabs while termcap
1364 * is used you will get other problems, so we
1365 * exclude tab from the execptions.
1366 */
1367 while (val == 0 || val == '\004' ||
1368 /* val == '\t' || */ val == '\n') {
1369
1370 strcat(xbuf,
1371 usecol ? (BC?BC:"\b") : UP);
1372 val++;
1373 }
1374 }
1375 *op++ = val;
1376 goto nextparam;
1377
1378 case '>': /* %>xy if val > x add y */
1379 /* This is from BSD (chng state)*/
1380
1381 if (val > *p++)
1382 val += *p++;
1383 else
1384 p++;
1385 continue;
1386
1387 case 'B': /* convert to BCD char coding */
1388 /* This is from BSD (chng state)*/
1389
1390 val += 6 * (val / 10);
1391 continue;
1392
1393 case 'D': /* weird Delta Data conversion */
1394 /* This is from BSD (chng state)*/
1395
1396 val -= 2 * (val % 16);
1397 continue;
1398
1399 case 'i': /* increment row/col by one */
1400 /* This is from BSD (chng state)*/
1401 col++;
1402 line++;
1403 val++;
1404 continue;
1405
1406 case 'm': /* xor both parameters by 0177 */
1407 /* This is from GNU (chng state)*/
1408 if (!gnugoto)
1409 goto badchar;
1410 col ^= 0177;
1411 line ^= 0177;
1412 goto setval;
1413
1414 case 'n': /* xor both parameters by 0140 */
1415 /* This is from BSD (chng state)*/
1416 col ^= 0140;
1417 line ^= 0140;
1418 goto setval;
1419
1420 case 'r': /* reverse row/col */
1421 /* This is from BSD (chng state)*/
1422 usecol = 1;
1423 goto setval;
1424
1425 default:
1426 badchar:
1427 printf("# BAD(%s). Bad format '%%%c' in '%s=%s'\n", tname, c, ent, quote(cm));
1428 if (!out_tty)
1429 error("BAD(%s). Bad format '%%%c' in '%s=%s'\n", tname, c, ent, quote(cm));
1430 hadbad = TRUE;
1431 /* goto badfmt;*/
1432 }
1433 }
1434 /*
1435 * append to output if there is space...
1436 */
1437 if ((op + strlen(xbuf)) >= &outbuf[OBUF_SIZE]) {
1438 overflow:
1439 printf("# BAD(%s). Buffer overflow in '%s=%s'\n", tname, ent, quote(cm));
1440 if (!out_tty)
1441 error("BAD(%s). Buffer overflow in '%s=%s'\n", tname, ent, quote(cm));
1442 return ("OVERFLOW");
1443 }
1444 if (hadbad)
1445 goto badfmt;
1446
1447 for (p = xbuf; *p; )
1448 *op++ = *p++;
1449 *op = '\0';
1450 return (outbuf);
1451 }
1452
1453 LOCAL char _quotetab[] = "E^^\\\\n\nr\rt\tb\bf\f";
1454
1455 #define isoctal(c) ((c) >= '0' && (c) <= '7')
1456
1457 LOCAL char *
checkquote(tname,s)1458 checkquote(tname, s)
1459 char *tname;
1460 char *s;
1461 {
1462 static char out[5*TBUF];
1463 char nm[16];
1464 int i;
1465 register Uchar c;
1466 register Uchar *ep = (Uchar *)s;
1467 register Uchar *bp;
1468 register Uchar *tp;
1469 char *p;
1470 BOOL out_tty = isatty(STDOUT_FILENO);
1471
1472 out[0] = '\0';
1473 if (s[0] == ' ' || s[0] == '\t')
1474 return (out);
1475
1476 ep = (Uchar *)strchr(s, '=');
1477 if (ep == NULL)
1478 return (out);
1479 i = ep - (Uchar *)s;
1480 ep++;
1481 p = tskip(s);
1482 if (ep > (Uchar *)p)
1483 return (out);
1484
1485 strlcpy(nm, s, sizeof (nm));
1486 if (i < sizeof (nm))
1487 nm[i] = '\0';
1488
1489 bp = (Uchar *)out;
1490
1491 for (; (c = *ep++) && c != ':'; *bp++ = c) {
1492 if (c == '^') {
1493 c = *ep++;
1494 if (c == '\0') {
1495 printf(
1496 "# NOTICE(%s). ^ quoting followed by no character in '%s'\n",
1497 tname, s);
1498 if (!out_tty)
1499 error("NOTICE(%s). ^ quoting followed by no character in '%s'\n",
1500 tname, s);
1501 break;
1502 }
1503 c &= 0x1F;
1504 } else if (c == '\\') {
1505 c = *ep++;
1506 if (c == '\0') {
1507 printf(
1508 "# NOTICE(%s). \\ quoting followed by no character in '%s'\n",
1509 tname, s);
1510 if (!out_tty)
1511 error("NOTICE(%s). \\ quoting followed by no character in '%s'\n",
1512 tname, s);
1513 break;
1514 }
1515 if (isoctal(c)) {
1516 for (c -= '0', i = 3; --i > 0 && isoctal(*ep); ) {
1517 c <<= 3;
1518 c |= *ep++ - '0';
1519 }
1520 if (c == 0) {
1521 char *p2 = tskip(s);
1522 int len;
1523 int pos = (char *)ep - s;
1524
1525 len = p2 - s - (*p2?1:0);
1526 pos -= 4-i;
1527 if (!nowarn && nm[0] != '.') {
1528 printf("# NOTICE(%s). NULL char (fixed) in entry ('%s') at abs position %d in '%.*s'\n",
1529 tname, nm, pos, len, s);
1530 if (!out_tty)
1531 error("NOTICE(%s). NULL char (fixed) in entry ('%s') at abs position %d in '%.*s'\n",
1532 tname, nm, pos, len, s);
1533 }
1534 }
1535 #ifdef __checkoctal__
1536 if (i > 0) {
1537 char *p2 = tskip(s);
1538 int len;
1539 int pos = (char *)ep - s;
1540
1541 len = p2 - s - (*p2?1:0);
1542 printf(
1543 "# NOTICE(%s). Nonoctal char '%c' in entry ('%s') at position %d (abs %d) in '%.*s'\n",
1544 tname, *ep, nm, 4-i, pos, len, s);
1545 if (!out_tty)
1546 error("NOTICE(%s). Nonoctal char '%c' in entry ('%s') at position %d (abs %d) in '%.*s'\n",
1547 tname, *ep, nm, 4-i, pos, len, s);
1548 }
1549 #endif
1550 } else {
1551 for (tp = (Uchar *)_quotetab; *tp; tp++) {
1552 if (*tp++ == c) {
1553 c = *tp;
1554 break;
1555 }
1556 }
1557 /*
1558 * Terminfo quotes not in termcap:
1559 * \a -> '^G'
1560 * \e -> '^['
1561 * \: -> ':'
1562 * \, -> ','
1563 * \s -> ' '
1564 * \l -> '\n'
1565 */
1566 if (*tp == '\0') {
1567 char *p2 = tskip(s);
1568 int len;
1569 int pos = (char *)&ep[-1] - s;
1570
1571 len = p2 - s - (*p2?1:0);
1572 if (!nowarn) {
1573 printf("# NOTICE(%s). Badly quoted char '\\%c' %sin ('%s') at abs position %d in '%.*s'\n",
1574 tname, c, strchr("ae:,sl", c)?"(fixed) ":"", nm, pos, len, s);
1575 if (!out_tty)
1576 error("NOTICE(%s). Badly quoted char '\\%c' %sin ('%s') at abs position %d in '%.*s'\n",
1577 tname, c, strchr("ae:,sl", c)?"(fixed) ":"", nm, pos, len, s);
1578 }
1579 }
1580 }
1581 }
1582 }
1583 *bp++ = '\0';
1584 return (out);
1585 }
1586
1587 LOCAL char *
quote(s)1588 quote(s)
1589 char *s;
1590 {
1591 static char out[10*TBUF];
1592 char *p1;
1593 char *p2;
1594 unsigned char c;
1595
1596 for (p1 = s, p2 = out; *p1; ) {
1597 c = *p1++;
1598 if (c == 033) { /* ESC -> \E */
1599 *p2++ = '\\';
1600 *p2++ = 'E';
1601 } else if (c == '\\') { /* \ -> \\ */
1602 *p2++ = '\\';
1603 *p2++ = '\\';
1604 } else if (c == '^') { /* ^ -> \^ */
1605 *p2++ = '\\';
1606 *p2++ = '^';
1607 } else if (!docaret && c == '\r') { /* CR -> \r */
1608 *p2++ = '\\';
1609 *p2++ = 'r';
1610 } else if (!docaret && c == '\n') { /* NL -> \n */
1611 *p2++ = '\\';
1612 *p2++ = 'n';
1613 } else if (!docaret && c == '\t') { /* TAB -> \t */
1614 *p2++ = '\\';
1615 *p2++ = 't';
1616 } else if (!docaret && c == '\b') { /* BS -> \b */
1617 *p2++ = '\\';
1618 *p2++ = 'b';
1619 } else if (!docaret && c == '\f') { /* FF -> \f */
1620 *p2++ = '\\';
1621 *p2++ = 'f';
1622 } else if (!dooctal &&
1623 c <= 0x1F) { /* Control C -> ^C */
1624 *p2++ = '^';
1625 *p2++ = '@' + c;
1626 } else if (c == ':' || c <= 0x1F || c >= 0x7F) {
1627 *p2++ = '\\';
1628 *p2++ = '0' + (c / 64) % 8;
1629 *p2++ = '0' + (c / 8) % 8;
1630 *p2++ = '0' + c % 8;
1631 } else {
1632 *p2++ = c;
1633 }
1634 }
1635 *p2 = '\0';
1636 return (out);
1637 }
1638
1639 /*
1640 * A number of escape sequences are provided in the string-
1641 * valued capabilities for easy encoding of characters there:
1642 *
1643 * \E maps to ESC
1644 * ^X maps to CTRL-X for any appropriate character X
1645 * \n maps to LINEFEED
1646 * \r maps to RETURN
1647 * \t maps to TAB
1648 * \b maps to BACKSPACE
1649 * \f maps to FORMFEED
1650 *
1651 * Finally, characters may be given as three octal digits after
1652 * a backslash (for example, \123), and the characters ^
1653 * (caret) and \ (backslash) may be given as \^ and \\ respec-
1654 * tively.
1655 *
1656 * If it is necessary to place a : in a capability it must be
1657 * escaped in octal as \072.
1658 *
1659 * If it is necessary to place a NUL character in a string
1660 * capability it must be encoded as \200. (The routines that
1661 * deal with termcap use C strings and strip the high bits of
1662 * the output very late, so that a \200 comes out as a \000
1663 * would.)
1664 */
1665
1666 LOCAL char *
requote(s)1667 requote(s)
1668 char *s;
1669 {
1670 char buf[5*TBUF];
1671 char *bp = buf;
1672
1673 tdecode(s, &bp);
1674 return (quote(buf));
1675 }
1676
1677 LOCAL void
compile_ent(tname,do_tc,obsolete_last)1678 compile_ent(tname, do_tc, obsolete_last)
1679 char *tname;
1680 BOOL do_tc;
1681 BOOL obsolete_last;
1682 {
1683 char unknown[5*TBUF]; /* Buffer fuer "unknown" Entries */
1684 char disabled[5*TBUF]; /* Buffer fuer :..xx: Entries */
1685
1686 tcsetflags((do_tc?0:TCF_NO_TC)|TCF_NO_SIZE|TCF_NO_STRIP);
1687 if (tgetent(NULL, tname) != 1)
1688 return;
1689 /* tbuf = tcgetbuf();*/
1690 /* strippedlen = strlen(tbuf);*/
1691
1692 /* checkentries(tname, &slen);*/
1693
1694 /* printf("tbuf: '%s'\n", tbuf);*/
1695 /*printf("full tbuf len: %d stripped tbuf len: %d\n", fullen, strippedlen);*/
1696 /*printf("string length: %d\n", slen);*/
1697
1698 checkbad(tname, unknown, disabled);
1699 outcap(tname, unknown, disabled, obsolete_last);
1700 }
1701
1702
1703 #define TRDBUF 8192
1704 #define TMAX 1024
1705 #define TINC 1
1706
1707 LOCAL void
read_names(fname,do_tc,obsolete_last)1708 read_names(fname, do_tc, obsolete_last)
1709 char *fname;
1710 BOOL do_tc;
1711 BOOL obsolete_last;
1712 {
1713 char nbuf[TMAX];
1714 char rdbuf[TRDBUF];
1715 char *tbuf;
1716 char *name = nbuf;
1717 register char *bp;
1718 register char *ep;
1719 register char *rbuf = rdbuf;
1720 register char c;
1721 register int count = 0;
1722 register int tfd;
1723 int nents = 0;
1724 int tbufsize = TINC;
1725
1726 tcsetflags(TCF_NO_TC|TCF_NO_SIZE);
1727
1728 tbuf = bp = malloc(tbufsize);
1729 if (bp == NULL)
1730 comerr("Cannot malloc termcap parsing buffer.\n");
1731
1732
1733 if (fname == NULL)
1734 fname = "/etc/termcap";
1735 tfd = open(fname, O_RDONLY);
1736 if (tfd < 0)
1737 comerr("Cannot open '%s'\n", fname);
1738
1739 /*
1740 * Search TERM entry in one file.
1741 */
1742 ep = bp;
1743 for (;;) {
1744 if (--count <= 0) {
1745 if ((count = read(tfd, rdbuf, sizeof (rdbuf))) <= 0) {
1746 close(tfd);
1747 error("Found %d terminal entries.\n", nents);
1748 error("TBuf size %d.\n", tbufsize);
1749 free(tbuf);
1750 return;
1751 }
1752 rbuf = rdbuf;
1753 }
1754 c = *rbuf++;
1755 if (c == '\n') {
1756 if (ep > bp && ep[-1] == '\\') {
1757 ep--;
1758 continue;
1759 }
1760 } else if (ep >= bp + (tbufsize-1)) {
1761 tbufsize += TINC;
1762 if ((bp = realloc(bp, tbufsize)) != NULL) {
1763 ep = bp + (ep - tbuf);
1764 tbuf = bp;
1765 *ep++ = c;
1766 continue;
1767 } else {
1768 comerr("Cannot grow termcap parsing buffer.\n");
1769 }
1770 } else {
1771 *ep++ = c;
1772 continue;
1773 }
1774 *ep = '\0';
1775
1776 if (tbuf[0] != '\0' && tbuf[0] != '#') {
1777 char *p;
1778
1779 /* printf("NAME: '%.20s'\n", tbuf);*/
1780 p = strchr(tbuf, '|');
1781 if (p == &tbuf[2]) {
1782 ++p;
1783 p = strchr(p, '|');
1784 }
1785 if (p == 0)
1786 p = strchr(tbuf, ':');
1787 if (p) {
1788 int amt = p - tbuf;
1789
1790 nents++;
1791 if (amt > (sizeof (nbuf)-1))
1792 amt = sizeof (nbuf)-1;
1793 strncpy(name, tbuf, amt);
1794 nbuf[amt] = '\0';
1795 /* printf("name: %s'\n", name);*/
1796 compile_ent(name, do_tc, obsolete_last);
1797 tcsetflags(TCF_NO_TC|TCF_NO_SIZE);
1798 } else {
1799 /*
1800 * This line is not a termcap entry
1801 */
1802 printf("%s\n", tbuf);
1803 }
1804 } else {
1805 /*
1806 * This line is comment
1807 */
1808 printf("%s\n", tbuf);
1809 }
1810 ep = bp;
1811 }
1812 }
1813