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