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