Changed maintainer.
[emacs.git] / src / termcap.c
blobc447ce8b4b07f7d82e90e9a82c687207e701a7ec
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 #ifdef IS_EBCDIC_HOST
177 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
178 gives meaning of character following \, or a space if no special meaning.
179 Sixteen characters per line within the string. */
181 static char esctab[]
182 = " \057\026 \047\014 \
183 \025 \015 \
184 \005 \013 \
186 #else
187 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
188 gives meaning of character following \, or a space if no special meaning.
189 Eight characters per line within the string. */
191 static char esctab[]
192 = " \007\010 \033\014 \
193 \012 \
194 \015 \011 \013 \
196 #endif
198 /* PTR points to a string value inside a termcap entry.
199 Copy that value, processing \ and ^ abbreviations,
200 into the block that *AREA points to,
201 or to newly allocated storage if AREA is NULL.
202 Return the address to which we copied the value,
203 or NULL if PTR is NULL. */
205 static char *
206 tgetst1 (ptr, area)
207 char *ptr;
208 char **area;
210 register char *p, *r;
211 register int c;
212 register int size;
213 char *ret;
214 register int c1;
216 if (!ptr)
217 return NULL;
219 /* `ret' gets address of where to store the string. */
220 if (!area)
222 /* Compute size of block needed (may overestimate). */
223 p = ptr;
224 while ((c = *p++) && c != ':' && c != '\n')
226 ret = (char *) xmalloc (p - ptr + 1);
228 else
229 ret = *area;
231 /* Copy the string value, stopping at null or colon.
232 Also process ^ and \ abbreviations. */
233 p = ptr;
234 r = ret;
235 while ((c = *p++) && c != ':' && c != '\n')
237 if (c == '^')
239 c = *p++;
240 if (c == '?')
241 c = 0177;
242 else
243 c &= 037;
245 else if (c == '\\')
247 c = *p++;
248 if (c >= '0' && c <= '7')
250 c -= '0';
251 size = 0;
253 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
255 c *= 8;
256 c += c1 - '0';
257 p++;
260 #ifdef IS_EBCDIC_HOST
261 else if (c >= 0200 && c < 0360)
263 c1 = esctab[(c & ~0100) - 0200];
264 if (c1 != ' ')
265 c = c1;
267 #else
268 else if (c >= 0100 && c < 0200)
270 c1 = esctab[(c & ~040) - 0100];
271 if (c1 != ' ')
272 c = c1;
274 #endif
276 *r++ = c;
278 *r = '\0';
279 /* Update *AREA. */
280 if (area)
281 *area = r + 1;
282 return ret;
285 /* Outputting a string with padding. */
287 short ospeed;
288 /* If OSPEED is 0, we use this as the actual baud rate. */
289 int tputs_baud_rate;
290 char PC;
292 /* Actual baud rate if positive;
293 - baud rate / 100 if negative. */
295 static int speeds[] =
297 #ifdef VMS
298 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
299 -20, -24, -36, -48, -72, -96, -192
300 #else /* not VMS */
301 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
302 -18, -24, -48, -96, -192, -288, -384, -576, -1152
303 #endif /* not VMS */
306 void
307 tputs (str, nlines, outfun)
308 register char *str;
309 int nlines;
310 register int (*outfun) ();
312 register int padcount = 0;
313 register int speed;
315 #ifdef emacs
316 extern baud_rate;
317 speed = baud_rate;
318 /* For quite high speeds, convert to the smaller
319 units to avoid overflow. */
320 if (speed > 10000)
321 speed = - speed / 100;
322 #else
323 if (ospeed == 0)
324 speed = tputs_baud_rate;
325 else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0]))
326 speed = speeds[ospeed];
327 else
328 speed = 0;
329 #endif
331 if (!str)
332 return;
334 while (*str >= '0' && *str <= '9')
336 padcount += *str++ - '0';
337 padcount *= 10;
339 if (*str == '.')
341 str++;
342 padcount += *str++ - '0';
344 if (*str == '*')
346 str++;
347 padcount *= nlines;
349 while (*str)
350 (*outfun) (*str++);
352 /* PADCOUNT is now in units of tenths of msec.
353 SPEED is measured in characters per 10 seconds
354 or in characters per .1 seconds (if negative).
355 We use the smaller units for larger speeds to avoid overflow. */
356 padcount *= speed;
357 padcount += 500;
358 padcount /= 1000;
359 if (speed < 0)
360 padcount = -padcount;
361 else
363 padcount += 50;
364 padcount /= 100;
367 while (padcount-- > 0)
368 (*outfun) (PC);
371 /* Finding the termcap entry in the termcap data base. */
373 struct termcap_buffer
375 char *beg;
376 int size;
377 char *ptr;
378 int ateof;
379 int full;
382 /* Forward declarations of static functions. */
384 static int scan_file ();
385 static char *gobble_line ();
386 static int compare_contin ();
387 static int name_match ();
389 #ifdef VMS
391 #include <rmsdef.h>
392 #include <fab.h>
393 #include <nam.h>
395 static int
396 valid_filename_p (fn)
397 char *fn;
399 struct FAB fab = cc$rms_fab;
400 struct NAM nam = cc$rms_nam;
401 char esa[NAM$C_MAXRSS];
403 fab.fab$l_fna = fn;
404 fab.fab$b_fns = strlen(fn);
405 fab.fab$l_nam = &nam;
406 fab.fab$l_fop = FAB$M_NAM;
408 nam.nam$l_esa = esa;
409 nam.nam$b_ess = sizeof esa;
411 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
414 #else /* !VMS */
416 #ifdef MSDOS /* MW, May 1993 */
417 static int
418 valid_filename_p (fn)
419 char *fn;
421 return *fn == '/' || fn[1] == ':';
423 #else
424 #define valid_filename_p(fn) (*(fn) == '/')
425 #endif
427 #endif /* !VMS */
429 /* Find the termcap entry data for terminal type NAME
430 and store it in the block that BP points to.
431 Record its address for future use.
433 If BP is null, space is dynamically allocated.
435 Return -1 if there is some difficulty accessing the data base
436 of terminal types,
437 0 if the data base is accessible but the type NAME is not defined
438 in it, and some other value otherwise. */
441 tgetent (bp, name)
442 char *bp, *name;
444 register char *termcap_name;
445 register int fd;
446 struct termcap_buffer buf;
447 register char *bp1;
448 char *tc_search_point;
449 char *term;
450 int malloc_size = 0;
451 register int c;
452 char *tcenv; /* TERMCAP value, if it contains :tc=. */
453 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
454 int filep;
456 #ifdef INTERNAL_TERMINAL
457 /* For the internal terminal we don't want to read any termcap file,
458 so fake it. */
459 if (!strcmp (name, "internal"))
461 term = INTERNAL_TERMINAL;
462 if (!bp)
464 malloc_size = 1 + strlen (term);
465 bp = (char *) xmalloc (malloc_size);
467 strcpy (bp, term);
468 goto ret;
470 #endif /* INTERNAL_TERMINAL */
472 /* For compatibility with programs like `less' that want to
473 put data in the termcap buffer themselves as a fallback. */
474 if (bp)
475 term_entry = bp;
477 termcap_name = getenv ("TERMCAP");
478 if (termcap_name && *termcap_name == '\0')
479 termcap_name = NULL;
480 #if defined (MSDOS) && !defined (TEST)
481 if (termcap_name && (*termcap_name == '\\'
482 || *termcap_name == '/'
483 || termcap_name[1] == ':'))
484 dostounix_filename(termcap_name);
485 #endif
487 filep = termcap_name && valid_filename_p (termcap_name);
489 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
490 it is a file name to use instead of /etc/termcap.
491 If it is non-null and does not start with /,
492 it is the entry itself, but only if
493 the name the caller requested matches the TERM variable. */
495 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
497 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
498 if (!indirect)
500 if (!bp)
501 bp = termcap_name;
502 else
503 strcpy (bp, termcap_name);
504 goto ret;
506 else
507 { /* It has tc=. Need to read /etc/termcap. */
508 tcenv = termcap_name;
509 termcap_name = NULL;
513 if (!termcap_name || !filep)
514 termcap_name = TERMCAP_FILE;
516 /* Here we know we must search a file and termcap_name has its name. */
518 #ifdef MSDOS
519 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
520 #else
521 fd = open (termcap_name, O_RDONLY, 0);
522 #endif
523 if (fd < 0)
524 return -1;
526 buf.size = BUFSIZE;
527 /* Add 1 to size to ensure room for terminating null. */
528 buf.beg = (char *) xmalloc (buf.size + 1);
529 term = indirect ? indirect : name;
531 if (!bp)
533 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
534 bp = (char *) xmalloc (malloc_size);
536 tc_search_point = bp1 = bp;
538 if (indirect)
539 /* Copy the data from the environment variable. */
541 strcpy (bp, tcenv);
542 bp1 += strlen (tcenv);
545 while (term)
547 /* Scan the file, reading it via buf, till find start of main entry. */
548 if (scan_file (term, fd, &buf) == 0)
550 close (fd);
551 free (buf.beg);
552 if (malloc_size)
553 free (bp);
554 return 0;
557 /* Free old `term' if appropriate. */
558 if (term != name)
559 free (term);
561 /* If BP is malloc'd by us, make sure it is big enough. */
562 if (malloc_size)
564 malloc_size = bp1 - bp + buf.size;
565 termcap_name = (char *) xrealloc (bp, malloc_size);
566 bp1 += termcap_name - bp;
567 tc_search_point += termcap_name - bp;
568 bp = termcap_name;
571 /* Copy the line of the entry from buf into bp. */
572 termcap_name = buf.ptr;
573 while ((*bp1++ = c = *termcap_name++) && c != '\n')
574 /* Drop out any \ newline sequence. */
575 if (c == '\\' && *termcap_name == '\n')
577 bp1--;
578 termcap_name++;
580 *bp1 = '\0';
582 /* Does this entry refer to another terminal type's entry?
583 If something is found, copy it into heap and null-terminate it. */
584 tc_search_point = find_capability (tc_search_point, "tc");
585 term = tgetst1 (tc_search_point, (char **) 0);
588 close (fd);
589 free (buf.beg);
591 if (malloc_size)
592 bp = (char *) xrealloc (bp, bp1 - bp + 1);
594 ret:
595 term_entry = bp;
596 return 1;
599 /* Given file open on FD and buffer BUFP,
600 scan the file from the beginning until a line is found
601 that starts the entry for terminal type STR.
602 Return 1 if successful, with that line in BUFP,
603 or 0 if no entry is found in the file. */
605 static int
606 scan_file (str, fd, bufp)
607 char *str;
608 int fd;
609 register struct termcap_buffer *bufp;
611 register char *end;
613 bufp->ptr = bufp->beg;
614 bufp->full = 0;
615 bufp->ateof = 0;
616 *bufp->ptr = '\0';
618 lseek (fd, 0L, 0);
620 while (!bufp->ateof)
622 /* Read a line into the buffer. */
623 end = NULL;
626 /* if it is continued, append another line to it,
627 until a non-continued line ends. */
628 end = gobble_line (fd, bufp, end);
630 while (!bufp->ateof && end[-2] == '\\');
632 if (*bufp->ptr != '#'
633 && name_match (bufp->ptr, str))
634 return 1;
636 /* Discard the line just processed. */
637 bufp->ptr = end;
639 return 0;
642 /* Return nonzero if NAME is one of the names specified
643 by termcap entry LINE. */
645 static int
646 name_match (line, name)
647 char *line, *name;
649 register char *tem;
651 if (!compare_contin (line, name))
652 return 1;
653 /* This line starts an entry. Is it the right one? */
654 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
655 if (*tem == '|' && !compare_contin (tem + 1, name))
656 return 1;
658 return 0;
661 static int
662 compare_contin (str1, str2)
663 register char *str1, *str2;
665 register int c1, c2;
666 while (1)
668 c1 = *str1++;
669 c2 = *str2++;
670 while (c1 == '\\' && *str1 == '\n')
672 str1++;
673 while ((c1 = *str1++) == ' ' || c1 == '\t');
675 if (c2 == '\0')
677 /* End of type being looked up. */
678 if (c1 == '|' || c1 == ':')
679 /* If end of name in data base, we win. */
680 return 0;
681 else
682 return 1;
684 else if (c1 != c2)
685 return 1;
689 /* Make sure that the buffer <- BUFP contains a full line
690 of the file open on FD, starting at the place BUFP->ptr
691 points to. Can read more of the file, discard stuff before
692 BUFP->ptr, or make the buffer bigger.
694 Return the pointer to after the newline ending the line,
695 or to the end of the file, if there is no newline to end it.
697 Can also merge on continuation lines. If APPEND_END is
698 non-null, it points past the newline of a line that is
699 continued; we add another line onto it and regard the whole
700 thing as one line. The caller decides when a line is continued. */
702 static char *
703 gobble_line (fd, bufp, append_end)
704 int fd;
705 register struct termcap_buffer *bufp;
706 char *append_end;
708 register char *end;
709 register int nread;
710 register char *buf = bufp->beg;
711 register char *tem;
713 if (!append_end)
714 append_end = bufp->ptr;
716 while (1)
718 end = append_end;
719 while (*end && *end != '\n') end++;
720 if (*end)
721 break;
722 if (bufp->ateof)
723 return buf + bufp->full;
724 if (bufp->ptr == buf)
726 if (bufp->full == bufp->size)
728 bufp->size *= 2;
729 /* Add 1 to size to ensure room for terminating null. */
730 tem = (char *) xrealloc (buf, bufp->size + 1);
731 bufp->ptr = (bufp->ptr - buf) + tem;
732 append_end = (append_end - buf) + tem;
733 bufp->beg = buf = tem;
736 else
738 append_end -= bufp->ptr - buf;
739 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
740 bufp->ptr = buf;
742 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
743 bufp->ateof = 1;
744 bufp->full += nread;
745 buf[bufp->full] = '\0';
747 return end + 1;
750 #ifdef TEST
752 #ifdef NULL
753 #undef NULL
754 #endif
756 #include <stdio.h>
758 main (argc, argv)
759 int argc;
760 char **argv;
762 char *term;
763 char *buf;
765 term = argv[1];
766 printf ("TERM: %s\n", term);
768 buf = (char *) tgetent (0, term);
769 if ((int) buf <= 0)
771 printf ("No entry.\n");
772 return 0;
775 printf ("Entry: %s\n", buf);
777 tprint ("cm");
778 tprint ("AL");
780 printf ("co: %d\n", tgetnum ("co"));
781 printf ("am: %d\n", tgetflag ("am"));
784 tprint (cap)
785 char *cap;
787 char *x = tgetstr (cap, 0);
788 register char *y;
790 printf ("%s: ", cap);
791 if (x)
793 for (y = x; *y; y++)
794 if (*y <= ' ' || *y == 0177)
795 printf ("\\%0o", *y);
796 else
797 putchar (*y);
798 free (x);
800 else
801 printf ("none");
802 putchar ('\n');
805 #endif /* TEST */