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