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