(speeds): Make it ints. Add some higher speeds.
[emacs.git] / src / termcap.c
blob0d77ae456045956cb4b313558ffa0f799963a326
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986, 1993, 1994 Free Software Foundation, Inc.
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.
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.
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. */
18 /* Emacs config.h may rename various library functions such as malloc. */
19 #ifdef HAVE_CONFIG_H
21 #include <config.h>
23 /* Get the O_* definitions for open et al. */
24 #include <sys/file.h>
25 #ifdef USG5
26 #include <fcntl.h>
27 #endif
29 #else /* not HAVE_CONFIG_H */
31 #if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
32 #define bcopy(s, d, n) memcpy ((d), (s), (n))
33 #endif
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
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #ifdef _POSIX_VERSION
48 #include <fcntl.h>
49 #endif
51 #endif /* not HAVE_CONFIG_H */
53 #ifndef NULL
54 #define NULL (char *) 0
55 #endif
57 #ifndef O_RDONLY
58 #define O_RDONLY 0
59 #endif
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. */
68 #ifndef BUFSIZE
69 #ifdef DEBUG
70 #define BUFSIZE bufsize
72 int bufsize = 128;
73 #else
74 #define BUFSIZE 2048
75 #endif
76 #endif
78 #ifndef TERMCAP_NAME
79 #define TERMCAP_NAME "/etc/termcap"
80 #endif
82 #ifndef emacs
83 static void
84 memory_out ()
86 write (2, "virtual memory exhausted\n", 25);
87 exit (1);
90 static char *
91 xmalloc (size)
92 unsigned size;
94 register char *tem = malloc (size);
96 if (!tem)
97 memory_out ();
98 return tem;
101 static char *
102 xrealloc (ptr, size)
103 char *ptr;
104 unsigned size;
106 register char *tem = realloc (ptr, size);
108 if (!tem)
109 memory_out ();
110 return tem;
112 #endif /* not emacs */
114 /* Looking up capabilities in the entry already found. */
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;
120 static char *tgetst1 ();
122 /* Search entry BP for capability CAP.
123 Return a pointer to the capability (in BP) if found,
124 0 if not found. */
126 static char *
127 find_capability (bp, cap)
128 register char *bp, *cap;
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;
139 tgetnum (cap)
140 char *cap;
142 register char *ptr = find_capability (term_entry, cap);
143 if (!ptr || ptr[-1] != '#')
144 return -1;
145 return atoi (ptr);
149 tgetflag (cap)
150 char *cap;
152 register char *ptr = find_capability (term_entry, cap);
153 return ptr && ptr[-1] == ':';
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'. */
161 char *
162 tgetstr (cap, area)
163 char *cap;
164 char **area;
166 register char *ptr = find_capability (term_entry, cap);
167 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
168 return NULL;
169 return tgetst1 (ptr, area);
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. */
176 static char esctab[]
177 = " \007\010 \033\014 \
178 \012 \
179 \015 \011 \013 \
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. */
189 static char *
190 tgetst1 (ptr, area)
191 char *ptr;
192 char **area;
194 register char *p, *r;
195 register int c;
196 register int size;
197 char *ret;
198 register int c1;
200 if (!ptr)
201 return NULL;
203 /* `ret' gets address of where to store the string. */
204 if (!area)
206 /* Compute size of block needed (may overestimate). */
207 p = ptr;
208 while ((c = *p++) && c != ':' && c != '\n')
210 ret = (char *) xmalloc (p - ptr + 1);
212 else
213 ret = *area;
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')
221 if (c == '^')
223 c = *p++;
224 if (c == '?')
225 c = 0177;
226 else
227 c &= 037;
229 else if (c == '\\')
231 c = *p++;
232 if (c >= '0' && c <= '7')
234 c -= '0';
235 size = 0;
237 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
239 c *= 8;
240 c += c1 - '0';
241 p++;
244 else if (c >= 0100 && c < 0200)
246 c1 = esctab[(c & ~040) - 0100];
247 if (c1 != ' ')
248 c = c1;
251 *r++ = c;
253 *r = '\0';
254 /* Update *AREA. */
255 if (area)
256 *area = r + 1;
257 return ret;
260 /* Outputting a string with padding. */
262 short ospeed;
263 /* If OSPEED is 0, we use this as the actual baud rate. */
264 int tputs_baud_rate;
265 char PC;
267 /* Actual baud rate if positive;
268 - baud rate / 100 if negative. */
270 static int speeds[] =
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, -288, -384, -576, -1152
278 #endif /* not VMS */
281 void
282 tputs (str, nlines, outfun)
283 register char *str;
284 int nlines;
285 register int (*outfun) ();
287 register int padcount = 0;
288 register int speed;
290 #ifdef emacs
291 extern baud_rate;
292 speed = baud_rate;
293 /* For quite high speeds, convert to the smaller
294 units to avoid overflow. */
295 if (speed > 10000)
296 speed = - speed / 100;
297 #else
298 if (ospeed == 0)
299 speed = tputs_baud_rate;
300 else
301 speed = speeds[ospeed];
302 #endif
304 if (!str)
305 return;
307 while (*str >= '0' && *str <= '9')
309 padcount += *str++ - '0';
310 padcount *= 10;
312 if (*str == '.')
314 str++;
315 padcount += *str++ - '0';
317 if (*str == '*')
319 str++;
320 padcount *= nlines;
322 while (*str)
323 (*outfun) (*str++);
325 /* PADCOUNT is now in units of tenths of msec.
326 SPEED is mesured in characters per 10 seconds
327 or in characters per .1 seconds (if negative).
328 We use the smaller units for larger speeds to avoid overflow. */
329 padcount *= speed;
330 padcount += 500;
331 padcount /= 1000;
332 if (speed < 0)
333 padcount = -padcount;
334 else
336 padcount += 50;
337 padcount /= 100;
340 while (padcount-- > 0)
341 (*outfun) (PC);
344 /* Finding the termcap entry in the termcap data base. */
346 struct buffer
348 char *beg;
349 int size;
350 char *ptr;
351 int ateof;
352 int full;
355 /* Forward declarations of static functions. */
357 static int scan_file ();
358 static char *gobble_line ();
359 static int compare_contin ();
360 static int name_match ();
362 #ifdef VMS
364 #include <rmsdef.h>
365 #include <fab.h>
366 #include <nam.h>
368 static int
369 valid_filename_p (fn)
370 char *fn;
372 struct FAB fab = cc$rms_fab;
373 struct NAM nam = cc$rms_nam;
374 char esa[NAM$C_MAXRSS];
376 fab.fab$l_fna = fn;
377 fab.fab$b_fns = strlen(fn);
378 fab.fab$l_nam = &nam;
379 fab.fab$l_fop = FAB$M_NAM;
381 nam.nam$l_esa = esa;
382 nam.nam$b_ess = sizeof esa;
384 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
387 #else /* !VMS */
389 #ifdef MSDOS /* MW, May 1993 */
390 static int
391 valid_filename_p (fn)
392 char *fn;
394 return *fn == '/' || fn[1] == ':';
396 #else
397 #define valid_filename_p(fn) (*(fn) == '/')
398 #endif
400 #endif /* !VMS */
402 /* Find the termcap entry data for terminal type NAME
403 and store it in the block that BP points to.
404 Record its address for future use.
406 If BP is null, space is dynamically allocated.
408 Return -1 if there is some difficulty accessing the data base
409 of terminal types,
410 0 if the data base is accessible but the type NAME is not defined
411 in it, and some other value otherwise. */
414 tgetent (bp, name)
415 char *bp, *name;
417 register char *termcap_name;
418 register int fd;
419 struct buffer buf;
420 register char *bp1;
421 char *bp2;
422 char *term;
423 int malloc_size = 0;
424 register int c;
425 char *tcenv; /* TERMCAP value, if it contains :tc=. */
426 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
427 int filep;
429 #ifdef INTERNAL_TERMINAL
430 /* For the internal terminal we don't want to read any termcap file,
431 so fake it. */
432 if (!strcmp (name, "internal"))
434 term = INTERNAL_TERMINAL;
435 if (!bp)
437 malloc_size = 1 + strlen (term);
438 bp = (char *) xmalloc (malloc_size);
440 strcpy (bp, term);
441 goto ret;
443 #endif /* INTERNAL_TERMINAL */
445 termcap_name = getenv ("TERMCAP");
446 if (termcap_name && *termcap_name == '\0')
447 termcap_name = NULL;
448 #if defined (MSDOS) && !defined (TEST)
449 if (termcap_name && (*termcap_name == '\\'
450 || *termcap_name == '/'
451 || termcap_name[1] == ':'))
452 dostounix_filename(termcap_name);
453 #endif
455 filep = termcap_name && valid_filename_p (termcap_name);
457 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
458 it is a file name to use instead of /etc/termcap.
459 If it is non-null and does not start with /,
460 it is the entry itself, but only if
461 the name the caller requested matches the TERM variable. */
463 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
465 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
466 if (!indirect)
468 if (!bp)
469 bp = termcap_name;
470 else
471 strcpy (bp, termcap_name);
472 goto ret;
474 else
475 { /* It has tc=. Need to read /etc/termcap. */
476 tcenv = termcap_name;
477 termcap_name = NULL;
481 if (!termcap_name || !filep)
482 termcap_name = TERMCAP_NAME;
484 /* Here we know we must search a file and termcap_name has its name. */
486 #ifdef MSDOS
487 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
488 #else
489 fd = open (termcap_name, O_RDONLY, 0);
490 #endif
491 if (fd < 0)
492 return -1;
494 buf.size = BUFSIZE;
495 /* Add 1 to size to ensure room for terminating null. */
496 buf.beg = (char *) xmalloc (buf.size + 1);
497 term = indirect ? indirect : name;
499 if (!bp)
501 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
502 bp = (char *) xmalloc (malloc_size);
504 bp1 = bp;
506 if (indirect)
507 /* Copy the data from the environment variable. */
509 strcpy (bp, tcenv);
510 bp1 += strlen (tcenv);
513 while (term)
515 /* Scan the file, reading it via buf, till find start of main entry. */
516 if (scan_file (term, fd, &buf) == 0)
518 close (fd);
519 free (buf.beg);
520 if (malloc_size)
521 free (bp);
522 return 0;
525 /* Free old `term' if appropriate. */
526 if (term != name)
527 free (term);
529 /* If BP is malloc'd by us, make sure it is big enough. */
530 if (malloc_size)
532 malloc_size = bp1 - bp + buf.size;
533 termcap_name = (char *) xrealloc (bp, malloc_size);
534 bp1 += termcap_name - bp;
535 bp = termcap_name;
538 bp2 = bp1;
540 /* Copy the line of the entry from buf into bp. */
541 termcap_name = buf.ptr;
542 while ((*bp1++ = c = *termcap_name++) && c != '\n')
543 /* Drop out any \ newline sequence. */
544 if (c == '\\' && *termcap_name == '\n')
546 bp1--;
547 termcap_name++;
549 *bp1 = '\0';
551 /* Does this entry refer to another terminal type's entry?
552 If something is found, copy it into heap and null-terminate it. */
553 term = tgetst1 (find_capability (bp2, "tc"), (char **) 0);
556 close (fd);
557 free (buf.beg);
559 if (malloc_size)
560 bp = (char *) xrealloc (bp, bp1 - bp + 1);
562 ret:
563 term_entry = bp;
564 if (malloc_size)
565 return (int) bp;
566 return 1;
569 /* Given file open on FD and buffer BUFP,
570 scan the file from the beginning until a line is found
571 that starts the entry for terminal type STR.
572 Return 1 if successful, with that line in BUFP,
573 or 0 if no entry is found in the file. */
575 static int
576 scan_file (str, fd, bufp)
577 char *str;
578 int fd;
579 register struct buffer *bufp;
581 register char *end;
583 bufp->ptr = bufp->beg;
584 bufp->full = 0;
585 bufp->ateof = 0;
586 *bufp->ptr = '\0';
588 lseek (fd, 0L, 0);
590 while (!bufp->ateof)
592 /* Read a line into the buffer. */
593 end = NULL;
596 /* if it is continued, append another line to it,
597 until a non-continued line ends. */
598 end = gobble_line (fd, bufp, end);
600 while (!bufp->ateof && end[-2] == '\\');
602 if (*bufp->ptr != '#'
603 && name_match (bufp->ptr, str))
604 return 1;
606 /* Discard the line just processed. */
607 bufp->ptr = end;
609 return 0;
612 /* Return nonzero if NAME is one of the names specified
613 by termcap entry LINE. */
615 static int
616 name_match (line, name)
617 char *line, *name;
619 register char *tem;
621 if (!compare_contin (line, name))
622 return 1;
623 /* This line starts an entry. Is it the right one? */
624 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
625 if (*tem == '|' && !compare_contin (tem + 1, name))
626 return 1;
628 return 0;
631 static int
632 compare_contin (str1, str2)
633 register char *str1, *str2;
635 register int c1, c2;
636 while (1)
638 c1 = *str1++;
639 c2 = *str2++;
640 while (c1 == '\\' && *str1 == '\n')
642 str1++;
643 while ((c1 = *str1++) == ' ' || c1 == '\t');
645 if (c2 == '\0')
647 /* End of type being looked up. */
648 if (c1 == '|' || c1 == ':')
649 /* If end of name in data base, we win. */
650 return 0;
651 else
652 return 1;
654 else if (c1 != c2)
655 return 1;
659 /* Make sure that the buffer <- BUFP contains a full line
660 of the file open on FD, starting at the place BUFP->ptr
661 points to. Can read more of the file, discard stuff before
662 BUFP->ptr, or make the buffer bigger.
664 Return the pointer to after the newline ending the line,
665 or to the end of the file, if there is no newline to end it.
667 Can also merge on continuation lines. If APPEND_END is
668 non-null, it points past the newline of a line that is
669 continued; we add another line onto it and regard the whole
670 thing as one line. The caller decides when a line is continued. */
672 static char *
673 gobble_line (fd, bufp, append_end)
674 int fd;
675 register struct buffer *bufp;
676 char *append_end;
678 register char *end;
679 register int nread;
680 register char *buf = bufp->beg;
681 register char *tem;
683 if (!append_end)
684 append_end = bufp->ptr;
686 while (1)
688 end = append_end;
689 while (*end && *end != '\n') end++;
690 if (*end)
691 break;
692 if (bufp->ateof)
693 return buf + bufp->full;
694 if (bufp->ptr == buf)
696 if (bufp->full == bufp->size)
698 bufp->size *= 2;
699 /* Add 1 to size to ensure room for terminating null. */
700 tem = (char *) xrealloc (buf, bufp->size + 1);
701 bufp->ptr = (bufp->ptr - buf) + tem;
702 append_end = (append_end - buf) + tem;
703 bufp->beg = buf = tem;
706 else
708 append_end -= bufp->ptr - buf;
709 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
710 bufp->ptr = buf;
712 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
713 bufp->ateof = 1;
714 bufp->full += nread;
715 buf[bufp->full] = '\0';
717 return end + 1;
720 #ifdef TEST
722 #ifdef NULL
723 #undef NULL
724 #endif
726 #include <stdio.h>
728 main (argc, argv)
729 int argc;
730 char **argv;
732 char *term;
733 char *buf;
735 term = argv[1];
736 printf ("TERM: %s\n", term);
738 buf = (char *) tgetent (0, term);
739 if ((int) buf <= 0)
741 printf ("No entry.\n");
742 return 0;
745 printf ("Entry: %s\n", buf);
747 tprint ("cm");
748 tprint ("AL");
750 printf ("co: %d\n", tgetnum ("co"));
751 printf ("am: %d\n", tgetflag ("am"));
754 tprint (cap)
755 char *cap;
757 char *x = tgetstr (cap, 0);
758 register char *y;
760 printf ("%s: ", cap);
761 if (x)
763 for (y = x; *y; y++)
764 if (*y <= ' ' || *y == 0177)
765 printf ("\\%0o", *y);
766 else
767 putchar (*y);
768 free (x);
770 else
771 printf ("none");
772 putchar ('\n');
775 #endif /* TEST */