(x_set_tool_bar_lines): When clearing the frame, also
[bpt/emacs.git] / src / termcap.c
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 86, 93, 94, 95 Free Software Foundation, Inc.
3
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.
8
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.
13
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. */
18
19 /* Emacs config.h may rename various library functions such as malloc. */
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #ifdef emacs
25
26 #include <lisp.h> /* xmalloc is here */
27 /* Get the O_* definitions for open et al. */
28 #include <sys/file.h>
29 #ifdef HAVE_FCNTL_H
30 #include <fcntl.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35
36 #else /* not emacs */
37
38 #ifdef STDC_HEADERS
39 #include <stdlib.h>
40 #include <string.h>
41 #else
42 char *getenv ();
43 char *malloc ();
44 char *realloc ();
45 #endif
46
47 /* Do this after the include, in case string.h prototypes bcopy. */
48 #if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
49 #define bcopy(s, d, n) memcpy ((d), (s), (n))
50 #endif
51
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #ifdef _POSIX_VERSION
56 #include <fcntl.h>
57 #endif
58
59 #endif /* not emacs */
60
61 #ifndef NULL
62 #define NULL (char *) 0
63 #endif
64
65 #ifndef O_RDONLY
66 #define O_RDONLY 0
67 #endif
68
69 /* BUFSIZE is the initial size allocated for the buffer
70 for reading the termcap file.
71 It is not a limit.
72 Make it large normally for speed.
73 Make it variable when debugging, so can exercise
74 increasing the space dynamically. */
75
76 #ifndef BUFSIZE
77 #ifdef DEBUG
78 #define BUFSIZE bufsize
79
80 int bufsize = 128;
81 #else
82 #define BUFSIZE 2048
83 #endif
84 #endif
85
86 #ifndef TERMCAP_FILE
87 #define TERMCAP_FILE "/etc/termcap"
88 #endif
89
90 #ifndef emacs
91 static void
92 memory_out ()
93 {
94 write (2, "virtual memory exhausted\n", 25);
95 exit (1);
96 }
97
98 static char *
99 xmalloc (size)
100 unsigned size;
101 {
102 register char *tem = malloc (size);
103
104 if (!tem)
105 memory_out ();
106 return tem;
107 }
108
109 static char *
110 xrealloc (ptr, size)
111 char *ptr;
112 unsigned size;
113 {
114 register char *tem = realloc (ptr, size);
115
116 if (!tem)
117 memory_out ();
118 return tem;
119 }
120 #endif /* not emacs */
121 \f
122 /* Looking up capabilities in the entry already found. */
123
124 /* The pointer to the data made by tgetent is left here
125 for tgetnum, tgetflag and tgetstr to find. */
126 static char *term_entry;
127
128 static char *tgetst1 ();
129
130 /* Search entry BP for capability CAP.
131 Return a pointer to the capability (in BP) if found,
132 0 if not found. */
133
134 static char *
135 find_capability (bp, cap)
136 register char *bp, *cap;
137 {
138 for (; *bp; bp++)
139 if (bp[0] == ':'
140 && bp[1] == cap[0]
141 && bp[2] == cap[1])
142 return &bp[4];
143 return NULL;
144 }
145
146 int
147 tgetnum (cap)
148 char *cap;
149 {
150 register char *ptr = find_capability (term_entry, cap);
151 if (!ptr || ptr[-1] != '#')
152 return -1;
153 return atoi (ptr);
154 }
155
156 int
157 tgetflag (cap)
158 char *cap;
159 {
160 register char *ptr = find_capability (term_entry, cap);
161 return ptr && ptr[-1] == ':';
162 }
163
164 /* Look up a string-valued capability CAP.
165 If AREA is non-null, it points to a pointer to a block in which
166 to store the string. That pointer is advanced over the space used.
167 If AREA is null, space is allocated with `malloc'. */
168
169 char *
170 tgetstr (cap, area)
171 char *cap;
172 char **area;
173 {
174 register char *ptr = find_capability (term_entry, cap);
175 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
176 return NULL;
177 return tgetst1 (ptr, area);
178 }
179
180 #ifdef IS_EBCDIC_HOST
181 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
182 gives meaning of character following \, or a space if no special meaning.
183 Sixteen characters per line within the string. */
184
185 static char esctab[]
186 = " \057\026 \047\014 \
187 \025 \015 \
188 \005 \013 \
189 ";
190 #else
191 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
192 gives meaning of character following \, or a space if no special meaning.
193 Eight characters per line within the string. */
194
195 static char esctab[]
196 = " \007\010 \033\014 \
197 \012 \
198 \015 \011 \013 \
199 ";
200 #endif
201
202 /* PTR points to a string value inside a termcap entry.
203 Copy that value, processing \ and ^ abbreviations,
204 into the block that *AREA points to,
205 or to newly allocated storage if AREA is NULL.
206 Return the address to which we copied the value,
207 or NULL if PTR is NULL. */
208
209 static char *
210 tgetst1 (ptr, area)
211 char *ptr;
212 char **area;
213 {
214 register char *p, *r;
215 register int c;
216 register int size;
217 char *ret;
218 register int c1;
219
220 if (!ptr)
221 return NULL;
222
223 /* `ret' gets address of where to store the string. */
224 if (!area)
225 {
226 /* Compute size of block needed (may overestimate). */
227 p = ptr;
228 while ((c = *p++) && c != ':' && c != '\n')
229 ;
230 ret = (char *) xmalloc (p - ptr + 1);
231 }
232 else
233 ret = *area;
234
235 /* Copy the string value, stopping at null or colon.
236 Also process ^ and \ abbreviations. */
237 p = ptr;
238 r = ret;
239 while ((c = *p++) && c != ':' && c != '\n')
240 {
241 if (c == '^')
242 {
243 c = *p++;
244 if (c == '?')
245 c = 0177;
246 else
247 c &= 037;
248 }
249 else if (c == '\\')
250 {
251 c = *p++;
252 if (c >= '0' && c <= '7')
253 {
254 c -= '0';
255 size = 0;
256
257 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
258 {
259 c *= 8;
260 c += c1 - '0';
261 p++;
262 }
263 }
264 #ifdef IS_EBCDIC_HOST
265 else if (c >= 0200 && c < 0360)
266 {
267 c1 = esctab[(c & ~0100) - 0200];
268 if (c1 != ' ')
269 c = c1;
270 }
271 #else
272 else if (c >= 0100 && c < 0200)
273 {
274 c1 = esctab[(c & ~040) - 0100];
275 if (c1 != ' ')
276 c = c1;
277 }
278 #endif
279 }
280 *r++ = c;
281 }
282 *r = '\0';
283 /* Update *AREA. */
284 if (area)
285 *area = r + 1;
286 return ret;
287 }
288 \f
289 /* Outputting a string with padding. */
290
291 short ospeed;
292 /* If OSPEED is 0, we use this as the actual baud rate. */
293 int tputs_baud_rate;
294 char PC;
295
296 /* Actual baud rate if positive;
297 - baud rate / 100 if negative. */
298
299 static int speeds[] =
300 {
301 #ifdef VMS
302 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
303 -20, -24, -36, -48, -72, -96, -192
304 #else /* not VMS */
305 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
306 -18, -24, -48, -96, -192, -288, -384, -576, -1152
307 #endif /* not VMS */
308 };
309
310 void
311 tputs (str, nlines, outfun)
312 register char *str;
313 int nlines;
314 register int (*outfun) ();
315 {
316 register int padcount = 0;
317 register int speed;
318
319 #ifdef emacs
320 extern int baud_rate;
321 speed = baud_rate;
322 /* For quite high speeds, convert to the smaller
323 units to avoid overflow. */
324 if (speed > 10000)
325 speed = - speed / 100;
326 #else
327 if (ospeed == 0)
328 speed = tputs_baud_rate;
329 else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0]))
330 speed = speeds[ospeed];
331 else
332 speed = 0;
333 #endif
334
335 if (!str)
336 return;
337
338 while (*str >= '0' && *str <= '9')
339 {
340 padcount += *str++ - '0';
341 padcount *= 10;
342 }
343 if (*str == '.')
344 {
345 str++;
346 padcount += *str++ - '0';
347 }
348 if (*str == '*')
349 {
350 str++;
351 padcount *= nlines;
352 }
353 while (*str)
354 (*outfun) (*str++);
355
356 /* PADCOUNT is now in units of tenths of msec.
357 SPEED is measured in characters per 10 seconds
358 or in characters per .1 seconds (if negative).
359 We use the smaller units for larger speeds to avoid overflow. */
360 padcount *= speed;
361 padcount += 500;
362 padcount /= 1000;
363 if (speed < 0)
364 padcount = -padcount;
365 else
366 {
367 padcount += 50;
368 padcount /= 100;
369 }
370
371 while (padcount-- > 0)
372 (*outfun) (PC);
373 }
374 \f
375 /* Finding the termcap entry in the termcap data base. */
376
377 struct termcap_buffer
378 {
379 char *beg;
380 int size;
381 char *ptr;
382 int ateof;
383 int full;
384 };
385
386 /* Forward declarations of static functions. */
387
388 static int scan_file ();
389 static char *gobble_line ();
390 static int compare_contin ();
391 static int name_match ();
392
393 #ifdef VMS
394
395 #include <rmsdef.h>
396 #include <fab.h>
397 #include <nam.h>
398
399 static int
400 valid_filename_p (fn)
401 char *fn;
402 {
403 struct FAB fab = cc$rms_fab;
404 struct NAM nam = cc$rms_nam;
405 char esa[NAM$C_MAXRSS];
406
407 fab.fab$l_fna = fn;
408 fab.fab$b_fns = strlen(fn);
409 fab.fab$l_nam = &nam;
410 fab.fab$l_fop = FAB$M_NAM;
411
412 nam.nam$l_esa = esa;
413 nam.nam$b_ess = sizeof esa;
414
415 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
416 }
417
418 #else /* !VMS */
419
420 #ifdef MSDOS /* MW, May 1993 */
421 static int
422 valid_filename_p (fn)
423 char *fn;
424 {
425 return *fn == '/' || fn[1] == ':';
426 }
427 #else
428 #define valid_filename_p(fn) (*(fn) == '/')
429 #endif
430
431 #endif /* !VMS */
432
433 /* Find the termcap entry data for terminal type NAME
434 and store it in the block that BP points to.
435 Record its address for future use.
436
437 If BP is null, space is dynamically allocated.
438
439 Return -1 if there is some difficulty accessing the data base
440 of terminal types,
441 0 if the data base is accessible but the type NAME is not defined
442 in it, and some other value otherwise. */
443
444 int
445 tgetent (bp, name)
446 char *bp, *name;
447 {
448 register char *termcap_name;
449 register int fd;
450 struct termcap_buffer buf;
451 register char *bp1;
452 char *tc_search_point;
453 char *term;
454 int malloc_size = 0;
455 register int c;
456 char *tcenv; /* TERMCAP value, if it contains :tc=. */
457 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
458 int filep;
459
460 #ifdef INTERNAL_TERMINAL
461 /* For the internal terminal we don't want to read any termcap file,
462 so fake it. */
463 if (!strcmp (name, "internal"))
464 {
465 term = INTERNAL_TERMINAL;
466 if (!bp)
467 {
468 malloc_size = 1 + strlen (term);
469 bp = (char *) xmalloc (malloc_size);
470 }
471 strcpy (bp, term);
472 goto ret;
473 }
474 #endif /* INTERNAL_TERMINAL */
475
476 /* For compatibility with programs like `less' that want to
477 put data in the termcap buffer themselves as a fallback. */
478 if (bp)
479 term_entry = bp;
480
481 termcap_name = getenv ("TERMCAP");
482 if (termcap_name && *termcap_name == '\0')
483 termcap_name = NULL;
484 #if defined (MSDOS) && !defined (TEST)
485 if (termcap_name && (*termcap_name == '\\'
486 || *termcap_name == '/'
487 || termcap_name[1] == ':'))
488 dostounix_filename(termcap_name);
489 #endif
490
491 filep = termcap_name && valid_filename_p (termcap_name);
492
493 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
494 it is a file name to use instead of /etc/termcap.
495 If it is non-null and does not start with /,
496 it is the entry itself, but only if
497 the name the caller requested matches the TERM variable. */
498
499 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
500 {
501 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
502 if (!indirect)
503 {
504 if (!bp)
505 bp = termcap_name;
506 else
507 strcpy (bp, termcap_name);
508 goto ret;
509 }
510 else
511 { /* It has tc=. Need to read /etc/termcap. */
512 tcenv = termcap_name;
513 termcap_name = NULL;
514 }
515 }
516
517 if (!termcap_name || !filep)
518 termcap_name = TERMCAP_FILE;
519
520 /* Here we know we must search a file and termcap_name has its name. */
521
522 #ifdef MSDOS
523 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
524 #else
525 fd = open (termcap_name, O_RDONLY, 0);
526 #endif
527 if (fd < 0)
528 return -1;
529
530 buf.size = BUFSIZE;
531 /* Add 1 to size to ensure room for terminating null. */
532 buf.beg = (char *) xmalloc (buf.size + 1);
533 term = indirect ? indirect : name;
534
535 if (!bp)
536 {
537 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
538 bp = (char *) xmalloc (malloc_size);
539 }
540 tc_search_point = bp1 = bp;
541
542 if (indirect)
543 /* Copy the data from the environment variable. */
544 {
545 strcpy (bp, tcenv);
546 bp1 += strlen (tcenv);
547 }
548
549 while (term)
550 {
551 /* Scan the file, reading it via buf, till find start of main entry. */
552 if (scan_file (term, fd, &buf) == 0)
553 {
554 close (fd);
555 free (buf.beg);
556 if (malloc_size)
557 free (bp);
558 return 0;
559 }
560
561 /* Free old `term' if appropriate. */
562 if (term != name)
563 free (term);
564
565 /* If BP is malloc'd by us, make sure it is big enough. */
566 if (malloc_size)
567 {
568 malloc_size = bp1 - bp + buf.size;
569 termcap_name = (char *) xrealloc (bp, malloc_size);
570 bp1 += termcap_name - bp;
571 tc_search_point += termcap_name - bp;
572 bp = termcap_name;
573 }
574
575 /* Copy the line of the entry from buf into bp. */
576 termcap_name = buf.ptr;
577 while ((*bp1++ = c = *termcap_name++) && c != '\n')
578 /* Drop out any \ newline sequence. */
579 if (c == '\\' && *termcap_name == '\n')
580 {
581 bp1--;
582 termcap_name++;
583 }
584 *bp1 = '\0';
585
586 /* Does this entry refer to another terminal type's entry?
587 If something is found, copy it into heap and null-terminate it. */
588 tc_search_point = find_capability (tc_search_point, "tc");
589 term = tgetst1 (tc_search_point, (char **) 0);
590 }
591
592 close (fd);
593 free (buf.beg);
594
595 if (malloc_size)
596 bp = (char *) xrealloc (bp, bp1 - bp + 1);
597
598 ret:
599 term_entry = bp;
600 return 1;
601 }
602
603 /* Given file open on FD and buffer BUFP,
604 scan the file from the beginning until a line is found
605 that starts the entry for terminal type STR.
606 Return 1 if successful, with that line in BUFP,
607 or 0 if no entry is found in the file. */
608
609 static int
610 scan_file (str, fd, bufp)
611 char *str;
612 int fd;
613 register struct termcap_buffer *bufp;
614 {
615 register char *end;
616
617 bufp->ptr = bufp->beg;
618 bufp->full = 0;
619 bufp->ateof = 0;
620 *bufp->ptr = '\0';
621
622 lseek (fd, 0L, 0);
623
624 while (!bufp->ateof)
625 {
626 /* Read a line into the buffer. */
627 end = NULL;
628 do
629 {
630 /* if it is continued, append another line to it,
631 until a non-continued line ends. */
632 end = gobble_line (fd, bufp, end);
633 }
634 while (!bufp->ateof && end[-2] == '\\');
635
636 if (*bufp->ptr != '#'
637 && name_match (bufp->ptr, str))
638 return 1;
639
640 /* Discard the line just processed. */
641 bufp->ptr = end;
642 }
643 return 0;
644 }
645
646 /* Return nonzero if NAME is one of the names specified
647 by termcap entry LINE. */
648
649 static int
650 name_match (line, name)
651 char *line, *name;
652 {
653 register char *tem;
654
655 if (!compare_contin (line, name))
656 return 1;
657 /* This line starts an entry. Is it the right one? */
658 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
659 if (*tem == '|' && !compare_contin (tem + 1, name))
660 return 1;
661
662 return 0;
663 }
664
665 static int
666 compare_contin (str1, str2)
667 register char *str1, *str2;
668 {
669 register int c1, c2;
670 while (1)
671 {
672 c1 = *str1++;
673 c2 = *str2++;
674 while (c1 == '\\' && *str1 == '\n')
675 {
676 str1++;
677 while ((c1 = *str1++) == ' ' || c1 == '\t');
678 }
679 if (c2 == '\0')
680 {
681 /* End of type being looked up. */
682 if (c1 == '|' || c1 == ':')
683 /* If end of name in data base, we win. */
684 return 0;
685 else
686 return 1;
687 }
688 else if (c1 != c2)
689 return 1;
690 }
691 }
692
693 /* Make sure that the buffer <- BUFP contains a full line
694 of the file open on FD, starting at the place BUFP->ptr
695 points to. Can read more of the file, discard stuff before
696 BUFP->ptr, or make the buffer bigger.
697
698 Return the pointer to after the newline ending the line,
699 or to the end of the file, if there is no newline to end it.
700
701 Can also merge on continuation lines. If APPEND_END is
702 non-null, it points past the newline of a line that is
703 continued; we add another line onto it and regard the whole
704 thing as one line. The caller decides when a line is continued. */
705
706 static char *
707 gobble_line (fd, bufp, append_end)
708 int fd;
709 register struct termcap_buffer *bufp;
710 char *append_end;
711 {
712 register char *end;
713 register int nread;
714 register char *buf = bufp->beg;
715 register char *tem;
716
717 if (!append_end)
718 append_end = bufp->ptr;
719
720 while (1)
721 {
722 end = append_end;
723 while (*end && *end != '\n') end++;
724 if (*end)
725 break;
726 if (bufp->ateof)
727 return buf + bufp->full;
728 if (bufp->ptr == buf)
729 {
730 if (bufp->full == bufp->size)
731 {
732 bufp->size *= 2;
733 /* Add 1 to size to ensure room for terminating null. */
734 tem = (char *) xrealloc (buf, bufp->size + 1);
735 bufp->ptr = (bufp->ptr - buf) + tem;
736 append_end = (append_end - buf) + tem;
737 bufp->beg = buf = tem;
738 }
739 }
740 else
741 {
742 append_end -= bufp->ptr - buf;
743 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
744 bufp->ptr = buf;
745 }
746 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
747 bufp->ateof = 1;
748 bufp->full += nread;
749 buf[bufp->full] = '\0';
750 }
751 return end + 1;
752 }
753 \f
754 #ifdef TEST
755
756 #ifdef NULL
757 #undef NULL
758 #endif
759
760 #include <stdio.h>
761
762 main (argc, argv)
763 int argc;
764 char **argv;
765 {
766 char *term;
767 char *buf;
768
769 term = argv[1];
770 printf ("TERM: %s\n", term);
771
772 buf = (char *) tgetent (0, term);
773 if ((int) buf <= 0)
774 {
775 printf ("No entry.\n");
776 return 0;
777 }
778
779 printf ("Entry: %s\n", buf);
780
781 tprint ("cm");
782 tprint ("AL");
783
784 printf ("co: %d\n", tgetnum ("co"));
785 printf ("am: %d\n", tgetflag ("am"));
786 }
787
788 tprint (cap)
789 char *cap;
790 {
791 char *x = tgetstr (cap, 0);
792 register char *y;
793
794 printf ("%s: ", cap);
795 if (x)
796 {
797 for (y = x; *y; y++)
798 if (*y <= ' ' || *y == 0177)
799 printf ("\\%0o", *y);
800 else
801 putchar (*y);
802 free (x);
803 }
804 else
805 printf ("none");
806 putchar ('\n');
807 }
808
809 #endif /* TEST */