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