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