* alloc.c: Do not define struct catchtag.
[bpt/emacs.git] / src / termcap.c
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.
4
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.
9
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.
14
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. */
19
20 /* Emacs config.h may rename various library functions such as malloc. */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #ifdef emacs
26
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
37
38 #else /* not emacs */
39
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
48
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
53
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #ifdef HAVE_FCNTL_H
58 #include <fcntl.h>
59 #endif
60
61 #endif /* not emacs */
62
63 #ifndef NULL
64 #define NULL (char *) 0
65 #endif
66
67 #ifndef O_RDONLY
68 #define O_RDONLY 0
69 #endif
70
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. */
77
78 #ifndef BUFSIZE
79 #ifdef DEBUG
80 #define BUFSIZE bufsize
81
82 int bufsize = 128;
83 #else
84 #define BUFSIZE 2048
85 #endif
86 #endif
87
88 #ifndef TERMCAP_FILE
89 #define TERMCAP_FILE "/etc/termcap"
90 #endif
91
92 #ifndef emacs
93 static void
94 memory_out ()
95 {
96 write (2, "virtual memory exhausted\n", 25);
97 exit (1);
98 }
99
100 static char *
101 xmalloc (size)
102 unsigned size;
103 {
104 register char *tem = malloc (size);
105
106 if (!tem)
107 memory_out ();
108 return tem;
109 }
110
111 static char *
112 xrealloc (ptr, size)
113 char *ptr;
114 unsigned size;
115 {
116 register char *tem = realloc (ptr, size);
117
118 if (!tem)
119 memory_out ();
120 return tem;
121 }
122 #endif /* not emacs */
123 \f
124 /* Looking up capabilities in the entry already found. */
125
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;
129
130 static char *tgetst1 ();
131
132 /* Search entry BP for capability CAP.
133 Return a pointer to the capability (in BP) if found,
134 0 if not found. */
135
136 static char *
137 find_capability (bp, cap)
138 register char *bp, *cap;
139 {
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;
146 }
147
148 int
149 tgetnum (cap)
150 char *cap;
151 {
152 register char *ptr = find_capability (term_entry, cap);
153 if (!ptr || ptr[-1] != '#')
154 return -1;
155 return atoi (ptr);
156 }
157
158 int
159 tgetflag (cap)
160 char *cap;
161 {
162 register char *ptr = find_capability (term_entry, cap);
163 return ptr && ptr[-1] == ':';
164 }
165
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'. */
170
171 char *
172 tgetstr (cap, area)
173 char *cap;
174 char **area;
175 {
176 register char *ptr = find_capability (term_entry, cap);
177 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
178 return NULL;
179 return tgetst1 (ptr, area);
180 }
181
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. */
186
187 static char esctab[]
188 = " \057\026 \047\014 \
189 \025 \015 \
190 \005 \013 \
191 ";
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. */
196
197 static char esctab[]
198 = " \007\010 \033\014 \
199 \012 \
200 \015 \011 \013 \
201 ";
202 #endif
203
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. */
210
211 static char *
212 tgetst1 (ptr, area)
213 char *ptr;
214 char **area;
215 {
216 register char *p, *r;
217 register int c;
218 register int size;
219 char *ret;
220 register int c1;
221
222 if (!ptr)
223 return NULL;
224
225 /* `ret' gets address of where to store the string. */
226 if (!area)
227 {
228 /* Compute size of block needed (may overestimate). */
229 p = ptr;
230 while ((c = *p++) && c != ':' && c != '\n')
231 ;
232 ret = (char *) xmalloc (p - ptr + 1);
233 }
234 else
235 ret = *area;
236
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')
242 {
243 if (c == '^')
244 {
245 c = *p++;
246 if (c == '?')
247 c = 0177;
248 else
249 c &= 037;
250 }
251 else if (c == '\\')
252 {
253 c = *p++;
254 if (c >= '0' && c <= '7')
255 {
256 c -= '0';
257 size = 0;
258
259 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
260 {
261 c *= 8;
262 c += c1 - '0';
263 p++;
264 }
265 }
266 #ifdef IS_EBCDIC_HOST
267 else if (c >= 0200 && c < 0360)
268 {
269 c1 = esctab[(c & ~0100) - 0200];
270 if (c1 != ' ')
271 c = c1;
272 }
273 #else
274 else if (c >= 0100 && c < 0200)
275 {
276 c1 = esctab[(c & ~040) - 0100];
277 if (c1 != ' ')
278 c = c1;
279 }
280 #endif
281 }
282 *r++ = c;
283 }
284
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.
290
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. */
294 {
295 register int last_p_param = 0;
296 int remove_p_params = 1;
297 struct { char *beg; int len; } cut[11];
298
299 for (cut[0].beg = p = ret; p < r - 3; p++)
300 {
301 if (!remove_p_params)
302 break;
303 if (*p == '%' && *(p + 1) == 'p')
304 {
305 if (*(p + 2) - '0' == 1 + last_p_param)
306 {
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;
311 }
312 else /* not continuous: bail */
313 remove_p_params = 0;
314 if (last_p_param > 10) /* too many: bail */
315 remove_p_params = 0;
316 }
317 }
318 if (remove_p_params && last_p_param)
319 {
320 register int i;
321 char *wp;
322
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;
327 }
328 }
329
330 *r = '\0';
331 /* Update *AREA. */
332 if (area)
333 *area = r + 1;
334 return ret;
335 }
336 \f
337 /* Outputting a string with padding. */
338
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
344
345 char PC;
346
347 #ifndef emacs
348 /* Actual baud rate if positive;
349 - baud rate / 100 if negative. */
350
351 static int speeds[] =
352 {
353 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
354 -18, -24, -48, -96, -192, -288, -384, -576, -1152
355 };
356
357 #endif /* not emacs */
358
359 void
360 tputs (str, nlines, outfun)
361 register char *str;
362 int nlines;
363 register int (*outfun) ();
364 {
365 register int padcount = 0;
366 register int speed;
367
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
381
382 if (!str)
383 return;
384
385 while (*str >= '0' && *str <= '9')
386 {
387 padcount += *str++ - '0';
388 padcount *= 10;
389 }
390 if (*str == '.')
391 {
392 str++;
393 padcount += *str++ - '0';
394 }
395 if (*str == '*')
396 {
397 str++;
398 padcount *= nlines;
399 }
400 while (*str)
401 (*outfun) (*str++);
402
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
413 {
414 padcount += 50;
415 padcount /= 100;
416 }
417
418 while (padcount-- > 0)
419 (*outfun) (PC);
420 }
421 \f
422 /* Finding the termcap entry in the termcap data base. */
423
424 struct termcap_buffer
425 {
426 char *beg;
427 int size;
428 char *ptr;
429 int ateof;
430 int full;
431 };
432
433 /* Forward declarations of static functions. */
434
435 static int scan_file ();
436 static char *gobble_line ();
437 static int compare_contin ();
438 static int name_match ();
439
440 #ifdef MSDOS /* MW, May 1993 */
441 static int
442 valid_filename_p (fn)
443 char *fn;
444 {
445 return *fn == '/' || fn[1] == ':';
446 }
447 #else
448 #define valid_filename_p(fn) (*(fn) == '/')
449 #endif
450
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.
454
455 If BP is null, space is dynamically allocated.
456
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. */
461
462 int
463 tgetent (bp, name)
464 char *bp, *name;
465 {
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;
477
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"))
482 {
483 term = INTERNAL_TERMINAL;
484 if (!bp)
485 {
486 malloc_size = 1 + strlen (term);
487 bp = (char *) xmalloc (malloc_size);
488 }
489 strcpy (bp, term);
490 goto ret;
491 }
492 #endif /* INTERNAL_TERMINAL */
493
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;
498
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
508
509 filep = termcap_name && valid_filename_p (termcap_name);
510
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. */
516
517 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
518 {
519 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
520 if (!indirect)
521 {
522 if (!bp)
523 bp = termcap_name;
524 else
525 strcpy (bp, termcap_name);
526 goto ret;
527 }
528 else
529 { /* It has tc=. Need to read /etc/termcap. */
530 tcenv = termcap_name;
531 termcap_name = NULL;
532 }
533 }
534
535 if (!termcap_name || !filep)
536 termcap_name = TERMCAP_FILE;
537
538 /* Here we know we must search a file and termcap_name has its name. */
539
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;
547
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;
552
553 if (!bp)
554 {
555 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
556 bp = (char *) xmalloc (malloc_size);
557 }
558 tc_search_point = bp1 = bp;
559
560 if (indirect)
561 /* Copy the data from the environment variable. */
562 {
563 strcpy (bp, tcenv);
564 bp1 += strlen (tcenv);
565 }
566
567 while (term)
568 {
569 /* Scan the file, reading it via buf, till find start of main entry. */
570 if (scan_file (term, fd, &buf) == 0)
571 {
572 close (fd);
573 free (buf.beg);
574 if (malloc_size)
575 free (bp);
576 return 0;
577 }
578
579 /* Free old `term' if appropriate. */
580 if (term != name)
581 free (term);
582
583 /* If BP is malloc'd by us, make sure it is big enough. */
584 if (malloc_size)
585 {
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;
591 }
592
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')
598 {
599 bp1--;
600 termcap_name++;
601 }
602 *bp1 = '\0';
603
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);
608 }
609
610 close (fd);
611 free (buf.beg);
612
613 if (malloc_size)
614 bp = (char *) xrealloc (bp, bp1 - bp + 1);
615
616 ret:
617 term_entry = bp;
618 return 1;
619 }
620
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. */
626
627 static int
628 scan_file (str, fd, bufp)
629 char *str;
630 int fd;
631 register struct termcap_buffer *bufp;
632 {
633 register char *end;
634
635 bufp->ptr = bufp->beg;
636 bufp->full = 0;
637 bufp->ateof = 0;
638 *bufp->ptr = '\0';
639
640 lseek (fd, 0L, 0);
641
642 while (!bufp->ateof)
643 {
644 /* Read a line into the buffer. */
645 end = NULL;
646 do
647 {
648 /* if it is continued, append another line to it,
649 until a non-continued line ends. */
650 end = gobble_line (fd, bufp, end);
651 }
652 while (!bufp->ateof && end[-2] == '\\');
653
654 if (*bufp->ptr != '#'
655 && name_match (bufp->ptr, str))
656 return 1;
657
658 /* Discard the line just processed. */
659 bufp->ptr = end;
660 }
661 return 0;
662 }
663
664 /* Return nonzero if NAME is one of the names specified
665 by termcap entry LINE. */
666
667 static int
668 name_match (line, name)
669 char *line, *name;
670 {
671 register char *tem;
672
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;
679
680 return 0;
681 }
682
683 static int
684 compare_contin (str1, str2)
685 register char *str1, *str2;
686 {
687 register int c1, c2;
688 while (1)
689 {
690 c1 = *str1++;
691 c2 = *str2++;
692 while (c1 == '\\' && *str1 == '\n')
693 {
694 str1++;
695 while ((c1 = *str1++) == ' ' || c1 == '\t');
696 }
697 if (c2 == '\0')
698 {
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;
705 }
706 else if (c1 != c2)
707 return 1;
708 }
709 }
710
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.
715
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.
718
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. */
723
724 static char *
725 gobble_line (fd, bufp, append_end)
726 int fd;
727 register struct termcap_buffer *bufp;
728 char *append_end;
729 {
730 register char *end;
731 register int nread;
732 register char *buf = bufp->beg;
733 register char *tem;
734
735 if (!append_end)
736 append_end = bufp->ptr;
737
738 while (1)
739 {
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)
747 {
748 if (bufp->full == bufp->size)
749 {
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;
756 }
757 }
758 else
759 {
760 append_end -= bufp->ptr - buf;
761 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
762 bufp->ptr = buf;
763 }
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';
768 }
769 return end + 1;
770 }
771 \f
772 #ifdef TEST
773
774 #ifdef NULL
775 #undef NULL
776 #endif
777
778 #include <stdio.h>
779
780 main (argc, argv)
781 int argc;
782 char **argv;
783 {
784 char *term;
785 char *buf;
786
787 term = argv[1];
788 printf ("TERM: %s\n", term);
789
790 buf = (char *) tgetent (0, term);
791 if ((int) buf <= 0)
792 {
793 printf ("No entry.\n");
794 return 0;
795 }
796
797 printf ("Entry: %s\n", buf);
798
799 tprint ("cm");
800 tprint ("AL");
801
802 printf ("co: %d\n", tgetnum ("co"));
803 printf ("am: %d\n", tgetflag ("am"));
804 }
805
806 tprint (cap)
807 char *cap;
808 {
809 char *x = tgetstr (cap, 0);
810 register char *y;
811
812 printf ("%s: ", cap);
813 if (x)
814 {
815 for (y = x; *y; y++)
816 if (*y <= ' ' || *y == 0177)
817 printf ("\\%0o", *y);
818 else
819 putchar (*y);
820 free (x);
821 }
822 else
823 printf ("none");
824 putchar ('\n');
825 }
826
827 #endif /* TEST */
828
829 /* arch-tag: c2e8d427-2271-4fac-95fe-411857238b80
830 (do not change this comment) */