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