(unlock_buffer, unlock_all_files, Flock_buffer)
[bpt/emacs.git] / src / termcap.c
CommitLineData
f02902f7
RM
1/* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986, 1993, 1994 Free Software Foundation, Inc.
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2, or (at your option)
7any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; see the file COPYING. If not, write to
16the 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
8fde62c1 20
f02902f7 21#include <config.h>
8fde62c1
RS
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
f02902f7
RM
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
39char *getenv ();
40char *malloc ();
41char *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
983d99b5
RM
57#ifndef O_RDONLY
58#define O_RDONLY 0
59#endif
60
f02902f7
RM
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
72int 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
83static void
84memory_out ()
85{
86 write (2, "virtual memory exhausted\n", 25);
87 exit (1);
88}
89
90static char *
91xmalloc (size)
92 unsigned size;
93{
94 register char *tem = malloc (size);
95
96 if (!tem)
97 memory_out ();
98 return tem;
99}
100
101static char *
102xrealloc (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. */
118static char *term_entry;
119
120static 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
126static char *
127find_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
138int
139tgetnum (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
148int
149tgetflag (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
161char *
162tgetstr (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
176static 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
189static char *
190tgetst1 (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 == '^')
d80bd4a0
RS
222 {
223 c = *p++;
224 if (c == '?')
225 c = 0177;
226 else
227 c &= 037;
228 }
f02902f7
RM
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
262short ospeed;
263/* If OSPEED is 0, we use this as the actual baud rate. */
264int tputs_baud_rate;
265char PC;
266
267/* Actual baud rate if positive;
268 - baud rate / 100 if negative. */
269
0c0b6c79 270static int speeds[] =
f02902f7
RM
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,
0c0b6c79 277 -18, -24, -48, -96, -192, -288, -384, -576, -1152
f02902f7
RM
278#endif /* not VMS */
279 };
280
281void
282tputs (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;
0c0b6c79
RS
293 /* For quite high speeds, convert to the smaller
294 units to avoid overflow. */
295 if (speed > 10000)
296 speed = - speed / 100;
f02902f7
RM
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
0c0b6c79
RS
325 /* PADCOUNT is now in units of tenths of msec.
326 SPEED is mesured 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;
f02902f7
RM
330 padcount += 500;
331 padcount /= 1000;
0c0b6c79 332 if (speed < 0)
f02902f7
RM
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
346struct 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
357static int scan_file ();
358static char *gobble_line ();
359static int compare_contin ();
360static int name_match ();
361
362#ifdef VMS
363
364#include <rmsdef.h>
365#include <fab.h>
366#include <nam.h>
367
368static int
369valid_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 */
390static int
391valid_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
413int
414tgetent (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
983d99b5 487 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
f02902f7 488#else
983d99b5 489 fd = open (termcap_name, O_RDONLY, 0);
f02902f7
RM
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 if (malloc_size)
565 return (int) bp;
566 return 1;
567}
568
569/* Given file open on FD and buffer BUFP,
570 scan the file from the beginning until a line is found
571 that starts the entry for terminal type STR.
572 Return 1 if successful, with that line in BUFP,
573 or 0 if no entry is found in the file. */
574
575static int
576scan_file (str, fd, bufp)
577 char *str;
578 int fd;
579 register struct buffer *bufp;
580{
581 register char *end;
582
583 bufp->ptr = bufp->beg;
584 bufp->full = 0;
585 bufp->ateof = 0;
586 *bufp->ptr = '\0';
587
588 lseek (fd, 0L, 0);
589
590 while (!bufp->ateof)
591 {
592 /* Read a line into the buffer. */
593 end = NULL;
594 do
595 {
596 /* if it is continued, append another line to it,
597 until a non-continued line ends. */
598 end = gobble_line (fd, bufp, end);
599 }
600 while (!bufp->ateof && end[-2] == '\\');
601
602 if (*bufp->ptr != '#'
603 && name_match (bufp->ptr, str))
604 return 1;
605
606 /* Discard the line just processed. */
607 bufp->ptr = end;
608 }
609 return 0;
610}
611
612/* Return nonzero if NAME is one of the names specified
613 by termcap entry LINE. */
614
615static int
616name_match (line, name)
617 char *line, *name;
618{
619 register char *tem;
620
621 if (!compare_contin (line, name))
622 return 1;
623 /* This line starts an entry. Is it the right one? */
624 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
625 if (*tem == '|' && !compare_contin (tem + 1, name))
626 return 1;
627
628 return 0;
629}
630
631static int
632compare_contin (str1, str2)
633 register char *str1, *str2;
634{
635 register int c1, c2;
636 while (1)
637 {
638 c1 = *str1++;
639 c2 = *str2++;
640 while (c1 == '\\' && *str1 == '\n')
641 {
642 str1++;
643 while ((c1 = *str1++) == ' ' || c1 == '\t');
644 }
645 if (c2 == '\0')
646 {
647 /* End of type being looked up. */
648 if (c1 == '|' || c1 == ':')
649 /* If end of name in data base, we win. */
650 return 0;
651 else
652 return 1;
653 }
654 else if (c1 != c2)
655 return 1;
656 }
657}
658
659/* Make sure that the buffer <- BUFP contains a full line
660 of the file open on FD, starting at the place BUFP->ptr
661 points to. Can read more of the file, discard stuff before
662 BUFP->ptr, or make the buffer bigger.
663
664 Return the pointer to after the newline ending the line,
665 or to the end of the file, if there is no newline to end it.
666
667 Can also merge on continuation lines. If APPEND_END is
668 non-null, it points past the newline of a line that is
669 continued; we add another line onto it and regard the whole
670 thing as one line. The caller decides when a line is continued. */
671
672static char *
673gobble_line (fd, bufp, append_end)
674 int fd;
675 register struct buffer *bufp;
676 char *append_end;
677{
678 register char *end;
679 register int nread;
680 register char *buf = bufp->beg;
681 register char *tem;
682
683 if (!append_end)
684 append_end = bufp->ptr;
685
686 while (1)
687 {
688 end = append_end;
689 while (*end && *end != '\n') end++;
690 if (*end)
691 break;
692 if (bufp->ateof)
693 return buf + bufp->full;
694 if (bufp->ptr == buf)
695 {
696 if (bufp->full == bufp->size)
697 {
698 bufp->size *= 2;
699 /* Add 1 to size to ensure room for terminating null. */
700 tem = (char *) xrealloc (buf, bufp->size + 1);
701 bufp->ptr = (bufp->ptr - buf) + tem;
702 append_end = (append_end - buf) + tem;
703 bufp->beg = buf = tem;
704 }
705 }
706 else
707 {
708 append_end -= bufp->ptr - buf;
709 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
710 bufp->ptr = buf;
711 }
712 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
713 bufp->ateof = 1;
714 bufp->full += nread;
715 buf[bufp->full] = '\0';
716 }
717 return end + 1;
718}
719\f
720#ifdef TEST
721
722#ifdef NULL
723#undef NULL
724#endif
725
726#include <stdio.h>
727
728main (argc, argv)
729 int argc;
730 char **argv;
731{
732 char *term;
733 char *buf;
734
735 term = argv[1];
736 printf ("TERM: %s\n", term);
737
738 buf = (char *) tgetent (0, term);
739 if ((int) buf <= 0)
740 {
741 printf ("No entry.\n");
742 return 0;
743 }
744
745 printf ("Entry: %s\n", buf);
746
747 tprint ("cm");
748 tprint ("AL");
749
750 printf ("co: %d\n", tgetnum ("co"));
751 printf ("am: %d\n", tgetflag ("am"));
752}
753
754tprint (cap)
755 char *cap;
756{
757 char *x = tgetstr (cap, 0);
758 register char *y;
759
760 printf ("%s: ", cap);
761 if (x)
762 {
763 for (y = x; *y; y++)
764 if (*y <= ' ' || *y == 0177)
765 printf ("\\%0o", *y);
766 else
767 putchar (*y);
768 free (x);
769 }
770 else
771 printf ("none");
772 putchar ('\n');
773}
774
775#endif /* TEST */
776