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