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. */
34 #define NULL (char *) 0
37 /* BUFSIZE is the initial size allocated for the buffer
38 for reading the termcap file.
40 Make it large normally for speed.
41 Make it variable when debugging, so can exercise
42 increasing the space dynamically. */
46 #define BUFSIZE bufsize
55 #define TERMCAP_FILE "/etc/termcap"
59 /* Looking up capabilities in the entry already found. */
61 /* The pointer to the data made by tgetent is left here
62 for tgetnum, tgetflag and tgetstr to find. */
63 static char *term_entry
;
65 static char *tgetst1 (char *ptr
, char **area
);
67 /* Search entry BP for capability CAP.
68 Return a pointer to the capability (in BP) if found,
72 find_capability (register char *bp
, register const char *cap
)
83 tgetnum (const char *cap
)
85 register char *ptr
= find_capability (term_entry
, cap
);
86 if (!ptr
|| ptr
[-1] != '#')
92 tgetflag (const char *cap
)
94 register char *ptr
= find_capability (term_entry
, cap
);
95 return ptr
&& ptr
[-1] == ':';
98 /* Look up a string-valued capability CAP.
99 If AREA is non-null, it points to a pointer to a block in which
100 to store the string. That pointer is advanced over the space used.
101 If AREA is null, space is allocated with `malloc'. */
104 tgetstr (const char *cap
, char **area
)
106 register char *ptr
= find_capability (term_entry
, cap
);
107 if (!ptr
|| (ptr
[-1] != '=' && ptr
[-1] != '~'))
109 return tgetst1 (ptr
, area
);
112 #ifdef IS_EBCDIC_HOST
113 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
114 gives meaning of character following \, or a space if no special meaning.
115 Sixteen characters per line within the string. */
117 static const char esctab
[]
118 = " \057\026 \047\014 \
123 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
124 gives meaning of character following \, or a space if no special meaning.
125 Eight characters per line within the string. */
127 static const char esctab
[]
128 = " \007\010 \033\014 \
134 /* PTR points to a string value inside a termcap entry.
135 Copy that value, processing \ and ^ abbreviations,
136 into the block that *AREA points to,
137 or to newly allocated storage if AREA is NULL.
138 Return the address to which we copied the value,
139 or NULL if PTR is NULL. */
142 tgetst1 (char *ptr
, char **area
)
144 register char *p
, *r
;
153 /* `ret' gets address of where to store the string. */
156 /* Compute size of block needed (may overestimate). */
158 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
160 ret
= (char *) xmalloc (p
- ptr
+ 1);
165 /* Copy the string value, stopping at null or colon.
166 Also process ^ and \ abbreviations. */
169 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
182 if (c
>= '0' && c
<= '7')
187 while (++size
< 3 && (c1
= *p
) >= '0' && c1
<= '7')
194 #ifdef IS_EBCDIC_HOST
195 else if (c
>= 0200 && c
< 0360)
197 c1
= esctab
[(c
& ~0100) - 0200];
202 else if (c
>= 0100 && c
< 0200)
204 c1
= esctab
[(c
& ~040) - 0100];
213 /* Sometimes entries have "%pN" which means use parameter N in the
214 next %-substitution. If all such N are continuous in the range
215 [1,9] we can remove each "%pN" because they are redundant, thus
216 reducing bandwidth requirements. True, Emacs is well beyond the
217 days of 150baud teletypes, but some of its users aren't much so.
219 This pass could probably be integrated into the one above but
220 abbreviation expansion makes that effort a little more hairy than
221 its worth; this is cleaner. */
223 register int last_p_param
= 0;
224 int remove_p_params
= 1;
225 struct { char *beg
; int len
; } cut
[11];
227 for (cut
[0].beg
= p
= ret
; p
< r
- 3; p
++)
229 if (!remove_p_params
)
231 if (*p
== '%' && *(p
+ 1) == 'p')
233 if (*(p
+ 2) - '0' == 1 + last_p_param
)
235 cut
[last_p_param
].len
= p
- cut
[last_p_param
].beg
;
238 cut
[last_p_param
].beg
= p
;
240 else /* not continuous: bail */
242 if (last_p_param
> 10) /* too many: bail */
246 if (remove_p_params
&& last_p_param
)
251 cut
[last_p_param
].len
= r
- cut
[last_p_param
].beg
;
252 for (i
= 0, wp
= ret
; i
<= last_p_param
; wp
+= cut
[i
++].len
)
253 memcpy (wp
, cut
[i
].beg
, cut
[i
].len
);
265 /* Outputting a string with padding. */
270 tputs (register const char *str
, int nlines
, int (*outfun
) (int))
272 register int padcount
= 0;
276 /* For quite high speeds, convert to the smaller
277 units to avoid overflow. */
279 speed
= - speed
/ 100;
284 while (*str
>= '0' && *str
<= '9')
286 padcount
+= *str
++ - '0';
292 padcount
+= *str
++ - '0';
302 /* PADCOUNT is now in units of tenths of msec.
303 SPEED is measured in characters per 10 seconds
304 or in characters per .1 seconds (if negative).
305 We use the smaller units for larger speeds to avoid overflow. */
310 padcount
= -padcount
;
317 while (padcount
-- > 0)
321 /* Finding the termcap entry in the termcap data base. */
323 struct termcap_buffer
332 /* Forward declarations of static functions. */
334 static int scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
);
335 static char *gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
);
336 static int compare_contin (register char *str1
, register char *str2
);
337 static int name_match (char *line
, char *name
);
339 #ifdef MSDOS /* MW, May 1993 */
341 valid_filename_p (char *fn
)
343 return *fn
== '/' || fn
[1] == ':';
346 #define valid_filename_p(fn) (*(fn) == '/')
349 /* Find the termcap entry data for terminal type NAME
350 and store it in the block that BP points to.
351 Record its address for future use.
353 If BP is null, space is dynamically allocated.
355 Return -1 if there is some difficulty accessing the data base
357 0 if the data base is accessible but the type NAME is not defined
358 in it, and some other value otherwise. */
361 tgetent (char *bp
, const char *name
)
363 register char *termcap_name
;
365 struct termcap_buffer buf
;
367 char *tc_search_point
;
369 ptrdiff_t malloc_size
= 0;
371 char *tcenv
= NULL
; /* TERMCAP value, if it contains :tc=. */
372 char *indirect
= NULL
; /* Terminal type in :tc= in TERMCAP value. */
375 #ifdef INTERNAL_TERMINAL
376 /* For the internal terminal we don't want to read any termcap file,
378 if (!strcmp (name
, "internal"))
380 term
= INTERNAL_TERMINAL
;
383 malloc_size
= 1 + strlen (term
);
384 bp
= (char *) xmalloc (malloc_size
);
389 #endif /* INTERNAL_TERMINAL */
391 /* For compatibility with programs like `less' that want to
392 put data in the termcap buffer themselves as a fallback. */
396 termcap_name
= getenv ("TERMCAP");
397 if (termcap_name
&& *termcap_name
== '\0')
399 #if defined (MSDOS) && !defined (TEST)
400 if (termcap_name
&& (*termcap_name
== '\\'
401 || *termcap_name
== '/'
402 || termcap_name
[1] == ':'))
403 dostounix_filename (termcap_name
);
406 filep
= termcap_name
&& valid_filename_p (termcap_name
);
408 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
409 it is a file name to use instead of /etc/termcap.
410 If it is non-null and does not start with /,
411 it is the entry itself, but only if
412 the name the caller requested matches the TERM variable. */
414 if (termcap_name
&& !filep
&& !strcmp (name
, getenv ("TERM")))
416 indirect
= tgetst1 (find_capability (termcap_name
, "tc"), (char **) 0);
422 strcpy (bp
, termcap_name
);
426 { /* It has tc=. Need to read /etc/termcap. */
427 tcenv
= termcap_name
;
432 if (!termcap_name
|| !filep
)
433 termcap_name
= TERMCAP_FILE
;
435 /* Here we know we must search a file and termcap_name has its name. */
438 fd
= open (termcap_name
, O_RDONLY
|O_TEXT
, 0);
440 fd
= open (termcap_name
, O_RDONLY
, 0);
446 /* Add 1 to size to ensure room for terminating null. */
447 buf
.beg
= (char *) xmalloc (buf
.size
+ 1);
448 term
= indirect
? indirect
: (char *)name
;
452 malloc_size
= indirect
? strlen (tcenv
) + 1 : buf
.size
;
453 bp
= (char *) xmalloc (malloc_size
);
455 tc_search_point
= bp1
= bp
;
458 /* Copy the data from the environment variable. */
461 bp1
+= strlen (tcenv
);
466 /* Scan the file, reading it via buf, till find start of main entry. */
467 if (scan_file (term
, fd
, &buf
) == 0)
476 /* Free old `term' if appropriate. */
480 /* If BP is malloc'd by us, make sure it is big enough. */
483 ptrdiff_t offset1
= bp1
- bp
, offset2
= tc_search_point
- bp
;
484 malloc_size
= offset1
+ buf
.size
;
485 bp
= termcap_name
= (char *) xrealloc (bp
, malloc_size
);
486 bp1
= termcap_name
+ offset1
;
487 tc_search_point
= termcap_name
+ offset2
;
490 /* Copy the line of the entry from buf into bp. */
491 termcap_name
= buf
.ptr
;
492 while ((*bp1
++ = c
= *termcap_name
++) && c
!= '\n')
493 /* Drop out any \ newline sequence. */
494 if (c
== '\\' && *termcap_name
== '\n')
501 /* Does this entry refer to another terminal type's entry?
502 If something is found, copy it into heap and null-terminate it. */
503 tc_search_point
= find_capability (tc_search_point
, "tc");
504 term
= tgetst1 (tc_search_point
, (char **) 0);
511 bp
= (char *) xrealloc (bp
, bp1
- bp
+ 1);
518 /* Given file open on FD and buffer BUFP,
519 scan the file from the beginning until a line is found
520 that starts the entry for terminal type STR.
521 Return 1 if successful, with that line in BUFP,
522 or 0 if no entry is found in the file. */
525 scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
)
529 bufp
->ptr
= bufp
->beg
;
538 /* Read a line into the buffer. */
542 /* if it is continued, append another line to it,
543 until a non-continued line ends. */
544 end
= gobble_line (fd
, bufp
, end
);
546 while (!bufp
->ateof
&& end
[-2] == '\\');
548 if (*bufp
->ptr
!= '#'
549 && name_match (bufp
->ptr
, str
))
552 /* Discard the line just processed. */
558 /* Return nonzero if NAME is one of the names specified
559 by termcap entry LINE. */
562 name_match (char *line
, char *name
)
566 if (!compare_contin (line
, name
))
568 /* This line starts an entry. Is it the right one? */
569 for (tem
= line
; *tem
&& *tem
!= '\n' && *tem
!= ':'; tem
++)
570 if (*tem
== '|' && !compare_contin (tem
+ 1, name
))
577 compare_contin (register char *str1
, register char *str2
)
584 while (c1
== '\\' && *str1
== '\n')
587 while ((c1
= *str1
++) == ' ' || c1
== '\t');
591 /* End of type being looked up. */
592 if (c1
== '|' || c1
== ':')
593 /* If end of name in data base, we win. */
603 /* Make sure that the buffer <- BUFP contains a full line
604 of the file open on FD, starting at the place BUFP->ptr
605 points to. Can read more of the file, discard stuff before
606 BUFP->ptr, or make the buffer bigger.
608 Return the pointer to after the newline ending the line,
609 or to the end of the file, if there is no newline to end it.
611 Can also merge on continuation lines. If APPEND_END is
612 non-null, it points past the newline of a line that is
613 continued; we add another line onto it and regard the whole
614 thing as one line. The caller decides when a line is continued. */
617 gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
)
621 register char *buf
= bufp
->beg
;
624 append_end
= bufp
->ptr
;
629 while (*end
&& *end
!= '\n') end
++;
633 return buf
+ bufp
->full
;
634 if (bufp
->ptr
== buf
)
636 if (bufp
->full
== bufp
->size
)
638 ptrdiff_t ptr_offset
= bufp
->ptr
- buf
;
639 ptrdiff_t append_end_offset
= append_end
- buf
;
640 /* Add 1 to size to ensure room for terminating null. */
641 ptrdiff_t size
= bufp
->size
+ 1;
642 bufp
->beg
= buf
= xpalloc (buf
, &size
, 1, -1, 1);
643 bufp
->size
= size
- 1;
644 bufp
->ptr
= buf
+ ptr_offset
;
645 append_end
= buf
+ append_end_offset
;
650 append_end
-= bufp
->ptr
- buf
;
651 memcpy (buf
, bufp
->ptr
, bufp
->full
-= bufp
->ptr
- buf
);
654 if (!(nread
= read (fd
, buf
+ bufp
->full
, bufp
->size
- bufp
->full
)))
657 buf
[bufp
->full
] = '\0';
673 char *x
= tgetstr (cap
, 0);
676 printf ("%s: ", cap
);
680 if (*y
<= ' ' || *y
== 0177)
681 printf ("\\%0o", *y
);
692 main (int argc
, char **argv
)
698 printf ("TERM: %s\n", term
);
700 buf
= (char *) tgetent (0, term
);
703 printf ("No entry.\n");
707 printf ("Entry: %s\n", buf
);
712 printf ("co: %d\n", tgetnum ("co"));
713 printf ("am: %d\n", tgetflag ("am"));