source: branches/libc-0.6/src/emx/gnu/termcap/termcap.c

Last change on this file was 1517, checked in by bird, 21 years ago

Fixing warnings. -Znofork

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