* bitmaps/README:
[bpt/emacs.git] / src / termcap.c
CommitLineData
f02902f7 1/* Work-alike for termcap, plus extra features.
0b5538bd 2 Copyright (C) 1985, 1986, 1993, 1994, 1995, 2000, 2001, 2002, 2003,
8cabe764 3 2004, 2005, 2006, 2007, 2008 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
4fc5845f
LK
17the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18Boston, MA 02110-1301, 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
3c29b031 56#ifdef HAVE_FCNTL_H
f02902f7
RM
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 }
56ffd194
TTN
283
284 /* Sometimes entries have "%pN" which means use parameter N in the
285 next %-substitution. If all such N are continuous in the range
286 [1,9] we can remove each "%pN" because they are redundant, thus
287 reducing bandwidth requirements. True, Emacs is well beyond the
288 days of 150baud teletypes, but some of its users aren't much so.
289
290 This pass could probably be integrated into the one above but
291 abbreviation expansion makes that effort a little more hairy than
292 its worth; this is cleaner. */
293 {
294 register int last_p_param = 0;
295 int remove_p_params = 1;
296 struct { char *beg; int len; } cut[11];
297
298 for (cut[0].beg = p = ret; p < r - 3; p++)
299 {
300 if (!remove_p_params)
301 break;
302 if (*p == '%' && *(p + 1) == 'p')
303 {
304 if (*(p + 2) - '0' == 1 + last_p_param)
305 {
306 cut[last_p_param].len = p - cut[last_p_param].beg;
307 last_p_param++;
308 p += 3;
309 cut[last_p_param].beg = p;
310 }
311 else /* not continuous: bail */
312 remove_p_params = 0;
313 if (last_p_param > 10) /* too many: bail */
314 remove_p_params = 0;
315 }
316 }
317 if (remove_p_params && last_p_param)
318 {
319 register int i;
320 char *wp;
321
322 cut[last_p_param].len = r - cut[last_p_param].beg;
323 for (i = 0, wp = ret; i <= last_p_param; wp += cut[i++].len)
324 bcopy (cut[i].beg, wp, cut[i].len);
325 r = wp;
326 }
327 }
328
f02902f7
RM
329 *r = '\0';
330 /* Update *AREA. */
331 if (area)
332 *area = r + 1;
333 return ret;
334}
335\f
336/* Outputting a string with padding. */
337
d8eb23bd
RS
338#ifndef emacs
339short ospeed;
f02902f7
RM
340/* If OSPEED is 0, we use this as the actual baud rate. */
341int tputs_baud_rate;
d8eb23bd 342#endif
e0f712ba 343
f02902f7
RM
344char PC;
345
d8eb23bd 346#ifndef emacs
f02902f7
RM
347/* Actual baud rate if positive;
348 - baud rate / 100 if negative. */
349
0c0b6c79 350static int speeds[] =
f02902f7 351 {
f02902f7 352 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
0c0b6c79 353 -18, -24, -48, -96, -192, -288, -384, -576, -1152
f02902f7
RM
354 };
355
d8eb23bd 356#endif /* not emacs */
d250dcfc 357
f02902f7
RM
358void
359tputs (str, nlines, outfun)
360 register char *str;
361 int nlines;
362 register int (*outfun) ();
363{
364 register int padcount = 0;
365 register int speed;
366
d8eb23bd 367#ifdef emacs
31ade731 368 extern EMACS_INT baud_rate;
f02902f7 369 speed = baud_rate;
0c0b6c79
RS
370 /* For quite high speeds, convert to the smaller
371 units to avoid overflow. */
372 if (speed > 10000)
373 speed = - speed / 100;
d8eb23bd
RS
374#else
375 if (ospeed == 0)
376 speed = tputs_baud_rate;
377 else
378 speed = speeds[ospeed];
379#endif
f02902f7
RM
380
381 if (!str)
382 return;
383
384 while (*str >= '0' && *str <= '9')
385 {
386 padcount += *str++ - '0';
387 padcount *= 10;
388 }
389 if (*str == '.')
390 {
391 str++;
392 padcount += *str++ - '0';
393 }
394 if (*str == '*')
395 {
396 str++;
397 padcount *= nlines;
398 }
399 while (*str)
400 (*outfun) (*str++);
401
0c0b6c79 402 /* PADCOUNT is now in units of tenths of msec.
95c74a10 403 SPEED is measured in characters per 10 seconds
0c0b6c79
RS
404 or in characters per .1 seconds (if negative).
405 We use the smaller units for larger speeds to avoid overflow. */
406 padcount *= speed;
f02902f7
RM
407 padcount += 500;
408 padcount /= 1000;
0c0b6c79 409 if (speed < 0)
f02902f7
RM
410 padcount = -padcount;
411 else
412 {
413 padcount += 50;
414 padcount /= 100;
415 }
416
417 while (padcount-- > 0)
418 (*outfun) (PC);
419}
420\f
421/* Finding the termcap entry in the termcap data base. */
422
df4cb2b7 423struct termcap_buffer
f02902f7
RM
424 {
425 char *beg;
426 int size;
427 char *ptr;
428 int ateof;
429 int full;
430 };
431
432/* Forward declarations of static functions. */
433
434static int scan_file ();
435static char *gobble_line ();
436static int compare_contin ();
437static int name_match ();
438
f02902f7
RM
439#ifdef MSDOS /* MW, May 1993 */
440static int
441valid_filename_p (fn)
442 char *fn;
443{
444 return *fn == '/' || fn[1] == ':';
445}
446#else
447#define valid_filename_p(fn) (*(fn) == '/')
448#endif
449
f02902f7
RM
450/* Find the termcap entry data for terminal type NAME
451 and store it in the block that BP points to.
452 Record its address for future use.
453
454 If BP is null, space is dynamically allocated.
455
456 Return -1 if there is some difficulty accessing the data base
457 of terminal types,
458 0 if the data base is accessible but the type NAME is not defined
459 in it, and some other value otherwise. */
460
461int
462tgetent (bp, name)
463 char *bp, *name;
464{
465 register char *termcap_name;
466 register int fd;
df4cb2b7 467 struct termcap_buffer buf;
f02902f7 468 register char *bp1;
f21b7e24 469 char *tc_search_point;
f02902f7
RM
470 char *term;
471 int malloc_size = 0;
472 register int c;
20ee9f08 473 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
f02902f7
RM
474 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
475 int filep;
476
477#ifdef INTERNAL_TERMINAL
478 /* For the internal terminal we don't want to read any termcap file,
479 so fake it. */
480 if (!strcmp (name, "internal"))
481 {
482 term = INTERNAL_TERMINAL;
483 if (!bp)
484 {
485 malloc_size = 1 + strlen (term);
486 bp = (char *) xmalloc (malloc_size);
487 }
488 strcpy (bp, term);
489 goto ret;
490 }
491#endif /* INTERNAL_TERMINAL */
492
3e19e687
DM
493 /* For compatibility with programs like `less' that want to
494 put data in the termcap buffer themselves as a fallback. */
495 if (bp)
496 term_entry = bp;
497
f02902f7
RM
498 termcap_name = getenv ("TERMCAP");
499 if (termcap_name && *termcap_name == '\0')
500 termcap_name = NULL;
501#if defined (MSDOS) && !defined (TEST)
502 if (termcap_name && (*termcap_name == '\\'
503 || *termcap_name == '/'
504 || termcap_name[1] == ':'))
505 dostounix_filename(termcap_name);
506#endif
507
508 filep = termcap_name && valid_filename_p (termcap_name);
509
510 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
511 it is a file name to use instead of /etc/termcap.
512 If it is non-null and does not start with /,
513 it is the entry itself, but only if
514 the name the caller requested matches the TERM variable. */
515
516 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
517 {
518 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
519 if (!indirect)
520 {
521 if (!bp)
522 bp = termcap_name;
523 else
524 strcpy (bp, termcap_name);
525 goto ret;
526 }
527 else
528 { /* It has tc=. Need to read /etc/termcap. */
529 tcenv = termcap_name;
530 termcap_name = NULL;
531 }
532 }
533
534 if (!termcap_name || !filep)
aa63d440 535 termcap_name = TERMCAP_FILE;
f02902f7
RM
536
537 /* Here we know we must search a file and termcap_name has its name. */
538
539#ifdef MSDOS
983d99b5 540 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
f02902f7 541#else
983d99b5 542 fd = open (termcap_name, O_RDONLY, 0);
f02902f7
RM
543#endif
544 if (fd < 0)
545 return -1;
546
547 buf.size = BUFSIZE;
548 /* Add 1 to size to ensure room for terminating null. */
549 buf.beg = (char *) xmalloc (buf.size + 1);
550 term = indirect ? indirect : name;
551
552 if (!bp)
553 {
554 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
555 bp = (char *) xmalloc (malloc_size);
556 }
f21b7e24 557 tc_search_point = bp1 = bp;
f02902f7
RM
558
559 if (indirect)
560 /* Copy the data from the environment variable. */
561 {
562 strcpy (bp, tcenv);
563 bp1 += strlen (tcenv);
564 }
565
566 while (term)
567 {
568 /* Scan the file, reading it via buf, till find start of main entry. */
569 if (scan_file (term, fd, &buf) == 0)
570 {
571 close (fd);
572 free (buf.beg);
573 if (malloc_size)
574 free (bp);
575 return 0;
576 }
577
578 /* Free old `term' if appropriate. */
579 if (term != name)
580 free (term);
581
582 /* If BP is malloc'd by us, make sure it is big enough. */
583 if (malloc_size)
584 {
49484b28
GM
585 int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
586 malloc_size = offset1 + buf.size;
587 bp = termcap_name = (char *) xrealloc (bp, malloc_size);
588 bp1 = termcap_name + offset1;
589 tc_search_point = termcap_name + offset2;
f02902f7
RM
590 }
591
f02902f7
RM
592 /* Copy the line of the entry from buf into bp. */
593 termcap_name = buf.ptr;
594 while ((*bp1++ = c = *termcap_name++) && c != '\n')
595 /* Drop out any \ newline sequence. */
596 if (c == '\\' && *termcap_name == '\n')
597 {
598 bp1--;
599 termcap_name++;
600 }
601 *bp1 = '\0';
602
603 /* Does this entry refer to another terminal type's entry?
604 If something is found, copy it into heap and null-terminate it. */
f21b7e24
KH
605 tc_search_point = find_capability (tc_search_point, "tc");
606 term = tgetst1 (tc_search_point, (char **) 0);
f02902f7
RM
607 }
608
609 close (fd);
610 free (buf.beg);
611
612 if (malloc_size)
613 bp = (char *) xrealloc (bp, bp1 - bp + 1);
614
615 ret:
616 term_entry = bp;
f02902f7
RM
617 return 1;
618}
619
620/* Given file open on FD and buffer BUFP,
621 scan the file from the beginning until a line is found
622 that starts the entry for terminal type STR.
623 Return 1 if successful, with that line in BUFP,
624 or 0 if no entry is found in the file. */
625
626static int
627scan_file (str, fd, bufp)
628 char *str;
629 int fd;
df4cb2b7 630 register struct termcap_buffer *bufp;
f02902f7
RM
631{
632 register char *end;
633
634 bufp->ptr = bufp->beg;
635 bufp->full = 0;
636 bufp->ateof = 0;
637 *bufp->ptr = '\0';
638
639 lseek (fd, 0L, 0);
640
641 while (!bufp->ateof)
642 {
643 /* Read a line into the buffer. */
644 end = NULL;
645 do
646 {
647 /* if it is continued, append another line to it,
648 until a non-continued line ends. */
649 end = gobble_line (fd, bufp, end);
650 }
651 while (!bufp->ateof && end[-2] == '\\');
652
653 if (*bufp->ptr != '#'
654 && name_match (bufp->ptr, str))
655 return 1;
656
657 /* Discard the line just processed. */
658 bufp->ptr = end;
659 }
660 return 0;
661}
662
663/* Return nonzero if NAME is one of the names specified
664 by termcap entry LINE. */
665
666static int
667name_match (line, name)
668 char *line, *name;
669{
670 register char *tem;
671
672 if (!compare_contin (line, name))
673 return 1;
674 /* This line starts an entry. Is it the right one? */
675 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
676 if (*tem == '|' && !compare_contin (tem + 1, name))
677 return 1;
678
679 return 0;
680}
681
682static int
683compare_contin (str1, str2)
684 register char *str1, *str2;
685{
686 register int c1, c2;
687 while (1)
688 {
689 c1 = *str1++;
690 c2 = *str2++;
691 while (c1 == '\\' && *str1 == '\n')
692 {
693 str1++;
694 while ((c1 = *str1++) == ' ' || c1 == '\t');
695 }
696 if (c2 == '\0')
697 {
698 /* End of type being looked up. */
699 if (c1 == '|' || c1 == ':')
700 /* If end of name in data base, we win. */
701 return 0;
702 else
703 return 1;
704 }
705 else if (c1 != c2)
706 return 1;
707 }
708}
709
710/* Make sure that the buffer <- BUFP contains a full line
711 of the file open on FD, starting at the place BUFP->ptr
712 points to. Can read more of the file, discard stuff before
713 BUFP->ptr, or make the buffer bigger.
714
715 Return the pointer to after the newline ending the line,
716 or to the end of the file, if there is no newline to end it.
717
718 Can also merge on continuation lines. If APPEND_END is
719 non-null, it points past the newline of a line that is
720 continued; we add another line onto it and regard the whole
721 thing as one line. The caller decides when a line is continued. */
722
723static char *
724gobble_line (fd, bufp, append_end)
725 int fd;
df4cb2b7 726 register struct termcap_buffer *bufp;
f02902f7
RM
727 char *append_end;
728{
729 register char *end;
730 register int nread;
731 register char *buf = bufp->beg;
732 register char *tem;
733
734 if (!append_end)
735 append_end = bufp->ptr;
736
737 while (1)
738 {
739 end = append_end;
740 while (*end && *end != '\n') end++;
741 if (*end)
742 break;
743 if (bufp->ateof)
744 return buf + bufp->full;
745 if (bufp->ptr == buf)
746 {
747 if (bufp->full == bufp->size)
748 {
749 bufp->size *= 2;
750 /* Add 1 to size to ensure room for terminating null. */
751 tem = (char *) xrealloc (buf, bufp->size + 1);
752 bufp->ptr = (bufp->ptr - buf) + tem;
753 append_end = (append_end - buf) + tem;
754 bufp->beg = buf = tem;
755 }
756 }
757 else
758 {
759 append_end -= bufp->ptr - buf;
760 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
761 bufp->ptr = buf;
762 }
763 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
764 bufp->ateof = 1;
765 bufp->full += nread;
766 buf[bufp->full] = '\0';
767 }
768 return end + 1;
769}
770\f
771#ifdef TEST
772
773#ifdef NULL
774#undef NULL
775#endif
776
777#include <stdio.h>
778
779main (argc, argv)
780 int argc;
781 char **argv;
782{
783 char *term;
784 char *buf;
785
786 term = argv[1];
787 printf ("TERM: %s\n", term);
788
789 buf = (char *) tgetent (0, term);
790 if ((int) buf <= 0)
791 {
792 printf ("No entry.\n");
793 return 0;
794 }
795
796 printf ("Entry: %s\n", buf);
797
798 tprint ("cm");
799 tprint ("AL");
800
801 printf ("co: %d\n", tgetnum ("co"));
802 printf ("am: %d\n", tgetflag ("am"));
803}
804
805tprint (cap)
806 char *cap;
807{
808 char *x = tgetstr (cap, 0);
809 register char *y;
810
811 printf ("%s: ", cap);
812 if (x)
813 {
814 for (y = x; *y; y++)
815 if (*y <= ' ' || *y == 0177)
816 printf ("\\%0o", *y);
817 else
818 putchar (*y);
819 free (x);
820 }
821 else
822 printf ("none");
823 putchar ('\n');
824}
825
826#endif /* TEST */
ab5796a9
MB
827
828/* arch-tag: c2e8d427-2271-4fac-95fe-411857238b80
829 (do not change this comment) */