Fix last change.
[emacs.git] / src / termcap.c
blob9b4a7f657f6f46ca506ea9dd3766135dab41e36b
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986, 1993, 1994, 1995, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA. */
20 /* Emacs config.h may rename various library functions such as malloc. */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #ifdef emacs
27 #include <setjmp.h>
28 #include <lisp.h> /* xmalloc is here */
29 /* Get the O_* definitions for open et al. */
30 #include <sys/file.h>
31 #ifdef HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
38 #else /* not emacs */
40 #ifdef STDC_HEADERS
41 #include <stdlib.h>
42 #include <string.h>
43 #else
44 char *getenv ();
45 char *malloc ();
46 char *realloc ();
47 #endif
49 /* Do this after the include, in case string.h prototypes bcopy. */
50 #if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
51 #define bcopy(s, d, n) memcpy ((d), (s), (n))
52 #endif
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #ifdef HAVE_FCNTL_H
58 #include <fcntl.h>
59 #endif
61 #endif /* not emacs */
63 #ifndef NULL
64 #define NULL (char *) 0
65 #endif
67 #ifndef O_RDONLY
68 #define O_RDONLY 0
69 #endif
71 /* BUFSIZE is the initial size allocated for the buffer
72 for reading the termcap file.
73 It is not a limit.
74 Make it large normally for speed.
75 Make it variable when debugging, so can exercise
76 increasing the space dynamically. */
78 #ifndef BUFSIZE
79 #ifdef DEBUG
80 #define BUFSIZE bufsize
82 int bufsize = 128;
83 #else
84 #define BUFSIZE 2048
85 #endif
86 #endif
88 #ifndef TERMCAP_FILE
89 #define TERMCAP_FILE "/etc/termcap"
90 #endif
92 #ifndef emacs
93 static void
94 memory_out ()
96 write (2, "virtual memory exhausted\n", 25);
97 exit (1);
100 static char *
101 xmalloc (size)
102 unsigned size;
104 register char *tem = malloc (size);
106 if (!tem)
107 memory_out ();
108 return tem;
111 static char *
112 xrealloc (ptr, size)
113 char *ptr;
114 unsigned size;
116 register char *tem = realloc (ptr, size);
118 if (!tem)
119 memory_out ();
120 return tem;
122 #endif /* not emacs */
124 /* Looking up capabilities in the entry already found. */
126 /* The pointer to the data made by tgetent is left here
127 for tgetnum, tgetflag and tgetstr to find. */
128 static char *term_entry;
130 static char *tgetst1 ();
132 /* Search entry BP for capability CAP.
133 Return a pointer to the capability (in BP) if found,
134 0 if not found. */
136 static char *
137 find_capability (bp, cap)
138 register char *bp, *cap;
140 for (; *bp; bp++)
141 if (bp[0] == ':'
142 && bp[1] == cap[0]
143 && bp[2] == cap[1])
144 return &bp[4];
145 return NULL;
149 tgetnum (cap)
150 char *cap;
152 register char *ptr = find_capability (term_entry, cap);
153 if (!ptr || ptr[-1] != '#')
154 return -1;
155 return atoi (ptr);
159 tgetflag (cap)
160 char *cap;
162 register char *ptr = find_capability (term_entry, cap);
163 return ptr && ptr[-1] == ':';
166 /* Look up a string-valued capability CAP.
167 If AREA is non-null, it points to a pointer to a block in which
168 to store the string. That pointer is advanced over the space used.
169 If AREA is null, space is allocated with `malloc'. */
171 char *
172 tgetstr (cap, area)
173 char *cap;
174 char **area;
176 register char *ptr = find_capability (term_entry, cap);
177 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
178 return NULL;
179 return tgetst1 (ptr, area);
182 #ifdef IS_EBCDIC_HOST
183 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
184 gives meaning of character following \, or a space if no special meaning.
185 Sixteen characters per line within the string. */
187 static const char esctab[]
188 = " \057\026 \047\014 \
189 \025 \015 \
190 \005 \013 \
192 #else
193 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
194 gives meaning of character following \, or a space if no special meaning.
195 Eight characters per line within the string. */
197 static const char esctab[]
198 = " \007\010 \033\014 \
199 \012 \
200 \015 \011 \013 \
202 #endif
204 /* PTR points to a string value inside a termcap entry.
205 Copy that value, processing \ and ^ abbreviations,
206 into the block that *AREA points to,
207 or to newly allocated storage if AREA is NULL.
208 Return the address to which we copied the value,
209 or NULL if PTR is NULL. */
211 static char *
212 tgetst1 (ptr, area)
213 char *ptr;
214 char **area;
216 register char *p, *r;
217 register int c;
218 register int size;
219 char *ret;
220 register int c1;
222 if (!ptr)
223 return NULL;
225 /* `ret' gets address of where to store the string. */
226 if (!area)
228 /* Compute size of block needed (may overestimate). */
229 p = ptr;
230 while ((c = *p++) && c != ':' && c != '\n')
232 ret = (char *) xmalloc (p - ptr + 1);
234 else
235 ret = *area;
237 /* Copy the string value, stopping at null or colon.
238 Also process ^ and \ abbreviations. */
239 p = ptr;
240 r = ret;
241 while ((c = *p++) && c != ':' && c != '\n')
243 if (c == '^')
245 c = *p++;
246 if (c == '?')
247 c = 0177;
248 else
249 c &= 037;
251 else if (c == '\\')
253 c = *p++;
254 if (c >= '0' && c <= '7')
256 c -= '0';
257 size = 0;
259 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
261 c *= 8;
262 c += c1 - '0';
263 p++;
266 #ifdef IS_EBCDIC_HOST
267 else if (c >= 0200 && c < 0360)
269 c1 = esctab[(c & ~0100) - 0200];
270 if (c1 != ' ')
271 c = c1;
273 #else
274 else if (c >= 0100 && c < 0200)
276 c1 = esctab[(c & ~040) - 0100];
277 if (c1 != ' ')
278 c = c1;
280 #endif
282 *r++ = c;
285 /* Sometimes entries have "%pN" which means use parameter N in the
286 next %-substitution. If all such N are continuous in the range
287 [1,9] we can remove each "%pN" because they are redundant, thus
288 reducing bandwidth requirements. True, Emacs is well beyond the
289 days of 150baud teletypes, but some of its users aren't much so.
291 This pass could probably be integrated into the one above but
292 abbreviation expansion makes that effort a little more hairy than
293 its worth; this is cleaner. */
295 register int last_p_param = 0;
296 int remove_p_params = 1;
297 struct { char *beg; int len; } cut[11];
299 for (cut[0].beg = p = ret; p < r - 3; p++)
301 if (!remove_p_params)
302 break;
303 if (*p == '%' && *(p + 1) == 'p')
305 if (*(p + 2) - '0' == 1 + last_p_param)
307 cut[last_p_param].len = p - cut[last_p_param].beg;
308 last_p_param++;
309 p += 3;
310 cut[last_p_param].beg = p;
312 else /* not continuous: bail */
313 remove_p_params = 0;
314 if (last_p_param > 10) /* too many: bail */
315 remove_p_params = 0;
318 if (remove_p_params && last_p_param)
320 register int i;
321 char *wp;
323 cut[last_p_param].len = r - cut[last_p_param].beg;
324 for (i = 0, wp = ret; i <= last_p_param; wp += cut[i++].len)
325 bcopy (cut[i].beg, wp, cut[i].len);
326 r = wp;
330 *r = '\0';
331 /* Update *AREA. */
332 if (area)
333 *area = r + 1;
334 return ret;
337 /* Outputting a string with padding. */
339 #ifndef emacs
340 short ospeed;
341 /* If OSPEED is 0, we use this as the actual baud rate. */
342 int tputs_baud_rate;
343 #endif
345 char PC;
347 #ifndef emacs
348 /* Actual baud rate if positive;
349 - baud rate / 100 if negative. */
351 static const int speeds[] =
353 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
354 -18, -24, -48, -96, -192, -288, -384, -576, -1152
357 #endif /* not emacs */
359 void
360 tputs (str, nlines, outfun)
361 register char *str;
362 int nlines;
363 register int (*outfun) ();
365 register int padcount = 0;
366 register int speed;
368 #ifdef emacs
369 extern EMACS_INT baud_rate;
370 speed = baud_rate;
371 /* For quite high speeds, convert to the smaller
372 units to avoid overflow. */
373 if (speed > 10000)
374 speed = - speed / 100;
375 #else
376 if (ospeed == 0)
377 speed = tputs_baud_rate;
378 else
379 speed = speeds[ospeed];
380 #endif
382 if (!str)
383 return;
385 while (*str >= '0' && *str <= '9')
387 padcount += *str++ - '0';
388 padcount *= 10;
390 if (*str == '.')
392 str++;
393 padcount += *str++ - '0';
395 if (*str == '*')
397 str++;
398 padcount *= nlines;
400 while (*str)
401 (*outfun) (*str++);
403 /* PADCOUNT is now in units of tenths of msec.
404 SPEED is measured in characters per 10 seconds
405 or in characters per .1 seconds (if negative).
406 We use the smaller units for larger speeds to avoid overflow. */
407 padcount *= speed;
408 padcount += 500;
409 padcount /= 1000;
410 if (speed < 0)
411 padcount = -padcount;
412 else
414 padcount += 50;
415 padcount /= 100;
418 while (padcount-- > 0)
419 (*outfun) (PC);
422 /* Finding the termcap entry in the termcap data base. */
424 struct termcap_buffer
426 char *beg;
427 int size;
428 char *ptr;
429 int ateof;
430 int full;
433 /* Forward declarations of static functions. */
435 static int scan_file ();
436 static char *gobble_line ();
437 static int compare_contin ();
438 static int name_match ();
440 #ifdef MSDOS /* MW, May 1993 */
441 static int
442 valid_filename_p (fn)
443 char *fn;
445 return *fn == '/' || fn[1] == ':';
447 #else
448 #define valid_filename_p(fn) (*(fn) == '/')
449 #endif
451 /* Find the termcap entry data for terminal type NAME
452 and store it in the block that BP points to.
453 Record its address for future use.
455 If BP is null, space is dynamically allocated.
457 Return -1 if there is some difficulty accessing the data base
458 of terminal types,
459 0 if the data base is accessible but the type NAME is not defined
460 in it, and some other value otherwise. */
463 tgetent (bp, name)
464 char *bp, *name;
466 register char *termcap_name;
467 register int fd;
468 struct termcap_buffer buf;
469 register char *bp1;
470 char *tc_search_point;
471 char *term;
472 int malloc_size = 0;
473 register int c;
474 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
475 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
476 int filep;
478 #ifdef INTERNAL_TERMINAL
479 /* For the internal terminal we don't want to read any termcap file,
480 so fake it. */
481 if (!strcmp (name, "internal"))
483 term = INTERNAL_TERMINAL;
484 if (!bp)
486 malloc_size = 1 + strlen (term);
487 bp = (char *) xmalloc (malloc_size);
489 strcpy (bp, term);
490 goto ret;
492 #endif /* INTERNAL_TERMINAL */
494 /* For compatibility with programs like `less' that want to
495 put data in the termcap buffer themselves as a fallback. */
496 if (bp)
497 term_entry = bp;
499 termcap_name = getenv ("TERMCAP");
500 if (termcap_name && *termcap_name == '\0')
501 termcap_name = NULL;
502 #if defined (MSDOS) && !defined (TEST)
503 if (termcap_name && (*termcap_name == '\\'
504 || *termcap_name == '/'
505 || termcap_name[1] == ':'))
506 dostounix_filename(termcap_name);
507 #endif
509 filep = termcap_name && valid_filename_p (termcap_name);
511 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
512 it is a file name to use instead of /etc/termcap.
513 If it is non-null and does not start with /,
514 it is the entry itself, but only if
515 the name the caller requested matches the TERM variable. */
517 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
519 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
520 if (!indirect)
522 if (!bp)
523 bp = termcap_name;
524 else
525 strcpy (bp, termcap_name);
526 goto ret;
528 else
529 { /* It has tc=. Need to read /etc/termcap. */
530 tcenv = termcap_name;
531 termcap_name = NULL;
535 if (!termcap_name || !filep)
536 termcap_name = TERMCAP_FILE;
538 /* Here we know we must search a file and termcap_name has its name. */
540 #ifdef MSDOS
541 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
542 #else
543 fd = open (termcap_name, O_RDONLY, 0);
544 #endif
545 if (fd < 0)
546 return -1;
548 buf.size = BUFSIZE;
549 /* Add 1 to size to ensure room for terminating null. */
550 buf.beg = (char *) xmalloc (buf.size + 1);
551 term = indirect ? indirect : name;
553 if (!bp)
555 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
556 bp = (char *) xmalloc (malloc_size);
558 tc_search_point = bp1 = bp;
560 if (indirect)
561 /* Copy the data from the environment variable. */
563 strcpy (bp, tcenv);
564 bp1 += strlen (tcenv);
567 while (term)
569 /* Scan the file, reading it via buf, till find start of main entry. */
570 if (scan_file (term, fd, &buf) == 0)
572 close (fd);
573 free (buf.beg);
574 if (malloc_size)
575 free (bp);
576 return 0;
579 /* Free old `term' if appropriate. */
580 if (term != name)
581 free (term);
583 /* If BP is malloc'd by us, make sure it is big enough. */
584 if (malloc_size)
586 int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
587 malloc_size = offset1 + buf.size;
588 bp = termcap_name = (char *) xrealloc (bp, malloc_size);
589 bp1 = termcap_name + offset1;
590 tc_search_point = termcap_name + offset2;
593 /* Copy the line of the entry from buf into bp. */
594 termcap_name = buf.ptr;
595 while ((*bp1++ = c = *termcap_name++) && c != '\n')
596 /* Drop out any \ newline sequence. */
597 if (c == '\\' && *termcap_name == '\n')
599 bp1--;
600 termcap_name++;
602 *bp1 = '\0';
604 /* Does this entry refer to another terminal type's entry?
605 If something is found, copy it into heap and null-terminate it. */
606 tc_search_point = find_capability (tc_search_point, "tc");
607 term = tgetst1 (tc_search_point, (char **) 0);
610 close (fd);
611 free (buf.beg);
613 if (malloc_size)
614 bp = (char *) xrealloc (bp, bp1 - bp + 1);
616 ret:
617 term_entry = bp;
618 return 1;
621 /* Given file open on FD and buffer BUFP,
622 scan the file from the beginning until a line is found
623 that starts the entry for terminal type STR.
624 Return 1 if successful, with that line in BUFP,
625 or 0 if no entry is found in the file. */
627 static int
628 scan_file (str, fd, bufp)
629 char *str;
630 int fd;
631 register struct termcap_buffer *bufp;
633 register char *end;
635 bufp->ptr = bufp->beg;
636 bufp->full = 0;
637 bufp->ateof = 0;
638 *bufp->ptr = '\0';
640 lseek (fd, 0L, 0);
642 while (!bufp->ateof)
644 /* Read a line into the buffer. */
645 end = NULL;
648 /* if it is continued, append another line to it,
649 until a non-continued line ends. */
650 end = gobble_line (fd, bufp, end);
652 while (!bufp->ateof && end[-2] == '\\');
654 if (*bufp->ptr != '#'
655 && name_match (bufp->ptr, str))
656 return 1;
658 /* Discard the line just processed. */
659 bufp->ptr = end;
661 return 0;
664 /* Return nonzero if NAME is one of the names specified
665 by termcap entry LINE. */
667 static int
668 name_match (line, name)
669 char *line, *name;
671 register char *tem;
673 if (!compare_contin (line, name))
674 return 1;
675 /* This line starts an entry. Is it the right one? */
676 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
677 if (*tem == '|' && !compare_contin (tem + 1, name))
678 return 1;
680 return 0;
683 static int
684 compare_contin (str1, str2)
685 register char *str1, *str2;
687 register int c1, c2;
688 while (1)
690 c1 = *str1++;
691 c2 = *str2++;
692 while (c1 == '\\' && *str1 == '\n')
694 str1++;
695 while ((c1 = *str1++) == ' ' || c1 == '\t');
697 if (c2 == '\0')
699 /* End of type being looked up. */
700 if (c1 == '|' || c1 == ':')
701 /* If end of name in data base, we win. */
702 return 0;
703 else
704 return 1;
706 else if (c1 != c2)
707 return 1;
711 /* Make sure that the buffer <- BUFP contains a full line
712 of the file open on FD, starting at the place BUFP->ptr
713 points to. Can read more of the file, discard stuff before
714 BUFP->ptr, or make the buffer bigger.
716 Return the pointer to after the newline ending the line,
717 or to the end of the file, if there is no newline to end it.
719 Can also merge on continuation lines. If APPEND_END is
720 non-null, it points past the newline of a line that is
721 continued; we add another line onto it and regard the whole
722 thing as one line. The caller decides when a line is continued. */
724 static char *
725 gobble_line (fd, bufp, append_end)
726 int fd;
727 register struct termcap_buffer *bufp;
728 char *append_end;
730 register char *end;
731 register int nread;
732 register char *buf = bufp->beg;
733 register char *tem;
735 if (!append_end)
736 append_end = bufp->ptr;
738 while (1)
740 end = append_end;
741 while (*end && *end != '\n') end++;
742 if (*end)
743 break;
744 if (bufp->ateof)
745 return buf + bufp->full;
746 if (bufp->ptr == buf)
748 if (bufp->full == bufp->size)
750 bufp->size *= 2;
751 /* Add 1 to size to ensure room for terminating null. */
752 tem = (char *) xrealloc (buf, bufp->size + 1);
753 bufp->ptr = (bufp->ptr - buf) + tem;
754 append_end = (append_end - buf) + tem;
755 bufp->beg = buf = tem;
758 else
760 append_end -= bufp->ptr - buf;
761 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
762 bufp->ptr = buf;
764 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
765 bufp->ateof = 1;
766 bufp->full += nread;
767 buf[bufp->full] = '\0';
769 return end + 1;
772 #ifdef TEST
774 #ifdef NULL
775 #undef NULL
776 #endif
778 #include <stdio.h>
780 main (argc, argv)
781 int argc;
782 char **argv;
784 char *term;
785 char *buf;
787 term = argv[1];
788 printf ("TERM: %s\n", term);
790 buf = (char *) tgetent (0, term);
791 if ((int) buf <= 0)
793 printf ("No entry.\n");
794 return 0;
797 printf ("Entry: %s\n", buf);
799 tprint ("cm");
800 tprint ("AL");
802 printf ("co: %d\n", tgetnum ("co"));
803 printf ("am: %d\n", tgetflag ("am"));
806 tprint (cap)
807 char *cap;
809 char *x = tgetstr (cap, 0);
810 register char *y;
812 printf ("%s: ", cap);
813 if (x)
815 for (y = x; *y; y++)
816 if (*y <= ' ' || *y == 0177)
817 printf ("\\%0o", *y);
818 else
819 putchar (*y);
820 free (x);
822 else
823 printf ("none");
824 putchar ('\n');
827 #endif /* TEST */
829 /* arch-tag: c2e8d427-2271-4fac-95fe-411857238b80
830 (do not change this comment) */