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