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