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