Add an undo command to url-cookie-mode
[emacs.git] / src / termcap.c
blob4d85323a9efccce61932d114ee8435bc29c3a351
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985-1986, 1993-1995, 2000-2008, 2011, 2013-2018 Free
3 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)
8 any later version.
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. If not, see <https://www.gnu.org/licenses/>. */
18 /* Since 2010-03, 073589f4, Emacs 24.1, this file is only used
19 by the MS-DOS port of Emacs. */
21 /* Emacs config.h may rename various library functions such as malloc. */
22 #include <config.h>
23 #include <sys/file.h>
24 #include <fcntl.h>
25 #include <unistd.h>
27 #include "lisp.h"
28 #include "tparam.h"
29 #ifdef MSDOS
30 #include "msdos.h"
31 #endif
33 /* BUFSIZE is the initial size allocated for the buffer
34 for reading the termcap file.
35 It is not a limit.
36 Make it large normally for speed.
37 Make it variable when debugging, so can exercise
38 increasing the space dynamically. */
40 #ifndef BUFSIZE
41 #ifdef DEBUG
42 #define BUFSIZE bufsize
44 int bufsize = 128;
45 #else
46 #define BUFSIZE 2048
47 #endif
48 #endif
50 #ifndef TERMCAP_FILE
51 #define TERMCAP_FILE "/etc/termcap"
52 #endif
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,
65 0 if not found. */
67 static char *
68 find_capability (register char *bp, register const char *cap)
70 for (; *bp; bp++)
71 if (bp[0] == ':'
72 && bp[1] == cap[0]
73 && bp[2] == cap[1])
74 return &bp[4];
75 return NULL;
78 int
79 tgetnum (const char *cap)
81 register char *ptr = find_capability (term_entry, cap);
82 if (!ptr || ptr[-1] != '#')
83 return -1;
84 return atoi (ptr);
87 int
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'. */
99 char *
100 tgetstr (const char *cap, char **area)
102 register char *ptr = find_capability (term_entry, cap);
103 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
104 return NULL;
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 \
115 \025 \015 \
116 \005 \013 \
118 #else
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 \
125 \012 \
126 \015 \011 \013 \
128 #endif
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. */
137 static char *
138 tgetst1 (char *ptr, char **area)
140 register char *p, *r;
141 register int c;
142 register int size;
143 char *ret;
144 register int c1;
146 if (!ptr)
147 return NULL;
149 /* `ret' gets address of where to store the string. */
150 if (!area)
152 /* Compute size of block needed (may overestimate). */
153 p = ptr;
154 while ((c = *p++) && c != ':' && c != '\n')
156 ret = xmalloc (p - ptr + 1);
158 else
159 ret = *area;
161 /* Copy the string value, stopping at null or colon.
162 Also process ^ and \ abbreviations. */
163 p = ptr;
164 r = ret;
165 while ((c = *p++) && c != ':' && c != '\n')
167 if (c == '^')
169 c = *p++;
170 if (c == '?')
171 c = 0177;
172 else
173 c &= 037;
175 else if (c == '\\')
177 c = *p++;
178 if (c >= '0' && c <= '7')
180 c -= '0';
181 size = 0;
183 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
185 c *= 8;
186 c += c1 - '0';
187 p++;
190 #ifdef IS_EBCDIC_HOST
191 else if (c >= 0200 && c < 0360)
193 c1 = esctab[(c & ~0100) - 0200];
194 if (c1 != ' ')
195 c = c1;
197 #else
198 else if (c >= 0100 && c < 0200)
200 c1 = esctab[(c & ~040) - 0100];
201 if (c1 != ' ')
202 c = c1;
204 #endif
206 *r++ = c;
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 int last_p_param = 0;
220 bool 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)
226 break;
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;
232 last_p_param++;
233 p += 3;
234 cut[last_p_param].beg = p;
236 else /* not continuous: bail */
237 remove_p_params = 0;
238 if (last_p_param > 10) /* too many: bail */
239 remove_p_params = 0;
242 if (remove_p_params && last_p_param)
244 register int i;
245 char *wp;
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);
250 r = wp;
254 *r = '\0';
255 /* Update *AREA. */
256 if (area)
257 *area = r + 1;
258 return ret;
261 /* Outputting a string with padding. */
263 char PC;
265 void
266 tputs (register const char *str, int nlines, int (*outfun) (int))
268 register int padcount = 0;
269 register int speed;
271 speed = baud_rate;
272 /* For quite high speeds, convert to the smaller
273 units to avoid overflow. */
274 if (speed > 10000)
275 speed = - speed / 100;
277 if (!str)
278 return;
280 while (*str >= '0' && *str <= '9')
282 padcount += *str++ - '0';
283 padcount *= 10;
285 if (*str == '.')
287 str++;
288 padcount += *str++ - '0';
290 if (*str == '*')
292 str++;
293 padcount *= nlines;
295 while (*str)
296 (*outfun) (*str++);
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. */
302 padcount *= speed;
303 padcount += 500;
304 padcount /= 1000;
305 if (speed < 0)
306 padcount = -padcount;
307 else
309 padcount += 50;
310 padcount /= 100;
313 while (padcount-- > 0)
314 (*outfun) (PC);
317 /* Finding the termcap entry in the termcap data base. */
319 struct termcap_buffer
321 char *beg;
322 ptrdiff_t size;
323 char *ptr;
324 bool ateof;
325 ptrdiff_t full;
328 /* Forward declarations of static functions. */
330 static bool scan_file (char *, int, struct termcap_buffer *);
331 static char *gobble_line (int, struct termcap_buffer *, char *);
332 static bool compare_contin (char *, char *);
333 static bool name_match (char *, char *);
335 static bool
336 valid_filename_p (char *fn)
338 #ifdef MSDOS
339 return *fn == '/' || fn[1] == ':';
340 #else
341 return *fn == '/';
342 #endif
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
352 of terminal types,
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;
360 register int fd;
361 struct termcap_buffer buf;
362 register char *bp1;
363 char *tc_search_point;
364 char *term;
365 ptrdiff_t malloc_size = 0;
366 int c;
367 char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */
368 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
369 bool filep;
371 #ifdef INTERNAL_TERMINAL
372 /* For the internal terminal we don't want to read any termcap file,
373 so fake it. */
374 if (!strcmp (name, "internal"))
376 term = INTERNAL_TERMINAL;
377 if (!bp)
379 malloc_size = 1 + strlen (term);
380 bp = xmalloc (malloc_size);
382 strcpy (bp, term);
383 goto ret;
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. */
389 if (bp)
390 term_entry = bp;
392 termcap_name = getenv ("TERMCAP");
393 if (termcap_name && *termcap_name == '\0')
394 termcap_name = NULL;
395 #if defined (MSDOS) && !defined (TEST)
396 if (termcap_name && (*termcap_name == '\\'
397 || *termcap_name == '/'
398 || termcap_name[1] == ':'))
399 dostounix_filename (termcap_name);
400 #endif
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"), 0);
413 if (!indirect)
415 if (!bp)
416 bp = termcap_name;
417 else
418 strcpy (bp, termcap_name);
419 goto ret;
421 else
422 { /* It has tc=. Need to read /etc/termcap. */
423 tcenv = termcap_name;
424 termcap_name = NULL;
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. */
433 fd = emacs_open (termcap_name, O_RDONLY | O_TEXT, 0);
434 if (fd < 0)
435 return -1;
437 buf.size = BUFSIZE;
438 /* Add 1 to size to ensure room for terminating null. */
439 buf.beg = xmalloc (buf.size + 1);
440 term = indirect ? indirect : (char *)name;
442 if (!bp)
444 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
445 bp = xmalloc (malloc_size);
447 tc_search_point = bp1 = bp;
449 if (indirect)
450 /* Copy the data from the environment variable. */
452 strcpy (bp, tcenv);
453 bp1 += strlen (tcenv);
456 while (term)
458 /* Scan the file, reading it via buf, till find start of main entry. */
459 if (scan_file (term, fd, &buf) == 0)
461 emacs_close (fd);
462 xfree (buf.beg);
463 if (malloc_size)
464 xfree (bp);
465 return 0;
468 /* Free old `term' if appropriate. */
469 if (term != name)
470 xfree (term);
472 /* If BP is malloc'd by us, make sure it is big enough. */
473 if (malloc_size)
475 ptrdiff_t offset1 = bp1 - bp, offset2 = tc_search_point - bp;
476 malloc_size = offset1 + buf.size;
477 bp = termcap_name = xrealloc (bp, malloc_size);
478 bp1 = termcap_name + offset1;
479 tc_search_point = termcap_name + offset2;
482 /* Copy the line of the entry from buf into bp. */
483 termcap_name = buf.ptr;
484 while ((*bp1++ = c = *termcap_name++) && c != '\n')
485 /* Drop out any \ newline sequence. */
486 if (c == '\\' && *termcap_name == '\n')
488 bp1--;
489 termcap_name++;
491 *bp1 = '\0';
493 /* Does this entry refer to another terminal type's entry?
494 If something is found, copy it into heap and null-terminate it. */
495 tc_search_point = find_capability (tc_search_point, "tc");
496 term = tgetst1 (tc_search_point, 0);
499 emacs_close (fd);
500 xfree (buf.beg);
502 if (malloc_size)
503 bp = xrealloc (bp, bp1 - bp + 1);
505 ret:
506 term_entry = bp;
507 return 1;
510 /* Given file open on FD and buffer BUFP,
511 scan the file from the beginning until a line is found
512 that starts the entry for terminal type STR.
513 Return 1 if successful, with that line in BUFP,
514 or 0 if no entry is found in the file. */
516 static bool
517 scan_file (char *str, int fd, struct termcap_buffer *bufp)
519 char *end;
521 bufp->ptr = bufp->beg;
522 bufp->full = 0;
523 bufp->ateof = 0;
524 *bufp->ptr = '\0';
526 lseek (fd, 0, 0);
528 while (!bufp->ateof)
530 /* Read a line into the buffer. */
531 end = NULL;
534 /* if it is continued, append another line to it,
535 until a non-continued line ends. */
536 end = gobble_line (fd, bufp, end);
538 while (!bufp->ateof && end[-2] == '\\');
540 if (*bufp->ptr != '#'
541 && name_match (bufp->ptr, str))
542 return 1;
544 /* Discard the line just processed. */
545 bufp->ptr = end;
547 return 0;
550 /* Return true if NAME is one of the names specified
551 by termcap entry LINE. */
553 static bool
554 name_match (char *line, char *name)
556 char *tem;
558 if (!compare_contin (line, name))
559 return 1;
560 /* This line starts an entry. Is it the right one? */
561 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
562 if (*tem == '|' && !compare_contin (tem + 1, name))
563 return 1;
565 return 0;
568 static bool
569 compare_contin (char *str1, char *str2)
571 while (1)
573 int c1 = *str1++;
574 int c2 = *str2++;
575 while (c1 == '\\' && *str1 == '\n')
577 str1++;
578 while ((c1 = *str1++) == ' ' || c1 == '\t')
579 continue;
581 if (c2 == '\0')
583 /* End of type being looked up. */
584 if (c1 == '|' || c1 == ':')
585 /* If end of name in data base, we win. */
586 return 0;
587 else
588 return 1;
590 else if (c1 != c2)
591 return 1;
595 /* Make sure that the buffer <- BUFP contains a full line
596 of the file open on FD, starting at the place BUFP->ptr
597 points to. Can read more of the file, discard stuff before
598 BUFP->ptr, or make the buffer bigger.
600 Return the pointer to after the newline ending the line,
601 or to the end of the file, if there is no newline to end it.
603 Can also merge on continuation lines. If APPEND_END is
604 non-null, it points past the newline of a line that is
605 continued; we add another line onto it and regard the whole
606 thing as one line. The caller decides when a line is continued. */
608 static char *
609 gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end)
611 register char *end;
612 register int nread;
613 register char *buf = bufp->beg;
615 if (!append_end)
616 append_end = bufp->ptr;
618 while (1)
620 end = append_end;
621 while (*end && *end != '\n') end++;
622 if (*end)
623 break;
624 if (bufp->ateof)
625 return buf + bufp->full;
626 if (bufp->ptr == buf)
628 if (bufp->full == bufp->size)
630 ptrdiff_t ptr_offset = bufp->ptr - buf;
631 ptrdiff_t append_end_offset = append_end - buf;
632 /* Add 1 to size to ensure room for terminating null. */
633 ptrdiff_t size = bufp->size + 1;
634 bufp->beg = buf = xpalloc (buf, &size, 1, -1, 1);
635 bufp->size = size - 1;
636 bufp->ptr = buf + ptr_offset;
637 append_end = buf + append_end_offset;
640 else
642 append_end -= bufp->ptr - buf;
643 memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf);
644 bufp->ptr = buf;
646 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
647 bufp->ateof = 1;
648 bufp->full += nread;
649 buf[bufp->full] = '\0';
651 return end + 1;