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