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