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, 2011 Free Software Foundation, Inc.
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)
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.
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. */
20 /* Emacs config.h may rename various library functions such as malloc. */
33 /* BUFSIZE is the initial size allocated for the buffer
34 for reading the termcap file.
36 Make it large normally for speed.
37 Make it variable when debugging, so can exercise
38 increasing the space dynamically. */
42 #define BUFSIZE bufsize
51 #define TERMCAP_FILE "/etc/termcap"
55 /* Looking up capabilities in the entry already found. */
57 /* The pointer to the data made by tgetent is left here
58 for tgetnum, tgetflag and tgetstr to find. */
59 static char *term_entry
;
61 static char *tgetst1 (char *ptr
, char **area
);
63 /* Search entry BP for capability CAP.
64 Return a pointer to the capability (in BP) if found,
68 find_capability (register char *bp
, register const char *cap
)
79 tgetnum (const char *cap
)
81 register char *ptr
= find_capability (term_entry
, cap
);
82 if (!ptr
|| ptr
[-1] != '#')
88 tgetflag (const char *cap
)
90 register char *ptr
= find_capability (term_entry
, cap
);
91 return ptr
&& ptr
[-1] == ':';
94 /* Look up a string-valued capability CAP.
95 If AREA is non-null, it points to a pointer to a block in which
96 to store the string. That pointer is advanced over the space used.
97 If AREA is null, space is allocated with `malloc'. */
100 tgetstr (const char *cap
, char **area
)
102 register char *ptr
= find_capability (term_entry
, cap
);
103 if (!ptr
|| (ptr
[-1] != '=' && ptr
[-1] != '~'))
105 return tgetst1 (ptr
, area
);
108 #ifdef IS_EBCDIC_HOST
109 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
110 gives meaning of character following \, or a space if no special meaning.
111 Sixteen characters per line within the string. */
113 static const char esctab
[]
114 = " \057\026 \047\014 \
119 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
120 gives meaning of character following \, or a space if no special meaning.
121 Eight characters per line within the string. */
123 static const char esctab
[]
124 = " \007\010 \033\014 \
130 /* PTR points to a string value inside a termcap entry.
131 Copy that value, processing \ and ^ abbreviations,
132 into the block that *AREA points to,
133 or to newly allocated storage if AREA is NULL.
134 Return the address to which we copied the value,
135 or NULL if PTR is NULL. */
138 tgetst1 (char *ptr
, char **area
)
140 register char *p
, *r
;
149 /* `ret' gets address of where to store the string. */
152 /* Compute size of block needed (may overestimate). */
154 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
156 ret
= (char *) xmalloc (p
- ptr
+ 1);
161 /* Copy the string value, stopping at null or colon.
162 Also process ^ and \ abbreviations. */
165 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
178 if (c
>= '0' && c
<= '7')
183 while (++size
< 3 && (c1
= *p
) >= '0' && c1
<= '7')
190 #ifdef IS_EBCDIC_HOST
191 else if (c
>= 0200 && c
< 0360)
193 c1
= esctab
[(c
& ~0100) - 0200];
198 else if (c
>= 0100 && c
< 0200)
200 c1
= esctab
[(c
& ~040) - 0100];
209 /* Sometimes entries have "%pN" which means use parameter N in the
210 next %-substitution. If all such N are continuous in the range
211 [1,9] we can remove each "%pN" because they are redundant, thus
212 reducing bandwidth requirements. True, Emacs is well beyond the
213 days of 150baud teletypes, but some of its users aren't much so.
215 This pass could probably be integrated into the one above but
216 abbreviation expansion makes that effort a little more hairy than
217 its worth; this is cleaner. */
219 register int last_p_param
= 0;
220 int remove_p_params
= 1;
221 struct { char *beg
; int len
; } cut
[11];
223 for (cut
[0].beg
= p
= ret
; p
< r
- 3; p
++)
225 if (!remove_p_params
)
227 if (*p
== '%' && *(p
+ 1) == 'p')
229 if (*(p
+ 2) - '0' == 1 + last_p_param
)
231 cut
[last_p_param
].len
= p
- cut
[last_p_param
].beg
;
234 cut
[last_p_param
].beg
= p
;
236 else /* not continuous: bail */
238 if (last_p_param
> 10) /* too many: bail */
242 if (remove_p_params
&& last_p_param
)
247 cut
[last_p_param
].len
= r
- cut
[last_p_param
].beg
;
248 for (i
= 0, wp
= ret
; i
<= last_p_param
; wp
+= cut
[i
++].len
)
249 memcpy (wp
, cut
[i
].beg
, cut
[i
].len
);
261 /* Outputting a string with padding. */
266 tputs (register const char *str
, int nlines
, int (*outfun
) (int))
268 register int padcount
= 0;
272 /* For quite high speeds, convert to the smaller
273 units to avoid overflow. */
275 speed
= - speed
/ 100;
280 while (*str
>= '0' && *str
<= '9')
282 padcount
+= *str
++ - '0';
288 padcount
+= *str
++ - '0';
298 /* PADCOUNT is now in units of tenths of msec.
299 SPEED is measured in characters per 10 seconds
300 or in characters per .1 seconds (if negative).
301 We use the smaller units for larger speeds to avoid overflow. */
306 padcount
= -padcount
;
313 while (padcount
-- > 0)
317 /* Finding the termcap entry in the termcap data base. */
319 struct termcap_buffer
328 /* Forward declarations of static functions. */
330 static int scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
);
331 static char *gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
);
332 static int compare_contin (register char *str1
, register char *str2
);
333 static int name_match (char *line
, char *name
);
335 #ifdef MSDOS /* MW, May 1993 */
337 valid_filename_p (char *fn
)
339 return *fn
== '/' || fn
[1] == ':';
342 #define valid_filename_p(fn) (*(fn) == '/')
345 /* Find the termcap entry data for terminal type NAME
346 and store it in the block that BP points to.
347 Record its address for future use.
349 If BP is null, space is dynamically allocated.
351 Return -1 if there is some difficulty accessing the data base
353 0 if the data base is accessible but the type NAME is not defined
354 in it, and some other value otherwise. */
357 tgetent (char *bp
, const char *name
)
359 register char *termcap_name
;
361 struct termcap_buffer buf
;
363 char *tc_search_point
;
365 ptrdiff_t malloc_size
= 0;
367 char *tcenv
= NULL
; /* TERMCAP value, if it contains :tc=. */
368 char *indirect
= NULL
; /* Terminal type in :tc= in TERMCAP value. */
371 #ifdef INTERNAL_TERMINAL
372 /* For the internal terminal we don't want to read any termcap file,
374 if (!strcmp (name
, "internal"))
376 term
= INTERNAL_TERMINAL
;
379 malloc_size
= 1 + strlen (term
);
380 bp
= (char *) xmalloc (malloc_size
);
385 #endif /* INTERNAL_TERMINAL */
387 /* For compatibility with programs like `less' that want to
388 put data in the termcap buffer themselves as a fallback. */
392 termcap_name
= getenv ("TERMCAP");
393 if (termcap_name
&& *termcap_name
== '\0')
395 #if defined (MSDOS) && !defined (TEST)
396 if (termcap_name
&& (*termcap_name
== '\\'
397 || *termcap_name
== '/'
398 || termcap_name
[1] == ':'))
399 dostounix_filename (termcap_name
);
402 filep
= termcap_name
&& valid_filename_p (termcap_name
);
404 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
405 it is a file name to use instead of /etc/termcap.
406 If it is non-null and does not start with /,
407 it is the entry itself, but only if
408 the name the caller requested matches the TERM variable. */
410 if (termcap_name
&& !filep
&& !strcmp (name
, getenv ("TERM")))
412 indirect
= tgetst1 (find_capability (termcap_name
, "tc"), (char **) 0);
418 strcpy (bp
, termcap_name
);
422 { /* It has tc=. Need to read /etc/termcap. */
423 tcenv
= termcap_name
;
428 if (!termcap_name
|| !filep
)
429 termcap_name
= TERMCAP_FILE
;
431 /* Here we know we must search a file and termcap_name has its name. */
434 fd
= open (termcap_name
, O_RDONLY
|O_TEXT
, 0);
436 fd
= open (termcap_name
, O_RDONLY
, 0);
442 /* Add 1 to size to ensure room for terminating null. */
443 buf
.beg
= (char *) xmalloc (buf
.size
+ 1);
444 term
= indirect
? indirect
: (char *)name
;
448 malloc_size
= indirect
? strlen (tcenv
) + 1 : buf
.size
;
449 bp
= (char *) xmalloc (malloc_size
);
451 tc_search_point
= bp1
= bp
;
454 /* Copy the data from the environment variable. */
457 bp1
+= strlen (tcenv
);
462 /* Scan the file, reading it via buf, till find start of main entry. */
463 if (scan_file (term
, fd
, &buf
) == 0)
472 /* Free old `term' if appropriate. */
476 /* If BP is malloc'd by us, make sure it is big enough. */
479 ptrdiff_t offset1
= bp1
- bp
, offset2
= tc_search_point
- bp
;
480 malloc_size
= offset1
+ buf
.size
;
481 bp
= termcap_name
= (char *) xrealloc (bp
, malloc_size
);
482 bp1
= termcap_name
+ offset1
;
483 tc_search_point
= termcap_name
+ offset2
;
486 /* Copy the line of the entry from buf into bp. */
487 termcap_name
= buf
.ptr
;
488 while ((*bp1
++ = c
= *termcap_name
++) && c
!= '\n')
489 /* Drop out any \ newline sequence. */
490 if (c
== '\\' && *termcap_name
== '\n')
497 /* Does this entry refer to another terminal type's entry?
498 If something is found, copy it into heap and null-terminate it. */
499 tc_search_point
= find_capability (tc_search_point
, "tc");
500 term
= tgetst1 (tc_search_point
, (char **) 0);
507 bp
= (char *) xrealloc (bp
, bp1
- bp
+ 1);
514 /* Given file open on FD and buffer BUFP,
515 scan the file from the beginning until a line is found
516 that starts the entry for terminal type STR.
517 Return 1 if successful, with that line in BUFP,
518 or 0 if no entry is found in the file. */
521 scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
)
525 bufp
->ptr
= bufp
->beg
;
534 /* Read a line into the buffer. */
538 /* if it is continued, append another line to it,
539 until a non-continued line ends. */
540 end
= gobble_line (fd
, bufp
, end
);
542 while (!bufp
->ateof
&& end
[-2] == '\\');
544 if (*bufp
->ptr
!= '#'
545 && name_match (bufp
->ptr
, str
))
548 /* Discard the line just processed. */
554 /* Return nonzero if NAME is one of the names specified
555 by termcap entry LINE. */
558 name_match (char *line
, char *name
)
562 if (!compare_contin (line
, name
))
564 /* This line starts an entry. Is it the right one? */
565 for (tem
= line
; *tem
&& *tem
!= '\n' && *tem
!= ':'; tem
++)
566 if (*tem
== '|' && !compare_contin (tem
+ 1, name
))
573 compare_contin (register char *str1
, register char *str2
)
580 while (c1
== '\\' && *str1
== '\n')
583 while ((c1
= *str1
++) == ' ' || c1
== '\t');
587 /* End of type being looked up. */
588 if (c1
== '|' || c1
== ':')
589 /* If end of name in data base, we win. */
599 /* Make sure that the buffer <- BUFP contains a full line
600 of the file open on FD, starting at the place BUFP->ptr
601 points to. Can read more of the file, discard stuff before
602 BUFP->ptr, or make the buffer bigger.
604 Return the pointer to after the newline ending the line,
605 or to the end of the file, if there is no newline to end it.
607 Can also merge on continuation lines. If APPEND_END is
608 non-null, it points past the newline of a line that is
609 continued; we add another line onto it and regard the whole
610 thing as one line. The caller decides when a line is continued. */
613 gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
)
617 register char *buf
= bufp
->beg
;
620 append_end
= bufp
->ptr
;
625 while (*end
&& *end
!= '\n') end
++;
629 return buf
+ bufp
->full
;
630 if (bufp
->ptr
== buf
)
632 if (bufp
->full
== bufp
->size
)
634 ptrdiff_t ptr_offset
= bufp
->ptr
- buf
;
635 ptrdiff_t append_end_offset
= append_end
- buf
;
636 /* Add 1 to size to ensure room for terminating null. */
637 ptrdiff_t size
= bufp
->size
+ 1;
638 bufp
->beg
= buf
= xpalloc (buf
, &size
, 1, -1, 1);
639 bufp
->size
= size
- 1;
640 bufp
->ptr
= buf
+ ptr_offset
;
641 append_end
= buf
+ append_end_offset
;
646 append_end
-= bufp
->ptr
- buf
;
647 memcpy (buf
, bufp
->ptr
, bufp
->full
-= bufp
->ptr
- buf
);
650 if (!(nread
= read (fd
, buf
+ bufp
->full
, bufp
->size
- bufp
->full
)))
653 buf
[bufp
->full
] = '\0';
665 char *x
= tgetstr (cap
, 0);
668 printf ("%s: ", cap
);
672 if (*y
<= ' ' || *y
== 0177)
673 printf ("\\%0o", *y
);
684 main (int argc
, char **argv
)
690 printf ("TERM: %s\n", term
);
692 buf
= (char *) tgetent (0, term
);
695 printf ("No entry.\n");
699 printf ("Entry: %s\n", buf
);
704 printf ("co: %d\n", tgetnum ("co"));
705 printf ("am: %d\n", tgetflag ("am"));