]> code.delx.au - gnu-emacs/blob - src/termcap.c
Merge from trunk.
[gnu-emacs] / src / termcap.c
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986, 1993, 1994, 1995, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008, 2011 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA. */
19
20 /* Emacs config.h may rename various library functions such as malloc. */
21 #include <config.h>
22 #include <setjmp.h>
23 #include <sys/file.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26
27 #include "lisp.h"
28 #include "tparam.h"
29 #ifdef MSDOS
30 #include "msdos.h"
31 #endif
32
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
48 int bufsize = 128;
49 #else
50 #define BUFSIZE 2048
51 #endif
52 #endif
53
54 #ifndef TERMCAP_FILE
55 #define TERMCAP_FILE "/etc/termcap"
56 #endif
57
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. */
63 static char *term_entry;
64
65 static char *tgetst1 (char *ptr, char **area);
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
71 static char *
72 find_capability (register char *bp, register const char *cap)
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
82 int
83 tgetnum (const char *cap)
84 {
85 register char *ptr = find_capability (term_entry, cap);
86 if (!ptr || ptr[-1] != '#')
87 return -1;
88 return atoi (ptr);
89 }
90
91 int
92 tgetflag (const char *cap)
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
103 char *
104 tgetstr (const char *cap, char **area)
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
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
117 static const char esctab[]
118 = " \057\026 \047\014 \
119 \025 \015 \
120 \005 \013 \
121 ";
122 #else
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
127 static const char esctab[]
128 = " \007\010 \033\014 \
129 \012 \
130 \015 \011 \013 \
131 ";
132 #endif
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
141 static char *
142 tgetst1 (char *ptr, char **area)
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 == '^')
172 {
173 c = *p++;
174 if (c == '?')
175 c = 0177;
176 else
177 c &= 037;
178 }
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 }
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
202 else if (c >= 0100 && c < 0200)
203 {
204 c1 = esctab[(c & ~040) - 0100];
205 if (c1 != ' ')
206 c = c1;
207 }
208 #endif
209 }
210 *r++ = c;
211 }
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)
253 memcpy (wp, cut[i].beg, cut[i].len);
254 r = wp;
255 }
256 }
257
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
267 char PC;
268
269 void
270 tputs (register const char *str, int nlines, int (*outfun) (int))
271 {
272 register int padcount = 0;
273 register int speed;
274
275 speed = baud_rate;
276 /* For quite high speeds, convert to the smaller
277 units to avoid overflow. */
278 if (speed > 10000)
279 speed = - speed / 100;
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
302 /* PADCOUNT is now in units of tenths of msec.
303 SPEED is measured in characters per 10 seconds
304 or in characters per .1 seconds (if negative).
305 We use the smaller units for larger speeds to avoid overflow. */
306 padcount *= speed;
307 padcount += 500;
308 padcount /= 1000;
309 if (speed < 0)
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
323 struct termcap_buffer
324 {
325 char *beg;
326 ptrdiff_t size;
327 char *ptr;
328 int ateof;
329 ptrdiff_t full;
330 };
331
332 /* Forward declarations of static functions. */
333
334 static int scan_file (char *str, int fd, register struct termcap_buffer *bufp);
335 static char *gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end);
336 static int compare_contin (register char *str1, register char *str2);
337 static int name_match (char *line, char *name);
338
339 #ifdef MSDOS /* MW, May 1993 */
340 static int
341 valid_filename_p (fn)
342 char *fn;
343 {
344 return *fn == '/' || fn[1] == ':';
345 }
346 #else
347 #define valid_filename_p(fn) (*(fn) == '/')
348 #endif
349
350 /* Find the termcap entry data for terminal type NAME
351 and store it in the block that BP points to.
352 Record its address for future use.
353
354 If BP is null, space is dynamically allocated.
355
356 Return -1 if there is some difficulty accessing the data base
357 of terminal types,
358 0 if the data base is accessible but the type NAME is not defined
359 in it, and some other value otherwise. */
360
361 int
362 tgetent (char *bp, const char *name)
363 {
364 register char *termcap_name;
365 register int fd;
366 struct termcap_buffer buf;
367 register char *bp1;
368 char *tc_search_point;
369 char *term;
370 ptrdiff_t malloc_size = 0;
371 register int c;
372 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
373 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
374 int filep;
375
376 #ifdef INTERNAL_TERMINAL
377 /* For the internal terminal we don't want to read any termcap file,
378 so fake it. */
379 if (!strcmp (name, "internal"))
380 {
381 term = INTERNAL_TERMINAL;
382 if (!bp)
383 {
384 malloc_size = 1 + strlen (term);
385 bp = (char *) xmalloc (malloc_size);
386 }
387 strcpy (bp, term);
388 goto ret;
389 }
390 #endif /* INTERNAL_TERMINAL */
391
392 /* For compatibility with programs like `less' that want to
393 put data in the termcap buffer themselves as a fallback. */
394 if (bp)
395 term_entry = bp;
396
397 termcap_name = getenv ("TERMCAP");
398 if (termcap_name && *termcap_name == '\0')
399 termcap_name = NULL;
400 #if defined (MSDOS) && !defined (TEST)
401 if (termcap_name && (*termcap_name == '\\'
402 || *termcap_name == '/'
403 || termcap_name[1] == ':'))
404 dostounix_filename(termcap_name);
405 #endif
406
407 filep = termcap_name && valid_filename_p (termcap_name);
408
409 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
410 it is a file name to use instead of /etc/termcap.
411 If it is non-null and does not start with /,
412 it is the entry itself, but only if
413 the name the caller requested matches the TERM variable. */
414
415 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
416 {
417 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
418 if (!indirect)
419 {
420 if (!bp)
421 bp = termcap_name;
422 else
423 strcpy (bp, termcap_name);
424 goto ret;
425 }
426 else
427 { /* It has tc=. Need to read /etc/termcap. */
428 tcenv = termcap_name;
429 termcap_name = NULL;
430 }
431 }
432
433 if (!termcap_name || !filep)
434 termcap_name = TERMCAP_FILE;
435
436 /* Here we know we must search a file and termcap_name has its name. */
437
438 #ifdef MSDOS
439 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
440 #else
441 fd = open (termcap_name, O_RDONLY, 0);
442 #endif
443 if (fd < 0)
444 return -1;
445
446 buf.size = BUFSIZE;
447 /* Add 1 to size to ensure room for terminating null. */
448 buf.beg = (char *) xmalloc (buf.size + 1);
449 term = indirect ? indirect : (char *)name;
450
451 if (!bp)
452 {
453 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
454 bp = (char *) xmalloc (malloc_size);
455 }
456 tc_search_point = bp1 = bp;
457
458 if (indirect)
459 /* Copy the data from the environment variable. */
460 {
461 strcpy (bp, tcenv);
462 bp1 += strlen (tcenv);
463 }
464
465 while (term)
466 {
467 /* Scan the file, reading it via buf, till find start of main entry. */
468 if (scan_file (term, fd, &buf) == 0)
469 {
470 close (fd);
471 xfree (buf.beg);
472 if (malloc_size)
473 xfree (bp);
474 return 0;
475 }
476
477 /* Free old `term' if appropriate. */
478 if (term != name)
479 xfree (term);
480
481 /* If BP is malloc'd by us, make sure it is big enough. */
482 if (malloc_size)
483 {
484 int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
485 malloc_size = offset1 + buf.size;
486 bp = termcap_name = (char *) xrealloc (bp, malloc_size);
487 bp1 = termcap_name + offset1;
488 tc_search_point = termcap_name + offset2;
489 }
490
491 /* Copy the line of the entry from buf into bp. */
492 termcap_name = buf.ptr;
493 while ((*bp1++ = c = *termcap_name++) && c != '\n')
494 /* Drop out any \ newline sequence. */
495 if (c == '\\' && *termcap_name == '\n')
496 {
497 bp1--;
498 termcap_name++;
499 }
500 *bp1 = '\0';
501
502 /* Does this entry refer to another terminal type's entry?
503 If something is found, copy it into heap and null-terminate it. */
504 tc_search_point = find_capability (tc_search_point, "tc");
505 term = tgetst1 (tc_search_point, (char **) 0);
506 }
507
508 close (fd);
509 xfree (buf.beg);
510
511 if (malloc_size)
512 bp = (char *) xrealloc (bp, bp1 - bp + 1);
513
514 ret:
515 term_entry = bp;
516 return 1;
517 }
518
519 /* Given file open on FD and buffer BUFP,
520 scan the file from the beginning until a line is found
521 that starts the entry for terminal type STR.
522 Return 1 if successful, with that line in BUFP,
523 or 0 if no entry is found in the file. */
524
525 static int
526 scan_file (char *str, int fd, register struct termcap_buffer *bufp)
527 {
528 register char *end;
529
530 bufp->ptr = bufp->beg;
531 bufp->full = 0;
532 bufp->ateof = 0;
533 *bufp->ptr = '\0';
534
535 lseek (fd, 0L, 0);
536
537 while (!bufp->ateof)
538 {
539 /* Read a line into the buffer. */
540 end = NULL;
541 do
542 {
543 /* if it is continued, append another line to it,
544 until a non-continued line ends. */
545 end = gobble_line (fd, bufp, end);
546 }
547 while (!bufp->ateof && end[-2] == '\\');
548
549 if (*bufp->ptr != '#'
550 && name_match (bufp->ptr, str))
551 return 1;
552
553 /* Discard the line just processed. */
554 bufp->ptr = end;
555 }
556 return 0;
557 }
558
559 /* Return nonzero if NAME is one of the names specified
560 by termcap entry LINE. */
561
562 static int
563 name_match (char *line, char *name)
564 {
565 register char *tem;
566
567 if (!compare_contin (line, name))
568 return 1;
569 /* This line starts an entry. Is it the right one? */
570 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
571 if (*tem == '|' && !compare_contin (tem + 1, name))
572 return 1;
573
574 return 0;
575 }
576
577 static int
578 compare_contin (register char *str1, register char *str2)
579 {
580 register int c1, c2;
581 while (1)
582 {
583 c1 = *str1++;
584 c2 = *str2++;
585 while (c1 == '\\' && *str1 == '\n')
586 {
587 str1++;
588 while ((c1 = *str1++) == ' ' || c1 == '\t');
589 }
590 if (c2 == '\0')
591 {
592 /* End of type being looked up. */
593 if (c1 == '|' || c1 == ':')
594 /* If end of name in data base, we win. */
595 return 0;
596 else
597 return 1;
598 }
599 else if (c1 != c2)
600 return 1;
601 }
602 }
603
604 /* Make sure that the buffer <- BUFP contains a full line
605 of the file open on FD, starting at the place BUFP->ptr
606 points to. Can read more of the file, discard stuff before
607 BUFP->ptr, or make the buffer bigger.
608
609 Return the pointer to after the newline ending the line,
610 or to the end of the file, if there is no newline to end it.
611
612 Can also merge on continuation lines. If APPEND_END is
613 non-null, it points past the newline of a line that is
614 continued; we add another line onto it and regard the whole
615 thing as one line. The caller decides when a line is continued. */
616
617 static char *
618 gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end)
619 {
620 register char *end;
621 register int nread;
622 register char *buf = bufp->beg;
623 register char *tem;
624
625 if (!append_end)
626 append_end = bufp->ptr;
627
628 while (1)
629 {
630 end = append_end;
631 while (*end && *end != '\n') end++;
632 if (*end)
633 break;
634 if (bufp->ateof)
635 return buf + bufp->full;
636 if (bufp->ptr == buf)
637 {
638 if (bufp->full == bufp->size)
639 {
640 if ((PTRDIFF_MAX - 1) / 2 < bufp->size)
641 memory_full (SIZE_MAX);
642 bufp->size *= 2;
643 /* Add 1 to size to ensure room for terminating null. */
644 tem = (char *) xrealloc (buf, bufp->size + 1);
645 bufp->ptr = (bufp->ptr - buf) + tem;
646 append_end = (append_end - buf) + tem;
647 bufp->beg = buf = tem;
648 }
649 }
650 else
651 {
652 append_end -= bufp->ptr - buf;
653 memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf);
654 bufp->ptr = buf;
655 }
656 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
657 bufp->ateof = 1;
658 bufp->full += nread;
659 buf[bufp->full] = '\0';
660 }
661 return end + 1;
662 }
663 \f
664 #ifdef TEST
665
666 #ifdef NULL
667 #undef NULL
668 #endif
669
670 #include <stdio.h>
671
672 main (argc, argv)
673 int argc;
674 char **argv;
675 {
676 char *term;
677 char *buf;
678
679 term = argv[1];
680 printf ("TERM: %s\n", term);
681
682 buf = (char *) tgetent (0, term);
683 if ((int) buf <= 0)
684 {
685 printf ("No entry.\n");
686 return 0;
687 }
688
689 printf ("Entry: %s\n", buf);
690
691 tprint ("cm");
692 tprint ("AL");
693
694 printf ("co: %d\n", tgetnum ("co"));
695 printf ("am: %d\n", tgetflag ("am"));
696 }
697
698 tprint (cap)
699 char *cap;
700 {
701 char *x = tgetstr (cap, 0);
702 register char *y;
703
704 printf ("%s: ", cap);
705 if (x)
706 {
707 for (y = x; *y; y++)
708 if (*y <= ' ' || *y == 0177)
709 printf ("\\%0o", *y);
710 else
711 putchar (*y);
712 free (x);
713 }
714 else
715 printf ("none");
716 putchar ('\n');
717 }
718
719 #endif /* TEST */