Merge profiler branch
[bpt/emacs.git] / 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 <sys/file.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25
26 #include "lisp.h"
27 #include "tparam.h"
28 #ifdef MSDOS
29 #include "msdos.h"
30 #endif
31
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
43 int bufsize = 128;
44 #else
45 #define BUFSIZE 2048
46 #endif
47 #endif
48
49 #ifndef TERMCAP_FILE
50 #define TERMCAP_FILE "/etc/termcap"
51 #endif
52
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. */
58 static char *term_entry;
59
60 static char *tgetst1 (char *ptr, char **area);
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
66 static char *
67 find_capability (register char *bp, register const char *cap)
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
77 int
78 tgetnum (const char *cap)
79 {
80 register char *ptr = find_capability (term_entry, cap);
81 if (!ptr || ptr[-1] != '#')
82 return -1;
83 return atoi (ptr);
84 }
85
86 int
87 tgetflag (const char *cap)
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
98 char *
99 tgetstr (const char *cap, char **area)
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
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
112 static const char esctab[]
113 = " \057\026 \047\014 \
114 \025 \015 \
115 \005 \013 \
116 ";
117 #else
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
122 static const char esctab[]
123 = " \007\010 \033\014 \
124 \012 \
125 \015 \011 \013 \
126 ";
127 #endif
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
136 static char *
137 tgetst1 (char *ptr, char **area)
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 ;
155 ret = xmalloc (p - ptr + 1);
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 == '^')
167 {
168 c = *p++;
169 if (c == '?')
170 c = 0177;
171 else
172 c &= 037;
173 }
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 }
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
197 else if (c >= 0100 && c < 0200)
198 {
199 c1 = esctab[(c & ~040) - 0100];
200 if (c1 != ' ')
201 c = c1;
202 }
203 #endif
204 }
205 *r++ = c;
206 }
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)
248 memcpy (wp, cut[i].beg, cut[i].len);
249 r = wp;
250 }
251 }
252
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
262 char PC;
263
264 void
265 tputs (register const char *str, int nlines, int (*outfun) (int))
266 {
267 register int padcount = 0;
268 register int speed;
269
270 speed = baud_rate;
271 /* For quite high speeds, convert to the smaller
272 units to avoid overflow. */
273 if (speed > 10000)
274 speed = - speed / 100;
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
297 /* PADCOUNT is now in units of tenths of msec.
298 SPEED is measured in characters per 10 seconds
299 or in characters per .1 seconds (if negative).
300 We use the smaller units for larger speeds to avoid overflow. */
301 padcount *= speed;
302 padcount += 500;
303 padcount /= 1000;
304 if (speed < 0)
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
318 struct termcap_buffer
319 {
320 char *beg;
321 ptrdiff_t size;
322 char *ptr;
323 int ateof;
324 ptrdiff_t full;
325 };
326
327 /* Forward declarations of static functions. */
328
329 static int scan_file (char *str, int fd, register struct termcap_buffer *bufp);
330 static char *gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end);
331 static int compare_contin (register char *str1, register char *str2);
332 static int name_match (char *line, char *name);
333
334 #ifdef MSDOS /* MW, May 1993 */
335 static int
336 valid_filename_p (char *fn)
337 {
338 return *fn == '/' || fn[1] == ':';
339 }
340 #else
341 #define valid_filename_p(fn) (*(fn) == '/')
342 #endif
343
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
355 int
356 tgetent (char *bp, const char *name)
357 {
358 register char *termcap_name;
359 register int fd;
360 struct termcap_buffer buf;
361 register char *bp1;
362 char *tc_search_point;
363 char *term;
364 ptrdiff_t malloc_size = 0;
365 register int c;
366 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
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);
379 bp = xmalloc (malloc_size);
380 }
381 strcpy (bp, term);
382 goto ret;
383 }
384 #endif /* INTERNAL_TERMINAL */
385
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
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] == ':'))
398 dostounix_filename (termcap_name);
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)
428 termcap_name = TERMCAP_FILE;
429
430 /* Here we know we must search a file and termcap_name has its name. */
431
432 #ifdef MSDOS
433 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
434 #else
435 fd = open (termcap_name, O_RDONLY, 0);
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. */
442 buf.beg = xmalloc (buf.size + 1);
443 term = indirect ? indirect : (char *)name;
444
445 if (!bp)
446 {
447 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
448 bp = xmalloc (malloc_size);
449 }
450 tc_search_point = bp1 = bp;
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);
465 xfree (buf.beg);
466 if (malloc_size)
467 xfree (bp);
468 return 0;
469 }
470
471 /* Free old `term' if appropriate. */
472 if (term != name)
473 xfree (term);
474
475 /* If BP is malloc'd by us, make sure it is big enough. */
476 if (malloc_size)
477 {
478 ptrdiff_t offset1 = bp1 - bp, offset2 = tc_search_point - bp;
479 malloc_size = offset1 + buf.size;
480 bp = termcap_name = xrealloc (bp, malloc_size);
481 bp1 = termcap_name + offset1;
482 tc_search_point = termcap_name + offset2;
483 }
484
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. */
498 tc_search_point = find_capability (tc_search_point, "tc");
499 term = tgetst1 (tc_search_point, (char **) 0);
500 }
501
502 close (fd);
503 xfree (buf.beg);
504
505 if (malloc_size)
506 bp = xrealloc (bp, bp1 - bp + 1);
507
508 ret:
509 term_entry = bp;
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
519 static int
520 scan_file (char *str, int fd, register struct termcap_buffer *bufp)
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
556 static int
557 name_match (char *line, char *name)
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
571 static int
572 compare_contin (register char *str1, register char *str2)
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
611 static char *
612 gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end)
613 {
614 register char *end;
615 register int nread;
616 register char *buf = bufp->beg;
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 {
633 ptrdiff_t ptr_offset = bufp->ptr - buf;
634 ptrdiff_t append_end_offset = append_end - buf;
635 /* Add 1 to size to ensure room for terminating null. */
636 ptrdiff_t size = bufp->size + 1;
637 bufp->beg = buf = xpalloc (buf, &size, 1, -1, 1);
638 bufp->size = size - 1;
639 bufp->ptr = buf + ptr_offset;
640 append_end = buf + append_end_offset;
641 }
642 }
643 else
644 {
645 append_end -= bufp->ptr - buf;
646 memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf);
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
659 #include <stdio.h>
660
661 static void
662 tprint (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
682 int
683 main (int argc, char **argv)
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"));
705
706 return 0;
707 }
708
709 #endif /* TEST */