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