]> code.delx.au - gnu-emacs/blob - src/termcap.c
4ad0cee6264e76aa736adc5b6fed9f57f47ca450
[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 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 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28
29 #include "lisp.h"
30
31 #ifndef NULL
32 #define NULL (char *) 0
33 #endif
34
35 /* BUFSIZE is the initial size allocated for the buffer
36 for reading the termcap file.
37 It is not a limit.
38 Make it large normally for speed.
39 Make it variable when debugging, so can exercise
40 increasing the space dynamically. */
41
42 #ifndef BUFSIZE
43 #ifdef DEBUG
44 #define BUFSIZE bufsize
45
46 int bufsize = 128;
47 #else
48 #define BUFSIZE 2048
49 #endif
50 #endif
51
52 #ifndef TERMCAP_FILE
53 #define TERMCAP_FILE "/etc/termcap"
54 #endif
55
56 \f
57 /* Looking up capabilities in the entry already found. */
58
59 /* The pointer to the data made by tgetent is left here
60 for tgetnum, tgetflag and tgetstr to find. */
61 static char *term_entry;
62
63 static char *tgetst1 (char *ptr, char **area);
64
65 /* Search entry BP for capability CAP.
66 Return a pointer to the capability (in BP) if found,
67 0 if not found. */
68
69 static char *
70 find_capability (register char *bp, register char *cap)
71 {
72 for (; *bp; bp++)
73 if (bp[0] == ':'
74 && bp[1] == cap[0]
75 && bp[2] == cap[1])
76 return &bp[4];
77 return NULL;
78 }
79
80 int
81 tgetnum (char *cap)
82 {
83 register char *ptr = find_capability (term_entry, cap);
84 if (!ptr || ptr[-1] != '#')
85 return -1;
86 return atoi (ptr);
87 }
88
89 int
90 tgetflag (char *cap)
91 {
92 register char *ptr = find_capability (term_entry, cap);
93 return ptr && ptr[-1] == ':';
94 }
95
96 /* Look up a string-valued capability CAP.
97 If AREA is non-null, it points to a pointer to a block in which
98 to store the string. That pointer is advanced over the space used.
99 If AREA is null, space is allocated with `malloc'. */
100
101 char *
102 tgetstr (char *cap, char **area)
103 {
104 register char *ptr = find_capability (term_entry, cap);
105 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
106 return NULL;
107 return tgetst1 (ptr, area);
108 }
109
110 #ifdef IS_EBCDIC_HOST
111 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
112 gives meaning of character following \, or a space if no special meaning.
113 Sixteen characters per line within the string. */
114
115 static const char esctab[]
116 = " \057\026 \047\014 \
117 \025 \015 \
118 \005 \013 \
119 ";
120 #else
121 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
122 gives meaning of character following \, or a space if no special meaning.
123 Eight characters per line within the string. */
124
125 static const char esctab[]
126 = " \007\010 \033\014 \
127 \012 \
128 \015 \011 \013 \
129 ";
130 #endif
131
132 /* PTR points to a string value inside a termcap entry.
133 Copy that value, processing \ and ^ abbreviations,
134 into the block that *AREA points to,
135 or to newly allocated storage if AREA is NULL.
136 Return the address to which we copied the value,
137 or NULL if PTR is NULL. */
138
139 static char *
140 tgetst1 (char *ptr, char **area)
141 {
142 register char *p, *r;
143 register int c;
144 register int size;
145 char *ret;
146 register int c1;
147
148 if (!ptr)
149 return NULL;
150
151 /* `ret' gets address of where to store the string. */
152 if (!area)
153 {
154 /* Compute size of block needed (may overestimate). */
155 p = ptr;
156 while ((c = *p++) && c != ':' && c != '\n')
157 ;
158 ret = (char *) xmalloc (p - ptr + 1);
159 }
160 else
161 ret = *area;
162
163 /* Copy the string value, stopping at null or colon.
164 Also process ^ and \ abbreviations. */
165 p = ptr;
166 r = ret;
167 while ((c = *p++) && c != ':' && c != '\n')
168 {
169 if (c == '^')
170 {
171 c = *p++;
172 if (c == '?')
173 c = 0177;
174 else
175 c &= 037;
176 }
177 else if (c == '\\')
178 {
179 c = *p++;
180 if (c >= '0' && c <= '7')
181 {
182 c -= '0';
183 size = 0;
184
185 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
186 {
187 c *= 8;
188 c += c1 - '0';
189 p++;
190 }
191 }
192 #ifdef IS_EBCDIC_HOST
193 else if (c >= 0200 && c < 0360)
194 {
195 c1 = esctab[(c & ~0100) - 0200];
196 if (c1 != ' ')
197 c = c1;
198 }
199 #else
200 else if (c >= 0100 && c < 0200)
201 {
202 c1 = esctab[(c & ~040) - 0100];
203 if (c1 != ' ')
204 c = c1;
205 }
206 #endif
207 }
208 *r++ = c;
209 }
210
211 /* Sometimes entries have "%pN" which means use parameter N in the
212 next %-substitution. If all such N are continuous in the range
213 [1,9] we can remove each "%pN" because they are redundant, thus
214 reducing bandwidth requirements. True, Emacs is well beyond the
215 days of 150baud teletypes, but some of its users aren't much so.
216
217 This pass could probably be integrated into the one above but
218 abbreviation expansion makes that effort a little more hairy than
219 its worth; this is cleaner. */
220 {
221 register int last_p_param = 0;
222 int remove_p_params = 1;
223 struct { char *beg; int len; } cut[11];
224
225 for (cut[0].beg = p = ret; p < r - 3; p++)
226 {
227 if (!remove_p_params)
228 break;
229 if (*p == '%' && *(p + 1) == 'p')
230 {
231 if (*(p + 2) - '0' == 1 + last_p_param)
232 {
233 cut[last_p_param].len = p - cut[last_p_param].beg;
234 last_p_param++;
235 p += 3;
236 cut[last_p_param].beg = p;
237 }
238 else /* not continuous: bail */
239 remove_p_params = 0;
240 if (last_p_param > 10) /* too many: bail */
241 remove_p_params = 0;
242 }
243 }
244 if (remove_p_params && last_p_param)
245 {
246 register int i;
247 char *wp;
248
249 cut[last_p_param].len = r - cut[last_p_param].beg;
250 for (i = 0, wp = ret; i <= last_p_param; wp += cut[i++].len)
251 memcpy (wp, cut[i].beg, cut[i].len);
252 r = wp;
253 }
254 }
255
256 *r = '\0';
257 /* Update *AREA. */
258 if (area)
259 *area = r + 1;
260 return ret;
261 }
262 \f
263 /* Outputting a string with padding. */
264
265 char PC;
266
267 void
268 tputs (register char *str, int nlines, register int (*outfun) (/* ??? */))
269 {
270 register int padcount = 0;
271 register int speed;
272
273 extern EMACS_INT baud_rate;
274 speed = baud_rate;
275 /* For quite high speeds, convert to the smaller
276 units to avoid overflow. */
277 if (speed > 10000)
278 speed = - speed / 100;
279
280 if (!str)
281 return;
282
283 while (*str >= '0' && *str <= '9')
284 {
285 padcount += *str++ - '0';
286 padcount *= 10;
287 }
288 if (*str == '.')
289 {
290 str++;
291 padcount += *str++ - '0';
292 }
293 if (*str == '*')
294 {
295 str++;
296 padcount *= nlines;
297 }
298 while (*str)
299 (*outfun) (*str++);
300
301 /* PADCOUNT is now in units of tenths of msec.
302 SPEED is measured in characters per 10 seconds
303 or in characters per .1 seconds (if negative).
304 We use the smaller units for larger speeds to avoid overflow. */
305 padcount *= speed;
306 padcount += 500;
307 padcount /= 1000;
308 if (speed < 0)
309 padcount = -padcount;
310 else
311 {
312 padcount += 50;
313 padcount /= 100;
314 }
315
316 while (padcount-- > 0)
317 (*outfun) (PC);
318 }
319 \f
320 /* Finding the termcap entry in the termcap data base. */
321
322 struct termcap_buffer
323 {
324 char *beg;
325 int size;
326 char *ptr;
327 int ateof;
328 int full;
329 };
330
331 /* Forward declarations of static functions. */
332
333 static int scan_file (char *str, int fd, register struct termcap_buffer *bufp);
334 static char *gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end);
335 static int compare_contin (register char *str1, register char *str2);
336 static int name_match (char *line, char *name);
337
338 #ifdef MSDOS /* MW, May 1993 */
339 static int
340 valid_filename_p (fn)
341 char *fn;
342 {
343 return *fn == '/' || fn[1] == ':';
344 }
345 #else
346 #define valid_filename_p(fn) (*(fn) == '/')
347 #endif
348
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
360 int
361 tgetent (char *bp, char *name)
362 {
363 register char *termcap_name;
364 register int fd;
365 struct termcap_buffer buf;
366 register char *bp1;
367 char *tc_search_point;
368 char *term;
369 int malloc_size = 0;
370 register int c;
371 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
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
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
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)
433 termcap_name = TERMCAP_FILE;
434
435 /* Here we know we must search a file and termcap_name has its name. */
436
437 #ifdef MSDOS
438 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
439 #else
440 fd = open (termcap_name, O_RDONLY, 0);
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);
448 term = indirect ? indirect : name;
449
450 if (!bp)
451 {
452 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
453 bp = (char *) xmalloc (malloc_size);
454 }
455 tc_search_point = bp1 = bp;
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);
470 free (buf.beg);
471 if (malloc_size)
472 free (bp);
473 return 0;
474 }
475
476 /* Free old `term' if appropriate. */
477 if (term != name)
478 free (term);
479
480 /* If BP is malloc'd by us, make sure it is big enough. */
481 if (malloc_size)
482 {
483 int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
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;
488 }
489
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. */
503 tc_search_point = find_capability (tc_search_point, "tc");
504 term = tgetst1 (tc_search_point, (char **) 0);
505 }
506
507 close (fd);
508 free (buf.beg);
509
510 if (malloc_size)
511 bp = (char *) xrealloc (bp, bp1 - bp + 1);
512
513 ret:
514 term_entry = bp;
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
524 static int
525 scan_file (char *str, int fd, register struct termcap_buffer *bufp)
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
561 static int
562 name_match (char *line, char *name)
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
576 static int
577 compare_contin (register char *str1, register char *str2)
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
616 static char *
617 gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end)
618 {
619 register char *end;
620 register int nread;
621 register char *buf = bufp->beg;
622 register char *tem;
623
624 if (!append_end)
625 append_end = bufp->ptr;
626
627 while (1)
628 {
629 end = append_end;
630 while (*end && *end != '\n') end++;
631 if (*end)
632 break;
633 if (bufp->ateof)
634 return buf + bufp->full;
635 if (bufp->ptr == buf)
636 {
637 if (bufp->full == bufp->size)
638 {
639 bufp->size *= 2;
640 /* Add 1 to size to ensure room for terminating null. */
641 tem = (char *) xrealloc (buf, bufp->size + 1);
642 bufp->ptr = (bufp->ptr - buf) + tem;
643 append_end = (append_end - buf) + tem;
644 bufp->beg = buf = tem;
645 }
646 }
647 else
648 {
649 append_end -= bufp->ptr - buf;
650 memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf);
651 bufp->ptr = buf;
652 }
653 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
654 bufp->ateof = 1;
655 bufp->full += nread;
656 buf[bufp->full] = '\0';
657 }
658 return end + 1;
659 }
660 \f
661 #ifdef TEST
662
663 #ifdef NULL
664 #undef NULL
665 #endif
666
667 #include <stdio.h>
668
669 main (argc, argv)
670 int argc;
671 char **argv;
672 {
673 char *term;
674 char *buf;
675
676 term = argv[1];
677 printf ("TERM: %s\n", term);
678
679 buf = (char *) tgetent (0, term);
680 if ((int) buf <= 0)
681 {
682 printf ("No entry.\n");
683 return 0;
684 }
685
686 printf ("Entry: %s\n", buf);
687
688 tprint ("cm");
689 tprint ("AL");
690
691 printf ("co: %d\n", tgetnum ("co"));
692 printf ("am: %d\n", tgetflag ("am"));
693 }
694
695 tprint (cap)
696 char *cap;
697 {
698 char *x = tgetstr (cap, 0);
699 register char *y;
700
701 printf ("%s: ", cap);
702 if (x)
703 {
704 for (y = x; *y; y++)
705 if (*y <= ' ' || *y == 0177)
706 printf ("\\%0o", *y);
707 else
708 putchar (*y);
709 free (x);
710 }
711 else
712 printf ("none");
713 putchar ('\n');
714 }
715
716 #endif /* TEST */
717
718 /* arch-tag: c2e8d427-2271-4fac-95fe-411857238b80
719 (do not change this comment) */