1 /* @(#)tgetent.c 1.50 21/07/22 Copyright 1986-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)tgetent.c 1.50 21/07/22 Copyright 1986-2021 J. Schilling";
6 #endif
7 /*
8 * Access routines for TERMCAP database.
9 *
10 * Copyright (c) 1986-2021 J. Schilling
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 /*
27 * XXX Non POSIX imports from libschily: geterrno()
28 */
29 #ifdef BSH
30 #include <schily/stdio.h>
31 #include "bsh.h"
32 #endif
33
34 #include <schily/standard.h>
35 #include <schily/stdlib.h>
36 #include <schily/unistd.h>
37 #include <schily/fcntl.h>
38 #include <schily/string.h>
39 #include <schily/signal.h>
40 #include <schily/errno.h>
41 #include <schily/schily.h>
42 #include <schily/ioctl.h> /* Need to be before termios.h (BSD botch) */
43 #include <schily/termios.h>
44 #include <schily/ctype.h>
45 #include <schily/utypes.h>
46 #include <schily/termcap.h>
47
48 #ifdef NO_LIBSCHILY
49 #define geterrno() (errno)
50 #endif
51 #ifdef BSH
52 #define getenv getcurenv
53 #endif
54
55 #ifdef pdp11
56 #define TRDBUF 512 /* Size for read(2) buffer */
57 #else
58 #define TRDBUF 8192 /* Size for read(2) buffer */
59 #endif
60 #define TMAX 1024 /* Historical termcap buffer size */
61 #define MAXLOOP 64 /* Max loop count for tc= directive */
62 /* A tc= nesting of 63 has been seen. */
63 #define TSIZE_SPACE 14 /* Space needed for: "li#xxx:co#yyy:" */
64
65 LOCAL char _Eterm[] = "TERM";
66 LOCAL char _Etermcap[] = "TERMCAP";
67 LOCAL char _Etermpath[] = "TERMPATH";
68 #if defined(INS_BASE) && defined(PROTOTYPES)
69 LOCAL char _termpath[] = ".termcap /etc/termcap" " " INS_BASE "/etc/termcap";
70 #else
71 LOCAL char _termpath[] = ".termcap /etc/termcap";
72 #endif
73 LOCAL char _tc[] = "tc";
74 /*
75 * Additional terminfo quotes
76 * "e^[" "::" ",," "s " "l\n"
77 */
78 #ifdef PROTOTYPES
79 LOCAL char _quotetab[] = "E^^\\\\e::,,s l\nn\nr\rt\ta\ab\bf\fv\v";
80 #else
81 LOCAL char _quotetab[] = "E^^\\\\e::,,s l\nn\nr\rt\ta\07b\bf\fv\v";
82 #endif
83
84 LOCAL char _etoolong[] = "Termcap entry too long\n";
85 LOCAL char _ebad[] = "Bad termcap entry\n";
86 LOCAL char _eloop[] = "Infinite tc= loop\n";
87 LOCAL char _enomem[] = "No memory for parsing termcap\n";
88
89 LOCAL char *tbuf = 0;
90 LOCAL int tbufsize = 0;
91 LOCAL BOOL tbufmalloc = FALSE;
92 LOCAL int tflags = 0;
93 LOCAL int loopcount = 0;
94
95 EXPORT int tgetent __PR((char *bp, char *name));
96 EXPORT int tcsetflags __PR((int flags));
97 EXPORT char *tcgetbuf __PR((void));
98 LOCAL int tchktc __PR((char *name));
99 LOCAL BOOL tmatch __PR((char *name));
100 LOCAL char *tskip __PR((char *ep));
101 LOCAL char *tfind __PR((char *ep, char *ent));
102 EXPORT int tgetnum __PR((char *ent));
103 EXPORT BOOL tgetflag __PR((char *ent));
104 EXPORT char *tgetstr __PR((char *ent, char **array));
105 EXPORT char *tdecode __PR((char *ep, char **array));
106 #if defined(TIOCGSIZE) || defined(TIOCGWINSZ)
107 LOCAL void tgetsize __PR((void));
108 LOCAL void tdeldup __PR((char *ent));
109 LOCAL char *tinsint __PR((char *ep, int i));
110 #endif
111 LOCAL void tstrip __PR((void));
112 LOCAL char *tmalloc __PR((int size));
113 LOCAL char *trealloc __PR((char *p, int size));
114 #ifdef NO_LIBSCHILY
115 #define ovstrcpy _ovstrcpy
116 LOCAL char *ovstrcpy __PR((char *p2, const char *p1));
117 #endif
118 LOCAL void e_tcname __PR((char *name));
119
120 /*
121 * Returns:
122 * 1 Entry found
123 * 0 Could not malloc() buffer with tgetent(NULL, name)
124 * 0 Database file opened, entry not found
125 * -1 Could not open database file
126 */
127 EXPORT int
tgetent(bp,name)128 tgetent(bp, name)
129 char *bp;
130 char *name;
131 {
132 char rdbuf[TRDBUF];
133 char *term;
134 char termpath[TMAX];
135 char *tp;
136 register char *ep;
137 register char *rbuf = rdbuf;
138 register char c;
139 register int count = 0;
140 register int tfd;
141 int err = 0;
142
143 tbufsize = TMAX;
144 if (tbufmalloc) {
145 if (tbuf)
146 free(tbuf);
147 tbufmalloc = FALSE;
148 }
149 tbuf = NULL;
150 if (name == NULL || *name == '\0') { /* No TERM name specfied */
151 if (bp)
152 bp[0] = '\0';
153 return (0);
154 }
155 if ((tbuf = bp) == NULL) {
156 tbufmalloc = TRUE;
157 tbuf = bp = tmalloc(tbufsize);
158 }
159 if ((tbuf = bp) == NULL) /* Could not malloc buffer */
160 return (0);
161 bp[0] = '\0'; /* Always start with clean termcap buffer */
162
163 /*
164 * First look, if TERMCAP exists
165 */
166 if (!(ep = getenv(_Etermcap)) || *ep == '\0') {
167 /*
168 * If no TERMCAP environment or empty TERMCAP environment
169 * use default termpath.
170 * Search rules:
171 * If TERMPATH exists, use it
172 * else concat $HOME/ with default termpath
173 * default termpath is .termcap /etc/termcap
174 */
175 setpath:
176 if ((ep = getenv(_Etermpath)) != NULL) {
177 strncpy(termpath, ep, sizeof (termpath));
178 } else {
179 termpath[0] = '\0';
180 if ((ep = getenv("HOME")) != NULL) {
181 strncpy(termpath, ep,
182 sizeof (termpath)-2-sizeof (_termpath));
183 strcat(termpath, "/");
184 }
185 strcat(termpath, _termpath);
186 }
187 } else {
188 if (*ep != '/') {
189 /*
190 * This doesn't seem to be a filename...
191 * It must be a preparsed termcap entry.
192 */
193 if (!(term = getenv(_Eterm)) ||
194 strcmp(name, term) == 0) {
195 /*
196 * If no TERM environment or TERM holds the
197 * same strings as "name" use preparsed entry.
198 */
199 tbuf = ep;
200 count = tmatch(name);
201 tbuf = bp;
202 if (count > 0) {
203 count = strlen(ep) + 1 + TSIZE_SPACE;
204 if (count > tbufsize) {
205 if (tbufmalloc) {
206 tbufsize = count;
207 tbuf = trealloc(bp,
208 count);
209 } else {
210 tbuf = NULL;
211 e_tcname(name);
212 write(STDERR_FILENO,
213 _etoolong,
214 sizeof (_etoolong) - 1);
215 }
216 }
217 if (tbuf)
218 ovstrcpy(tbuf, ep);
219 goto out; /* We use the preparsed entry */
220 }
221 }
222 /*
223 * If the preparsed termcap entry does not match
224 * our term, we need to read the file.
225 * Set up the internal or external TERMPATH for
226 * this purpose.
227 */
228 goto setpath;
229 }
230 /*
231 * If TERMCAP starts with a '/' use it as TERMPATH.
232 */
233 strncpy(termpath, ep, sizeof (termpath));
234 }
235 termpath[sizeof (termpath)-1] = '\0';
236 tp = termpath;
237
238 nextfile:
239 /*
240 * Loop over TERMPATH string.
241 */
242 ep = tp;
243 while (*tp++) {
244 if (*tp == ' ' || *tp == ':') {
245 *tp++ = '\0';
246 break;
247 }
248 }
249 if (*ep == '\0') { /* End of TERMPATH */
250 if (err != 0)
251 return (-1); /* Signal open error */
252 return (0); /* Signal not found */
253 }
254
255 if ((tfd = open(ep, O_RDONLY)) < 0) {
256 err = geterrno();
257
258 strncpy(tbuf, ep, TMAX); /* Remember failed path */
259 tbuf[TMAX-1] = 0;
260 #ifdef SHOULD_WE
261 if (err == ENOENT || err == EACCES)
262 goto nextfile;
263 return (-1);
264 #else
265 goto nextfile;
266 #endif
267 }
268
269 /*
270 * Search TERM entry in one file.
271 */
272 ep = bp;
273 for (;;) {
274 if (--count <= 0) {
275 if ((count = read(tfd, rdbuf, sizeof (rdbuf))) <= 0) {
276 close(tfd);
277 goto nextfile; /* Not found, check next */
278 }
279 rbuf = rdbuf;
280 }
281 c = *rbuf++;
282 if (c == '\n') {
283 if (ep > bp && ep[-1] == '\\') {
284 ep--;
285 continue;
286 }
287 } else if (ep >= bp + (tbufsize-1)) {
288 if (tbufmalloc) {
289 tbufsize += TMAX;
290 if ((bp = trealloc(bp, tbufsize)) != NULL) {
291 ep = bp + (ep - tbuf);
292 tbuf = bp;
293 *ep++ = c;
294 continue;
295 } else { /* No memory for buffer */
296 tbuf = NULL;
297 goto out;
298 }
299 }
300 e_tcname(name);
301 write(STDERR_FILENO, _etoolong, sizeof (_etoolong) - 1);
302 } else {
303 *ep++ = c;
304 continue;
305 }
306 *ep = '\0';
307 if (tmatch(name)) { /* Entry matches name */
308 close(tfd);
309 goto out;
310 }
311 ep = bp;
312 }
313 out:
314 count = tchktc(name);
315 bp = tbuf;
316 if (tbufmalloc) {
317 if (count <= 0) {
318 #ifdef __free_buffer__ /* Keep buf for failed path */
319 if (bp) {
320 free(bp);
321 tbuf = NULL;
322 tbufmalloc = FALSE;
323 }
324 #endif
325 return (count);
326 }
327 /*
328 * Did change size in tchktc() ?
329 */
330 count = strlen(bp) + 1;
331 if (count != tbufsize) {
332 tbufsize = count;
333 if ((tbuf = bp = trealloc(bp, tbufsize)) == NULL)
334 return (0);
335 }
336 return (1);
337 }
338 if (count <= 0) /* If no match (TERM not found) */
339 bp[0] = '\0'; /* clear termcap buffer */
340 return (count);
341 }
342
343 /*
344 * Set the termcap flags.
345 * It allows e.g. to prevent tgetent() from following tc= entries
346 * and from modifying the co# and li# entries.
347 * This is a libxtermcap extension.
348 */
349 EXPORT int
tcsetflags(flags)350 tcsetflags(flags)
351 int flags;
352 {
353 int oflags = tflags;
354
355 tflags = flags;
356 return (oflags);
357 }
358
359 /*
360 * Return the current buffer that holds the parsed termcap entry.
361 * This function is needed if the buffer is allocated and a user
362 * likes to do own string parsing on the buffer.
363 * This is a libxtermcap extension.
364 */
365 EXPORT char *
tcgetbuf()366 tcgetbuf()
367 {
368 return (tbuf);
369 }
370
371 LOCAL int
tchktc(name)372 tchktc(name)
373 char *name;
374 {
375 register char *ep;
376 register char *np;
377 register char *tcname;
378 char tcbuf[TMAX];
379 char *otbuf = tbuf;
380 int otbufsize = tbufsize;
381 BOOL otbufmalloc = tbufmalloc;
382 BOOL needfree;
383 char *xtbuf;
384 int ret;
385
386 if (tbuf == NULL)
387 return (0);
388
389 ep = tbuf + strlen(tbuf) - 2;
390 while (ep > tbuf && *--ep != ':') {
391 if (ep <= tbuf) {
392 /*
393 * There was no colon in tbuf...tbuf + strlen(tbuf) - 3,
394 * so there cannot be any valid capability in tbuf.
395 * First check for a valid but empty termcap entry,
396 * such as: "tname:"
397 */
398 ep = tbuf + strlen(tbuf) - 1;
399 if (ep > tbuf && *ep == ':') {
400 return (1); /* Success */
401 }
402 e_tcname(name);
403 write(STDERR_FILENO, _ebad, sizeof (_ebad) - 1);
404 return (0);
405 }
406 }
407 ep++;
408 if (ep[0] != 't' || ep[1] != 'c' || (tflags & TCF_NO_TC) != 0)
409 goto out;
410
411 ep = tfind(tbuf, _tc);
412 if (ep == NULL || *ep != '=') {
413 e_tcname(name);
414 write(STDERR_FILENO, _ebad, sizeof (_ebad) - 1);
415 return (0);
416 }
417 ep -= 2; /* Correct for propper append */
418 strncpy(tcbuf, &ep[2], sizeof (tcbuf));
419 tcname = tcbuf;
420 tcname[sizeof (tcbuf)-1] = '\0';
421
422 do {
423 tcname++;
424 for (np = tcname; *np; np++)
425 if (*np == ':')
426 break;
427 *np = '\0';
428 if (++loopcount > MAXLOOP) {
429 e_tcname(name);
430 write(STDERR_FILENO, _eloop, sizeof (_eloop) - 1);
431 return (0);
432 }
433 tbufmalloc = FALSE; /* Do not free buffer now! */
434 ret = tgetent(NULL, tcname);
435 *np = ':';
436 xtbuf = tbuf;
437 needfree = tbufmalloc;
438 tbuf = otbuf;
439 tbufsize = otbufsize;
440 tbufmalloc = otbufmalloc;
441 loopcount = 0;
442 if (ret != 1) {
443 if (needfree && xtbuf != NULL)
444 free(xtbuf);
445 return (ret);
446 }
447 np = tskip(xtbuf); /* skip over the name part */
448 /*
449 * Add nullbyte and 14 bytes for the space needed by tgetsize()
450 */
451 ret = ep - otbuf + strlen(np) + 1 + TSIZE_SPACE;
452 if (ret >= (unsigned)(tbufsize-1)) {
453 if (tbufmalloc) {
454 tbufsize = ret;
455 if ((otbuf =
456 trealloc(otbuf, tbufsize)) != NULL) {
457 ep = otbuf + (ep - tbuf);
458 tbuf = otbuf;
459 } else {
460 if (needfree && xtbuf != NULL)
461 free(xtbuf);
462 return (0);
463 }
464 } else {
465 e_tcname(name);
466 write(STDERR_FILENO, _etoolong,
467 sizeof (_etoolong) - 1);
468 ret = tbufsize - 1 - (ep - otbuf);
469 if (ret < 0)
470 ret = 0;
471 np[ret] = '\0';
472 }
473 }
474 strcpy(ep, np);
475 ep += strlen(ep);
476 if (needfree && xtbuf != NULL)
477 free(xtbuf);
478
479 } while ((tcname = tfind(tcname, _tc)) != NULL && *tcname == '=');
480 out:
481 #if defined(TIOCGSIZE) || defined(TIOCGWINSZ)
482 if ((tflags & TCF_NO_SIZE) == 0)
483 tgetsize();
484 #endif
485 if ((tflags & TCF_NO_STRIP) == 0)
486 tstrip();
487 return (1);
488 }
489
490 /*
491 * Check if the current 'tbuf' contains a termcap entry for a terminal
492 * that matches 'name'.
493 */
494 LOCAL BOOL
tmatch(name)495 tmatch(name)
496 char *name;
497 {
498 register char *np;
499 register char *ep;
500
501 if (tbuf == NULL)
502 return (FALSE);
503
504 ep = tbuf;
505 if (*ep == '#') /* Kommentar */
506 return (FALSE);
507 for (; ; ep++) {
508 for (np = name; *np; ep++, np++) /* Solange name */
509 if (*ep != *np) /* gleich ist */
510 break;
511 if (*np == '\0') { /* Name am Ende */
512 if (*ep == '|' || *ep == ':' || *ep == '\0')
513 return (TRUE);
514 }
515 while (*ep && *ep != '|' && *ep != ':') /* Rest dieses */
516 ep++; /* Namens */
517 if (*ep == ':' || *ep == '\0')
518 return (FALSE);
519 }
520 }
521
522 /*
523 * Skip past next ':'.
524 * If the are two consecutive ':', the returned pointer may point to ':'.
525 */
526 LOCAL char *
tskip(ep)527 tskip(ep)
528 register char *ep;
529 {
530 while (*ep) {
531 if (*ep++ == ':')
532 return (ep); /* return first ':' */
533 }
534 return (ep); /* not found */
535 }
536
537 /*
538 * Find a two charater entry in string that is found in 'ep'.
539 * Return the character that follows the two character entry (if found)
540 * or NULL if the entry could not be found.
541 */
542 LOCAL char *
tfind(ep,ent)543 tfind(ep, ent)
544 register char *ep;
545 char *ent;
546 {
547 register char e0 = ent[0];
548 register char e1 = ent[1];
549
550 for (;;) {
551 ep = tskip(ep);
552 if (*ep == '\0')
553 break;
554 if (*ep == ':')
555 continue;
556 if (e0 != *ep++)
557 continue;
558 if (*ep == '\0')
559 break;
560 if (e1 != *ep++)
561 continue;
562 return (ep);
563 }
564 return ((char *)NULL);
565 }
566
567 /*
568 * Search for a numeric entry in form 'en#123' to represent a decimal number
569 * or 'en#0123' to represent a octal number.
570 * Return numeric value or -1 if found 'en@'.
571 */
572 EXPORT int
tgetnum(ent)573 tgetnum(ent)
574 char *ent;
575 {
576 register Uchar *ep = (Uchar *)tbuf;
577 register int val;
578 register int base;
579
580 if (tbuf == NULL)
581 return (-1);
582
583 for (;;) {
584 ep = (Uchar *)tfind((char *)ep, ent);
585 if (!ep || *ep == '@')
586 return (-1);
587 if (*ep == '#')
588 break;
589 }
590 base = 10;
591 if (*++ep == '0')
592 base = 8;
593 for (val = 0; isdigit(*ep); ) {
594 val *= base;
595 val += (*ep++ - '0');
596 }
597 return (val);
598 }
599
600 /*
601 * Search for a boolean entry in form 'en' to represent a TRUE value
602 * or 'en@' to represent a FALSE value.
603 * An entry in the form 'en@' is mainly used to overwrite similar entries
604 * found later from a tc= entry.
605 */
606 EXPORT BOOL
tgetflag(ent)607 tgetflag(ent)
608 char *ent;
609 {
610 register char *ep = tbuf;
611
612 if (tbuf == NULL)
613 return (FALSE);
614
615 for (;;) {
616 ep = tfind(ep, ent);
617 if (!ep || *ep == '@')
618 return (FALSE);
619 if (*ep == '\0' || *ep == ':')
620 return (TRUE);
621 }
622 }
623
624 /*
625 * Search for a string entry in form 'en=val'.
626 * Return string parameter or NULL if found 'en@'.
627 */
628 EXPORT char *
tgetstr(ent,array)629 tgetstr(ent, array)
630 char *ent;
631 char *array[];
632 {
633 register char *ep = tbuf;
634 char *np = NULL;
635 char *ap = NULL;
636 char buf[TMAX];
637
638 if (tbuf == NULL)
639 return ((char *)0);
640
641 if (array == NULL) {
642 np = buf;
643 array = &np;
644 }
645 for (;;) {
646 ep = tfind(ep, ent);
647 if (!ep || *ep == '@')
648 return ((char *)NULL);
649 if (*ep == '=') {
650 if (np && strlen(ep) >= sizeof (buf)) {
651 ap = np = tmalloc(strlen(ep));
652 if (np == NULL)
653 return (np);
654 array = &np;
655 }
656 ep = tdecode(++ep, array);
657 if (ep && np) {
658 np = ep;
659 ep = tmalloc(strlen(ep)+1);
660 if (ep != NULL)
661 strcpy(ep, np);
662 if (ap)
663 free(ap);
664 }
665 return (ep);
666 }
667 }
668 }
669
670 #define isoctal(c) ((c) >= '0' && (c) <= '7')
671 /*
672 * Decode a string and replace the escape sequences by what
673 * they mean (e.g. \E by ESC).
674 * The space used to hold the decoded string is taken from
675 * the second parameter.
676 * Note that old 'vi' implementations limit the total space for
677 * all decoded strings to 256 bytes.
678 */
679 EXPORT char *
tdecode(pp,array)680 tdecode(pp, array)
681 char *pp;
682 char *array[];
683 {
684 int i;
685 register Uchar c;
686 register Uchar *ep = (Uchar *)pp;
687 register Uchar *bp;
688 register Uchar *tp;
689
690 bp = (Uchar *)array[0];
691
692 for (; (c = *ep++) && c != ':'; *bp++ = c) {
693 if (c == '^') {
694 c = *ep++;
695 if (c == '\0')
696 break;
697 else if (c == '?')
698 c |= 0x40;
699 else
700 c &= 0x1f;
701 continue;
702 } else if (c != '\\') {
703 continue;
704 }
705 /*
706 * Handle the \xxx and \C escape sequences here:
707 */
708 c = *ep++;
709 if (c == '\0')
710 break;
711 if (isoctal(c)) {
712 for (c -= '0', i = 3; --i > 0 &&
713 *ep && isoctal(*ep); ) {
714 c <<= 3;
715 c |= *ep++ - '0';
716 }
717 if (*ep == '\0')
718 break;
719 /*
720 * Terminfo maps NULL chars to 0200
721 */
722 if (c == '\0')
723 c = '\200';
724 } else for (tp = (Uchar *)_quotetab; *tp; tp++) {
725 if (*tp++ == c) {
726 c = *tp;
727 break;
728 }
729 }
730 }
731 *bp++ = '\0';
732 ep = (Uchar *)array[0];
733 array[0] = (char *)bp;
734 return ((char *)ep);
735 }
736
737 #if defined(TIOCGSIZE) || defined(TIOCGWINSZ)
738
739 /*
740 * Get the current size of the terminal (window) and insert the
741 * apropriate values for 'li#' and 'co#' before the other terminal
742 * capabilities.
743 */
744 LOCAL void
tgetsize()745 tgetsize()
746 {
747 #ifdef TIOCGWINSZ
748 struct winsize ws;
749 #else
750 struct ttysize ts;
751 #endif
752 register int lines = 0;
753 register int cols = 0;
754 register char *ep;
755 register char *lp;
756 register char *cp;
757 int len;
758
759 if (tbuf == NULL)
760 return;
761
762 #ifdef TIOCGWINSZ
763 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) >= 0) {
764 lines = ws.ws_row;
765 cols = ws.ws_col;
766 }
767 #else
768 if (ioctl(STDOUT_FILENO, TIOCGSIZE, (char *)&ts) >= 0) {
769 lines = ts.ts_lines;
770 cols = ts.ts_cols;
771 }
772 #endif
773 if (lines == 0 || cols == 0 || lines > 999 || cols > 999)
774 return;
775
776 len = strlen(tbuf) + 1 + TSIZE_SPACE;
777 if (len > tbufsize) {
778 if (tbufmalloc) {
779 tbufsize = len;
780 if ((tbuf = trealloc(tbuf, tbufsize)) == NULL)
781 return;
782 } else {
783 return;
784 }
785 }
786 ep = tskip(tbuf); /* skip over the name part */
787 /*
788 * Backwards copy to create a gap for the string we like to add.
789 */
790 lp = &tbuf[len-1-TSIZE_SPACE]; /* The curent end of the buffer */
791 for (cp = &lp[TSIZE_SPACE]; lp >= ep; cp--, lp--)
792 *cp = *lp;
793
794 *ep++ = 'l';
795 *ep++ = 'i';
796 *ep++ = '#';
797 ep = tinsint(ep, lines);
798 *ep++ = ':';
799 *ep++ = 'c';
800 *ep++ = 'o';
801 *ep++ = '#';
802 ep = tinsint(ep, cols);
803 *ep++ = ':';
804 while (ep <= cp)
805 *ep++ = ' ';
806 *--ep = ':';
807 }
808
809 /*
810 * Delete duplicate named numeric entries.
811 */
812 LOCAL void
tdeldup(ent)813 tdeldup(ent)
814 char *ent;
815 {
816 register char *ep;
817 register char *p;
818
819 if (tbuf == NULL)
820 return;
821
822 if ((ep = tfind(tbuf, ent)) != NULL) {
823 while ((ep = tfind(ep, ent)) && *ep == '#') {
824 p = ep;
825 while (*p)
826 if (*p++ == ':')
827 break;
828 ep -= 3;
829 ovstrcpy(ep, --p);
830 }
831 }
832 }
833
834 /*
835 * Insert a number into a terminal capability buffer.
836 */
837 LOCAL char *
tinsint(ep,i)838 tinsint(ep, i)
839 register char *ep;
840 register int i;
841 {
842 register char c;
843
844 if ((c = i / 100) != 0) {
845 *ep++ = c + '0';
846 i %= 100;
847 if (i / 10 == 0)
848 *ep++ = '0';
849 }
850 if ((c = i / 10) != 0)
851 *ep++ = c + '0';
852 *ep++ = i % 10 + '0';
853 return (ep);
854 }
855
856 #endif /* defined(TIOCGSIZE) || defined(TIOCGWINSZ) */
857
858 /*
859 * Strip down the termcap entry to make it as short as possible.
860 * This is done by first deleting duplicate 'li#' and 'co#' entries
861 * and then removing succesive ':' chars and spaces between ':'.
862 */
863 LOCAL void
tstrip()864 tstrip()
865 {
866 register char *bp = tbuf;
867 register char *p;
868
869 if (bp == NULL)
870 return;
871
872 #if defined(TIOCGSIZE) || defined(TIOCGWINSZ)
873 tdeldup("li");
874 tdeldup("co");
875 #endif
876
877 #ifdef needed
878 while (*bp) {
879 if (*bp++ == ':') {
880 if (*bp == ':') {
881 p = bp;
882 while (*p == ':')
883 p++;
884 ovstrcpy(bp, p);
885 }
886 }
887 }
888 bp = tbuf;
889 #endif
890 while (*bp) {
891 if (*bp == '\\') {
892 ++bp;
893 if (*bp++ == '\0')
894 break;
895 continue;
896 }
897 if (*bp++ == ':') {
898 if (*bp == ':' || *bp == ' ' || *bp == '\t') {
899 p = bp;
900 while (*p)
901 if (*p++ == ':')
902 break;
903 ovstrcpy(bp--, p);
904 }
905 }
906 }
907 }
908
909 LOCAL char *
tmalloc(size)910 tmalloc(size)
911 int size;
912 {
913 char *ret;
914
915 if ((ret = malloc(size)) != NULL)
916 return (ret);
917 write(STDERR_FILENO, _enomem, sizeof (_enomem) - 1);
918 return ((char *)NULL);
919 }
920
921 LOCAL char *
trealloc(p,size)922 trealloc(p, size)
923 char *p;
924 int size;
925 {
926 char *ret;
927
928 if ((ret = realloc(p, size)) != NULL)
929 return (ret);
930 write(STDERR_FILENO, _enomem, sizeof (_enomem) - 1);
931 return ((char *)NULL);
932 }
933
934 #ifdef NO_LIBSCHILY
935 /*
936 * A strcpy() that works with overlapping buffers
937 */
938 LOCAL char *
ovstrcpy(p2,p1)939 ovstrcpy(p2, p1)
940 register char *p2;
941 register const char *p1;
942 {
943 char *ret = p2;
944
945 while ((*p2++ = *p1++) != '\0')
946 ;
947
948 return (ret);
949 }
950 #endif
951
952 LOCAL void
e_tcname(name)953 e_tcname(name)
954 char *name;
955 {
956 write(STDERR_FILENO, name, strlen(name));
957 write(STDERR_FILENO, ": ", 2);
958 }
959