(Fdefalias): Doc fix.
[emacs.git] / src / termcap.c
blobf41f24f160db5614ea262f705a381aa0ba5ffbda
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 /* Get the O_* definitions for open et al. */
27 #include <sys/file.h>
28 #ifdef USG5
29 #include <fcntl.h>
30 #endif
32 #else /* not emacs */
34 #ifdef STDC_HEADERS
35 #include <stdlib.h>
36 #include <string.h>
37 #else
38 char *getenv ();
39 char *malloc ();
40 char *realloc ();
41 #endif
43 /* Do this after the include, in case string.h prototypes bcopy. */
44 #if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
45 #define bcopy(s, d, n) memcpy ((d), (s), (n))
46 #endif
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 #ifdef _POSIX_VERSION
52 #include <fcntl.h>
53 #endif
55 #endif /* not emacs */
57 #ifndef NULL
58 #define NULL (char *) 0
59 #endif
61 #ifndef O_RDONLY
62 #define O_RDONLY 0
63 #endif
65 /* BUFSIZE is the initial size allocated for the buffer
66 for reading the termcap file.
67 It is not a limit.
68 Make it large normally for speed.
69 Make it variable when debugging, so can exercise
70 increasing the space dynamically. */
72 #ifndef BUFSIZE
73 #ifdef DEBUG
74 #define BUFSIZE bufsize
76 int bufsize = 128;
77 #else
78 #define BUFSIZE 2048
79 #endif
80 #endif
82 #ifndef TERMCAP_FILE
83 #define TERMCAP_FILE "/etc/termcap"
84 #endif
86 #ifndef emacs
87 static void
88 memory_out ()
90 write (2, "virtual memory exhausted\n", 25);
91 exit (1);
94 static char *
95 xmalloc (size)
96 unsigned size;
98 register char *tem = malloc (size);
100 if (!tem)
101 memory_out ();
102 return tem;
105 static char *
106 xrealloc (ptr, size)
107 char *ptr;
108 unsigned size;
110 register char *tem = realloc (ptr, size);
112 if (!tem)
113 memory_out ();
114 return tem;
116 #endif /* not emacs */
118 /* Looking up capabilities in the entry already found. */
120 /* The pointer to the data made by tgetent is left here
121 for tgetnum, tgetflag and tgetstr to find. */
122 static char *term_entry;
124 static char *tgetst1 ();
126 /* Search entry BP for capability CAP.
127 Return a pointer to the capability (in BP) if found,
128 0 if not found. */
130 static char *
131 find_capability (bp, cap)
132 register char *bp, *cap;
134 for (; *bp; bp++)
135 if (bp[0] == ':'
136 && bp[1] == cap[0]
137 && bp[2] == cap[1])
138 return &bp[4];
139 return NULL;
143 tgetnum (cap)
144 char *cap;
146 register char *ptr = find_capability (term_entry, cap);
147 if (!ptr || ptr[-1] != '#')
148 return -1;
149 return atoi (ptr);
153 tgetflag (cap)
154 char *cap;
156 register char *ptr = find_capability (term_entry, cap);
157 return ptr && ptr[-1] == ':';
160 /* Look up a string-valued capability CAP.
161 If AREA is non-null, it points to a pointer to a block in which
162 to store the string. That pointer is advanced over the space used.
163 If AREA is null, space is allocated with `malloc'. */
165 char *
166 tgetstr (cap, area)
167 char *cap;
168 char **area;
170 register char *ptr = find_capability (term_entry, cap);
171 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
172 return NULL;
173 return tgetst1 (ptr, area);
176 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
177 gives meaning of character following \, or a space if no special meaning.
178 Eight characters per line within the string. */
180 static char esctab[]
181 = " \007\010 \033\014 \
182 \012 \
183 \015 \011 \013 \
186 /* PTR points to a string value inside a termcap entry.
187 Copy that value, processing \ and ^ abbreviations,
188 into the block that *AREA points to,
189 or to newly allocated storage if AREA is NULL.
190 Return the address to which we copied the value,
191 or NULL if PTR is NULL. */
193 static char *
194 tgetst1 (ptr, area)
195 char *ptr;
196 char **area;
198 register char *p, *r;
199 register int c;
200 register int size;
201 char *ret;
202 register int c1;
204 if (!ptr)
205 return NULL;
207 /* `ret' gets address of where to store the string. */
208 if (!area)
210 /* Compute size of block needed (may overestimate). */
211 p = ptr;
212 while ((c = *p++) && c != ':' && c != '\n')
214 ret = (char *) xmalloc (p - ptr + 1);
216 else
217 ret = *area;
219 /* Copy the string value, stopping at null or colon.
220 Also process ^ and \ abbreviations. */
221 p = ptr;
222 r = ret;
223 while ((c = *p++) && c != ':' && c != '\n')
225 if (c == '^')
227 c = *p++;
228 if (c == '?')
229 c = 0177;
230 else
231 c &= 037;
233 else if (c == '\\')
235 c = *p++;
236 if (c >= '0' && c <= '7')
238 c -= '0';
239 size = 0;
241 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
243 c *= 8;
244 c += c1 - '0';
245 p++;
248 else if (c >= 0100 && c < 0200)
250 c1 = esctab[(c & ~040) - 0100];
251 if (c1 != ' ')
252 c = c1;
255 *r++ = c;
257 *r = '\0';
258 /* Update *AREA. */
259 if (area)
260 *area = r + 1;
261 return ret;
264 /* Outputting a string with padding. */
266 short ospeed;
267 /* If OSPEED is 0, we use this as the actual baud rate. */
268 int tputs_baud_rate;
269 char PC;
271 /* Actual baud rate if positive;
272 - baud rate / 100 if negative. */
274 static int speeds[] =
276 #ifdef VMS
277 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
278 -20, -24, -36, -48, -72, -96, -192
279 #else /* not VMS */
280 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
281 -18, -24, -48, -96, -192, -288, -384, -576, -1152
282 #endif /* not VMS */
285 void
286 tputs (str, nlines, outfun)
287 register char *str;
288 int nlines;
289 register int (*outfun) ();
291 register int padcount = 0;
292 register int speed;
294 #ifdef emacs
295 extern baud_rate;
296 speed = baud_rate;
297 /* For quite high speeds, convert to the smaller
298 units to avoid overflow. */
299 if (speed > 10000)
300 speed = - speed / 100;
301 #else
302 if (ospeed == 0)
303 speed = tputs_baud_rate;
304 else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0]))
305 speed = speeds[ospeed];
306 else
307 speed = 0;
308 #endif
310 if (!str)
311 return;
313 while (*str >= '0' && *str <= '9')
315 padcount += *str++ - '0';
316 padcount *= 10;
318 if (*str == '.')
320 str++;
321 padcount += *str++ - '0';
323 if (*str == '*')
325 str++;
326 padcount *= nlines;
328 while (*str)
329 (*outfun) (*str++);
331 /* PADCOUNT is now in units of tenths of msec.
332 SPEED is measured in characters per 10 seconds
333 or in characters per .1 seconds (if negative).
334 We use the smaller units for larger speeds to avoid overflow. */
335 padcount *= speed;
336 padcount += 500;
337 padcount /= 1000;
338 if (speed < 0)
339 padcount = -padcount;
340 else
342 padcount += 50;
343 padcount /= 100;
346 while (padcount-- > 0)
347 (*outfun) (PC);
350 /* Finding the termcap entry in the termcap data base. */
352 struct buffer
354 char *beg;
355 int size;
356 char *ptr;
357 int ateof;
358 int full;
361 /* Forward declarations of static functions. */
363 static int scan_file ();
364 static char *gobble_line ();
365 static int compare_contin ();
366 static int name_match ();
368 #ifdef VMS
370 #include <rmsdef.h>
371 #include <fab.h>
372 #include <nam.h>
374 static int
375 valid_filename_p (fn)
376 char *fn;
378 struct FAB fab = cc$rms_fab;
379 struct NAM nam = cc$rms_nam;
380 char esa[NAM$C_MAXRSS];
382 fab.fab$l_fna = fn;
383 fab.fab$b_fns = strlen(fn);
384 fab.fab$l_nam = &nam;
385 fab.fab$l_fop = FAB$M_NAM;
387 nam.nam$l_esa = esa;
388 nam.nam$b_ess = sizeof esa;
390 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
393 #else /* !VMS */
395 #ifdef MSDOS /* MW, May 1993 */
396 static int
397 valid_filename_p (fn)
398 char *fn;
400 return *fn == '/' || fn[1] == ':';
402 #else
403 #define valid_filename_p(fn) (*(fn) == '/')
404 #endif
406 #endif /* !VMS */
408 /* Find the termcap entry data for terminal type NAME
409 and store it in the block that BP points to.
410 Record its address for future use.
412 If BP is null, space is dynamically allocated.
414 Return -1 if there is some difficulty accessing the data base
415 of terminal types,
416 0 if the data base is accessible but the type NAME is not defined
417 in it, and some other value otherwise. */
420 tgetent (bp, name)
421 char *bp, *name;
423 register char *termcap_name;
424 register int fd;
425 struct buffer buf;
426 register char *bp1;
427 char *tc_search_point;
428 char *term;
429 int malloc_size = 0;
430 register int c;
431 char *tcenv; /* TERMCAP value, if it contains :tc=. */
432 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
433 int filep;
435 #ifdef INTERNAL_TERMINAL
436 /* For the internal terminal we don't want to read any termcap file,
437 so fake it. */
438 if (!strcmp (name, "internal"))
440 term = INTERNAL_TERMINAL;
441 if (!bp)
443 malloc_size = 1 + strlen (term);
444 bp = (char *) xmalloc (malloc_size);
446 strcpy (bp, term);
447 goto ret;
449 #endif /* INTERNAL_TERMINAL */
451 /* For compatibility with programs like `less' that want to
452 put data in the termcap buffer themselves as a fallback. */
453 if (bp)
454 term_entry = bp;
456 termcap_name = getenv ("TERMCAP");
457 if (termcap_name && *termcap_name == '\0')
458 termcap_name = NULL;
459 #if defined (MSDOS) && !defined (TEST)
460 if (termcap_name && (*termcap_name == '\\'
461 || *termcap_name == '/'
462 || termcap_name[1] == ':'))
463 dostounix_filename(termcap_name);
464 #endif
466 filep = termcap_name && valid_filename_p (termcap_name);
468 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
469 it is a file name to use instead of /etc/termcap.
470 If it is non-null and does not start with /,
471 it is the entry itself, but only if
472 the name the caller requested matches the TERM variable. */
474 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
476 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
477 if (!indirect)
479 if (!bp)
480 bp = termcap_name;
481 else
482 strcpy (bp, termcap_name);
483 goto ret;
485 else
486 { /* It has tc=. Need to read /etc/termcap. */
487 tcenv = termcap_name;
488 termcap_name = NULL;
492 if (!termcap_name || !filep)
493 termcap_name = TERMCAP_FILE;
495 /* Here we know we must search a file and termcap_name has its name. */
497 #ifdef MSDOS
498 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
499 #else
500 fd = open (termcap_name, O_RDONLY, 0);
501 #endif
502 if (fd < 0)
503 return -1;
505 buf.size = BUFSIZE;
506 /* Add 1 to size to ensure room for terminating null. */
507 buf.beg = (char *) xmalloc (buf.size + 1);
508 term = indirect ? indirect : name;
510 if (!bp)
512 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
513 bp = (char *) xmalloc (malloc_size);
515 tc_search_point = bp1 = bp;
517 if (indirect)
518 /* Copy the data from the environment variable. */
520 strcpy (bp, tcenv);
521 bp1 += strlen (tcenv);
524 while (term)
526 /* Scan the file, reading it via buf, till find start of main entry. */
527 if (scan_file (term, fd, &buf) == 0)
529 close (fd);
530 free (buf.beg);
531 if (malloc_size)
532 free (bp);
533 return 0;
536 /* Free old `term' if appropriate. */
537 if (term != name)
538 free (term);
540 /* If BP is malloc'd by us, make sure it is big enough. */
541 if (malloc_size)
543 malloc_size = bp1 - bp + buf.size;
544 termcap_name = (char *) xrealloc (bp, malloc_size);
545 bp1 += termcap_name - bp;
546 tc_search_point += termcap_name - bp;
547 bp = termcap_name;
550 /* Copy the line of the entry from buf into bp. */
551 termcap_name = buf.ptr;
552 while ((*bp1++ = c = *termcap_name++) && c != '\n')
553 /* Drop out any \ newline sequence. */
554 if (c == '\\' && *termcap_name == '\n')
556 bp1--;
557 termcap_name++;
559 *bp1 = '\0';
561 /* Does this entry refer to another terminal type's entry?
562 If something is found, copy it into heap and null-terminate it. */
563 tc_search_point = find_capability (tc_search_point, "tc");
564 term = tgetst1 (tc_search_point, (char **) 0);
567 close (fd);
568 free (buf.beg);
570 if (malloc_size)
571 bp = (char *) xrealloc (bp, bp1 - bp + 1);
573 ret:
574 term_entry = bp;
575 return 1;
578 /* Given file open on FD and buffer BUFP,
579 scan the file from the beginning until a line is found
580 that starts the entry for terminal type STR.
581 Return 1 if successful, with that line in BUFP,
582 or 0 if no entry is found in the file. */
584 static int
585 scan_file (str, fd, bufp)
586 char *str;
587 int fd;
588 register struct buffer *bufp;
590 register char *end;
592 bufp->ptr = bufp->beg;
593 bufp->full = 0;
594 bufp->ateof = 0;
595 *bufp->ptr = '\0';
597 lseek (fd, 0L, 0);
599 while (!bufp->ateof)
601 /* Read a line into the buffer. */
602 end = NULL;
605 /* if it is continued, append another line to it,
606 until a non-continued line ends. */
607 end = gobble_line (fd, bufp, end);
609 while (!bufp->ateof && end[-2] == '\\');
611 if (*bufp->ptr != '#'
612 && name_match (bufp->ptr, str))
613 return 1;
615 /* Discard the line just processed. */
616 bufp->ptr = end;
618 return 0;
621 /* Return nonzero if NAME is one of the names specified
622 by termcap entry LINE. */
624 static int
625 name_match (line, name)
626 char *line, *name;
628 register char *tem;
630 if (!compare_contin (line, name))
631 return 1;
632 /* This line starts an entry. Is it the right one? */
633 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
634 if (*tem == '|' && !compare_contin (tem + 1, name))
635 return 1;
637 return 0;
640 static int
641 compare_contin (str1, str2)
642 register char *str1, *str2;
644 register int c1, c2;
645 while (1)
647 c1 = *str1++;
648 c2 = *str2++;
649 while (c1 == '\\' && *str1 == '\n')
651 str1++;
652 while ((c1 = *str1++) == ' ' || c1 == '\t');
654 if (c2 == '\0')
656 /* End of type being looked up. */
657 if (c1 == '|' || c1 == ':')
658 /* If end of name in data base, we win. */
659 return 0;
660 else
661 return 1;
663 else if (c1 != c2)
664 return 1;
668 /* Make sure that the buffer <- BUFP contains a full line
669 of the file open on FD, starting at the place BUFP->ptr
670 points to. Can read more of the file, discard stuff before
671 BUFP->ptr, or make the buffer bigger.
673 Return the pointer to after the newline ending the line,
674 or to the end of the file, if there is no newline to end it.
676 Can also merge on continuation lines. If APPEND_END is
677 non-null, it points past the newline of a line that is
678 continued; we add another line onto it and regard the whole
679 thing as one line. The caller decides when a line is continued. */
681 static char *
682 gobble_line (fd, bufp, append_end)
683 int fd;
684 register struct buffer *bufp;
685 char *append_end;
687 register char *end;
688 register int nread;
689 register char *buf = bufp->beg;
690 register char *tem;
692 if (!append_end)
693 append_end = bufp->ptr;
695 while (1)
697 end = append_end;
698 while (*end && *end != '\n') end++;
699 if (*end)
700 break;
701 if (bufp->ateof)
702 return buf + bufp->full;
703 if (bufp->ptr == buf)
705 if (bufp->full == bufp->size)
707 bufp->size *= 2;
708 /* Add 1 to size to ensure room for terminating null. */
709 tem = (char *) xrealloc (buf, bufp->size + 1);
710 bufp->ptr = (bufp->ptr - buf) + tem;
711 append_end = (append_end - buf) + tem;
712 bufp->beg = buf = tem;
715 else
717 append_end -= bufp->ptr - buf;
718 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
719 bufp->ptr = buf;
721 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
722 bufp->ateof = 1;
723 bufp->full += nread;
724 buf[bufp->full] = '\0';
726 return end + 1;
729 #ifdef TEST
731 #ifdef NULL
732 #undef NULL
733 #endif
735 #include <stdio.h>
737 main (argc, argv)
738 int argc;
739 char **argv;
741 char *term;
742 char *buf;
744 term = argv[1];
745 printf ("TERM: %s\n", term);
747 buf = (char *) tgetent (0, term);
748 if ((int) buf <= 0)
750 printf ("No entry.\n");
751 return 0;
754 printf ("Entry: %s\n", buf);
756 tprint ("cm");
757 tprint ("AL");
759 printf ("co: %d\n", tgetnum ("co"));
760 printf ("am: %d\n", tgetflag ("am"));
763 tprint (cap)
764 char *cap;
766 char *x = tgetstr (cap, 0);
767 register char *y;
769 printf ("%s: ", cap);
770 if (x)
772 for (y = x; *y; y++)
773 if (*y <= ' ' || *y == 0177)
774 printf ("\\%0o", *y);
775 else
776 putchar (*y);
777 free (x);
779 else
780 printf ("none");
781 putchar ('\n');
784 #endif /* TEST */