[REGEX_FREE]: Use ((void)0) instead of just (0).
[emacs.git] / src / termcap.c
blobbf16ed2824caa9b65bd3010e1c2fa06c94dbc8ce
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 measured 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 return 1;
567 /* Given file open on FD and buffer BUFP,
568 scan the file from the beginning until a line is found
569 that starts the entry for terminal type STR.
570 Return 1 if successful, with that line in BUFP,
571 or 0 if no entry is found in the file. */
573 static int
574 scan_file (str, fd, bufp)
575 char *str;
576 int fd;
577 register struct buffer *bufp;
579 register char *end;
581 bufp->ptr = bufp->beg;
582 bufp->full = 0;
583 bufp->ateof = 0;
584 *bufp->ptr = '\0';
586 lseek (fd, 0L, 0);
588 while (!bufp->ateof)
590 /* Read a line into the buffer. */
591 end = NULL;
594 /* if it is continued, append another line to it,
595 until a non-continued line ends. */
596 end = gobble_line (fd, bufp, end);
598 while (!bufp->ateof && end[-2] == '\\');
600 if (*bufp->ptr != '#'
601 && name_match (bufp->ptr, str))
602 return 1;
604 /* Discard the line just processed. */
605 bufp->ptr = end;
607 return 0;
610 /* Return nonzero if NAME is one of the names specified
611 by termcap entry LINE. */
613 static int
614 name_match (line, name)
615 char *line, *name;
617 register char *tem;
619 if (!compare_contin (line, name))
620 return 1;
621 /* This line starts an entry. Is it the right one? */
622 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
623 if (*tem == '|' && !compare_contin (tem + 1, name))
624 return 1;
626 return 0;
629 static int
630 compare_contin (str1, str2)
631 register char *str1, *str2;
633 register int c1, c2;
634 while (1)
636 c1 = *str1++;
637 c2 = *str2++;
638 while (c1 == '\\' && *str1 == '\n')
640 str1++;
641 while ((c1 = *str1++) == ' ' || c1 == '\t');
643 if (c2 == '\0')
645 /* End of type being looked up. */
646 if (c1 == '|' || c1 == ':')
647 /* If end of name in data base, we win. */
648 return 0;
649 else
650 return 1;
652 else if (c1 != c2)
653 return 1;
657 /* Make sure that the buffer <- BUFP contains a full line
658 of the file open on FD, starting at the place BUFP->ptr
659 points to. Can read more of the file, discard stuff before
660 BUFP->ptr, or make the buffer bigger.
662 Return the pointer to after the newline ending the line,
663 or to the end of the file, if there is no newline to end it.
665 Can also merge on continuation lines. If APPEND_END is
666 non-null, it points past the newline of a line that is
667 continued; we add another line onto it and regard the whole
668 thing as one line. The caller decides when a line is continued. */
670 static char *
671 gobble_line (fd, bufp, append_end)
672 int fd;
673 register struct buffer *bufp;
674 char *append_end;
676 register char *end;
677 register int nread;
678 register char *buf = bufp->beg;
679 register char *tem;
681 if (!append_end)
682 append_end = bufp->ptr;
684 while (1)
686 end = append_end;
687 while (*end && *end != '\n') end++;
688 if (*end)
689 break;
690 if (bufp->ateof)
691 return buf + bufp->full;
692 if (bufp->ptr == buf)
694 if (bufp->full == bufp->size)
696 bufp->size *= 2;
697 /* Add 1 to size to ensure room for terminating null. */
698 tem = (char *) xrealloc (buf, bufp->size + 1);
699 bufp->ptr = (bufp->ptr - buf) + tem;
700 append_end = (append_end - buf) + tem;
701 bufp->beg = buf = tem;
704 else
706 append_end -= bufp->ptr - buf;
707 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
708 bufp->ptr = buf;
710 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
711 bufp->ateof = 1;
712 bufp->full += nread;
713 buf[bufp->full] = '\0';
715 return end + 1;
718 #ifdef TEST
720 #ifdef NULL
721 #undef NULL
722 #endif
724 #include <stdio.h>
726 main (argc, argv)
727 int argc;
728 char **argv;
730 char *term;
731 char *buf;
733 term = argv[1];
734 printf ("TERM: %s\n", term);
736 buf = (char *) tgetent (0, term);
737 if ((int) buf <= 0)
739 printf ("No entry.\n");
740 return 0;
743 printf ("Entry: %s\n", buf);
745 tprint ("cm");
746 tprint ("AL");
748 printf ("co: %d\n", tgetnum ("co"));
749 printf ("am: %d\n", tgetflag ("am"));
752 tprint (cap)
753 char *cap;
755 char *x = tgetstr (cap, 0);
756 register char *y;
758 printf ("%s: ", cap);
759 if (x)
761 for (y = x; *y; y++)
762 if (*y <= ' ' || *y == 0177)
763 printf ("\\%0o", *y);
764 else
765 putchar (*y);
766 free (x);
768 else
769 printf ("none");
770 putchar ('\n');
773 #endif /* TEST */