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. */
32 /* BUFSIZE is the initial size allocated for the buffer
33 for reading the termcap file.
35 Make it large normally for speed.
36 Make it variable when debugging, so can exercise
37 increasing the space dynamically. */
41 #define BUFSIZE bufsize
50 #define TERMCAP_FILE "/etc/termcap"
54 /* Looking up capabilities in the entry already found. */
56 /* The pointer to the data made by tgetent is left here
57 for tgetnum, tgetflag and tgetstr to find. */
58 static char *term_entry
;
60 static char *tgetst1 (char *ptr
, char **area
);
62 /* Search entry BP for capability CAP.
63 Return a pointer to the capability (in BP) if found,
67 find_capability (register char *bp
, register const char *cap
)
78 tgetnum (const char *cap
)
80 register char *ptr
= find_capability (term_entry
, cap
);
81 if (!ptr
|| ptr
[-1] != '#')
87 tgetflag (const char *cap
)
89 register char *ptr
= find_capability (term_entry
, cap
);
90 return ptr
&& ptr
[-1] == ':';
93 /* Look up a string-valued capability CAP.
94 If AREA is non-null, it points to a pointer to a block in which
95 to store the string. That pointer is advanced over the space used.
96 If AREA is null, space is allocated with `malloc'. */
99 tgetstr (const char *cap
, char **area
)
101 register char *ptr
= find_capability (term_entry
, cap
);
102 if (!ptr
|| (ptr
[-1] != '=' && ptr
[-1] != '~'))
104 return tgetst1 (ptr
, area
);
107 #ifdef IS_EBCDIC_HOST
108 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
109 gives meaning of character following \, or a space if no special meaning.
110 Sixteen characters per line within the string. */
112 static const char esctab
[]
113 = " \057\026 \047\014 \
118 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
119 gives meaning of character following \, or a space if no special meaning.
120 Eight characters per line within the string. */
122 static const char esctab
[]
123 = " \007\010 \033\014 \
129 /* PTR points to a string value inside a termcap entry.
130 Copy that value, processing \ and ^ abbreviations,
131 into the block that *AREA points to,
132 or to newly allocated storage if AREA is NULL.
133 Return the address to which we copied the value,
134 or NULL if PTR is NULL. */
137 tgetst1 (char *ptr
, char **area
)
139 register char *p
, *r
;
148 /* `ret' gets address of where to store the string. */
151 /* Compute size of block needed (may overestimate). */
153 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
155 ret
= xmalloc (p
- ptr
+ 1);
160 /* Copy the string value, stopping at null or colon.
161 Also process ^ and \ abbreviations. */
164 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
177 if (c
>= '0' && c
<= '7')
182 while (++size
< 3 && (c1
= *p
) >= '0' && c1
<= '7')
189 #ifdef IS_EBCDIC_HOST
190 else if (c
>= 0200 && c
< 0360)
192 c1
= esctab
[(c
& ~0100) - 0200];
197 else if (c
>= 0100 && c
< 0200)
199 c1
= esctab
[(c
& ~040) - 0100];
208 /* Sometimes entries have "%pN" which means use parameter N in the
209 next %-substitution. If all such N are continuous in the range
210 [1,9] we can remove each "%pN" because they are redundant, thus
211 reducing bandwidth requirements. True, Emacs is well beyond the
212 days of 150baud teletypes, but some of its users aren't much so.
214 This pass could probably be integrated into the one above but
215 abbreviation expansion makes that effort a little more hairy than
216 its worth; this is cleaner. */
218 register int last_p_param
= 0;
219 int remove_p_params
= 1;
220 struct { char *beg
; int len
; } cut
[11];
222 for (cut
[0].beg
= p
= ret
; p
< r
- 3; p
++)
224 if (!remove_p_params
)
226 if (*p
== '%' && *(p
+ 1) == 'p')
228 if (*(p
+ 2) - '0' == 1 + last_p_param
)
230 cut
[last_p_param
].len
= p
- cut
[last_p_param
].beg
;
233 cut
[last_p_param
].beg
= p
;
235 else /* not continuous: bail */
237 if (last_p_param
> 10) /* too many: bail */
241 if (remove_p_params
&& last_p_param
)
246 cut
[last_p_param
].len
= r
- cut
[last_p_param
].beg
;
247 for (i
= 0, wp
= ret
; i
<= last_p_param
; wp
+= cut
[i
++].len
)
248 memcpy (wp
, cut
[i
].beg
, cut
[i
].len
);
260 /* Outputting a string with padding. */
265 tputs (register const char *str
, int nlines
, int (*outfun
) (int))
267 register int padcount
= 0;
271 /* For quite high speeds, convert to the smaller
272 units to avoid overflow. */
274 speed
= - speed
/ 100;
279 while (*str
>= '0' && *str
<= '9')
281 padcount
+= *str
++ - '0';
287 padcount
+= *str
++ - '0';
297 /* PADCOUNT is now in units of tenths of msec.
298 SPEED is measured in characters per 10 seconds
299 or in characters per .1 seconds (if negative).
300 We use the smaller units for larger speeds to avoid overflow. */
305 padcount
= -padcount
;
312 while (padcount
-- > 0)
316 /* Finding the termcap entry in the termcap data base. */
318 struct termcap_buffer
327 /* Forward declarations of static functions. */
329 static int scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
);
330 static char *gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
);
331 static int compare_contin (register char *str1
, register char *str2
);
332 static int name_match (char *line
, char *name
);
334 #ifdef MSDOS /* MW, May 1993 */
336 valid_filename_p (char *fn
)
338 return *fn
== '/' || fn
[1] == ':';
341 #define valid_filename_p(fn) (*(fn) == '/')
344 /* Find the termcap entry data for terminal type NAME
345 and store it in the block that BP points to.
346 Record its address for future use.
348 If BP is null, space is dynamically allocated.
350 Return -1 if there is some difficulty accessing the data base
352 0 if the data base is accessible but the type NAME is not defined
353 in it, and some other value otherwise. */
356 tgetent (char *bp
, const char *name
)
358 register char *termcap_name
;
360 struct termcap_buffer buf
;
362 char *tc_search_point
;
364 ptrdiff_t malloc_size
= 0;
366 char *tcenv
= NULL
; /* TERMCAP value, if it contains :tc=. */
367 char *indirect
= NULL
; /* Terminal type in :tc= in TERMCAP value. */
370 #ifdef INTERNAL_TERMINAL
371 /* For the internal terminal we don't want to read any termcap file,
373 if (!strcmp (name
, "internal"))
375 term
= INTERNAL_TERMINAL
;
378 malloc_size
= 1 + strlen (term
);
379 bp
= xmalloc (malloc_size
);
384 #endif /* INTERNAL_TERMINAL */
386 /* For compatibility with programs like `less' that want to
387 put data in the termcap buffer themselves as a fallback. */
391 termcap_name
= getenv ("TERMCAP");
392 if (termcap_name
&& *termcap_name
== '\0')
394 #if defined (MSDOS) && !defined (TEST)
395 if (termcap_name
&& (*termcap_name
== '\\'
396 || *termcap_name
== '/'
397 || termcap_name
[1] == ':'))
398 dostounix_filename (termcap_name
);
401 filep
= termcap_name
&& valid_filename_p (termcap_name
);
403 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
404 it is a file name to use instead of /etc/termcap.
405 If it is non-null and does not start with /,
406 it is the entry itself, but only if
407 the name the caller requested matches the TERM variable. */
409 if (termcap_name
&& !filep
&& !strcmp (name
, getenv ("TERM")))
411 indirect
= tgetst1 (find_capability (termcap_name
, "tc"), (char **) 0);
417 strcpy (bp
, termcap_name
);
421 { /* It has tc=. Need to read /etc/termcap. */
422 tcenv
= termcap_name
;
427 if (!termcap_name
|| !filep
)
428 termcap_name
= TERMCAP_FILE
;
430 /* Here we know we must search a file and termcap_name has its name. */
433 fd
= open (termcap_name
, O_RDONLY
|O_TEXT
, 0);
435 fd
= open (termcap_name
, O_RDONLY
, 0);
441 /* Add 1 to size to ensure room for terminating null. */
442 buf
.beg
= xmalloc (buf
.size
+ 1);
443 term
= indirect
? indirect
: (char *)name
;
447 malloc_size
= indirect
? strlen (tcenv
) + 1 : buf
.size
;
448 bp
= xmalloc (malloc_size
);
450 tc_search_point
= bp1
= bp
;
453 /* Copy the data from the environment variable. */
456 bp1
+= strlen (tcenv
);
461 /* Scan the file, reading it via buf, till find start of main entry. */
462 if (scan_file (term
, fd
, &buf
) == 0)
471 /* Free old `term' if appropriate. */
475 /* If BP is malloc'd by us, make sure it is big enough. */
478 ptrdiff_t offset1
= bp1
- bp
, offset2
= tc_search_point
- bp
;
479 malloc_size
= offset1
+ buf
.size
;
480 bp
= termcap_name
= xrealloc (bp
, malloc_size
);
481 bp1
= termcap_name
+ offset1
;
482 tc_search_point
= termcap_name
+ offset2
;
485 /* Copy the line of the entry from buf into bp. */
486 termcap_name
= buf
.ptr
;
487 while ((*bp1
++ = c
= *termcap_name
++) && c
!= '\n')
488 /* Drop out any \ newline sequence. */
489 if (c
== '\\' && *termcap_name
== '\n')
496 /* Does this entry refer to another terminal type's entry?
497 If something is found, copy it into heap and null-terminate it. */
498 tc_search_point
= find_capability (tc_search_point
, "tc");
499 term
= tgetst1 (tc_search_point
, (char **) 0);
506 bp
= xrealloc (bp
, bp1
- bp
+ 1);
513 /* Given file open on FD and buffer BUFP,
514 scan the file from the beginning until a line is found
515 that starts the entry for terminal type STR.
516 Return 1 if successful, with that line in BUFP,
517 or 0 if no entry is found in the file. */
520 scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
)
524 bufp
->ptr
= bufp
->beg
;
533 /* Read a line into the buffer. */
537 /* if it is continued, append another line to it,
538 until a non-continued line ends. */
539 end
= gobble_line (fd
, bufp
, end
);
541 while (!bufp
->ateof
&& end
[-2] == '\\');
543 if (*bufp
->ptr
!= '#'
544 && name_match (bufp
->ptr
, str
))
547 /* Discard the line just processed. */
553 /* Return nonzero if NAME is one of the names specified
554 by termcap entry LINE. */
557 name_match (char *line
, char *name
)
561 if (!compare_contin (line
, name
))
563 /* This line starts an entry. Is it the right one? */
564 for (tem
= line
; *tem
&& *tem
!= '\n' && *tem
!= ':'; tem
++)
565 if (*tem
== '|' && !compare_contin (tem
+ 1, name
))
572 compare_contin (register char *str1
, register char *str2
)
579 while (c1
== '\\' && *str1
== '\n')
582 while ((c1
= *str1
++) == ' ' || c1
== '\t');
586 /* End of type being looked up. */
587 if (c1
== '|' || c1
== ':')
588 /* If end of name in data base, we win. */
598 /* Make sure that the buffer <- BUFP contains a full line
599 of the file open on FD, starting at the place BUFP->ptr
600 points to. Can read more of the file, discard stuff before
601 BUFP->ptr, or make the buffer bigger.
603 Return the pointer to after the newline ending the line,
604 or to the end of the file, if there is no newline to end it.
606 Can also merge on continuation lines. If APPEND_END is
607 non-null, it points past the newline of a line that is
608 continued; we add another line onto it and regard the whole
609 thing as one line. The caller decides when a line is continued. */
612 gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
)
616 register char *buf
= bufp
->beg
;
619 append_end
= bufp
->ptr
;
624 while (*end
&& *end
!= '\n') end
++;
628 return buf
+ bufp
->full
;
629 if (bufp
->ptr
== buf
)
631 if (bufp
->full
== bufp
->size
)
633 ptrdiff_t ptr_offset
= bufp
->ptr
- buf
;
634 ptrdiff_t append_end_offset
= append_end
- buf
;
635 /* Add 1 to size to ensure room for terminating null. */
636 ptrdiff_t size
= bufp
->size
+ 1;
637 bufp
->beg
= buf
= xpalloc (buf
, &size
, 1, -1, 1);
638 bufp
->size
= size
- 1;
639 bufp
->ptr
= buf
+ ptr_offset
;
640 append_end
= buf
+ append_end_offset
;
645 append_end
-= bufp
->ptr
- buf
;
646 memcpy (buf
, bufp
->ptr
, bufp
->full
-= bufp
->ptr
- buf
);
649 if (!(nread
= read (fd
, buf
+ bufp
->full
, bufp
->size
- bufp
->full
)))
652 buf
[bufp
->full
] = '\0';
664 char *x
= tgetstr (cap
, 0);
667 printf ("%s: ", cap
);
671 if (*y
<= ' ' || *y
== 0177)
672 printf ("\\%0o", *y
);
683 main (int argc
, char **argv
)
689 printf ("TERM: %s\n", term
);
691 buf
= (char *) tgetent (0, term
);
694 printf ("No entry.\n");
698 printf ("Entry: %s\n", buf
);
703 printf ("co: %d\n", tgetnum ("co"));
704 printf ("am: %d\n", tgetflag ("am"));