source: vendor/bash/3.1/lib/termcap/termcap.c

Last change on this file was 3228, checked in by bird, 18 years ago

bash 3.1

File size: 16.8 KB
Line 
1/* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 86, 93, 94, 95 Free Software Foundation, Inc.
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2, or (at your option)
7any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; see the file COPYING. If not, write to the
16Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
17
18/* Emacs config.h may rename various library functions such as malloc. */
19#ifdef HAVE_CONFIG_H
20
21#include <config.h>
22
23/* Get the O_* definitions for open et al. */
24#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
25# include <sys/file.h>
26#endif
27
28#include <fcntl.h>
29
30#ifdef HAVE_STDLIB_H
31# include <stdlib.h>
32#else
33extern char *getenv ();
34extern char *malloc ();
35extern char *realloc ();
36#endif
37
38#else /* not HAVE_CONFIG_H */
39
40#ifdef STDC_HEADERS
41#include <stdlib.h>
42#include <string.h>
43#else
44char *getenv ();
45char *malloc ();
46char *realloc ();
47#endif
48
49/* Do this after the include, in case string.h prototypes bcopy. */
50#if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
51#define bcopy(s, d, n) memcpy ((d), (s), (n))
52#endif
53
54#ifdef HAVE_UNISTD_H
55#include <unistd.h>
56#endif
57#ifdef _POSIX_VERSION
58#include <fcntl.h>
59#endif
60
61#endif /* not HAVE_CONFIG_H */
62
63#ifndef NULL
64#define NULL (char *) 0
65#endif
66
67#ifndef O_RDONLY
68#define O_RDONLY 0
69#endif
70
71/* BUFSIZE is the initial size allocated for the buffer
72 for reading the termcap file.
73 It is not a limit.
74 Make it large normally for speed.
75 Make it variable when debugging, so can exercise
76 increasing the space dynamically. */
77
78#ifndef BUFSIZE
79#ifdef DEBUG
80#define BUFSIZE bufsize
81
82int bufsize = 128;
83#else
84#define BUFSIZE 2048
85#endif
86#endif
87
88#include "ltcap.h"
89
90#ifndef TERMCAP_FILE
91#define TERMCAP_FILE "/etc/termcap"
92#endif
93
94#ifndef emacs
95static void
96memory_out ()
97{
98 write (2, "virtual memory exhausted\n", 25);
99 exit (1);
100}
101
102static char *
103xmalloc (size)
104 unsigned size;
105{
106 register char *tem = malloc (size);
107
108 if (!tem)
109 memory_out ();
110 return tem;
111}
112
113static char *
114xrealloc (ptr, size)
115 char *ptr;
116 unsigned size;
117{
118 register char *tem = realloc (ptr, size);
119
120 if (!tem)
121 memory_out ();
122 return tem;
123}
124#endif /* not emacs */
125
126
127/* Looking up capabilities in the entry already found. */
128
129/* The pointer to the data made by tgetent is left here
130 for tgetnum, tgetflag and tgetstr to find. */
131static char *term_entry;
132
133static char *tgetst1 ();
134
135/* Search entry BP for capability CAP.
136 Return a pointer to the capability (in BP) if found,
137 0 if not found. */
138
139static char *
140find_capability (bp, cap)
141 register char *bp, *cap;
142{
143 for (; *bp; bp++)
144 if (bp[0] == ':'
145 && bp[1] == cap[0]
146 && bp[2] == cap[1])
147 return &bp[4];
148 return NULL;
149}
150
151__private_extern__
152int
153tgetnum (cap)
154 char *cap;
155{
156 register char *ptr = find_capability (term_entry, cap);
157 if (!ptr || ptr[-1] != '#')
158 return -1;
159 return atoi (ptr);
160}
161
162__private_extern__
163int
164tgetflag (cap)
165 char *cap;
166{
167 register char *ptr = find_capability (term_entry, cap);
168 return ptr && ptr[-1] == ':';
169}
170
171/* Look up a string-valued capability CAP.
172 If AREA is non-null, it points to a pointer to a block in which
173 to store the string. That pointer is advanced over the space used.
174 If AREA is null, space is allocated with `malloc'. */
175
176__private_extern__
177char *
178tgetstr (cap, area)
179 char *cap;
180 char **area;
181{
182 register char *ptr = find_capability (term_entry, cap);
183 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
184 return NULL;
185 return tgetst1 (ptr, area);
186}
187
188/* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
189 gives meaning of character following \, or a space if no special meaning.
190 Eight characters per line within the string. */
191
192static char esctab[]
193 = " \007\010 \033\014 \
194 \012 \
195 \015 \011 \013 \
196 ";
197
198/* PTR points to a string value inside a termcap entry.
199 Copy that value, processing \ and ^ abbreviations,
200 into the block that *AREA points to,
201 or to newly allocated storage if AREA is NULL.
202 Return the address to which we copied the value,
203 or NULL if PTR is NULL. */
204
205static char *
206tgetst1 (ptr, area)
207 char *ptr;
208 char **area;
209{
210 register char *p, *r;
211 register int c;
212 register int size;
213 char *ret;
214 register int c1;
215
216 if (!ptr)
217 return NULL;
218
219 /* `ret' gets address of where to store the string. */
220 if (!area)
221 {
222 /* Compute size of block needed (may overestimate). */
223 p = ptr;
224 while ((c = *p++) && c != ':' && c != '\n')
225 ;
226 ret = (char *) xmalloc (p - ptr + 1);
227 }
228 else
229 ret = *area;
230
231 /* Copy the string value, stopping at null or colon.
232 Also process ^ and \ abbreviations. */
233 p = ptr;
234 r = ret;
235 while ((c = *p++) && c != ':' && c != '\n')
236 {
237 if (c == '^')
238 {
239 c = *p++;
240 if (c == '?')
241 c = 0177;
242 else
243 c &= 037;
244 }
245 else if (c == '\\')
246 {
247 c = *p++;
248 if (c >= '0' && c <= '7')
249 {
250 c -= '0';
251 size = 0;
252
253 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
254 {
255 c *= 8;
256 c += c1 - '0';
257 p++;
258 }
259 }
260 else if (c >= 0100 && c < 0200)
261 {
262 c1 = esctab[(c & ~040) - 0100];
263 if (c1 != ' ')
264 c = c1;
265 }
266 }
267 *r++ = c;
268 }
269 *r = '\0';
270 /* Update *AREA. */
271 if (area)
272 *area = r + 1;
273 return ret;
274}
275
276
277/* Outputting a string with padding. */
278
279short ospeed;
280/* If OSPEED is 0, we use this as the actual baud rate. */
281int tputs_baud_rate;
282__private_extern__ char PC = '\0';
283
284/* Actual baud rate if positive;
285 - baud rate / 100 if negative. */
286
287static int speeds[] =
288 {
289#ifdef VMS
290 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
291 -20, -24, -36, -48, -72, -96, -192
292#else /* not VMS */
293 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
294 -18, -24, -48, -96, -192, -288, -384, -576, -1152
295#endif /* not VMS */
296 };
297
298__private_extern__
299void
300tputs (str, nlines, outfun)
301 register char *str;
302 int nlines;
303 register int (*outfun) ();
304{
305 register int padcount = 0;
306 register int speed;
307
308#ifdef emacs
309 extern baud_rate;
310 speed = baud_rate;
311 /* For quite high speeds, convert to the smaller
312 units to avoid overflow. */
313 if (speed > 10000)
314 speed = - speed / 100;
315#else
316 if (ospeed == 0)
317 speed = tputs_baud_rate;
318 else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0]))
319 speed = speeds[ospeed];
320 else
321 speed = 0;
322#endif
323
324 if (!str)
325 return;
326
327 while (*str >= '0' && *str <= '9')
328 {
329 padcount += *str++ - '0';
330 padcount *= 10;
331 }
332 if (*str == '.')
333 {
334 str++;
335 padcount += *str++ - '0';
336 }
337 if (*str == '*')
338 {
339 str++;
340 padcount *= nlines;
341 }
342 while (*str)
343 (*outfun) (*str++);
344
345 /* PADCOUNT is now in units of tenths of msec.
346 SPEED is measured in characters per 10 seconds
347 or in characters per .1 seconds (if negative).
348 We use the smaller units for larger speeds to avoid overflow. */
349 padcount *= speed;
350 padcount += 500;
351 padcount /= 1000;
352 if (speed < 0)
353 padcount = -padcount;
354 else
355 {
356 padcount += 50;
357 padcount /= 100;
358 }
359
360 while (padcount-- > 0)
361 (*outfun) (PC);
362}
363
364
365/* Finding the termcap entry in the termcap data base. */
366
367struct buffer
368 {
369 char *beg;
370 int size;
371 char *ptr;
372 int ateof;
373 int full;
374 };
375
376/* Forward declarations of static functions. */
377
378static int scan_file ();
379static char *gobble_line ();
380static int compare_contin ();
381static int name_match ();
382
383#ifdef VMS
384
385#include <rmsdef.h>
386#include <fab.h>
387#include <nam.h>
388
389static int
390valid_filename_p (fn)
391 char *fn;
392{
393 struct FAB fab = cc$rms_fab;
394 struct NAM nam = cc$rms_nam;
395 char esa[NAM$C_MAXRSS];
396
397 fab.fab$l_fna = fn;
398 fab.fab$b_fns = strlen(fn);
399 fab.fab$l_nam = &nam;
400 fab.fab$l_fop = FAB$M_NAM;
401
402 nam.nam$l_esa = esa;
403 nam.nam$b_ess = sizeof esa;
404
405 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
406}
407
408#else /* !VMS */
409
410#ifdef MSDOS /* MW, May 1993 */
411static int
412valid_filename_p (fn)
413 char *fn;
414{
415 return *fn == '\\' || *fn == '/' ||
416 (*fn >= 'A' && *fn <= 'z' && fn[1] == ':');
417}
418#else
419#define valid_filename_p(fn) (*(fn) == '/')
420#endif
421
422#endif /* !VMS */
423
424/* Find the termcap entry data for terminal type NAME
425 and store it in the block that BP points to.
426 Record its address for future use.
427
428 If BP is null, space is dynamically allocated.
429
430 Return -1 if there is some difficulty accessing the data base
431 of terminal types,
432 0 if the data base is accessible but the type NAME is not defined
433 in it, and some other value otherwise. */
434
435__private_extern__
436int
437tgetent (bp, name)
438 char *bp, *name;
439{
440 register char *termcap_name;
441 register int fd;
442 struct buffer buf;
443 register char *bp1;
444 char *bp2;
445 char *term;
446 int malloc_size = 0;
447 register int c;
448 char *tcenv; /* TERMCAP value, if it contains :tc=. */
449 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
450 int filep;
451
452#ifdef INTERNAL_TERMINAL
453 /* For the internal terminal we don't want to read any termcap file,
454 so fake it. */
455 if (!strcmp (name, "internal"))
456 {
457 term = INTERNAL_TERMINAL;
458 if (!bp)
459 {
460 malloc_size = 1 + strlen (term);
461 bp = (char *) xmalloc (malloc_size);
462 }
463 strcpy (bp, term);
464 goto ret;
465 }
466#endif /* INTERNAL_TERMINAL */
467
468 /* For compatibility with programs like `less' that want to
469 put data in the termcap buffer themselves as a fallback. */
470 if (bp)
471 term_entry = bp;
472
473 termcap_name = getenv ("TERMCAP");
474 if (termcap_name && *termcap_name == '\0')
475 termcap_name = NULL;
476#if 0
477#if defined (MSDOS) && !defined (TEST)
478 if (termcap_name && (*termcap_name == '\\'
479 || *termcap_name == '/'
480 || termcap_name[1] == ':'))
481 dostounix_filename(termcap_name);
482#endif
483#endif
484
485 filep = termcap_name && valid_filename_p (termcap_name);
486
487 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
488 it is a file name to use instead of /etc/termcap.
489 If it is non-null and does not start with /,
490 it is the entry itself, but only if
491 the name the caller requested matches the TERM variable. */
492
493 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
494 {
495 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
496 if (!indirect)
497 {
498 if (!bp)
499 bp = termcap_name;
500 else
501 strcpy (bp, termcap_name);
502 goto ret;
503 }
504 else
505 { /* It has tc=. Need to read /etc/termcap. */
506 tcenv = termcap_name;
507 termcap_name = NULL;
508 }
509 }
510
511 if (!termcap_name || !filep)
512 termcap_name = TERMCAP_FILE;
513
514 /* Here we know we must search a file and termcap_name has its name. */
515
516#ifdef MSDOS
517 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
518#else
519 fd = open (termcap_name, O_RDONLY, 0);
520#endif
521 if (fd < 0)
522 return -1;
523
524 buf.size = BUFSIZE;
525 /* Add 1 to size to ensure room for terminating null. */
526 buf.beg = (char *) xmalloc (buf.size + 1);
527 term = indirect ? indirect : name;
528
529 if (!bp)
530 {
531 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
532 bp = (char *) xmalloc (malloc_size);
533 }
534 bp1 = bp;
535
536 if (indirect)
537 /* Copy the data from the environment variable. */
538 {
539 strcpy (bp, tcenv);
540 bp1 += strlen (tcenv);
541 }
542
543 while (term)
544 {
545 /* Scan the file, reading it via buf, till find start of main entry. */
546 if (scan_file (term, fd, &buf) == 0)
547 {
548 close (fd);
549 free (buf.beg);
550 if (malloc_size)
551 free (bp);
552 return 0;
553 }
554
555 /* Free old `term' if appropriate. */
556 if (term != name)
557 free (term);
558
559 /* If BP is malloc'd by us, make sure it is big enough. */
560 if (malloc_size)
561 {
562 malloc_size = bp1 - bp + buf.size;
563 termcap_name = (char *) xrealloc (bp, malloc_size);
564 bp1 += termcap_name - bp;
565 bp = termcap_name;
566 }
567
568 bp2 = bp1;
569
570 /* Copy the line of the entry from buf into bp. */
571 termcap_name = buf.ptr;
572 while ((*bp1++ = c = *termcap_name++) && c != '\n')
573 /* Drop out any \ newline sequence. */
574 if (c == '\\' && *termcap_name == '\n')
575 {
576 bp1--;
577 termcap_name++;
578 }
579 *bp1 = '\0';
580
581 /* Does this entry refer to another terminal type's entry?
582 If something is found, copy it into heap and null-terminate it. */
583 term = tgetst1 (find_capability (bp2, "tc"), (char **) 0);
584 }
585
586 close (fd);
587 free (buf.beg);
588
589 if (malloc_size)
590 bp = (char *) xrealloc (bp, bp1 - bp + 1);
591
592 ret:
593 term_entry = bp;
594 return 1;
595}
596
597/* Given file open on FD and buffer BUFP,
598 scan the file from the beginning until a line is found
599 that starts the entry for terminal type STR.
600 Return 1 if successful, with that line in BUFP,
601 or 0 if no entry is found in the file. */
602
603static int
604scan_file (str, fd, bufp)
605 char *str;
606 int fd;
607 register struct buffer *bufp;
608{
609 register char *end;
610
611 bufp->ptr = bufp->beg;
612 bufp->full = 0;
613 bufp->ateof = 0;
614 *bufp->ptr = '\0';
615
616 lseek (fd, 0L, 0);
617
618 while (!bufp->ateof)
619 {
620 /* Read a line into the buffer. */
621 end = NULL;
622 do
623 {
624 /* if it is continued, append another line to it,
625 until a non-continued line ends. */
626 end = gobble_line (fd, bufp, end);
627 }
628 while (!bufp->ateof && end[-2] == '\\');
629
630 if (*bufp->ptr != '#'
631 && name_match (bufp->ptr, str))
632 return 1;
633
634 /* Discard the line just processed. */
635 bufp->ptr = end;
636 }
637 return 0;
638}
639
640/* Return nonzero if NAME is one of the names specified
641 by termcap entry LINE. */
642
643static int
644name_match (line, name)
645 char *line, *name;
646{
647 register char *tem;
648
649 if (!compare_contin (line, name))
650 return 1;
651 /* This line starts an entry. Is it the right one? */
652 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
653 if (*tem == '|' && !compare_contin (tem + 1, name))
654 return 1;
655
656 return 0;
657}
658
659static int
660compare_contin (str1, str2)
661 register char *str1, *str2;
662{
663 register int c1, c2;
664 while (1)
665 {
666 c1 = *str1++;
667 c2 = *str2++;
668 while (c1 == '\\' && *str1 == '\n')
669 {
670 str1++;
671 while ((c1 = *str1++) == ' ' || c1 == '\t');
672 }
673 if (c2 == '\0')
674 {
675 /* End of type being looked up. */
676 if (c1 == '|' || c1 == ':')
677 /* If end of name in data base, we win. */
678 return 0;
679 else
680 return 1;
681 }
682 else if (c1 != c2)
683 return 1;
684 }
685}
686
687/* Make sure that the buffer <- BUFP contains a full line
688 of the file open on FD, starting at the place BUFP->ptr
689 points to. Can read more of the file, discard stuff before
690 BUFP->ptr, or make the buffer bigger.
691
692 Return the pointer to after the newline ending the line,
693 or to the end of the file, if there is no newline to end it.
694
695 Can also merge on continuation lines. If APPEND_END is
696 non-null, it points past the newline of a line that is
697 continued; we add another line onto it and regard the whole
698 thing as one line. The caller decides when a line is continued. */
699
700static char *
701gobble_line (fd, bufp, append_end)
702 int fd;
703 register struct buffer *bufp;
704 char *append_end;
705{
706 register char *end;
707 register int nread;
708 register char *buf = bufp->beg;
709 register char *tem;
710
711 if (!append_end)
712 append_end = bufp->ptr;
713
714 while (1)
715 {
716 end = append_end;
717 while (*end && *end != '\n') end++;
718 if (*end)
719 break;
720 if (bufp->ateof)
721 return buf + bufp->full;
722 if (bufp->ptr == buf)
723 {
724 if (bufp->full == bufp->size)
725 {
726 bufp->size *= 2;
727 /* Add 1 to size to ensure room for terminating null. */
728 tem = (char *) xrealloc (buf, bufp->size + 1);
729 bufp->ptr = (bufp->ptr - buf) + tem;
730 append_end = (append_end - buf) + tem;
731 bufp->beg = buf = tem;
732 }
733 }
734 else
735 {
736 append_end -= bufp->ptr - buf;
737 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
738 bufp->ptr = buf;
739 }
740 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
741 bufp->ateof = 1;
742 bufp->full += nread;
743 buf[bufp->full] = '\0';
744 }
745 return end + 1;
746}
747
748
749#ifdef TEST
750
751#ifdef NULL
752#undef NULL
753#endif
754
755#include <stdio.h>
756
757main (argc, argv)
758 int argc;
759 char **argv;
760{
761 char *term;
762 char *buf;
763
764 term = argv[1];
765 printf ("TERM: %s\n", term);
766
767 buf = (char *) tgetent (0, term);
768 if ((int) buf <= 0)
769 {
770 printf ("No entry.\n");
771 return 0;
772 }
773
774 printf ("Entry: %s\n", buf);
775
776 tprint ("cm");
777 tprint ("AL");
778
779 printf ("co: %d\n", tgetnum ("co"));
780 printf ("am: %d\n", tgetflag ("am"));
781}
782
783tprint (cap)
784 char *cap;
785{
786 char *x = tgetstr (cap, 0);
787 register char *y;
788
789 printf ("%s: ", cap);
790 if (x)
791 {
792 for (y = x; *y; y++)
793 if (*y <= ' ' || *y == 0177)
794 printf ("\\%0o", *y);
795 else
796 putchar (*y);
797 free (x);
798 }
799 else
800 printf ("none");
801 putchar ('\n');
802}
803
804#endif /* TEST */
Note: See TracBrowser for help on using the repository browser.