1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986 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 1, 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; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 In other words, you are welcome to use, share and improve this program.
19 You are forbidden to forbid anyone else to use, share and improve
20 what you give them. Help stamp out software-hoarding! */
21
22
23
24 /* BUFSIZE is the initial size allocated for the buffer
25 for reading the termcap file.
26 It is not a limit.
27 Make it large normally for speed.
28 Make it variable when debugging, so can exercise
29 increasing the space dynamically. */
30
31 #include <sys/types.h>
32
33 #ifdef emacs
34 #include "config.h"
35 #endif
36
37 #ifndef BUFSIZE
38 #ifdef DEBUG
39 #define BUFSIZE bufsize
40
41 int bufsize = 128;
42 #else
43 #define BUFSIZE 2048
44 #endif
45 #endif
46
47 #ifndef emacs
48 static
memory_out()49 memory_out ()
50 {
51 write (2, "Virtual memory exhausted\n", 25);
52 exit (1);
53 }
54
55 static int
xmalloc(size)56 xmalloc (size)
57 int size;
58 {
59 register tem = malloc (size);
60 if (!tem)
61 memory_out ();
62 return tem;
63 }
64
65 static int
xrealloc(ptr,size)66 xrealloc (ptr, size)
67 int ptr;
68 int size;
69 {
70 register tem = realloc (ptr, size);
71 if (!tem)
72 memory_out ();
73 return tem;
74 }
75 #endif /* not emacs */
76
77 /* Looking up capabilities in the entry already found */
78
79 /* The pointer to the data made by tgetent is left here
80 for tgetnum, tgetflag and tgetstr to find. */
81
82 static char *term_entry;
83
84 static char *tgetst1 ();
85
86 /* This is the main subroutine that is used to search
87 an entry for a particular capability */
88
89 static char *
find_capability(bp,cap)90 find_capability (bp, cap)
91 register char *bp, *cap;
92 {
93 for (; *bp; bp++)
94 if (bp[0] == ':'
95 && bp[1] == cap[0]
96 && bp[2] == cap[1])
97 return &bp[4];
98 return 0;
99 }
100
101 int
tgetnum(cap)102 tgetnum (cap)
103 char *cap;
104 {
105 register char *ptr = find_capability (term_entry, cap);
106 if (!ptr || ptr[-1] != '#')
107 return -1;
108 return atoi (ptr);
109 }
110
111 int
tgetflag(cap)112 tgetflag (cap)
113 char *cap;
114 {
115 register char *ptr = find_capability (term_entry, cap);
116 return 0 != ptr && ptr[-1] == ':';
117 }
118
119 /* Look up a string-valued capability `cap'.
120 If `area' is nonzero, it points to a pointer to a block in which
121 to store the string. That pointer is advanced over the space used.
122 If `area' is zero, space is allocated with `malloc'. */
123
124 char *
tgetstr(cap,area)125 tgetstr (cap, area)
126 char *cap;
127 char **area;
128 {
129 register char *ptr = find_capability (term_entry, cap);
130 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
131 return 0;
132 return tgetst1 (ptr, area);
133 }
134
135 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
136 gives meaning of character following \, or a space if no special meaning.
137 Eight characters per line within the string. */
138
139 static char esctab[]
140 = " \007\010 \033\014 \
141 \012 \
142 \015 \011 \013 \
143 ";
144
145 /* Given a pointer to a string value inside a termcap entry (`ptr'),
146 copy the value and process \ and ^ abbreviations.
147 Copy into block that *area points to,
148 or to newly allocated storage if area is 0. */
149
150 static char *
tgetst1(ptr,area)151 tgetst1 (ptr, area)
152 char *ptr;
153 char **area;
154 {
155 register char *p, *r;
156 register int c;
157 register int size;
158 char *ret;
159 register int c1;
160
161 if (!ptr)
162 return 0;
163
164 /* `ret' gets address of where to store the string */
165 if (!area)
166 {
167 /* Compute size of block needed (may overestimate) */
168 p = ptr;
169 while ((c = *p++) && c != ':' && c != '\n');
170 ret = (char *) xmalloc (p - ptr + 1);
171 }
172 else
173 ret = *area;
174
175 /* Copy the string value, stopping at null or colon. */
176 /* Also process ^ and \ abbreviations. */
177 p = ptr;
178 r = ret;
179 while ((c = *p++) && c != ':' && c != '\n')
180 {
181 if (c == '^')
182 c = *p++ & 037;
183 else if (c == '\\')
184 {
185 c = *p++;
186 if (c >= '0' && c <= '7')
187 {
188 c -= '0';
189 size = 0;
190
191 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
192 {
193 c *= 8;
194 c += c1 - '0';
195 p++;
196 }
197 }
198 else if (c >= 0100 && c < 0200)
199 {
200 c1 = esctab[(c & ~040) - 0100];
201 if (c1 != ' ')
202 c = c1;
203 }
204 }
205 *r++ = c;
206 }
207 *r = 0;
208 /* Update *area */
209 if (area)
210 *area = r + 1;
211 return ret;
212 }
213
214 /* Outputting a string with padding */
215
216 short ospeed;
217 char PC;
218
219 /* Actual baud rate if positive;
220 - baud rate / 100 if negative. */
221
222 static short speeds[] =
223 {
224 #ifdef VMS
225 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
226 -20, -24, -36, -48, -72, -96, -192
227 #else /* not VMS */
228 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
229 -18, -24, -48, -96, -192, -384
230 #endif /* not VMS */
231 };
232
tputs(string,nlines,outfun)233 tputs (string, nlines, outfun)
234 register char *string;
235 int nlines;
236 register int (*outfun) ();
237 {
238 register int padcount = 0;
239
240 if (string == (char *) 0)
241 return;
242 while (*string >= '0' && *string <= '9')
243 {
244 padcount += *string++ - '0';
245 padcount *= 10;
246 }
247 if (*string == '.')
248 {
249 string++;
250 padcount += *string++ - '0';
251 }
252 if (*string == '*')
253 {
254 string++;
255 padcount *= nlines;
256 }
257 while (*string)
258 (*outfun) (*string++);
259
260 /* padcount is now in units of tenths of msec. */
261 padcount *= speeds[ospeed];
262 padcount += 500;
263 padcount /= 1000;
264 if (speeds[ospeed] < 0)
265 padcount = -padcount;
266 else
267 {
268 padcount += 50;
269 padcount /= 100;
270 }
271
272 while (padcount-- > 0)
273 (*outfun) (PC);
274 }
275
276 /* Finding the termcap entry in the termcap data base */
277
278 struct buffer
279 {
280 char *beg;
281 int size;
282 char *ptr;
283 int ateof;
284 int full;
285 };
286
287 /* Forward declarations of static functions */
288
289 static int scan_file ();
290 static char *gobble_line ();
291 static int compare_contin ();
292 static int name_match ();
293
294 #ifdef VMS
295
296 #include <rmsdef.h>
297 #include <fab.h>
298 #include <nam.h>
299
300 static int
legal_filename_p(fn)301 legal_filename_p (fn)
302 char *fn;
303 {
304 struct FAB fab = cc$rms_fab;
305 struct NAM nam = cc$rms_nam;
306 char esa[NAM$C_MAXRSS];
307
308 fab.fab$l_fna = fn;
309 fab.fab$b_fns = strlen(fn);
310 fab.fab$l_nam = &nam;
311 fab.fab$l_fop = FAB$M_NAM;
312
313 nam.nam$l_esa = esa;
314 nam.nam$b_ess = sizeof esa;
315
316 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
317 }
318
319 #endif /* VMS */
320
321 /* Find the termcap entry data for terminal type `name'
322 and store it in the block that `bp' points to.
323 Record its address for future use.
324
325 If `bp' is zero, space is dynamically allocated. */
326
327 int
tgetent(bp,name)328 tgetent (bp, name)
329 char *bp, *name;
330 {
331 register char *tem;
332 register int fd;
333 struct buffer buf;
334 register char *bp1;
335 char *bp2;
336 char *term;
337 int malloc_size = 0;
338 register int c;
339 char *tcenv; /* TERMCAP value, if it contais :tc=. */
340 char *indirect = 0; /* Terminal type in :tc= in TERMCAP value. */
341 int filep;
342
343 tem = (char *) getenv ("TERMCAP");
344 if (tem && *tem == 0) tem = 0;
345
346 #ifdef VMS
347 filep = tem && legal_filename_p (tem);
348 #else
349 filep = tem && (*tem == '/');
350 #endif /* VMS */
351
352 /* If tem is non-null and starts with / (in the un*x case, that is),
353 it is a file name to use instead of /etc/termcap.
354 If it is non-null and does not start with /,
355 it is the entry itself, but only if
356 the name the caller requested matches the TERM variable. */
357
358 if (tem && !filep && !strcmp (name, getenv ("TERM")))
359 {
360 indirect = tgetst1 (find_capability (tem, "tc"), 0);
361 if (!indirect)
362 {
363 if (!bp)
364 bp = tem;
365 else
366 strcpy (bp, tem);
367 goto ret;
368 }
369 else
370 { /* we will need to read /etc/termcap */
371 tcenv = tem;
372 tem = 0;
373 }
374 }
375 else
376 indirect = (char *) 0;
377
378 if (!tem)
379 #ifdef VMS
380 tem = "emacs_library:[etc]termcap.dat";
381 #else
382 tem = "/etc/termcap";
383 #endif
384
385 /* Here we know we must search a file and tem has its name. */
386
387 fd = open (tem, 0, 0);
388 if (fd < 0)
389 return -1;
390
391 buf.size = BUFSIZE;
392 /* Add 1 to size to ensure room for terminating null. */
393 buf.beg = (char *) xmalloc (buf.size + 1);
394 term = indirect ? indirect : name;
395
396 if (!bp)
397 {
398 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
399 bp = (char *) xmalloc (malloc_size);
400 }
401 bp1 = bp;
402
403 if (indirect) /* copy the data from the environment variable */
404 {
405 strcpy (bp, tcenv);
406 bp1 += strlen (tcenv);
407 }
408
409 while (term)
410 {
411 /* Scan file, reading it via buf, till find start of main entry */
412 if (scan_file (term, fd, &buf) == 0)
413 return 0;
414
415 /* Free old `term' if appropriate. */
416 if (term != name)
417 free (term);
418
419 /* If `bp' is malloc'd by us, make sure it is big enough. */
420 if (malloc_size)
421 {
422 malloc_size = bp1 - bp + buf.size;
423 tem = (char *) xrealloc (bp, malloc_size);
424 bp1 += tem - bp;
425 bp = tem;
426 }
427
428 bp2 = bp1;
429
430 /* Copy the line of the entry from buf into bp. */
431 tem = buf.ptr;
432 while ((*bp1++ = c = *tem++) && c != '\n')
433 /* Drop out any \ newline sequence. */
434 if (c == '\\' && *tem == '\n')
435 {
436 bp1--;
437 tem++;
438 }
439 *bp1 = 0;
440
441 /* Does this entry refer to another terminal type's entry? */
442 /* If something is found, copy it into heap and null-terminate it */
443 term = tgetst1 (find_capability (bp2, "tc"), 0);
444 }
445
446 close (fd);
447 free (buf.beg);
448
449 if (malloc_size)
450 {
451 bp = (char *) xrealloc (bp, bp1 - bp + 1);
452 }
453
454 ret:
455 term_entry = bp;
456 if (malloc_size)
457 return (int) bp;
458 return 1;
459 }
460
461 /* Given file open on `fd' and buffer `bufp',
462 scan the file from the beginning until a line is found
463 that starts the entry for terminal type `string'.
464 Returns 1 if successful, with that line in `bufp',
465 or returns 0 if no entry found in the file. */
466
467 static int
scan_file(string,fd,bufp)468 scan_file (string, fd, bufp)
469 char *string;
470 int fd;
471 register struct buffer *bufp;
472 {
473 register char *tem;
474 register char *end;
475
476 bufp->ptr = bufp->beg;
477 bufp->full = 0;
478 bufp->ateof = 0;
479 *bufp->ptr = 0;
480
481 lseek (fd, (off_t) 0, 0);
482
483 while (!bufp->ateof)
484 {
485 /* Read a line into the buffer */
486 end = 0;
487 do
488 {
489 /* if it is continued, append another line to it,
490 until a non-continued line ends */
491 end = gobble_line (fd, bufp, end);
492 }
493 while (!bufp->ateof && end[-2] == '\\');
494
495 if (*bufp->ptr != '#'
496 && name_match (bufp->ptr, string))
497 return 1;
498
499 /* Discard the line just processed */
500 bufp->ptr = end;
501 }
502 return 0;
503 }
504
505 /* Return nonzero if NAME is one of the names specified
506 by termcap entry LINE. */
507
508 static int
name_match(line,name)509 name_match (line, name)
510 char *line, *name;
511 {
512 register char *tem;
513
514 if (!compare_contin (line, name))
515 return 1;
516 /* This line starts an entry. Is it the right one? */
517 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
518 if (*tem == '|' && !compare_contin (tem + 1, name))
519 return 1;
520
521 return 0;
522 }
523
524 static int
compare_contin(str1,str2)525 compare_contin (str1, str2)
526 register char *str1, *str2;
527 {
528 register int c1, c2;
529 while (1)
530 {
531 c1 = *str1++;
532 c2 = *str2++;
533 while (c1 == '\\' && *str1 == '\n')
534 {
535 str1++;
536 while ((c1 = *str1++) == ' ' || c1 == '\t');
537 }
538 if (c2 == '\0') /* end of type being looked up */
539 {
540 if (c1 == '|' || c1 == ':') /* If end of name in data base, */
541 return 0; /* we win. */
542 else
543 return 1;
544 }
545 else if (c1 != c2)
546 return 1;
547 }
548 }
549
550 /* Make sure that the buffer <- `bufp' contains a full line
551 of the file open on `fd', starting at the place `bufp->ptr'
552 points to. Can read more of the file, discard stuff before
553 `bufp->ptr', or make the buffer bigger.
554
555 Returns the pointer to after the newline ending the line,
556 or to the end of the file, if there is no newline to end it.
557
558 Can also merge on continuation lines. If `append_end' is
559 nonzero, it points past the newline of a line that is
560 continued; we add another line onto it and regard the whole
561 thing as one line. The caller decides when a line is continued. */
562
563 static char *
gobble_line(fd,bufp,append_end)564 gobble_line (fd, bufp, append_end)
565 int fd;
566 register struct buffer *bufp;
567 char *append_end;
568 {
569 register char *end;
570 register int nread;
571 register char *buf = bufp->beg;
572 register char *tem;
573
574 if (append_end == 0)
575 append_end = bufp->ptr;
576
577 while (1)
578 {
579 end = append_end;
580 while (*end && *end != '\n') end++;
581 if (*end)
582 break;
583 if (bufp->ateof)
584 return buf + bufp->full;
585 if (bufp->ptr == buf)
586 {
587 if (bufp->full == bufp->size)
588 {
589 bufp->size *= 2;
590 /* Add 1 to size to ensure room for terminating null. */
591 tem = (char *) xrealloc (buf, bufp->size + 1);
592 bufp->ptr = (bufp->ptr - buf) + tem;
593 append_end = (append_end - buf) + tem;
594 bufp->beg = buf = tem;
595 }
596 }
597 else
598 {
599 append_end -= bufp->ptr - buf;
600 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
601 bufp->ptr = buf;
602 }
603 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
604 bufp->ateof = 1;
605 bufp->full += nread;
606 buf[bufp->full] = 0;
607 }
608 return end + 1;
609 }
610
611 #ifdef TEST
612
613 #include <stdio.h>
614
main(argc,argv)615 main (argc, argv)
616 int argc;
617 char **argv;
618 {
619 char *term;
620 char *buf;
621
622 term = argv[1];
623 printf ("TERM: %s\n", term);
624
625 buf = (char *) tgetent (0, term);
626 if ((int) buf <= 0)
627 {
628 printf ("No entry.\n");
629 return 0;
630 }
631
632 printf ("Entry: %s\n", buf);
633
634 tprint ("cm");
635 tprint ("AL");
636
637 printf ("co: %d\n", tgetnum ("co"));
638 printf ("am: %d\n", tgetflag ("am"));
639 }
640
tprint(cap)641 tprint (cap)
642 char *cap;
643 {
644 char *x = tgetstr (cap, 0);
645 register char *y;
646
647 printf ("%s: ", cap);
648 if (x)
649 {
650 for (y = x; *y; y++)
651 if (*y <= ' ' || *y == 0177)
652 printf ("\\%0o", *y);
653 else
654 putchar (*y);
655 free (x);
656 }
657 else
658 printf ("none");
659 putchar ('\n');
660 }
661
662 #endif /* TEST */
663
664