Initial revision
[emacs.git] / src / termcap.c
blob595fc425c181893ab9460f2cf0700e9da6c7c5a1
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, 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Emacs config.h may rename various library functions such as malloc. */
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #ifdef emacs
25 /* Get the O_* definitions for open et al. */
26 #include <sys/file.h>
27 #ifdef USG5
28 #include <fcntl.h>
29 #endif
31 #else /* not emacs */
33 #ifdef STDC_HEADERS
34 #include <stdlib.h>
35 #include <string.h>
36 #else
37 char *getenv ();
38 char *malloc ();
39 char *realloc ();
40 #endif
42 /* Do this after the include, in case string.h prototypes bcopy. */
43 #if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
44 #define bcopy(s, d, n) memcpy ((d), (s), (n))
45 #endif
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #ifdef _POSIX_VERSION
51 #include <fcntl.h>
52 #endif
54 #endif /* not emacs */
56 #ifndef NULL
57 #define NULL (char *) 0
58 #endif
60 #ifndef O_RDONLY
61 #define O_RDONLY 0
62 #endif
64 /* BUFSIZE is the initial size allocated for the buffer
65 for reading the termcap file.
66 It is not a limit.
67 Make it large normally for speed.
68 Make it variable when debugging, so can exercise
69 increasing the space dynamically. */
71 #ifndef BUFSIZE
72 #ifdef DEBUG
73 #define BUFSIZE bufsize
75 int bufsize = 128;
76 #else
77 #define BUFSIZE 2048
78 #endif
79 #endif
81 #ifndef TERMCAP_FILE
82 #define TERMCAP_FILE "/etc/termcap"
83 #endif
85 #ifndef emacs
86 static void
87 memory_out ()
89 write (2, "virtual memory exhausted\n", 25);
90 exit (1);
93 static char *
94 xmalloc (size)
95 unsigned size;
97 register char *tem = malloc (size);
99 if (!tem)
100 memory_out ();
101 return tem;
104 static char *
105 xrealloc (ptr, size)
106 char *ptr;
107 unsigned size;
109 register char *tem = realloc (ptr, size);
111 if (!tem)
112 memory_out ();
113 return tem;
115 #endif /* not emacs */
117 /* Looking up capabilities in the entry already found. */
119 /* The pointer to the data made by tgetent is left here
120 for tgetnum, tgetflag and tgetstr to find. */
121 static char *term_entry;
123 static char *tgetst1 ();
125 /* Search entry BP for capability CAP.
126 Return a pointer to the capability (in BP) if found,
127 0 if not found. */
129 static char *
130 find_capability (bp, cap)
131 register char *bp, *cap;
133 for (; *bp; bp++)
134 if (bp[0] == ':'
135 && bp[1] == cap[0]
136 && bp[2] == cap[1])
137 return &bp[4];
138 return NULL;
142 tgetnum (cap)
143 char *cap;
145 register char *ptr = find_capability (term_entry, cap);
146 if (!ptr || ptr[-1] != '#')
147 return -1;
148 return atoi (ptr);
152 tgetflag (cap)
153 char *cap;
155 register char *ptr = find_capability (term_entry, cap);
156 return ptr && ptr[-1] == ':';
159 /* Look up a string-valued capability CAP.
160 If AREA is non-null, it points to a pointer to a block in which
161 to store the string. That pointer is advanced over the space used.
162 If AREA is null, space is allocated with `malloc'. */
164 char *
165 tgetstr (cap, area)
166 char *cap;
167 char **area;
169 register char *ptr = find_capability (term_entry, cap);
170 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
171 return NULL;
172 return tgetst1 (ptr, area);
175 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
176 gives meaning of character following \, or a space if no special meaning.
177 Eight characters per line within the string. */
179 static char esctab[]
180 = " \007\010 \033\014 \
181 \012 \
182 \015 \011 \013 \
185 /* PTR points to a string value inside a termcap entry.
186 Copy that value, processing \ and ^ abbreviations,
187 into the block that *AREA points to,
188 or to newly allocated storage if AREA is NULL.
189 Return the address to which we copied the value,
190 or NULL if PTR is NULL. */
192 static char *
193 tgetst1 (ptr, area)
194 char *ptr;
195 char **area;
197 register char *p, *r;
198 register int c;
199 register int size;
200 char *ret;
201 register int c1;
203 if (!ptr)
204 return NULL;
206 /* `ret' gets address of where to store the string. */
207 if (!area)
209 /* Compute size of block needed (may overestimate). */
210 p = ptr;
211 while ((c = *p++) && c != ':' && c != '\n')
213 ret = (char *) xmalloc (p - ptr + 1);
215 else
216 ret = *area;
218 /* Copy the string value, stopping at null or colon.
219 Also process ^ and \ abbreviations. */
220 p = ptr;
221 r = ret;
222 while ((c = *p++) && c != ':' && c != '\n')
224 if (c == '^')
226 c = *p++;
227 if (c == '?')
228 c = 0177;
229 else
230 c &= 037;
232 else if (c == '\\')
234 c = *p++;
235 if (c >= '0' && c <= '7')
237 c -= '0';
238 size = 0;
240 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
242 c *= 8;
243 c += c1 - '0';
244 p++;
247 else if (c >= 0100 && c < 0200)
249 c1 = esctab[(c & ~040) - 0100];
250 if (c1 != ' ')
251 c = c1;
254 *r++ = c;
256 *r = '\0';
257 /* Update *AREA. */
258 if (area)
259 *area = r + 1;
260 return ret;
263 /* Outputting a string with padding. */
265 short ospeed;
266 /* If OSPEED is 0, we use this as the actual baud rate. */
267 int tputs_baud_rate;
268 char PC;
270 /* Actual baud rate if positive;
271 - baud rate / 100 if negative. */
273 static int speeds[] =
275 #ifdef VMS
276 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
277 -20, -24, -36, -48, -72, -96, -192
278 #else /* not VMS */
279 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
280 -18, -24, -48, -96, -192, -288, -384, -576, -1152
281 #endif /* not VMS */
284 void
285 tputs (str, nlines, outfun)
286 register char *str;
287 int nlines;
288 register int (*outfun) ();
290 register int padcount = 0;
291 register int speed;
293 #ifdef emacs
294 extern baud_rate;
295 speed = baud_rate;
296 /* For quite high speeds, convert to the smaller
297 units to avoid overflow. */
298 if (speed > 10000)
299 speed = - speed / 100;
300 #else
301 if (ospeed == 0)
302 speed = tputs_baud_rate;
303 else
304 speed = speeds[ospeed];
305 #endif
307 if (!str)
308 return;
310 while (*str >= '0' && *str <= '9')
312 padcount += *str++ - '0';
313 padcount *= 10;
315 if (*str == '.')
317 str++;
318 padcount += *str++ - '0';
320 if (*str == '*')
322 str++;
323 padcount *= nlines;
325 while (*str)
326 (*outfun) (*str++);
328 /* PADCOUNT is now in units of tenths of msec.
329 SPEED is measured in characters per 10 seconds
330 or in characters per .1 seconds (if negative).
331 We use the smaller units for larger speeds to avoid overflow. */
332 padcount *= speed;
333 padcount += 500;
334 padcount /= 1000;
335 if (speed < 0)
336 padcount = -padcount;
337 else
339 padcount += 50;
340 padcount /= 100;
343 while (padcount-- > 0)
344 (*outfun) (PC);
347 /* Finding the termcap entry in the termcap data base. */
349 struct buffer
351 char *beg;
352 int size;
353 char *ptr;
354 int ateof;
355 int full;
358 /* Forward declarations of static functions. */
360 static int scan_file ();
361 static char *gobble_line ();
362 static int compare_contin ();
363 static int name_match ();
365 #ifdef VMS
367 #include <rmsdef.h>
368 #include <fab.h>
369 #include <nam.h>
371 static int
372 valid_filename_p (fn)
373 char *fn;
375 struct FAB fab = cc$rms_fab;
376 struct NAM nam = cc$rms_nam;
377 char esa[NAM$C_MAXRSS];
379 fab.fab$l_fna = fn;
380 fab.fab$b_fns = strlen(fn);
381 fab.fab$l_nam = &nam;
382 fab.fab$l_fop = FAB$M_NAM;
384 nam.nam$l_esa = esa;
385 nam.nam$b_ess = sizeof esa;
387 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
390 #else /* !VMS */
392 #ifdef MSDOS /* MW, May 1993 */
393 static int
394 valid_filename_p (fn)
395 char *fn;
397 return *fn == '/' || fn[1] == ':';
399 #else
400 #define valid_filename_p(fn) (*(fn) == '/')
401 #endif
403 #endif /* !VMS */
405 /* Find the termcap entry data for terminal type NAME
406 and store it in the block that BP points to.
407 Record its address for future use.
409 If BP is null, space is dynamically allocated.
411 Return -1 if there is some difficulty accessing the data base
412 of terminal types,
413 0 if the data base is accessible but the type NAME is not defined
414 in it, and some other value otherwise. */
417 tgetent (bp, name)
418 char *bp, *name;
420 register char *termcap_name;
421 register int fd;
422 struct buffer buf;
423 register char *bp1;
424 char *bp2;
425 char *term;
426 int malloc_size = 0;
427 register int c;
428 char *tcenv; /* TERMCAP value, if it contains :tc=. */
429 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
430 int filep;
432 #ifdef INTERNAL_TERMINAL
433 /* For the internal terminal we don't want to read any termcap file,
434 so fake it. */
435 if (!strcmp (name, "internal"))
437 term = INTERNAL_TERMINAL;
438 if (!bp)
440 malloc_size = 1 + strlen (term);
441 bp = (char *) xmalloc (malloc_size);
443 strcpy (bp, term);
444 goto ret;
446 #endif /* INTERNAL_TERMINAL */
448 /* For compatibility with programs like `less' that want to
449 put data in the termcap buffer themselves as a fallback. */
450 if (bp)
451 term_entry = bp;
453 termcap_name = getenv ("TERMCAP");
454 if (termcap_name && *termcap_name == '\0')
455 termcap_name = NULL;
456 #if defined (MSDOS) && !defined (TEST)
457 if (termcap_name && (*termcap_name == '\\'
458 || *termcap_name == '/'
459 || termcap_name[1] == ':'))
460 dostounix_filename(termcap_name);
461 #endif
463 filep = termcap_name && valid_filename_p (termcap_name);
465 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
466 it is a file name to use instead of /etc/termcap.
467 If it is non-null and does not start with /,
468 it is the entry itself, but only if
469 the name the caller requested matches the TERM variable. */
471 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
473 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
474 if (!indirect)
476 if (!bp)
477 bp = termcap_name;
478 else
479 strcpy (bp, termcap_name);
480 goto ret;
482 else
483 { /* It has tc=. Need to read /etc/termcap. */
484 tcenv = termcap_name;
485 termcap_name = NULL;
489 if (!termcap_name || !filep)
490 termcap_name = TERMCAP_FILE;
492 /* Here we know we must search a file and termcap_name has its name. */
494 #ifdef MSDOS
495 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
496 #else
497 fd = open (termcap_name, O_RDONLY, 0);
498 #endif
499 if (fd < 0)
500 return -1;
502 buf.size = BUFSIZE;
503 /* Add 1 to size to ensure room for terminating null. */
504 buf.beg = (char *) xmalloc (buf.size + 1);
505 term = indirect ? indirect : name;
507 if (!bp)
509 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
510 bp = (char *) xmalloc (malloc_size);
512 bp1 = bp;
514 if (indirect)
515 /* Copy the data from the environment variable. */
517 strcpy (bp, tcenv);
518 bp1 += strlen (tcenv);
521 while (term)
523 /* Scan the file, reading it via buf, till find start of main entry. */
524 if (scan_file (term, fd, &buf) == 0)
526 close (fd);
527 free (buf.beg);
528 if (malloc_size)
529 free (bp);
530 return 0;
533 /* Free old `term' if appropriate. */
534 if (term != name)
535 free (term);
537 /* If BP is malloc'd by us, make sure it is big enough. */
538 if (malloc_size)
540 malloc_size = bp1 - bp + buf.size;
541 termcap_name = (char *) xrealloc (bp, malloc_size);
542 bp1 += termcap_name - bp;
543 bp = termcap_name;
546 bp2 = bp1;
548 /* Copy the line of the entry from buf into bp. */
549 termcap_name = buf.ptr;
550 while ((*bp1++ = c = *termcap_name++) && c != '\n')
551 /* Drop out any \ newline sequence. */
552 if (c == '\\' && *termcap_name == '\n')
554 bp1--;
555 termcap_name++;
557 *bp1 = '\0';
559 /* Does this entry refer to another terminal type's entry?
560 If something is found, copy it into heap and null-terminate it. */
561 term = tgetst1 (find_capability (bp2, "tc"), (char **) 0);
564 close (fd);
565 free (buf.beg);
567 if (malloc_size)
568 bp = (char *) xrealloc (bp, bp1 - bp + 1);
570 ret:
571 term_entry = bp;
572 return 1;
575 /* Given file open on FD and buffer BUFP,
576 scan the file from the beginning until a line is found
577 that starts the entry for terminal type STR.
578 Return 1 if successful, with that line in BUFP,
579 or 0 if no entry is found in the file. */
581 static int
582 scan_file (str, fd, bufp)
583 char *str;
584 int fd;
585 register struct buffer *bufp;
587 register char *end;
589 bufp->ptr = bufp->beg;
590 bufp->full = 0;
591 bufp->ateof = 0;
592 *bufp->ptr = '\0';
594 lseek (fd, 0L, 0);
596 while (!bufp->ateof)
598 /* Read a line into the buffer. */
599 end = NULL;
602 /* if it is continued, append another line to it,
603 until a non-continued line ends. */
604 end = gobble_line (fd, bufp, end);
606 while (!bufp->ateof && end[-2] == '\\');
608 if (*bufp->ptr != '#'
609 && name_match (bufp->ptr, str))
610 return 1;
612 /* Discard the line just processed. */
613 bufp->ptr = end;
615 return 0;
618 /* Return nonzero if NAME is one of the names specified
619 by termcap entry LINE. */
621 static int
622 name_match (line, name)
623 char *line, *name;
625 register char *tem;
627 if (!compare_contin (line, name))
628 return 1;
629 /* This line starts an entry. Is it the right one? */
630 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
631 if (*tem == '|' && !compare_contin (tem + 1, name))
632 return 1;
634 return 0;
637 static int
638 compare_contin (str1, str2)
639 register char *str1, *str2;
641 register int c1, c2;
642 while (1)
644 c1 = *str1++;
645 c2 = *str2++;
646 while (c1 == '\\' && *str1 == '\n')
648 str1++;
649 while ((c1 = *str1++) == ' ' || c1 == '\t');
651 if (c2 == '\0')
653 /* End of type being looked up. */
654 if (c1 == '|' || c1 == ':')
655 /* If end of name in data base, we win. */
656 return 0;
657 else
658 return 1;
660 else if (c1 != c2)
661 return 1;
665 /* Make sure that the buffer <- BUFP contains a full line
666 of the file open on FD, starting at the place BUFP->ptr
667 points to. Can read more of the file, discard stuff before
668 BUFP->ptr, or make the buffer bigger.
670 Return the pointer to after the newline ending the line,
671 or to the end of the file, if there is no newline to end it.
673 Can also merge on continuation lines. If APPEND_END is
674 non-null, it points past the newline of a line that is
675 continued; we add another line onto it and regard the whole
676 thing as one line. The caller decides when a line is continued. */
678 static char *
679 gobble_line (fd, bufp, append_end)
680 int fd;
681 register struct buffer *bufp;
682 char *append_end;
684 register char *end;
685 register int nread;
686 register char *buf = bufp->beg;
687 register char *tem;
689 if (!append_end)
690 append_end = bufp->ptr;
692 while (1)
694 end = append_end;
695 while (*end && *end != '\n') end++;
696 if (*end)
697 break;
698 if (bufp->ateof)
699 return buf + bufp->full;
700 if (bufp->ptr == buf)
702 if (bufp->full == bufp->size)
704 bufp->size *= 2;
705 /* Add 1 to size to ensure room for terminating null. */
706 tem = (char *) xrealloc (buf, bufp->size + 1);
707 bufp->ptr = (bufp->ptr - buf) + tem;
708 append_end = (append_end - buf) + tem;
709 bufp->beg = buf = tem;
712 else
714 append_end -= bufp->ptr - buf;
715 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
716 bufp->ptr = buf;
718 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
719 bufp->ateof = 1;
720 bufp->full += nread;
721 buf[bufp->full] = '\0';
723 return end + 1;
726 #ifdef TEST
728 #ifdef NULL
729 #undef NULL
730 #endif
732 #include <stdio.h>
734 main (argc, argv)
735 int argc;
736 char **argv;
738 char *term;
739 char *buf;
741 term = argv[1];
742 printf ("TERM: %s\n", term);
744 buf = (char *) tgetent (0, term);
745 if ((int) buf <= 0)
747 printf ("No entry.\n");
748 return 0;
751 printf ("Entry: %s\n", buf);
753 tprint ("cm");
754 tprint ("AL");
756 printf ("co: %d\n", tgetnum ("co"));
757 printf ("am: %d\n", tgetflag ("am"));
760 tprint (cap)
761 char *cap;
763 char *x = tgetstr (cap, 0);
764 register char *y;
766 printf ("%s: ", cap);
767 if (x)
769 for (y = x; *y; y++)
770 if (*y <= ' ' || *y == 0177)
771 printf ("\\%0o", *y);
772 else
773 putchar (*y);
774 free (x);
776 else
777 printf ("none");
778 putchar ('\n');
781 #endif /* TEST */