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