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