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