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