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 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. */
23 #include <lisp.h> /* xmalloc is here */
24 /* Get the O_* definitions for open et al. */
34 #define NULL (char *) 0
41 /* BUFSIZE is the initial size allocated for the buffer
42 for reading the termcap file.
44 Make it large normally for speed.
45 Make it variable when debugging, so can exercise
46 increasing the space dynamically. */
50 #define BUFSIZE bufsize
59 #define TERMCAP_FILE "/etc/termcap"
63 /* Looking up capabilities in the entry already found. */
65 /* The pointer to the data made by tgetent is left here
66 for tgetnum, tgetflag and tgetstr to find. */
67 static char *term_entry
;
69 static char *tgetst1 (char *ptr
, char **area
);
71 /* Search entry BP for capability CAP.
72 Return a pointer to the capability (in BP) if found,
76 find_capability (register char *bp
, register char *cap
)
89 register char *ptr
= find_capability (term_entry
, cap
);
90 if (!ptr
|| ptr
[-1] != '#')
98 register char *ptr
= find_capability (term_entry
, cap
);
99 return ptr
&& ptr
[-1] == ':';
102 /* Look up a string-valued capability CAP.
103 If AREA is non-null, it points to a pointer to a block in which
104 to store the string. That pointer is advanced over the space used.
105 If AREA is null, space is allocated with `malloc'. */
108 tgetstr (char *cap
, char **area
)
110 register char *ptr
= find_capability (term_entry
, cap
);
111 if (!ptr
|| (ptr
[-1] != '=' && ptr
[-1] != '~'))
113 return tgetst1 (ptr
, area
);
116 #ifdef IS_EBCDIC_HOST
117 /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted,
118 gives meaning of character following \, or a space if no special meaning.
119 Sixteen characters per line within the string. */
121 static const char esctab
[]
122 = " \057\026 \047\014 \
127 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
128 gives meaning of character following \, or a space if no special meaning.
129 Eight characters per line within the string. */
131 static const char esctab
[]
132 = " \007\010 \033\014 \
138 /* PTR points to a string value inside a termcap entry.
139 Copy that value, processing \ and ^ abbreviations,
140 into the block that *AREA points to,
141 or to newly allocated storage if AREA is NULL.
142 Return the address to which we copied the value,
143 or NULL if PTR is NULL. */
146 tgetst1 (char *ptr
, char **area
)
148 register char *p
, *r
;
157 /* `ret' gets address of where to store the string. */
160 /* Compute size of block needed (may overestimate). */
162 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
164 ret
= (char *) xmalloc (p
- ptr
+ 1);
169 /* Copy the string value, stopping at null or colon.
170 Also process ^ and \ abbreviations. */
173 while ((c
= *p
++) && c
!= ':' && c
!= '\n')
186 if (c
>= '0' && c
<= '7')
191 while (++size
< 3 && (c1
= *p
) >= '0' && c1
<= '7')
198 #ifdef IS_EBCDIC_HOST
199 else if (c
>= 0200 && c
< 0360)
201 c1
= esctab
[(c
& ~0100) - 0200];
206 else if (c
>= 0100 && c
< 0200)
208 c1
= esctab
[(c
& ~040) - 0100];
217 /* Sometimes entries have "%pN" which means use parameter N in the
218 next %-substitution. If all such N are continuous in the range
219 [1,9] we can remove each "%pN" because they are redundant, thus
220 reducing bandwidth requirements. True, Emacs is well beyond the
221 days of 150baud teletypes, but some of its users aren't much so.
223 This pass could probably be integrated into the one above but
224 abbreviation expansion makes that effort a little more hairy than
225 its worth; this is cleaner. */
227 register int last_p_param
= 0;
228 int remove_p_params
= 1;
229 struct { char *beg
; int len
; } cut
[11];
231 for (cut
[0].beg
= p
= ret
; p
< r
- 3; p
++)
233 if (!remove_p_params
)
235 if (*p
== '%' && *(p
+ 1) == 'p')
237 if (*(p
+ 2) - '0' == 1 + last_p_param
)
239 cut
[last_p_param
].len
= p
- cut
[last_p_param
].beg
;
242 cut
[last_p_param
].beg
= p
;
244 else /* not continuous: bail */
246 if (last_p_param
> 10) /* too many: bail */
250 if (remove_p_params
&& last_p_param
)
255 cut
[last_p_param
].len
= r
- cut
[last_p_param
].beg
;
256 for (i
= 0, wp
= ret
; i
<= last_p_param
; wp
+= cut
[i
++].len
)
257 memcpy (wp
, cut
[i
].beg
, cut
[i
].len
);
269 /* Outputting a string with padding. */
273 /* If OSPEED is 0, we use this as the actual baud rate. */
280 /* Actual baud rate if positive;
281 - baud rate / 100 if negative. */
283 static const int speeds
[] =
285 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
286 -18, -24, -48, -96, -192, -288, -384, -576, -1152
289 #endif /* not emacs */
292 tputs (register char *str
, int nlines
, register int (*outfun
) (/* ??? */))
294 register int padcount
= 0;
298 extern EMACS_INT baud_rate
;
300 /* For quite high speeds, convert to the smaller
301 units to avoid overflow. */
303 speed
= - speed
/ 100;
306 speed
= tputs_baud_rate
;
308 speed
= speeds
[ospeed
];
314 while (*str
>= '0' && *str
<= '9')
316 padcount
+= *str
++ - '0';
322 padcount
+= *str
++ - '0';
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. */
340 padcount
= -padcount
;
347 while (padcount
-- > 0)
351 /* Finding the termcap entry in the termcap data base. */
353 struct termcap_buffer
362 /* Forward declarations of static functions. */
364 static int scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
);
365 static char *gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
);
366 static int compare_contin (register char *str1
, register char *str2
);
367 static int name_match (char *line
, char *name
);
369 #ifdef MSDOS /* MW, May 1993 */
371 valid_filename_p (fn
)
374 return *fn
== '/' || fn
[1] == ':';
377 #define valid_filename_p(fn) (*(fn) == '/')
380 /* Find the termcap entry data for terminal type NAME
381 and store it in the block that BP points to.
382 Record its address for future use.
384 If BP is null, space is dynamically allocated.
386 Return -1 if there is some difficulty accessing the data base
388 0 if the data base is accessible but the type NAME is not defined
389 in it, and some other value otherwise. */
392 tgetent (char *bp
, char *name
)
394 register char *termcap_name
;
396 struct termcap_buffer buf
;
398 char *tc_search_point
;
402 char *tcenv
= NULL
; /* TERMCAP value, if it contains :tc=. */
403 char *indirect
= NULL
; /* Terminal type in :tc= in TERMCAP value. */
406 #ifdef INTERNAL_TERMINAL
407 /* For the internal terminal we don't want to read any termcap file,
409 if (!strcmp (name
, "internal"))
411 term
= INTERNAL_TERMINAL
;
414 malloc_size
= 1 + strlen (term
);
415 bp
= (char *) xmalloc (malloc_size
);
420 #endif /* INTERNAL_TERMINAL */
422 /* For compatibility with programs like `less' that want to
423 put data in the termcap buffer themselves as a fallback. */
427 termcap_name
= getenv ("TERMCAP");
428 if (termcap_name
&& *termcap_name
== '\0')
430 #if defined (MSDOS) && !defined (TEST)
431 if (termcap_name
&& (*termcap_name
== '\\'
432 || *termcap_name
== '/'
433 || termcap_name
[1] == ':'))
434 dostounix_filename(termcap_name
);
437 filep
= termcap_name
&& valid_filename_p (termcap_name
);
439 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
440 it is a file name to use instead of /etc/termcap.
441 If it is non-null and does not start with /,
442 it is the entry itself, but only if
443 the name the caller requested matches the TERM variable. */
445 if (termcap_name
&& !filep
&& !strcmp (name
, getenv ("TERM")))
447 indirect
= tgetst1 (find_capability (termcap_name
, "tc"), (char **) 0);
453 strcpy (bp
, termcap_name
);
457 { /* It has tc=. Need to read /etc/termcap. */
458 tcenv
= termcap_name
;
463 if (!termcap_name
|| !filep
)
464 termcap_name
= TERMCAP_FILE
;
466 /* Here we know we must search a file and termcap_name has its name. */
469 fd
= open (termcap_name
, O_RDONLY
|O_TEXT
, 0);
471 fd
= open (termcap_name
, O_RDONLY
, 0);
477 /* Add 1 to size to ensure room for terminating null. */
478 buf
.beg
= (char *) xmalloc (buf
.size
+ 1);
479 term
= indirect
? indirect
: name
;
483 malloc_size
= indirect
? strlen (tcenv
) + 1 : buf
.size
;
484 bp
= (char *) xmalloc (malloc_size
);
486 tc_search_point
= bp1
= bp
;
489 /* Copy the data from the environment variable. */
492 bp1
+= strlen (tcenv
);
497 /* Scan the file, reading it via buf, till find start of main entry. */
498 if (scan_file (term
, fd
, &buf
) == 0)
507 /* Free old `term' if appropriate. */
511 /* If BP is malloc'd by us, make sure it is big enough. */
514 int offset1
= bp1
- bp
, offset2
= tc_search_point
- bp
;
515 malloc_size
= offset1
+ buf
.size
;
516 bp
= termcap_name
= (char *) xrealloc (bp
, malloc_size
);
517 bp1
= termcap_name
+ offset1
;
518 tc_search_point
= termcap_name
+ offset2
;
521 /* Copy the line of the entry from buf into bp. */
522 termcap_name
= buf
.ptr
;
523 while ((*bp1
++ = c
= *termcap_name
++) && c
!= '\n')
524 /* Drop out any \ newline sequence. */
525 if (c
== '\\' && *termcap_name
== '\n')
532 /* Does this entry refer to another terminal type's entry?
533 If something is found, copy it into heap and null-terminate it. */
534 tc_search_point
= find_capability (tc_search_point
, "tc");
535 term
= tgetst1 (tc_search_point
, (char **) 0);
542 bp
= (char *) xrealloc (bp
, bp1
- bp
+ 1);
549 /* Given file open on FD and buffer BUFP,
550 scan the file from the beginning until a line is found
551 that starts the entry for terminal type STR.
552 Return 1 if successful, with that line in BUFP,
553 or 0 if no entry is found in the file. */
556 scan_file (char *str
, int fd
, register struct termcap_buffer
*bufp
)
560 bufp
->ptr
= bufp
->beg
;
569 /* Read a line into the buffer. */
573 /* if it is continued, append another line to it,
574 until a non-continued line ends. */
575 end
= gobble_line (fd
, bufp
, end
);
577 while (!bufp
->ateof
&& end
[-2] == '\\');
579 if (*bufp
->ptr
!= '#'
580 && name_match (bufp
->ptr
, str
))
583 /* Discard the line just processed. */
589 /* Return nonzero if NAME is one of the names specified
590 by termcap entry LINE. */
593 name_match (char *line
, char *name
)
597 if (!compare_contin (line
, name
))
599 /* This line starts an entry. Is it the right one? */
600 for (tem
= line
; *tem
&& *tem
!= '\n' && *tem
!= ':'; tem
++)
601 if (*tem
== '|' && !compare_contin (tem
+ 1, name
))
608 compare_contin (register char *str1
, register char *str2
)
615 while (c1
== '\\' && *str1
== '\n')
618 while ((c1
= *str1
++) == ' ' || c1
== '\t');
622 /* End of type being looked up. */
623 if (c1
== '|' || c1
== ':')
624 /* If end of name in data base, we win. */
634 /* Make sure that the buffer <- BUFP contains a full line
635 of the file open on FD, starting at the place BUFP->ptr
636 points to. Can read more of the file, discard stuff before
637 BUFP->ptr, or make the buffer bigger.
639 Return the pointer to after the newline ending the line,
640 or to the end of the file, if there is no newline to end it.
642 Can also merge on continuation lines. If APPEND_END is
643 non-null, it points past the newline of a line that is
644 continued; we add another line onto it and regard the whole
645 thing as one line. The caller decides when a line is continued. */
648 gobble_line (int fd
, register struct termcap_buffer
*bufp
, char *append_end
)
652 register char *buf
= bufp
->beg
;
656 append_end
= bufp
->ptr
;
661 while (*end
&& *end
!= '\n') end
++;
665 return buf
+ bufp
->full
;
666 if (bufp
->ptr
== buf
)
668 if (bufp
->full
== bufp
->size
)
671 /* Add 1 to size to ensure room for terminating null. */
672 tem
= (char *) xrealloc (buf
, bufp
->size
+ 1);
673 bufp
->ptr
= (bufp
->ptr
- buf
) + tem
;
674 append_end
= (append_end
- buf
) + tem
;
675 bufp
->beg
= buf
= tem
;
680 append_end
-= bufp
->ptr
- buf
;
681 memcpy (buf
, bufp
->ptr
, bufp
->full
-= bufp
->ptr
- buf
);
684 if (!(nread
= read (fd
, buf
+ bufp
->full
, bufp
->size
- bufp
->full
)))
687 buf
[bufp
->full
] = '\0';
708 printf ("TERM: %s\n", term
);
710 buf
= (char *) tgetent (0, term
);
713 printf ("No entry.\n");
717 printf ("Entry: %s\n", buf
);
722 printf ("co: %d\n", tgetnum ("co"));
723 printf ("am: %d\n", tgetflag ("am"));
729 char *x
= tgetstr (cap
, 0);
732 printf ("%s: ", cap
);
736 if (*y
<= ' ' || *y
== 0177)
737 printf ("\\%0o", *y
);
749 /* arch-tag: c2e8d427-2271-4fac-95fe-411857238b80
750 (do not change this comment) */