(mm-inline-media-tests): Add
[emacs.git] / src / termcap.c
blobc9bf1a40b52214a24f69f4b72c80fbcfb2bd3fe9
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 86, 93, 94, 95, 2000 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, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 /* Emacs config.h may rename various library functions such as malloc. */
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #ifdef emacs
26 #include <lisp.h> /* xmalloc is here */
27 /* Get the O_* definitions for open et al. */
28 #include <sys/file.h>
29 #ifdef HAVE_FCNTL_H
30 #include <fcntl.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
36 #else /* not emacs */
38 #ifdef STDC_HEADERS
39 #include <stdlib.h>
40 #include <string.h>
41 #else
42 char *getenv ();
43 char *malloc ();
44 char *realloc ();
45 #endif
47 /* Do this after the include, in case string.h prototypes bcopy. */
48 #if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
49 #define bcopy(s, d, n) memcpy ((d), (s), (n))
50 #endif
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #ifdef _POSIX_VERSION
56 #include <fcntl.h>
57 #endif
59 #endif /* not emacs */
61 #ifndef NULL
62 #define NULL (char *) 0
63 #endif
65 #ifndef O_RDONLY
66 #define O_RDONLY 0
67 #endif
69 /* BUFSIZE is the initial size allocated for the buffer
70 for reading the termcap file.
71 It is not a limit.
72 Make it large normally for speed.
73 Make it variable when debugging, so can exercise
74 increasing the space dynamically. */
76 #ifndef BUFSIZE
77 #ifdef DEBUG
78 #define BUFSIZE bufsize
80 int bufsize = 128;
81 #else
82 #define BUFSIZE 2048
83 #endif
84 #endif
86 #ifndef TERMCAP_FILE
87 #define TERMCAP_FILE "/etc/termcap"
88 #endif
90 #ifndef emacs
91 static void
92 memory_out ()
94 write (2, "virtual memory exhausted\n", 25);
95 exit (1);
98 static char *
99 xmalloc (size)
100 unsigned size;
102 register char *tem = malloc (size);
104 if (!tem)
105 memory_out ();
106 return tem;
109 static char *
110 xrealloc (ptr, size)
111 char *ptr;
112 unsigned size;
114 register char *tem = realloc (ptr, size);
116 if (!tem)
117 memory_out ();
118 return tem;
120 #endif /* not emacs */
122 /* Looking up capabilities in the entry already found. */
124 /* The pointer to the data made by tgetent is left here
125 for tgetnum, tgetflag and tgetstr to find. */
126 static char *term_entry;
128 static char *tgetst1 ();
130 /* Search entry BP for capability CAP.
131 Return a pointer to the capability (in BP) if found,
132 0 if not found. */
134 static char *
135 find_capability (bp, cap)
136 register char *bp, *cap;
138 for (; *bp; bp++)
139 if (bp[0] == ':'
140 && bp[1] == cap[0]
141 && bp[2] == cap[1])
142 return &bp[4];
143 return NULL;
147 tgetnum (cap)
148 char *cap;
150 register char *ptr = find_capability (term_entry, cap);
151 if (!ptr || ptr[-1] != '#')
152 return -1;
153 return atoi (ptr);
157 tgetflag (cap)
158 char *cap;
160 register char *ptr = find_capability (term_entry, cap);
161 return ptr && ptr[-1] == ':';
164 /* Look up a string-valued capability CAP.
165 If AREA is non-null, it points to a pointer to a block in which
166 to store the string. That pointer is advanced over the space used.
167 If AREA is null, space is allocated with `malloc'. */
169 char *
170 tgetstr (cap, area)
171 char *cap;
172 char **area;
174 register char *ptr = find_capability (term_entry, cap);
175 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
176 return NULL;
177 return tgetst1 (ptr, area);
180 #ifdef IS_EBCDIC_HOST
181 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
182 gives meaning of character following \, or a space if no special meaning.
183 Sixteen characters per line within the string. */
185 static char esctab[]
186 = " \057\026 \047\014 \
187 \025 \015 \
188 \005 \013 \
190 #else
191 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
192 gives meaning of character following \, or a space if no special meaning.
193 Eight characters per line within the string. */
195 static char esctab[]
196 = " \007\010 \033\014 \
197 \012 \
198 \015 \011 \013 \
200 #endif
202 /* PTR points to a string value inside a termcap entry.
203 Copy that value, processing \ and ^ abbreviations,
204 into the block that *AREA points to,
205 or to newly allocated storage if AREA is NULL.
206 Return the address to which we copied the value,
207 or NULL if PTR is NULL. */
209 static char *
210 tgetst1 (ptr, area)
211 char *ptr;
212 char **area;
214 register char *p, *r;
215 register int c;
216 register int size;
217 char *ret;
218 register int c1;
220 if (!ptr)
221 return NULL;
223 /* `ret' gets address of where to store the string. */
224 if (!area)
226 /* Compute size of block needed (may overestimate). */
227 p = ptr;
228 while ((c = *p++) && c != ':' && c != '\n')
230 ret = (char *) xmalloc (p - ptr + 1);
232 else
233 ret = *area;
235 /* Copy the string value, stopping at null or colon.
236 Also process ^ and \ abbreviations. */
237 p = ptr;
238 r = ret;
239 while ((c = *p++) && c != ':' && c != '\n')
241 if (c == '^')
243 c = *p++;
244 if (c == '?')
245 c = 0177;
246 else
247 c &= 037;
249 else if (c == '\\')
251 c = *p++;
252 if (c >= '0' && c <= '7')
254 c -= '0';
255 size = 0;
257 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
259 c *= 8;
260 c += c1 - '0';
261 p++;
264 #ifdef IS_EBCDIC_HOST
265 else if (c >= 0200 && c < 0360)
267 c1 = esctab[(c & ~0100) - 0200];
268 if (c1 != ' ')
269 c = c1;
271 #else
272 else if (c >= 0100 && c < 0200)
274 c1 = esctab[(c & ~040) - 0100];
275 if (c1 != ' ')
276 c = c1;
278 #endif
280 *r++ = c;
282 *r = '\0';
283 /* Update *AREA. */
284 if (area)
285 *area = r + 1;
286 return ret;
289 /* Outputting a string with padding. */
291 /* If OSPEED is 0, we use this as the actual baud rate. */
292 int tputs_baud_rate;
293 char PC;
295 /* Actual baud rate if positive;
296 - baud rate / 100 if negative. */
298 static int speeds[] =
300 #ifdef VMS
301 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
302 -20, -24, -36, -48, -72, -96, -192
303 #else /* not VMS */
304 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
305 -18, -24, -48, -96, -192, -288, -384, -576, -1152
306 #endif /* not VMS */
309 void
310 tputs (str, nlines, outfun)
311 register char *str;
312 int nlines;
313 register int (*outfun) ();
315 register int padcount = 0;
316 register int speed;
318 extern int baud_rate;
319 speed = baud_rate;
320 /* For quite high speeds, convert to the smaller
321 units to avoid overflow. */
322 if (speed > 10000)
323 speed = - speed / 100;
325 if (!str)
326 return;
328 while (*str >= '0' && *str <= '9')
330 padcount += *str++ - '0';
331 padcount *= 10;
333 if (*str == '.')
335 str++;
336 padcount += *str++ - '0';
338 if (*str == '*')
340 str++;
341 padcount *= nlines;
343 while (*str)
344 (*outfun) (*str++);
346 /* PADCOUNT is now in units of tenths of msec.
347 SPEED is measured in characters per 10 seconds
348 or in characters per .1 seconds (if negative).
349 We use the smaller units for larger speeds to avoid overflow. */
350 padcount *= speed;
351 padcount += 500;
352 padcount /= 1000;
353 if (speed < 0)
354 padcount = -padcount;
355 else
357 padcount += 50;
358 padcount /= 100;
361 while (padcount-- > 0)
362 (*outfun) (PC);
365 /* Finding the termcap entry in the termcap data base. */
367 struct termcap_buffer
369 char *beg;
370 int size;
371 char *ptr;
372 int ateof;
373 int full;
376 /* Forward declarations of static functions. */
378 static int scan_file ();
379 static char *gobble_line ();
380 static int compare_contin ();
381 static int name_match ();
383 #ifdef VMS
385 #include <rmsdef.h>
386 #include <fab.h>
387 #include <nam.h>
389 static int
390 valid_filename_p (fn)
391 char *fn;
393 struct FAB fab = cc$rms_fab;
394 struct NAM nam = cc$rms_nam;
395 char esa[NAM$C_MAXRSS];
397 fab.fab$l_fna = fn;
398 fab.fab$b_fns = strlen(fn);
399 fab.fab$l_nam = &nam;
400 fab.fab$l_fop = FAB$M_NAM;
402 nam.nam$l_esa = esa;
403 nam.nam$b_ess = sizeof esa;
405 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
408 #else /* !VMS */
410 #ifdef MSDOS /* MW, May 1993 */
411 static int
412 valid_filename_p (fn)
413 char *fn;
415 return *fn == '/' || fn[1] == ':';
417 #else
418 #define valid_filename_p(fn) (*(fn) == '/')
419 #endif
421 #endif /* !VMS */
423 /* Find the termcap entry data for terminal type NAME
424 and store it in the block that BP points to.
425 Record its address for future use.
427 If BP is null, space is dynamically allocated.
429 Return -1 if there is some difficulty accessing the data base
430 of terminal types,
431 0 if the data base is accessible but the type NAME is not defined
432 in it, and some other value otherwise. */
435 tgetent (bp, name)
436 char *bp, *name;
438 register char *termcap_name;
439 register int fd;
440 struct termcap_buffer buf;
441 register char *bp1;
442 char *tc_search_point;
443 char *term;
444 int malloc_size = 0;
445 register int c;
446 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
447 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
448 int filep;
450 #ifdef INTERNAL_TERMINAL
451 /* For the internal terminal we don't want to read any termcap file,
452 so fake it. */
453 if (!strcmp (name, "internal"))
455 term = INTERNAL_TERMINAL;
456 if (!bp)
458 malloc_size = 1 + strlen (term);
459 bp = (char *) xmalloc (malloc_size);
461 strcpy (bp, term);
462 goto ret;
464 #endif /* INTERNAL_TERMINAL */
466 /* For compatibility with programs like `less' that want to
467 put data in the termcap buffer themselves as a fallback. */
468 if (bp)
469 term_entry = bp;
471 termcap_name = getenv ("TERMCAP");
472 if (termcap_name && *termcap_name == '\0')
473 termcap_name = NULL;
474 #if defined (MSDOS) && !defined (TEST)
475 if (termcap_name && (*termcap_name == '\\'
476 || *termcap_name == '/'
477 || termcap_name[1] == ':'))
478 dostounix_filename(termcap_name);
479 #endif
481 filep = termcap_name && valid_filename_p (termcap_name);
483 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
484 it is a file name to use instead of /etc/termcap.
485 If it is non-null and does not start with /,
486 it is the entry itself, but only if
487 the name the caller requested matches the TERM variable. */
489 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
491 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
492 if (!indirect)
494 if (!bp)
495 bp = termcap_name;
496 else
497 strcpy (bp, termcap_name);
498 goto ret;
500 else
501 { /* It has tc=. Need to read /etc/termcap. */
502 tcenv = termcap_name;
503 termcap_name = NULL;
507 if (!termcap_name || !filep)
508 termcap_name = TERMCAP_FILE;
510 /* Here we know we must search a file and termcap_name has its name. */
512 #ifdef MSDOS
513 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
514 #else
515 fd = open (termcap_name, O_RDONLY, 0);
516 #endif
517 if (fd < 0)
518 return -1;
520 buf.size = BUFSIZE;
521 /* Add 1 to size to ensure room for terminating null. */
522 buf.beg = (char *) xmalloc (buf.size + 1);
523 term = indirect ? indirect : name;
525 if (!bp)
527 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
528 bp = (char *) xmalloc (malloc_size);
530 tc_search_point = bp1 = bp;
532 if (indirect)
533 /* Copy the data from the environment variable. */
535 strcpy (bp, tcenv);
536 bp1 += strlen (tcenv);
539 while (term)
541 /* Scan the file, reading it via buf, till find start of main entry. */
542 if (scan_file (term, fd, &buf) == 0)
544 close (fd);
545 free (buf.beg);
546 if (malloc_size)
547 free (bp);
548 return 0;
551 /* Free old `term' if appropriate. */
552 if (term != name)
553 free (term);
555 /* If BP is malloc'd by us, make sure it is big enough. */
556 if (malloc_size)
558 int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
559 malloc_size = offset1 + buf.size;
560 bp = termcap_name = (char *) xrealloc (bp, malloc_size);
561 bp1 = termcap_name + offset1;
562 tc_search_point = termcap_name + offset2;
565 /* Copy the line of the entry from buf into bp. */
566 termcap_name = buf.ptr;
567 while ((*bp1++ = c = *termcap_name++) && c != '\n')
568 /* Drop out any \ newline sequence. */
569 if (c == '\\' && *termcap_name == '\n')
571 bp1--;
572 termcap_name++;
574 *bp1 = '\0';
576 /* Does this entry refer to another terminal type's entry?
577 If something is found, copy it into heap and null-terminate it. */
578 tc_search_point = find_capability (tc_search_point, "tc");
579 term = tgetst1 (tc_search_point, (char **) 0);
582 close (fd);
583 free (buf.beg);
585 if (malloc_size)
586 bp = (char *) xrealloc (bp, bp1 - bp + 1);
588 ret:
589 term_entry = bp;
590 return 1;
593 /* Given file open on FD and buffer BUFP,
594 scan the file from the beginning until a line is found
595 that starts the entry for terminal type STR.
596 Return 1 if successful, with that line in BUFP,
597 or 0 if no entry is found in the file. */
599 static int
600 scan_file (str, fd, bufp)
601 char *str;
602 int fd;
603 register struct termcap_buffer *bufp;
605 register char *end;
607 bufp->ptr = bufp->beg;
608 bufp->full = 0;
609 bufp->ateof = 0;
610 *bufp->ptr = '\0';
612 lseek (fd, 0L, 0);
614 while (!bufp->ateof)
616 /* Read a line into the buffer. */
617 end = NULL;
620 /* if it is continued, append another line to it,
621 until a non-continued line ends. */
622 end = gobble_line (fd, bufp, end);
624 while (!bufp->ateof && end[-2] == '\\');
626 if (*bufp->ptr != '#'
627 && name_match (bufp->ptr, str))
628 return 1;
630 /* Discard the line just processed. */
631 bufp->ptr = end;
633 return 0;
636 /* Return nonzero if NAME is one of the names specified
637 by termcap entry LINE. */
639 static int
640 name_match (line, name)
641 char *line, *name;
643 register char *tem;
645 if (!compare_contin (line, name))
646 return 1;
647 /* This line starts an entry. Is it the right one? */
648 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
649 if (*tem == '|' && !compare_contin (tem + 1, name))
650 return 1;
652 return 0;
655 static int
656 compare_contin (str1, str2)
657 register char *str1, *str2;
659 register int c1, c2;
660 while (1)
662 c1 = *str1++;
663 c2 = *str2++;
664 while (c1 == '\\' && *str1 == '\n')
666 str1++;
667 while ((c1 = *str1++) == ' ' || c1 == '\t');
669 if (c2 == '\0')
671 /* End of type being looked up. */
672 if (c1 == '|' || c1 == ':')
673 /* If end of name in data base, we win. */
674 return 0;
675 else
676 return 1;
678 else if (c1 != c2)
679 return 1;
683 /* Make sure that the buffer <- BUFP contains a full line
684 of the file open on FD, starting at the place BUFP->ptr
685 points to. Can read more of the file, discard stuff before
686 BUFP->ptr, or make the buffer bigger.
688 Return the pointer to after the newline ending the line,
689 or to the end of the file, if there is no newline to end it.
691 Can also merge on continuation lines. If APPEND_END is
692 non-null, it points past the newline of a line that is
693 continued; we add another line onto it and regard the whole
694 thing as one line. The caller decides when a line is continued. */
696 static char *
697 gobble_line (fd, bufp, append_end)
698 int fd;
699 register struct termcap_buffer *bufp;
700 char *append_end;
702 register char *end;
703 register int nread;
704 register char *buf = bufp->beg;
705 register char *tem;
707 if (!append_end)
708 append_end = bufp->ptr;
710 while (1)
712 end = append_end;
713 while (*end && *end != '\n') end++;
714 if (*end)
715 break;
716 if (bufp->ateof)
717 return buf + bufp->full;
718 if (bufp->ptr == buf)
720 if (bufp->full == bufp->size)
722 bufp->size *= 2;
723 /* Add 1 to size to ensure room for terminating null. */
724 tem = (char *) xrealloc (buf, bufp->size + 1);
725 bufp->ptr = (bufp->ptr - buf) + tem;
726 append_end = (append_end - buf) + tem;
727 bufp->beg = buf = tem;
730 else
732 append_end -= bufp->ptr - buf;
733 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
734 bufp->ptr = buf;
736 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
737 bufp->ateof = 1;
738 bufp->full += nread;
739 buf[bufp->full] = '\0';
741 return end + 1;
744 #ifdef TEST
746 #ifdef NULL
747 #undef NULL
748 #endif
750 #include <stdio.h>
752 main (argc, argv)
753 int argc;
754 char **argv;
756 char *term;
757 char *buf;
759 term = argv[1];
760 printf ("TERM: %s\n", term);
762 buf = (char *) tgetent (0, term);
763 if ((int) buf <= 0)
765 printf ("No entry.\n");
766 return 0;
769 printf ("Entry: %s\n", buf);
771 tprint ("cm");
772 tprint ("AL");
774 printf ("co: %d\n", tgetnum ("co"));
775 printf ("am: %d\n", tgetflag ("am"));
778 tprint (cap)
779 char *cap;
781 char *x = tgetstr (cap, 0);
782 register char *y;
784 printf ("%s: ", cap);
785 if (x)
787 for (y = x; *y; y++)
788 if (*y <= ' ' || *y == 0177)
789 printf ("\\%0o", *y);
790 else
791 putchar (*y);
792 free (x);
794 else
795 printf ("none");
796 putchar ('\n');
799 #endif /* TEST */