Handle floating point errors in ns-fonts (Bug#7887).
[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 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
29 #ifndef NULL
30 #define NULL (char *) 0
31 #endif
32
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
44 int bufsize = 128;
45 #else
46 #define BUFSIZE 2048
47 #endif
48 #endif
49
50 #ifndef TERMCAP_FILE
51 #define TERMCAP_FILE "/etc/termcap"
52 #endif
53
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. */
59 static char *term_entry;
60
61 static char *tgetst1 (char *ptr, char **area);
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
67 static char *
68 find_capability (register char *bp, register char *cap)
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
78 int
79 tgetnum (char *cap)
80 {
81 register char *ptr = find_capability (term_entry, cap);
82 if (!ptr || ptr[-1] != '#')
83 return -1;
84 return atoi (ptr);
85 }
86
87 int
88 tgetflag (char *cap)
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
99 char *
100 tgetstr (char *cap, char **area)
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
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
113 static const char esctab[]
114 = " \057\026 \047\014 \
115 \025 \015 \
116 \005 \013 \
117 ";
118 #else
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
123 static const char esctab[]
124 = " \007\010 \033\014 \
125 \012 \
126 \015 \011 \013 \
127 ";
128 #endif
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
137 static char *
138 tgetst1 (char *ptr, char **area)
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 ;
156 ret = (char *) xmalloc (p - ptr + 1);
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 == '^')
168 {
169 c = *p++;
170 if (c == '?')
171 c = 0177;
172 else
173 c &= 037;
174 }
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 }
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
198 else if (c >= 0100 && c < 0200)
199 {
200 c1 = esctab[(c & ~040) - 0100];
201 if (c1 != ' ')
202 c = c1;
203 }
204 #endif
205 }
206 *r++ = c;
207 }
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)
249 memcpy (wp, cut[i].beg, cut[i].len);
250 r = wp;
251 }
252 }
253
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
263 char PC;
264
265 void
266 tputs (register char *str, int nlines, register int (*outfun) (/* ??? */))
267 {
268 register int padcount = 0;
269 register int speed;
270
271 extern EMACS_INT baud_rate;
272 speed = baud_rate;
273 /* For quite high speeds, convert to the smaller
274 units to avoid overflow. */
275 if (speed > 10000)
276 speed = - speed / 100;
277
278 if (!str)
279 return;
280
281 while (*str >= '0' && *str <= '9')
282 {
283 padcount += *str++ - '0';
284 padcount *= 10;
285 }
286 if (*str == '.')
287 {
288 str++;
289 padcount += *str++ - '0';
290 }
291 if (*str == '*')
292 {
293 str++;
294 padcount *= nlines;
295 }
296 while (*str)
297 (*outfun) (*str++);
298
299 /* PADCOUNT is now in units of tenths of msec.
300 SPEED is measured in characters per 10 seconds
301 or in characters per .1 seconds (if negative).
302 We use the smaller units for larger speeds to avoid overflow. */
303 padcount *= speed;
304 padcount += 500;
305 padcount /= 1000;
306 if (speed < 0)
307 padcount = -padcount;
308 else
309 {
310 padcount += 50;
311 padcount /= 100;
312 }
313
314 while (padcount-- > 0)
315 (*outfun) (PC);
316 }
317 \f
318 /* Finding the termcap entry in the termcap data base. */
319
320 struct termcap_buffer
321 {
322 char *beg;
323 int size;
324 char *ptr;
325 int ateof;
326 int full;
327 };
328
329 /* Forward declarations of static functions. */
330
331 static int scan_file (char *str, int fd, register struct termcap_buffer *bufp);
332 static char *gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end);
333 static int compare_contin (register char *str1, register char *str2);
334 static int name_match (char *line, char *name);
335
336 #ifdef MSDOS /* MW, May 1993 */
337 static int
338 valid_filename_p (fn)
339 char *fn;
340 {
341 return *fn == '/' || fn[1] == ':';
342 }
343 #else
344 #define valid_filename_p(fn) (*(fn) == '/')
345 #endif
346
347 /* Find the termcap entry data for terminal type NAME
348 and store it in the block that BP points to.
349 Record its address for future use.
350
351 If BP is null, space is dynamically allocated.
352
353 Return -1 if there is some difficulty accessing the data base
354 of terminal types,
355 0 if the data base is accessible but the type NAME is not defined
356 in it, and some other value otherwise. */
357
358 int
359 tgetent (char *bp, char *name)
360 {
361 register char *termcap_name;
362 register int fd;
363 struct termcap_buffer buf;
364 register char *bp1;
365 char *tc_search_point;
366 char *term;
367 int malloc_size = 0;
368 register int c;
369 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
370 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
371 int filep;
372
373 #ifdef INTERNAL_TERMINAL
374 /* For the internal terminal we don't want to read any termcap file,
375 so fake it. */
376 if (!strcmp (name, "internal"))
377 {
378 term = INTERNAL_TERMINAL;
379 if (!bp)
380 {
381 malloc_size = 1 + strlen (term);
382 bp = (char *) xmalloc (malloc_size);
383 }
384 strcpy (bp, term);
385 goto ret;
386 }
387 #endif /* INTERNAL_TERMINAL */
388
389 /* For compatibility with programs like `less' that want to
390 put data in the termcap buffer themselves as a fallback. */
391 if (bp)
392 term_entry = bp;
393
394 termcap_name = getenv ("TERMCAP");
395 if (termcap_name && *termcap_name == '\0')
396 termcap_name = NULL;
397 #if defined (MSDOS) && !defined (TEST)
398 if (termcap_name && (*termcap_name == '\\'
399 || *termcap_name == '/'
400 || termcap_name[1] == ':'))
401 dostounix_filename(termcap_name);
402 #endif
403
404 filep = termcap_name && valid_filename_p (termcap_name);
405
406 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
407 it is a file name to use instead of /etc/termcap.
408 If it is non-null and does not start with /,
409 it is the entry itself, but only if
410 the name the caller requested matches the TERM variable. */
411
412 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
413 {
414 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
415 if (!indirect)
416 {
417 if (!bp)
418 bp = termcap_name;
419 else
420 strcpy (bp, termcap_name);
421 goto ret;
422 }
423 else
424 { /* It has tc=. Need to read /etc/termcap. */
425 tcenv = termcap_name;
426 termcap_name = NULL;
427 }
428 }
429
430 if (!termcap_name || !filep)
431 termcap_name = TERMCAP_FILE;
432
433 /* Here we know we must search a file and termcap_name has its name. */
434
435 #ifdef MSDOS
436 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
437 #else
438 fd = open (termcap_name, O_RDONLY, 0);
439 #endif
440 if (fd < 0)
441 return -1;
442
443 buf.size = BUFSIZE;
444 /* Add 1 to size to ensure room for terminating null. */
445 buf.beg = (char *) xmalloc (buf.size + 1);
446 term = indirect ? indirect : name;
447
448 if (!bp)
449 {
450 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
451 bp = (char *) xmalloc (malloc_size);
452 }
453 tc_search_point = bp1 = bp;
454
455 if (indirect)
456 /* Copy the data from the environment variable. */
457 {
458 strcpy (bp, tcenv);
459 bp1 += strlen (tcenv);
460 }
461
462 while (term)
463 {
464 /* Scan the file, reading it via buf, till find start of main entry. */
465 if (scan_file (term, fd, &buf) == 0)
466 {
467 close (fd);
468 free (buf.beg);
469 if (malloc_size)
470 free (bp);
471 return 0;
472 }
473
474 /* Free old `term' if appropriate. */
475 if (term != name)
476 free (term);
477
478 /* If BP is malloc'd by us, make sure it is big enough. */
479 if (malloc_size)
480 {
481 int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
482 malloc_size = offset1 + buf.size;
483 bp = termcap_name = (char *) xrealloc (bp, malloc_size);
484 bp1 = termcap_name + offset1;
485 tc_search_point = termcap_name + offset2;
486 }
487
488 /* Copy the line of the entry from buf into bp. */
489 termcap_name = buf.ptr;
490 while ((*bp1++ = c = *termcap_name++) && c != '\n')
491 /* Drop out any \ newline sequence. */
492 if (c == '\\' && *termcap_name == '\n')
493 {
494 bp1--;
495 termcap_name++;
496 }
497 *bp1 = '\0';
498
499 /* Does this entry refer to another terminal type's entry?
500 If something is found, copy it into heap and null-terminate it. */
501 tc_search_point = find_capability (tc_search_point, "tc");
502 term = tgetst1 (tc_search_point, (char **) 0);
503 }
504
505 close (fd);
506 free (buf.beg);
507
508 if (malloc_size)
509 bp = (char *) xrealloc (bp, bp1 - bp + 1);
510
511 ret:
512 term_entry = bp;
513 return 1;
514 }
515
516 /* Given file open on FD and buffer BUFP,
517 scan the file from the beginning until a line is found
518 that starts the entry for terminal type STR.
519 Return 1 if successful, with that line in BUFP,
520 or 0 if no entry is found in the file. */
521
522 static int
523 scan_file (char *str, int fd, register struct termcap_buffer *bufp)
524 {
525 register char *end;
526
527 bufp->ptr = bufp->beg;
528 bufp->full = 0;
529 bufp->ateof = 0;
530 *bufp->ptr = '\0';
531
532 lseek (fd, 0L, 0);
533
534 while (!bufp->ateof)
535 {
536 /* Read a line into the buffer. */
537 end = NULL;
538 do
539 {
540 /* if it is continued, append another line to it,
541 until a non-continued line ends. */
542 end = gobble_line (fd, bufp, end);
543 }
544 while (!bufp->ateof && end[-2] == '\\');
545
546 if (*bufp->ptr != '#'
547 && name_match (bufp->ptr, str))
548 return 1;
549
550 /* Discard the line just processed. */
551 bufp->ptr = end;
552 }
553 return 0;
554 }
555
556 /* Return nonzero if NAME is one of the names specified
557 by termcap entry LINE. */
558
559 static int
560 name_match (char *line, char *name)
561 {
562 register char *tem;
563
564 if (!compare_contin (line, name))
565 return 1;
566 /* This line starts an entry. Is it the right one? */
567 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
568 if (*tem == '|' && !compare_contin (tem + 1, name))
569 return 1;
570
571 return 0;
572 }
573
574 static int
575 compare_contin (register char *str1, register char *str2)
576 {
577 register int c1, c2;
578 while (1)
579 {
580 c1 = *str1++;
581 c2 = *str2++;
582 while (c1 == '\\' && *str1 == '\n')
583 {
584 str1++;
585 while ((c1 = *str1++) == ' ' || c1 == '\t');
586 }
587 if (c2 == '\0')
588 {
589 /* End of type being looked up. */
590 if (c1 == '|' || c1 == ':')
591 /* If end of name in data base, we win. */
592 return 0;
593 else
594 return 1;
595 }
596 else if (c1 != c2)
597 return 1;
598 }
599 }
600
601 /* Make sure that the buffer <- BUFP contains a full line
602 of the file open on FD, starting at the place BUFP->ptr
603 points to. Can read more of the file, discard stuff before
604 BUFP->ptr, or make the buffer bigger.
605
606 Return the pointer to after the newline ending the line,
607 or to the end of the file, if there is no newline to end it.
608
609 Can also merge on continuation lines. If APPEND_END is
610 non-null, it points past the newline of a line that is
611 continued; we add another line onto it and regard the whole
612 thing as one line. The caller decides when a line is continued. */
613
614 static char *
615 gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end)
616 {
617 register char *end;
618 register int nread;
619 register char *buf = bufp->beg;
620 register char *tem;
621
622 if (!append_end)
623 append_end = bufp->ptr;
624
625 while (1)
626 {
627 end = append_end;
628 while (*end && *end != '\n') end++;
629 if (*end)
630 break;
631 if (bufp->ateof)
632 return buf + bufp->full;
633 if (bufp->ptr == buf)
634 {
635 if (bufp->full == bufp->size)
636 {
637 bufp->size *= 2;
638 /* Add 1 to size to ensure room for terminating null. */
639 tem = (char *) xrealloc (buf, bufp->size + 1);
640 bufp->ptr = (bufp->ptr - buf) + tem;
641 append_end = (append_end - buf) + tem;
642 bufp->beg = buf = tem;
643 }
644 }
645 else
646 {
647 append_end -= bufp->ptr - buf;
648 memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf);
649 bufp->ptr = buf;
650 }
651 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
652 bufp->ateof = 1;
653 bufp->full += nread;
654 buf[bufp->full] = '\0';
655 }
656 return end + 1;
657 }
658 \f
659 #ifdef TEST
660
661 #ifdef NULL
662 #undef NULL
663 #endif
664
665 #include <stdio.h>
666
667 main (argc, argv)
668 int argc;
669 char **argv;
670 {
671 char *term;
672 char *buf;
673
674 term = argv[1];
675 printf ("TERM: %s\n", term);
676
677 buf = (char *) tgetent (0, term);
678 if ((int) buf <= 0)
679 {
680 printf ("No entry.\n");
681 return 0;
682 }
683
684 printf ("Entry: %s\n", buf);
685
686 tprint ("cm");
687 tprint ("AL");
688
689 printf ("co: %d\n", tgetnum ("co"));
690 printf ("am: %d\n", tgetflag ("am"));
691 }
692
693 tprint (cap)
694 char *cap;
695 {
696 char *x = tgetstr (cap, 0);
697 register char *y;
698
699 printf ("%s: ", cap);
700 if (x)
701 {
702 for (y = x; *y; y++)
703 if (*y <= ' ' || *y == 0177)
704 printf ("\\%0o", *y);
705 else
706 putchar (*y);
707 free (x);
708 }
709 else
710 printf ("none");
711 putchar ('\n');
712 }
713
714 #endif /* TEST */
715