Fix previous change.
[emacs.git] / src / termcap.c
blobbcd1ece8cfd41174502af505f944a6bc37c14965
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986, 1993, 1994 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
21 #include <config.h>
23 /* Get the O_* definitions for open et al. */
24 #include <sys/file.h>
25 #ifdef USG5
26 #include <fcntl.h>
27 #endif
29 #else /* not HAVE_CONFIG_H */
31 #if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
32 #define bcopy(s, d, n) memcpy ((d), (s), (n))
33 #endif
35 #ifdef STDC_HEADERS
36 #include <stdlib.h>
37 #include <string.h>
38 #else
39 char *getenv ();
40 char *malloc ();
41 char *realloc ();
42 #endif
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #ifdef _POSIX_VERSION
48 #include <fcntl.h>
49 #endif
51 #endif /* not HAVE_CONFIG_H */
53 #ifndef NULL
54 #define NULL (char *) 0
55 #endif
57 #ifndef O_RDONLY
58 #define O_RDONLY 0
59 #endif
61 /* BUFSIZE is the initial size allocated for the buffer
62 for reading the termcap file.
63 It is not a limit.
64 Make it large normally for speed.
65 Make it variable when debugging, so can exercise
66 increasing the space dynamically. */
68 #ifndef BUFSIZE
69 #ifdef DEBUG
70 #define BUFSIZE bufsize
72 int bufsize = 128;
73 #else
74 #define BUFSIZE 2048
75 #endif
76 #endif
78 #ifndef TERMCAP_NAME
79 #define TERMCAP_NAME "/etc/termcap"
80 #endif
82 #ifndef emacs
83 static void
84 memory_out ()
86 write (2, "virtual memory exhausted\n", 25);
87 exit (1);
90 static char *
91 xmalloc (size)
92 unsigned size;
94 register char *tem = malloc (size);
96 if (!tem)
97 memory_out ();
98 return tem;
101 static char *
102 xrealloc (ptr, size)
103 char *ptr;
104 unsigned size;
106 register char *tem = realloc (ptr, size);
108 if (!tem)
109 memory_out ();
110 return tem;
112 #endif /* not emacs */
114 /* Looking up capabilities in the entry already found. */
116 /* The pointer to the data made by tgetent is left here
117 for tgetnum, tgetflag and tgetstr to find. */
118 static char *term_entry;
120 static char *tgetst1 ();
122 /* Search entry BP for capability CAP.
123 Return a pointer to the capability (in BP) if found,
124 0 if not found. */
126 static char *
127 find_capability (bp, cap)
128 register char *bp, *cap;
130 for (; *bp; bp++)
131 if (bp[0] == ':'
132 && bp[1] == cap[0]
133 && bp[2] == cap[1])
134 return &bp[4];
135 return NULL;
139 tgetnum (cap)
140 char *cap;
142 register char *ptr = find_capability (term_entry, cap);
143 if (!ptr || ptr[-1] != '#')
144 return -1;
145 return atoi (ptr);
149 tgetflag (cap)
150 char *cap;
152 register char *ptr = find_capability (term_entry, cap);
153 return ptr && ptr[-1] == ':';
156 /* Look up a string-valued capability CAP.
157 If AREA is non-null, it points to a pointer to a block in which
158 to store the string. That pointer is advanced over the space used.
159 If AREA is null, space is allocated with `malloc'. */
161 char *
162 tgetstr (cap, area)
163 char *cap;
164 char **area;
166 register char *ptr = find_capability (term_entry, cap);
167 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
168 return NULL;
169 return tgetst1 (ptr, area);
172 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
173 gives meaning of character following \, or a space if no special meaning.
174 Eight characters per line within the string. */
176 static char esctab[]
177 = " \007\010 \033\014 \
178 \012 \
179 \015 \011 \013 \
182 /* PTR points to a string value inside a termcap entry.
183 Copy that value, processing \ and ^ abbreviations,
184 into the block that *AREA points to,
185 or to newly allocated storage if AREA is NULL.
186 Return the address to which we copied the value,
187 or NULL if PTR is NULL. */
189 static char *
190 tgetst1 (ptr, area)
191 char *ptr;
192 char **area;
194 register char *p, *r;
195 register int c;
196 register int size;
197 char *ret;
198 register int c1;
200 if (!ptr)
201 return NULL;
203 /* `ret' gets address of where to store the string. */
204 if (!area)
206 /* Compute size of block needed (may overestimate). */
207 p = ptr;
208 while ((c = *p++) && c != ':' && c != '\n')
210 ret = (char *) xmalloc (p - ptr + 1);
212 else
213 ret = *area;
215 /* Copy the string value, stopping at null or colon.
216 Also process ^ and \ abbreviations. */
217 p = ptr;
218 r = ret;
219 while ((c = *p++) && c != ':' && c != '\n')
221 if (c == '^')
223 c = *p++;
224 if (c == '?')
225 c = 0177;
226 else
227 c &= 037;
229 else if (c == '\\')
231 c = *p++;
232 if (c >= '0' && c <= '7')
234 c -= '0';
235 size = 0;
237 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
239 c *= 8;
240 c += c1 - '0';
241 p++;
244 else if (c >= 0100 && c < 0200)
246 c1 = esctab[(c & ~040) - 0100];
247 if (c1 != ' ')
248 c = c1;
251 *r++ = c;
253 *r = '\0';
254 /* Update *AREA. */
255 if (area)
256 *area = r + 1;
257 return ret;
260 /* Outputting a string with padding. */
262 short ospeed;
263 /* If OSPEED is 0, we use this as the actual baud rate. */
264 int tputs_baud_rate;
265 char PC;
267 /* Actual baud rate if positive;
268 - baud rate / 100 if negative. */
270 static short speeds[] =
272 #ifdef VMS
273 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
274 -20, -24, -36, -48, -72, -96, -192
275 #else /* not VMS */
276 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
277 -18, -24, -48, -96, -192, -384
278 #endif /* not VMS */
281 void
282 tputs (str, nlines, outfun)
283 register char *str;
284 int nlines;
285 register int (*outfun) ();
287 register int padcount = 0;
288 register int speed;
290 #ifdef emacs
291 extern baud_rate;
292 speed = baud_rate;
293 #else
294 if (ospeed == 0)
295 speed = tputs_baud_rate;
296 else
297 speed = speeds[ospeed];
298 #endif
300 if (!str)
301 return;
303 while (*str >= '0' && *str <= '9')
305 padcount += *str++ - '0';
306 padcount *= 10;
308 if (*str == '.')
310 str++;
311 padcount += *str++ - '0';
313 if (*str == '*')
315 str++;
316 padcount *= nlines;
318 while (*str)
319 (*outfun) (*str++);
321 /* padcount is now in units of tenths of msec. */
322 padcount *= speeds[ospeed];
323 padcount += 500;
324 padcount /= 1000;
325 if (speeds[ospeed] < 0)
326 padcount = -padcount;
327 else
329 padcount += 50;
330 padcount /= 100;
333 while (padcount-- > 0)
334 (*outfun) (PC);
337 /* Finding the termcap entry in the termcap data base. */
339 struct buffer
341 char *beg;
342 int size;
343 char *ptr;
344 int ateof;
345 int full;
348 /* Forward declarations of static functions. */
350 static int scan_file ();
351 static char *gobble_line ();
352 static int compare_contin ();
353 static int name_match ();
355 #ifdef VMS
357 #include <rmsdef.h>
358 #include <fab.h>
359 #include <nam.h>
361 static int
362 valid_filename_p (fn)
363 char *fn;
365 struct FAB fab = cc$rms_fab;
366 struct NAM nam = cc$rms_nam;
367 char esa[NAM$C_MAXRSS];
369 fab.fab$l_fna = fn;
370 fab.fab$b_fns = strlen(fn);
371 fab.fab$l_nam = &nam;
372 fab.fab$l_fop = FAB$M_NAM;
374 nam.nam$l_esa = esa;
375 nam.nam$b_ess = sizeof esa;
377 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
380 #else /* !VMS */
382 #ifdef MSDOS /* MW, May 1993 */
383 static int
384 valid_filename_p (fn)
385 char *fn;
387 return *fn == '/' || fn[1] == ':';
389 #else
390 #define valid_filename_p(fn) (*(fn) == '/')
391 #endif
393 #endif /* !VMS */
395 /* Find the termcap entry data for terminal type NAME
396 and store it in the block that BP points to.
397 Record its address for future use.
399 If BP is null, space is dynamically allocated.
401 Return -1 if there is some difficulty accessing the data base
402 of terminal types,
403 0 if the data base is accessible but the type NAME is not defined
404 in it, and some other value otherwise. */
407 tgetent (bp, name)
408 char *bp, *name;
410 register char *termcap_name;
411 register int fd;
412 struct buffer buf;
413 register char *bp1;
414 char *bp2;
415 char *term;
416 int malloc_size = 0;
417 register int c;
418 char *tcenv; /* TERMCAP value, if it contains :tc=. */
419 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
420 int filep;
422 #ifdef INTERNAL_TERMINAL
423 /* For the internal terminal we don't want to read any termcap file,
424 so fake it. */
425 if (!strcmp (name, "internal"))
427 term = INTERNAL_TERMINAL;
428 if (!bp)
430 malloc_size = 1 + strlen (term);
431 bp = (char *) xmalloc (malloc_size);
433 strcpy (bp, term);
434 goto ret;
436 #endif /* INTERNAL_TERMINAL */
438 termcap_name = getenv ("TERMCAP");
439 if (termcap_name && *termcap_name == '\0')
440 termcap_name = NULL;
441 #if defined (MSDOS) && !defined (TEST)
442 if (termcap_name && (*termcap_name == '\\'
443 || *termcap_name == '/'
444 || termcap_name[1] == ':'))
445 dostounix_filename(termcap_name);
446 #endif
448 filep = termcap_name && valid_filename_p (termcap_name);
450 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
451 it is a file name to use instead of /etc/termcap.
452 If it is non-null and does not start with /,
453 it is the entry itself, but only if
454 the name the caller requested matches the TERM variable. */
456 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
458 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
459 if (!indirect)
461 if (!bp)
462 bp = termcap_name;
463 else
464 strcpy (bp, termcap_name);
465 goto ret;
467 else
468 { /* It has tc=. Need to read /etc/termcap. */
469 tcenv = termcap_name;
470 termcap_name = NULL;
474 if (!termcap_name || !filep)
475 termcap_name = TERMCAP_NAME;
477 /* Here we know we must search a file and termcap_name has its name. */
479 #ifdef MSDOS
480 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
481 #else
482 fd = open (termcap_name, O_RDONLY, 0);
483 #endif
484 if (fd < 0)
485 return -1;
487 buf.size = BUFSIZE;
488 /* Add 1 to size to ensure room for terminating null. */
489 buf.beg = (char *) xmalloc (buf.size + 1);
490 term = indirect ? indirect : name;
492 if (!bp)
494 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
495 bp = (char *) xmalloc (malloc_size);
497 bp1 = bp;
499 if (indirect)
500 /* Copy the data from the environment variable. */
502 strcpy (bp, tcenv);
503 bp1 += strlen (tcenv);
506 while (term)
508 /* Scan the file, reading it via buf, till find start of main entry. */
509 if (scan_file (term, fd, &buf) == 0)
511 close (fd);
512 free (buf.beg);
513 if (malloc_size)
514 free (bp);
515 return 0;
518 /* Free old `term' if appropriate. */
519 if (term != name)
520 free (term);
522 /* If BP is malloc'd by us, make sure it is big enough. */
523 if (malloc_size)
525 malloc_size = bp1 - bp + buf.size;
526 termcap_name = (char *) xrealloc (bp, malloc_size);
527 bp1 += termcap_name - bp;
528 bp = termcap_name;
531 bp2 = bp1;
533 /* Copy the line of the entry from buf into bp. */
534 termcap_name = buf.ptr;
535 while ((*bp1++ = c = *termcap_name++) && c != '\n')
536 /* Drop out any \ newline sequence. */
537 if (c == '\\' && *termcap_name == '\n')
539 bp1--;
540 termcap_name++;
542 *bp1 = '\0';
544 /* Does this entry refer to another terminal type's entry?
545 If something is found, copy it into heap and null-terminate it. */
546 term = tgetst1 (find_capability (bp2, "tc"), (char **) 0);
549 close (fd);
550 free (buf.beg);
552 if (malloc_size)
553 bp = (char *) xrealloc (bp, bp1 - bp + 1);
555 ret:
556 term_entry = bp;
557 if (malloc_size)
558 return (int) bp;
559 return 1;
562 /* Given file open on FD and buffer BUFP,
563 scan the file from the beginning until a line is found
564 that starts the entry for terminal type STR.
565 Return 1 if successful, with that line in BUFP,
566 or 0 if no entry is found in the file. */
568 static int
569 scan_file (str, fd, bufp)
570 char *str;
571 int fd;
572 register struct buffer *bufp;
574 register char *end;
576 bufp->ptr = bufp->beg;
577 bufp->full = 0;
578 bufp->ateof = 0;
579 *bufp->ptr = '\0';
581 lseek (fd, 0L, 0);
583 while (!bufp->ateof)
585 /* Read a line into the buffer. */
586 end = NULL;
589 /* if it is continued, append another line to it,
590 until a non-continued line ends. */
591 end = gobble_line (fd, bufp, end);
593 while (!bufp->ateof && end[-2] == '\\');
595 if (*bufp->ptr != '#'
596 && name_match (bufp->ptr, str))
597 return 1;
599 /* Discard the line just processed. */
600 bufp->ptr = end;
602 return 0;
605 /* Return nonzero if NAME is one of the names specified
606 by termcap entry LINE. */
608 static int
609 name_match (line, name)
610 char *line, *name;
612 register char *tem;
614 if (!compare_contin (line, name))
615 return 1;
616 /* This line starts an entry. Is it the right one? */
617 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
618 if (*tem == '|' && !compare_contin (tem + 1, name))
619 return 1;
621 return 0;
624 static int
625 compare_contin (str1, str2)
626 register char *str1, *str2;
628 register int c1, c2;
629 while (1)
631 c1 = *str1++;
632 c2 = *str2++;
633 while (c1 == '\\' && *str1 == '\n')
635 str1++;
636 while ((c1 = *str1++) == ' ' || c1 == '\t');
638 if (c2 == '\0')
640 /* End of type being looked up. */
641 if (c1 == '|' || c1 == ':')
642 /* If end of name in data base, we win. */
643 return 0;
644 else
645 return 1;
647 else if (c1 != c2)
648 return 1;
652 /* Make sure that the buffer <- BUFP contains a full line
653 of the file open on FD, starting at the place BUFP->ptr
654 points to. Can read more of the file, discard stuff before
655 BUFP->ptr, or make the buffer bigger.
657 Return the pointer to after the newline ending the line,
658 or to the end of the file, if there is no newline to end it.
660 Can also merge on continuation lines. If APPEND_END is
661 non-null, it points past the newline of a line that is
662 continued; we add another line onto it and regard the whole
663 thing as one line. The caller decides when a line is continued. */
665 static char *
666 gobble_line (fd, bufp, append_end)
667 int fd;
668 register struct buffer *bufp;
669 char *append_end;
671 register char *end;
672 register int nread;
673 register char *buf = bufp->beg;
674 register char *tem;
676 if (!append_end)
677 append_end = bufp->ptr;
679 while (1)
681 end = append_end;
682 while (*end && *end != '\n') end++;
683 if (*end)
684 break;
685 if (bufp->ateof)
686 return buf + bufp->full;
687 if (bufp->ptr == buf)
689 if (bufp->full == bufp->size)
691 bufp->size *= 2;
692 /* Add 1 to size to ensure room for terminating null. */
693 tem = (char *) xrealloc (buf, bufp->size + 1);
694 bufp->ptr = (bufp->ptr - buf) + tem;
695 append_end = (append_end - buf) + tem;
696 bufp->beg = buf = tem;
699 else
701 append_end -= bufp->ptr - buf;
702 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
703 bufp->ptr = buf;
705 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
706 bufp->ateof = 1;
707 bufp->full += nread;
708 buf[bufp->full] = '\0';
710 return end + 1;
713 #ifdef TEST
715 #ifdef NULL
716 #undef NULL
717 #endif
719 #include <stdio.h>
721 main (argc, argv)
722 int argc;
723 char **argv;
725 char *term;
726 char *buf;
728 term = argv[1];
729 printf ("TERM: %s\n", term);
731 buf = (char *) tgetent (0, term);
732 if ((int) buf <= 0)
734 printf ("No entry.\n");
735 return 0;
738 printf ("Entry: %s\n", buf);
740 tprint ("cm");
741 tprint ("AL");
743 printf ("co: %d\n", tgetnum ("co"));
744 printf ("am: %d\n", tgetflag ("am"));
747 tprint (cap)
748 char *cap;
750 char *x = tgetstr (cap, 0);
751 register char *y;
753 printf ("%s: ", cap);
754 if (x)
756 for (y = x; *y; y++)
757 if (*y <= ' ' || *y == 0177)
758 printf ("\\%0o", *y);
759 else
760 putchar (*y);
761 free (x);
763 else
764 printf ("none");
765 putchar ('\n');
768 #endif /* TEST */