Remove support for Mac Carbon.
[bpt/emacs.git] / src / termcap.c
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986, 1993, 1994, 1995, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA. */
19
20 /* Emacs config.h may rename various library functions such as malloc. */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #ifdef emacs
26
27 #include <lisp.h> /* xmalloc is here */
28 /* Get the O_* definitions for open et al. */
29 #include <sys/file.h>
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #else /* not emacs */
38
39 #ifdef STDC_HEADERS
40 #include <stdlib.h>
41 #include <string.h>
42 #else
43 char *getenv ();
44 char *malloc ();
45 char *realloc ();
46 #endif
47
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
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56 #ifdef HAVE_FCNTL_H
57 #include <fcntl.h>
58 #endif
59
60 #endif /* not emacs */
61
62 #ifndef NULL
63 #define NULL (char *) 0
64 #endif
65
66 #ifndef O_RDONLY
67 #define O_RDONLY 0
68 #endif
69
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
81 int bufsize = 128;
82 #else
83 #define BUFSIZE 2048
84 #endif
85 #endif
86
87 #ifndef TERMCAP_FILE
88 #define TERMCAP_FILE "/etc/termcap"
89 #endif
90
91 #ifndef emacs
92 static void
93 memory_out ()
94 {
95 write (2, "virtual memory exhausted\n", 25);
96 exit (1);
97 }
98
99 static char *
100 xmalloc (size)
101 unsigned size;
102 {
103 register char *tem = malloc (size);
104
105 if (!tem)
106 memory_out ();
107 return tem;
108 }
109
110 static char *
111 xrealloc (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. */
127 static char *term_entry;
128
129 static 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
135 static char *
136 find_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
147 int
148 tgetnum (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
157 int
158 tgetflag (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
170 char *
171 tgetstr (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
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
186 static char esctab[]
187 = " \057\026 \047\014 \
188 \025 \015 \
189 \005 \013 \
190 ";
191 #else
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
196 static char esctab[]
197 = " \007\010 \033\014 \
198 \012 \
199 \015 \011 \013 \
200 ";
201 #endif
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
210 static char *
211 tgetst1 (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 == '^')
243 {
244 c = *p++;
245 if (c == '?')
246 c = 0177;
247 else
248 c &= 037;
249 }
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 }
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
273 else if (c >= 0100 && c < 0200)
274 {
275 c1 = esctab[(c & ~040) - 0100];
276 if (c1 != ' ')
277 c = c1;
278 }
279 #endif
280 }
281 *r++ = c;
282 }
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
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
338 #ifndef emacs
339 short ospeed;
340 /* If OSPEED is 0, we use this as the actual baud rate. */
341 int tputs_baud_rate;
342 #endif
343
344 char PC;
345
346 #ifndef emacs
347 /* Actual baud rate if positive;
348 - baud rate / 100 if negative. */
349
350 static int speeds[] =
351 {
352 #ifdef VMS
353 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
354 -20, -24, -36, -48, -72, -96, -192
355 #else /* not VMS */
356 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
357 -18, -24, -48, -96, -192, -288, -384, -576, -1152
358 #endif /* not VMS */
359 };
360
361 #endif /* not emacs */
362
363 void
364 tputs (str, nlines, outfun)
365 register char *str;
366 int nlines;
367 register int (*outfun) ();
368 {
369 register int padcount = 0;
370 register int speed;
371
372 #ifdef emacs
373 extern EMACS_INT baud_rate;
374 speed = baud_rate;
375 /* For quite high speeds, convert to the smaller
376 units to avoid overflow. */
377 if (speed > 10000)
378 speed = - speed / 100;
379 #else
380 if (ospeed == 0)
381 speed = tputs_baud_rate;
382 else
383 speed = speeds[ospeed];
384 #endif
385
386 if (!str)
387 return;
388
389 while (*str >= '0' && *str <= '9')
390 {
391 padcount += *str++ - '0';
392 padcount *= 10;
393 }
394 if (*str == '.')
395 {
396 str++;
397 padcount += *str++ - '0';
398 }
399 if (*str == '*')
400 {
401 str++;
402 padcount *= nlines;
403 }
404 while (*str)
405 (*outfun) (*str++);
406
407 /* PADCOUNT is now in units of tenths of msec.
408 SPEED is measured in characters per 10 seconds
409 or in characters per .1 seconds (if negative).
410 We use the smaller units for larger speeds to avoid overflow. */
411 padcount *= speed;
412 padcount += 500;
413 padcount /= 1000;
414 if (speed < 0)
415 padcount = -padcount;
416 else
417 {
418 padcount += 50;
419 padcount /= 100;
420 }
421
422 while (padcount-- > 0)
423 (*outfun) (PC);
424 }
425 \f
426 /* Finding the termcap entry in the termcap data base. */
427
428 struct termcap_buffer
429 {
430 char *beg;
431 int size;
432 char *ptr;
433 int ateof;
434 int full;
435 };
436
437 /* Forward declarations of static functions. */
438
439 static int scan_file ();
440 static char *gobble_line ();
441 static int compare_contin ();
442 static int name_match ();
443
444 #ifdef VMS
445
446 #include <rmsdef.h>
447 #include <fab.h>
448 #include <nam.h>
449 #include <starlet.h>
450
451 static int
452 valid_filename_p (fn)
453 char *fn;
454 {
455 struct FAB fab = cc$rms_fab;
456 struct NAM nam = cc$rms_nam;
457 char esa[NAM$C_MAXRSS];
458
459 fab.fab$l_fna = fn;
460 fab.fab$b_fns = strlen(fn);
461 fab.fab$l_nam = &nam;
462 fab.fab$l_fop = FAB$M_NAM;
463
464 nam.nam$l_esa = esa;
465 nam.nam$b_ess = sizeof esa;
466
467 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
468 }
469
470 #else /* !VMS */
471
472 #ifdef MSDOS /* MW, May 1993 */
473 static int
474 valid_filename_p (fn)
475 char *fn;
476 {
477 return *fn == '/' || fn[1] == ':';
478 }
479 #else
480 #define valid_filename_p(fn) (*(fn) == '/')
481 #endif
482
483 #endif /* !VMS */
484
485 /* Find the termcap entry data for terminal type NAME
486 and store it in the block that BP points to.
487 Record its address for future use.
488
489 If BP is null, space is dynamically allocated.
490
491 Return -1 if there is some difficulty accessing the data base
492 of terminal types,
493 0 if the data base is accessible but the type NAME is not defined
494 in it, and some other value otherwise. */
495
496 int
497 tgetent (bp, name)
498 char *bp, *name;
499 {
500 register char *termcap_name;
501 register int fd;
502 struct termcap_buffer buf;
503 register char *bp1;
504 char *tc_search_point;
505 char *term;
506 int malloc_size = 0;
507 register int c;
508 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
509 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
510 int filep;
511
512 #ifdef INTERNAL_TERMINAL
513 /* For the internal terminal we don't want to read any termcap file,
514 so fake it. */
515 if (!strcmp (name, "internal"))
516 {
517 term = INTERNAL_TERMINAL;
518 if (!bp)
519 {
520 malloc_size = 1 + strlen (term);
521 bp = (char *) xmalloc (malloc_size);
522 }
523 strcpy (bp, term);
524 goto ret;
525 }
526 #endif /* INTERNAL_TERMINAL */
527
528 /* For compatibility with programs like `less' that want to
529 put data in the termcap buffer themselves as a fallback. */
530 if (bp)
531 term_entry = bp;
532
533 termcap_name = getenv ("TERMCAP");
534 if (termcap_name && *termcap_name == '\0')
535 termcap_name = NULL;
536 #if defined (MSDOS) && !defined (TEST)
537 if (termcap_name && (*termcap_name == '\\'
538 || *termcap_name == '/'
539 || termcap_name[1] == ':'))
540 dostounix_filename(termcap_name);
541 #endif
542
543 filep = termcap_name && valid_filename_p (termcap_name);
544
545 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
546 it is a file name to use instead of /etc/termcap.
547 If it is non-null and does not start with /,
548 it is the entry itself, but only if
549 the name the caller requested matches the TERM variable. */
550
551 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
552 {
553 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
554 if (!indirect)
555 {
556 if (!bp)
557 bp = termcap_name;
558 else
559 strcpy (bp, termcap_name);
560 goto ret;
561 }
562 else
563 { /* It has tc=. Need to read /etc/termcap. */
564 tcenv = termcap_name;
565 termcap_name = NULL;
566 }
567 }
568
569 if (!termcap_name || !filep)
570 termcap_name = TERMCAP_FILE;
571
572 /* Here we know we must search a file and termcap_name has its name. */
573
574 #ifdef MSDOS
575 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
576 #else
577 fd = open (termcap_name, O_RDONLY, 0);
578 #endif
579 if (fd < 0)
580 return -1;
581
582 buf.size = BUFSIZE;
583 /* Add 1 to size to ensure room for terminating null. */
584 buf.beg = (char *) xmalloc (buf.size + 1);
585 term = indirect ? indirect : name;
586
587 if (!bp)
588 {
589 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
590 bp = (char *) xmalloc (malloc_size);
591 }
592 tc_search_point = bp1 = bp;
593
594 if (indirect)
595 /* Copy the data from the environment variable. */
596 {
597 strcpy (bp, tcenv);
598 bp1 += strlen (tcenv);
599 }
600
601 while (term)
602 {
603 /* Scan the file, reading it via buf, till find start of main entry. */
604 if (scan_file (term, fd, &buf) == 0)
605 {
606 close (fd);
607 free (buf.beg);
608 if (malloc_size)
609 free (bp);
610 return 0;
611 }
612
613 /* Free old `term' if appropriate. */
614 if (term != name)
615 free (term);
616
617 /* If BP is malloc'd by us, make sure it is big enough. */
618 if (malloc_size)
619 {
620 int offset1 = bp1 - bp, offset2 = tc_search_point - bp;
621 malloc_size = offset1 + buf.size;
622 bp = termcap_name = (char *) xrealloc (bp, malloc_size);
623 bp1 = termcap_name + offset1;
624 tc_search_point = termcap_name + offset2;
625 }
626
627 /* Copy the line of the entry from buf into bp. */
628 termcap_name = buf.ptr;
629 while ((*bp1++ = c = *termcap_name++) && c != '\n')
630 /* Drop out any \ newline sequence. */
631 if (c == '\\' && *termcap_name == '\n')
632 {
633 bp1--;
634 termcap_name++;
635 }
636 *bp1 = '\0';
637
638 /* Does this entry refer to another terminal type's entry?
639 If something is found, copy it into heap and null-terminate it. */
640 tc_search_point = find_capability (tc_search_point, "tc");
641 term = tgetst1 (tc_search_point, (char **) 0);
642 }
643
644 close (fd);
645 free (buf.beg);
646
647 if (malloc_size)
648 bp = (char *) xrealloc (bp, bp1 - bp + 1);
649
650 ret:
651 term_entry = bp;
652 return 1;
653 }
654
655 /* Given file open on FD and buffer BUFP,
656 scan the file from the beginning until a line is found
657 that starts the entry for terminal type STR.
658 Return 1 if successful, with that line in BUFP,
659 or 0 if no entry is found in the file. */
660
661 static int
662 scan_file (str, fd, bufp)
663 char *str;
664 int fd;
665 register struct termcap_buffer *bufp;
666 {
667 register char *end;
668
669 bufp->ptr = bufp->beg;
670 bufp->full = 0;
671 bufp->ateof = 0;
672 *bufp->ptr = '\0';
673
674 lseek (fd, 0L, 0);
675
676 while (!bufp->ateof)
677 {
678 /* Read a line into the buffer. */
679 end = NULL;
680 do
681 {
682 /* if it is continued, append another line to it,
683 until a non-continued line ends. */
684 end = gobble_line (fd, bufp, end);
685 }
686 while (!bufp->ateof && end[-2] == '\\');
687
688 if (*bufp->ptr != '#'
689 && name_match (bufp->ptr, str))
690 return 1;
691
692 /* Discard the line just processed. */
693 bufp->ptr = end;
694 }
695 return 0;
696 }
697
698 /* Return nonzero if NAME is one of the names specified
699 by termcap entry LINE. */
700
701 static int
702 name_match (line, name)
703 char *line, *name;
704 {
705 register char *tem;
706
707 if (!compare_contin (line, name))
708 return 1;
709 /* This line starts an entry. Is it the right one? */
710 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
711 if (*tem == '|' && !compare_contin (tem + 1, name))
712 return 1;
713
714 return 0;
715 }
716
717 static int
718 compare_contin (str1, str2)
719 register char *str1, *str2;
720 {
721 register int c1, c2;
722 while (1)
723 {
724 c1 = *str1++;
725 c2 = *str2++;
726 while (c1 == '\\' && *str1 == '\n')
727 {
728 str1++;
729 while ((c1 = *str1++) == ' ' || c1 == '\t');
730 }
731 if (c2 == '\0')
732 {
733 /* End of type being looked up. */
734 if (c1 == '|' || c1 == ':')
735 /* If end of name in data base, we win. */
736 return 0;
737 else
738 return 1;
739 }
740 else if (c1 != c2)
741 return 1;
742 }
743 }
744
745 /* Make sure that the buffer <- BUFP contains a full line
746 of the file open on FD, starting at the place BUFP->ptr
747 points to. Can read more of the file, discard stuff before
748 BUFP->ptr, or make the buffer bigger.
749
750 Return the pointer to after the newline ending the line,
751 or to the end of the file, if there is no newline to end it.
752
753 Can also merge on continuation lines. If APPEND_END is
754 non-null, it points past the newline of a line that is
755 continued; we add another line onto it and regard the whole
756 thing as one line. The caller decides when a line is continued. */
757
758 static char *
759 gobble_line (fd, bufp, append_end)
760 int fd;
761 register struct termcap_buffer *bufp;
762 char *append_end;
763 {
764 register char *end;
765 register int nread;
766 register char *buf = bufp->beg;
767 register char *tem;
768
769 if (!append_end)
770 append_end = bufp->ptr;
771
772 while (1)
773 {
774 end = append_end;
775 while (*end && *end != '\n') end++;
776 if (*end)
777 break;
778 if (bufp->ateof)
779 return buf + bufp->full;
780 if (bufp->ptr == buf)
781 {
782 if (bufp->full == bufp->size)
783 {
784 bufp->size *= 2;
785 /* Add 1 to size to ensure room for terminating null. */
786 tem = (char *) xrealloc (buf, bufp->size + 1);
787 bufp->ptr = (bufp->ptr - buf) + tem;
788 append_end = (append_end - buf) + tem;
789 bufp->beg = buf = tem;
790 }
791 }
792 else
793 {
794 append_end -= bufp->ptr - buf;
795 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
796 bufp->ptr = buf;
797 }
798 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
799 bufp->ateof = 1;
800 bufp->full += nread;
801 buf[bufp->full] = '\0';
802 }
803 return end + 1;
804 }
805 \f
806 #ifdef TEST
807
808 #ifdef NULL
809 #undef NULL
810 #endif
811
812 #include <stdio.h>
813
814 main (argc, argv)
815 int argc;
816 char **argv;
817 {
818 char *term;
819 char *buf;
820
821 term = argv[1];
822 printf ("TERM: %s\n", term);
823
824 buf = (char *) tgetent (0, term);
825 if ((int) buf <= 0)
826 {
827 printf ("No entry.\n");
828 return 0;
829 }
830
831 printf ("Entry: %s\n", buf);
832
833 tprint ("cm");
834 tprint ("AL");
835
836 printf ("co: %d\n", tgetnum ("co"));
837 printf ("am: %d\n", tgetflag ("am"));
838 }
839
840 tprint (cap)
841 char *cap;
842 {
843 char *x = tgetstr (cap, 0);
844 register char *y;
845
846 printf ("%s: ", cap);
847 if (x)
848 {
849 for (y = x; *y; y++)
850 if (*y <= ' ' || *y == 0177)
851 printf ("\\%0o", *y);
852 else
853 putchar (*y);
854 free (x);
855 }
856 else
857 printf ("none");
858 putchar ('\n');
859 }
860
861 #endif /* TEST */
862
863 /* arch-tag: c2e8d427-2271-4fac-95fe-411857238b80
864 (do not change this comment) */