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