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