(cvs-enabledp): Ignore errors.
[emacs.git] / src / termcap.c
blob472005f13934a5de373632133d1025f601694c21
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 86, 93, 94, 95 Free Software Foundation, Inc.
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.
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.
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, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 /* Emacs config.h may rename various library functions such as malloc. */
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #ifdef emacs
26 #include <lisp.h> /* xmalloc is here */
27 /* Get the O_* definitions for open et al. */
28 #include <sys/file.h>
29 #ifdef HAVE_FCNTL_H
30 #include <fcntl.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
36 #else /* not emacs */
38 #ifdef STDC_HEADERS
39 #include <stdlib.h>
40 #include <string.h>
41 #else
42 char *getenv ();
43 char *malloc ();
44 char *realloc ();
45 #endif
47 /* Do this after the include, in case string.h prototypes bcopy. */
48 #if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
49 #define bcopy(s, d, n) memcpy ((d), (s), (n))
50 #endif
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #ifdef _POSIX_VERSION
56 #include <fcntl.h>
57 #endif
59 #endif /* not emacs */
61 #ifndef NULL
62 #define NULL (char *) 0
63 #endif
65 #ifndef O_RDONLY
66 #define O_RDONLY 0
67 #endif
69 /* BUFSIZE is the initial size allocated for the buffer
70 for reading the termcap file.
71 It is not a limit.
72 Make it large normally for speed.
73 Make it variable when debugging, so can exercise
74 increasing the space dynamically. */
76 #ifndef BUFSIZE
77 #ifdef DEBUG
78 #define BUFSIZE bufsize
80 int bufsize = 128;
81 #else
82 #define BUFSIZE 2048
83 #endif
84 #endif
86 #ifndef TERMCAP_FILE
87 #define TERMCAP_FILE "/etc/termcap"
88 #endif
90 #ifndef emacs
91 static void
92 memory_out ()
94 write (2, "virtual memory exhausted\n", 25);
95 exit (1);
98 static char *
99 xmalloc (size)
100 unsigned size;
102 register char *tem = malloc (size);
104 if (!tem)
105 memory_out ();
106 return tem;
109 static char *
110 xrealloc (ptr, size)
111 char *ptr;
112 unsigned size;
114 register char *tem = realloc (ptr, size);
116 if (!tem)
117 memory_out ();
118 return tem;
120 #endif /* not emacs */
122 /* Looking up capabilities in the entry already found. */
124 /* The pointer to the data made by tgetent is left here
125 for tgetnum, tgetflag and tgetstr to find. */
126 static char *term_entry;
128 static char *tgetst1 ();
130 /* Search entry BP for capability CAP.
131 Return a pointer to the capability (in BP) if found,
132 0 if not found. */
134 static char *
135 find_capability (bp, cap)
136 register char *bp, *cap;
138 for (; *bp; bp++)
139 if (bp[0] == ':'
140 && bp[1] == cap[0]
141 && bp[2] == cap[1])
142 return &bp[4];
143 return NULL;
147 tgetnum (cap)
148 char *cap;
150 register char *ptr = find_capability (term_entry, cap);
151 if (!ptr || ptr[-1] != '#')
152 return -1;
153 return atoi (ptr);
157 tgetflag (cap)
158 char *cap;
160 register char *ptr = find_capability (term_entry, cap);
161 return ptr && ptr[-1] == ':';
164 /* Look up a string-valued capability CAP.
165 If AREA is non-null, it points to a pointer to a block in which
166 to store the string. That pointer is advanced over the space used.
167 If AREA is null, space is allocated with `malloc'. */
169 char *
170 tgetstr (cap, area)
171 char *cap;
172 char **area;
174 register char *ptr = find_capability (term_entry, cap);
175 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
176 return NULL;
177 return tgetst1 (ptr, area);
180 #ifdef IS_EBCDIC_HOST
181 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
182 gives meaning of character following \, or a space if no special meaning.
183 Sixteen characters per line within the string. */
185 static char esctab[]
186 = " \057\026 \047\014 \
187 \025 \015 \
188 \005 \013 \
190 #else
191 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
192 gives meaning of character following \, or a space if no special meaning.
193 Eight characters per line within the string. */
195 static char esctab[]
196 = " \007\010 \033\014 \
197 \012 \
198 \015 \011 \013 \
200 #endif
202 /* PTR points to a string value inside a termcap entry.
203 Copy that value, processing \ and ^ abbreviations,
204 into the block that *AREA points to,
205 or to newly allocated storage if AREA is NULL.
206 Return the address to which we copied the value,
207 or NULL if PTR is NULL. */
209 static char *
210 tgetst1 (ptr, area)
211 char *ptr;
212 char **area;
214 register char *p, *r;
215 register int c;
216 register int size;
217 char *ret;
218 register int c1;
220 if (!ptr)
221 return NULL;
223 /* `ret' gets address of where to store the string. */
224 if (!area)
226 /* Compute size of block needed (may overestimate). */
227 p = ptr;
228 while ((c = *p++) && c != ':' && c != '\n')
230 ret = (char *) xmalloc (p - ptr + 1);
232 else
233 ret = *area;
235 /* Copy the string value, stopping at null or colon.
236 Also process ^ and \ abbreviations. */
237 p = ptr;
238 r = ret;
239 while ((c = *p++) && c != ':' && c != '\n')
241 if (c == '^')
243 c = *p++;
244 if (c == '?')
245 c = 0177;
246 else
247 c &= 037;
249 else if (c == '\\')
251 c = *p++;
252 if (c >= '0' && c <= '7')
254 c -= '0';
255 size = 0;
257 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
259 c *= 8;
260 c += c1 - '0';
261 p++;
264 #ifdef IS_EBCDIC_HOST
265 else if (c >= 0200 && c < 0360)
267 c1 = esctab[(c & ~0100) - 0200];
268 if (c1 != ' ')
269 c = c1;
271 #else
272 else if (c >= 0100 && c < 0200)
274 c1 = esctab[(c & ~040) - 0100];
275 if (c1 != ' ')
276 c = c1;
278 #endif
280 *r++ = c;
282 *r = '\0';
283 /* Update *AREA. */
284 if (area)
285 *area = r + 1;
286 return ret;
289 /* Outputting a string with padding. */
291 short ospeed;
292 /* If OSPEED is 0, we use this as the actual baud rate. */
293 int tputs_baud_rate;
294 char PC;
296 /* Actual baud rate if positive;
297 - baud rate / 100 if negative. */
299 static int speeds[] =
301 #ifdef VMS
302 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
303 -20, -24, -36, -48, -72, -96, -192
304 #else /* not VMS */
305 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
306 -18, -24, -48, -96, -192, -288, -384, -576, -1152
307 #endif /* not VMS */
310 void
311 tputs (str, nlines, outfun)
312 register char *str;
313 int nlines;
314 register int (*outfun) ();
316 register int padcount = 0;
317 register int speed;
319 #ifdef emacs
320 extern int baud_rate;
321 speed = baud_rate;
322 /* For quite high speeds, convert to the smaller
323 units to avoid overflow. */
324 if (speed > 10000)
325 speed = - speed / 100;
326 #else
327 if (ospeed == 0)
328 speed = tputs_baud_rate;
329 else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0]))
330 speed = speeds[ospeed];
331 else
332 speed = 0;
333 #endif
335 if (!str)
336 return;
338 while (*str >= '0' && *str <= '9')
340 padcount += *str++ - '0';
341 padcount *= 10;
343 if (*str == '.')
345 str++;
346 padcount += *str++ - '0';
348 if (*str == '*')
350 str++;
351 padcount *= nlines;
353 while (*str)
354 (*outfun) (*str++);
356 /* PADCOUNT is now in units of tenths of msec.
357 SPEED is measured in characters per 10 seconds
358 or in characters per .1 seconds (if negative).
359 We use the smaller units for larger speeds to avoid overflow. */
360 padcount *= speed;
361 padcount += 500;
362 padcount /= 1000;
363 if (speed < 0)
364 padcount = -padcount;
365 else
367 padcount += 50;
368 padcount /= 100;
371 while (padcount-- > 0)
372 (*outfun) (PC);
375 /* Finding the termcap entry in the termcap data base. */
377 struct termcap_buffer
379 char *beg;
380 int size;
381 char *ptr;
382 int ateof;
383 int full;
386 /* Forward declarations of static functions. */
388 static int scan_file ();
389 static char *gobble_line ();
390 static int compare_contin ();
391 static int name_match ();
393 #ifdef VMS
395 #include <rmsdef.h>
396 #include <fab.h>
397 #include <nam.h>
399 static int
400 valid_filename_p (fn)
401 char *fn;
403 struct FAB fab = cc$rms_fab;
404 struct NAM nam = cc$rms_nam;
405 char esa[NAM$C_MAXRSS];
407 fab.fab$l_fna = fn;
408 fab.fab$b_fns = strlen(fn);
409 fab.fab$l_nam = &nam;
410 fab.fab$l_fop = FAB$M_NAM;
412 nam.nam$l_esa = esa;
413 nam.nam$b_ess = sizeof esa;
415 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
418 #else /* !VMS */
420 #ifdef MSDOS /* MW, May 1993 */
421 static int
422 valid_filename_p (fn)
423 char *fn;
425 return *fn == '/' || fn[1] == ':';
427 #else
428 #define valid_filename_p(fn) (*(fn) == '/')
429 #endif
431 #endif /* !VMS */
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.
437 If BP is null, space is dynamically allocated.
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. */
445 tgetent (bp, name)
446 char *bp, *name;
448 register char *termcap_name;
449 register int fd;
450 struct termcap_buffer buf;
451 register char *bp1;
452 char *tc_search_point;
453 char *term;
454 int malloc_size = 0;
455 register int c;
456 char *tcenv; /* TERMCAP value, if it contains :tc=. */
457 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
458 int filep;
460 #ifdef INTERNAL_TERMINAL
461 /* For the internal terminal we don't want to read any termcap file,
462 so fake it. */
463 if (!strcmp (name, "internal"))
465 term = INTERNAL_TERMINAL;
466 if (!bp)
468 malloc_size = 1 + strlen (term);
469 bp = (char *) xmalloc (malloc_size);
471 strcpy (bp, term);
472 goto ret;
474 #endif /* INTERNAL_TERMINAL */
476 /* For compatibility with programs like `less' that want to
477 put data in the termcap buffer themselves as a fallback. */
478 if (bp)
479 term_entry = bp;
481 termcap_name = getenv ("TERMCAP");
482 if (termcap_name && *termcap_name == '\0')
483 termcap_name = NULL;
484 #if defined (MSDOS) && !defined (TEST)
485 if (termcap_name && (*termcap_name == '\\'
486 || *termcap_name == '/'
487 || termcap_name[1] == ':'))
488 dostounix_filename(termcap_name);
489 #endif
491 filep = termcap_name && valid_filename_p (termcap_name);
493 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
494 it is a file name to use instead of /etc/termcap.
495 If it is non-null and does not start with /,
496 it is the entry itself, but only if
497 the name the caller requested matches the TERM variable. */
499 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
501 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
502 if (!indirect)
504 if (!bp)
505 bp = termcap_name;
506 else
507 strcpy (bp, termcap_name);
508 goto ret;
510 else
511 { /* It has tc=. Need to read /etc/termcap. */
512 tcenv = termcap_name;
513 termcap_name = NULL;
517 if (!termcap_name || !filep)
518 termcap_name = TERMCAP_FILE;
520 /* Here we know we must search a file and termcap_name has its name. */
522 #ifdef MSDOS
523 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
524 #else
525 fd = open (termcap_name, O_RDONLY, 0);
526 #endif
527 if (fd < 0)
528 return -1;
530 buf.size = BUFSIZE;
531 /* Add 1 to size to ensure room for terminating null. */
532 buf.beg = (char *) xmalloc (buf.size + 1);
533 term = indirect ? indirect : name;
535 if (!bp)
537 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
538 bp = (char *) xmalloc (malloc_size);
540 tc_search_point = bp1 = bp;
542 if (indirect)
543 /* Copy the data from the environment variable. */
545 strcpy (bp, tcenv);
546 bp1 += strlen (tcenv);
549 while (term)
551 /* Scan the file, reading it via buf, till find start of main entry. */
552 if (scan_file (term, fd, &buf) == 0)
554 close (fd);
555 free (buf.beg);
556 if (malloc_size)
557 free (bp);
558 return 0;
561 /* Free old `term' if appropriate. */
562 if (term != name)
563 free (term);
565 /* If BP is malloc'd by us, make sure it is big enough. */
566 if (malloc_size)
568 malloc_size = bp1 - bp + buf.size;
569 termcap_name = (char *) xrealloc (bp, malloc_size);
570 bp1 += termcap_name - bp;
571 tc_search_point += termcap_name - bp;
572 bp = termcap_name;
575 /* Copy the line of the entry from buf into bp. */
576 termcap_name = buf.ptr;
577 while ((*bp1++ = c = *termcap_name++) && c != '\n')
578 /* Drop out any \ newline sequence. */
579 if (c == '\\' && *termcap_name == '\n')
581 bp1--;
582 termcap_name++;
584 *bp1 = '\0';
586 /* Does this entry refer to another terminal type's entry?
587 If something is found, copy it into heap and null-terminate it. */
588 tc_search_point = find_capability (tc_search_point, "tc");
589 term = tgetst1 (tc_search_point, (char **) 0);
592 close (fd);
593 free (buf.beg);
595 if (malloc_size)
596 bp = (char *) xrealloc (bp, bp1 - bp + 1);
598 ret:
599 term_entry = bp;
600 return 1;
603 /* Given file open on FD and buffer BUFP,
604 scan the file from the beginning until a line is found
605 that starts the entry for terminal type STR.
606 Return 1 if successful, with that line in BUFP,
607 or 0 if no entry is found in the file. */
609 static int
610 scan_file (str, fd, bufp)
611 char *str;
612 int fd;
613 register struct termcap_buffer *bufp;
615 register char *end;
617 bufp->ptr = bufp->beg;
618 bufp->full = 0;
619 bufp->ateof = 0;
620 *bufp->ptr = '\0';
622 lseek (fd, 0L, 0);
624 while (!bufp->ateof)
626 /* Read a line into the buffer. */
627 end = NULL;
630 /* if it is continued, append another line to it,
631 until a non-continued line ends. */
632 end = gobble_line (fd, bufp, end);
634 while (!bufp->ateof && end[-2] == '\\');
636 if (*bufp->ptr != '#'
637 && name_match (bufp->ptr, str))
638 return 1;
640 /* Discard the line just processed. */
641 bufp->ptr = end;
643 return 0;
646 /* Return nonzero if NAME is one of the names specified
647 by termcap entry LINE. */
649 static int
650 name_match (line, name)
651 char *line, *name;
653 register char *tem;
655 if (!compare_contin (line, name))
656 return 1;
657 /* This line starts an entry. Is it the right one? */
658 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
659 if (*tem == '|' && !compare_contin (tem + 1, name))
660 return 1;
662 return 0;
665 static int
666 compare_contin (str1, str2)
667 register char *str1, *str2;
669 register int c1, c2;
670 while (1)
672 c1 = *str1++;
673 c2 = *str2++;
674 while (c1 == '\\' && *str1 == '\n')
676 str1++;
677 while ((c1 = *str1++) == ' ' || c1 == '\t');
679 if (c2 == '\0')
681 /* End of type being looked up. */
682 if (c1 == '|' || c1 == ':')
683 /* If end of name in data base, we win. */
684 return 0;
685 else
686 return 1;
688 else if (c1 != c2)
689 return 1;
693 /* Make sure that the buffer <- BUFP contains a full line
694 of the file open on FD, starting at the place BUFP->ptr
695 points to. Can read more of the file, discard stuff before
696 BUFP->ptr, or make the buffer bigger.
698 Return the pointer to after the newline ending the line,
699 or to the end of the file, if there is no newline to end it.
701 Can also merge on continuation lines. If APPEND_END is
702 non-null, it points past the newline of a line that is
703 continued; we add another line onto it and regard the whole
704 thing as one line. The caller decides when a line is continued. */
706 static char *
707 gobble_line (fd, bufp, append_end)
708 int fd;
709 register struct termcap_buffer *bufp;
710 char *append_end;
712 register char *end;
713 register int nread;
714 register char *buf = bufp->beg;
715 register char *tem;
717 if (!append_end)
718 append_end = bufp->ptr;
720 while (1)
722 end = append_end;
723 while (*end && *end != '\n') end++;
724 if (*end)
725 break;
726 if (bufp->ateof)
727 return buf + bufp->full;
728 if (bufp->ptr == buf)
730 if (bufp->full == bufp->size)
732 bufp->size *= 2;
733 /* Add 1 to size to ensure room for terminating null. */
734 tem = (char *) xrealloc (buf, bufp->size + 1);
735 bufp->ptr = (bufp->ptr - buf) + tem;
736 append_end = (append_end - buf) + tem;
737 bufp->beg = buf = tem;
740 else
742 append_end -= bufp->ptr - buf;
743 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
744 bufp->ptr = buf;
746 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
747 bufp->ateof = 1;
748 bufp->full += nread;
749 buf[bufp->full] = '\0';
751 return end + 1;
754 #ifdef TEST
756 #ifdef NULL
757 #undef NULL
758 #endif
760 #include <stdio.h>
762 main (argc, argv)
763 int argc;
764 char **argv;
766 char *term;
767 char *buf;
769 term = argv[1];
770 printf ("TERM: %s\n", term);
772 buf = (char *) tgetent (0, term);
773 if ((int) buf <= 0)
775 printf ("No entry.\n");
776 return 0;
779 printf ("Entry: %s\n", buf);
781 tprint ("cm");
782 tprint ("AL");
784 printf ("co: %d\n", tgetnum ("co"));
785 printf ("am: %d\n", tgetflag ("am"));
788 tprint (cap)
789 char *cap;
791 char *x = tgetstr (cap, 0);
792 register char *y;
794 printf ("%s: ", cap);
795 if (x)
797 for (y = x; *y; y++)
798 if (*y <= ' ' || *y == 0177)
799 printf ("\\%0o", *y);
800 else
801 putchar (*y);
802 free (x);
804 else
805 printf ("none");
806 putchar ('\n');
809 #endif /* TEST */