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