comparison src/termcap.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 84b14dcb0985
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
1 /* Work-alike for termcap, plus extra features.
2 Copyright (C) 1985, 1986, 1993 Free Software Foundation, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 /* Synched up with: Not synched with FSF. */
22
23 /* config.h may rename various library functions such as malloc. */
24 #ifdef emacs
25 #include <config.h>
26 #include "lisp.h" /* For encapsulated open, close, read */
27 #include "device.h" /* For DEVICE_BAUD_RATE */
28 #else /* not emacs */
29 #if defined(USG) || defined(STDC_HEADERS)
30 #define memcpy(d, s, n) memcpy ((d), (s), (n))
31 #endif
32
33 #ifdef STDC_HEADERS
34 #include <stdlib.h>
35 #include <string.h>
36 #else
37 char *getenv ();
38 char *malloc ();
39 char *realloc ();
40 #endif
41
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 #ifdef _POSIX_VERSION
46 #include <fcntl.h>
47 #endif
48
49 #endif /* not emacs */
50
51 /* BUFSIZE is the initial size allocated for the buffer
52 for reading the termcap file.
53 It is not a limit.
54 Make it large normally for speed.
55 Make it variable when debugging, so can exercise
56 increasing the space dynamically. */
57
58 #ifndef BUFSIZE
59 #ifdef DEBUG
60 #define BUFSIZE bufsize
61
62 int bufsize = 128;
63 #else
64 #define BUFSIZE 2048
65 #endif
66 #endif
67
68 #ifndef emacs
69 static void
70 memory_out ()
71 {
72 write (2, "virtual memory exhausted\n", 25);
73 exit (1);
74 }
75
76 static char *
77 xmalloc (size)
78 unsigned int size;
79 {
80 char *tem = malloc (size);
81
82 if (!tem)
83 memory_out ();
84 return tem;
85 }
86
87 static char *
88 xrealloc (ptr, size)
89 char *ptr;
90 unsigned size;
91 {
92 char *tem = realloc (ptr, size);
93
94 if (!tem)
95 memory_out ();
96 return tem;
97 }
98 #endif /* not emacs */
99
100 /* Looking up capabilities in the entry already found. */
101
102 /* The pointer to the data made by tgetent is left here
103 for tgetnum, tgetflag and tgetstr to find. */
104 static char *term_entry;
105
106 static CONST char *tgetst1 (CONST char *ptr, char **area);
107
108 /* Search entry BP for capability CAP.
109 Return a pointer to the capability (in BP) if found,
110 0 if not found. */
111
112 static CONST char *
113 find_capability (bp, cap)
114 CONST char *bp;
115 CONST char *cap;
116 {
117 for (; *bp; bp++)
118 if (bp[0] == ':'
119 && bp[1] == cap[0]
120 && bp[2] == cap[1])
121 return &bp[4];
122 return 0;
123 }
124
125 int
126 tgetnum (cap)
127 CONST char *cap;
128 {
129 CONST char *ptr = find_capability (term_entry, cap);
130 if (!ptr || ptr[-1] != '#')
131 return -1;
132 return atoi (ptr);
133 }
134
135 int
136 tgetflag (cap)
137 CONST char *cap;
138 {
139 CONST char *ptr = find_capability (term_entry, cap);
140 return 0 != ptr && ptr[-1] == ':';
141 }
142
143 /* Look up a string-valued capability CAP.
144 If AREA is nonzero, it points to a pointer to a block in which
145 to store the string. That pointer is advanced over the space used.
146 If AREA is zero, space is allocated with `malloc'. */
147
148 CONST char *
149 tgetstr (cap, area)
150 CONST char *cap;
151 char **area;
152 {
153 CONST char *ptr = find_capability (term_entry, cap);
154 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
155 return 0;
156 return tgetst1 (ptr, area);
157 }
158
159 /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
160 gives meaning of character following \, or a space if no special meaning.
161 Eight characters per line within the string. */
162
163 static char esctab[]
164 = " \007\010 \033\014 "
165 " \012 "
166 " \015 \011 \013 "
167 " ";
168
169 /* PTR points to a string value inside a termcap entry.
170 Copy that value, processing \ and ^ abbreviations,
171 into the block that *AREA points to,
172 or to newly allocated storage if AREA is 0. */
173
174 static CONST char *
175 tgetst1 (ptr, area)
176 CONST char *ptr;
177 char **area;
178 {
179 CONST char *p;
180 char *r;
181 int c;
182 int size;
183 char *ret;
184 int c1;
185
186 if (!ptr)
187 return 0;
188
189 /* `ret' gets address of where to store the string. */
190 if (!area)
191 {
192 /* Compute size of block needed (may overestimate). */
193 p = ptr;
194 while ((c = *p++) && c != ':' && c != '\n')
195 ;
196 ret = (char *) xmalloc (p - ptr + 1);
197 }
198 else
199 ret = *area;
200
201 /* Copy the string value, stopping at null or colon.
202 Also process ^ and \ abbreviations. */
203 p = ptr;
204 r = ret;
205 while ((c = *p++) && c != ':' && c != '\n')
206 {
207 if (c == '^')
208 c = *p++ & 037;
209 else if (c == '\\')
210 {
211 c = *p++;
212 if (c >= '0' && c <= '7')
213 {
214 c -= '0';
215 size = 0;
216
217 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
218 {
219 c *= 8;
220 c += c1 - '0';
221 p++;
222 }
223 }
224 else if (c >= 0100 && c < 0200)
225 {
226 c1 = esctab[(c & ~040) - 0100];
227 if (c1 != ' ')
228 c = c1;
229 }
230 }
231 *r++ = c;
232 }
233 *r = 0;
234 /* Update *AREA. */
235 if (area)
236 *area = r + 1;
237 return ret;
238 }
239
240 /* Outputting a string with padding. */
241
242 #ifdef LINUX
243 speed_t ospeed;
244 #else
245 short ospeed;
246 #endif
247 /* If `ospeed' is 0, we use `tputs_baud_rate' as the actual baud rate. */
248 int tputs_baud_rate;
249 char PC;
250
251 /* Actual baud rate if positive;
252 - baud rate / 100 if negative. */
253
254 static short speeds[] =
255 {
256 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
257 -18, -24, -48, -96, -192, -288, -384, -576, -1152
258 };
259
260 void
261 tputs (string, nlines, outfun)
262 CONST char *string;
263 int nlines;
264 void (*outfun) (int);
265 {
266 int padcount = 0;
267 int speed;
268
269 #ifdef emacs
270 speed = DEVICE_BAUD_RATE (XDEVICE (Fselected_device (Qnil)));
271 #else
272 if (ospeed == 0)
273 speed = tputs_baud_rate;
274 else
275 speed = speeds[ospeed];
276 #endif
277
278 if (string == (char *) 0)
279 return;
280
281 while (isdigit (* (CONST unsigned char *) string))
282 {
283 padcount += *string++ - '0';
284 padcount *= 10;
285 }
286 if (*string == '.')
287 {
288 string++;
289 padcount += *string++ - '0';
290 }
291 if (*string == '*')
292 {
293 string++;
294 padcount *= nlines;
295 }
296 while (*string)
297 (*outfun) (*string++);
298
299 /* padcount is now in units of tenths of msec. */
300 padcount *= speeds[ospeed];
301 padcount += 500;
302 padcount /= 1000;
303 if (speeds[ospeed] < 0)
304 padcount = -padcount;
305 else
306 {
307 padcount += 50;
308 padcount /= 100;
309 }
310
311 while (padcount-- > 0)
312 (*outfun) (PC);
313 }
314
315 /* Finding the termcap entry in the termcap data base. */
316
317 struct buffer
318 {
319 char *beg;
320 int size;
321 char *ptr;
322 int ateof;
323 int full;
324 };
325
326 /* Forward declarations of static functions. */
327
328 static int scan_file ();
329 static char *gobble_line ();
330 static int compare_contin ();
331 static int name_match ();
332
333
334 /* Find the termcap entry data for terminal type NAME
335 and store it in the block that BP points to.
336 Record its address for future use.
337
338 If BP is zero, space is dynamically allocated. */
339
340 extern char *getenv ();
341
342 int
343 tgetent (bp, name)
344 char *bp;
345 CONST char *name;
346 {
347 char *tem;
348 int fd;
349 struct buffer buf;
350 char *bp1;
351 char *bp2;
352 CONST char *term;
353 int malloc_size = 0;
354 int c;
355 char *tcenv; /* TERMCAP value, if it contais :tc=. */
356 CONST char *indirect = 0; /* Terminal type in :tc= in TERMCAP value. */
357
358 tem = getenv ("TERMCAP");
359 if (tem && *tem == 0) tem = 0;
360
361
362 /* If tem is non-null and starts with / (in the un*x case, that is),
363 it is a file name to use instead of /etc/termcap.
364 If it is non-null and does not start with /,
365 it is the entry itself, but only if
366 the name the caller requested matches the TERM variable. */
367
368 if (tem && !IS_DIRECTORY_SEP (*tem) && !strcmp (name, (char *) getenv ("TERM")))
369 {
370 indirect = tgetst1 (find_capability (tem, "tc"), 0);
371 if (!indirect)
372 {
373 if (!bp)
374 bp = tem;
375 else
376 strcpy (bp, tem);
377 goto ret;
378 }
379 else
380 { /* We will need to read /etc/termcap. */
381 tcenv = tem;
382 tem = 0;
383 }
384 }
385 else
386 indirect = (char *) 0;
387
388 if (!tem)
389 tem = "/etc/termcap";
390
391 /* Here we know we must search a file and tem has its name. */
392
393 fd = open (tem, 0, 0);
394 if (fd < 0)
395 return -1;
396
397 buf.size = BUFSIZE;
398 /* Add 1 to size to ensure room for terminating null. */
399 buf.beg = (char *) xmalloc (buf.size + 1);
400 term = indirect ? indirect : name;
401
402 if (!bp)
403 {
404 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
405 bp = (char *) xmalloc (malloc_size);
406 }
407 bp1 = bp;
408
409 if (indirect)
410 /* Copy the data from the environment variable. */
411 {
412 strcpy (bp, tcenv);
413 bp1 += strlen (tcenv);
414 }
415
416 while (term)
417 {
418 /* Scan the file, reading it via buf, till find start of main entry. */
419 if (scan_file (term, fd, &buf) == 0)
420 return 0;
421
422 /* Free old `term' if appropriate. */
423 if (term != name)
424 xfree (term);
425
426 /* If BP is malloc'd by us, make sure it is big enough. */
427 if (malloc_size)
428 {
429 malloc_size = bp1 - bp + buf.size;
430 tem = (char *) xrealloc (bp, malloc_size);
431 bp1 += tem - bp;
432 bp = tem;
433 }
434
435 bp2 = bp1;
436
437 /* Copy the line of the entry from buf into bp. */
438 tem = buf.ptr;
439 while ((*bp1++ = c = *tem++) && c != '\n')
440 /* Drop out any \ newline sequence. */
441 if (c == '\\' && *tem == '\n')
442 {
443 bp1--;
444 tem++;
445 }
446 *bp1 = 0;
447
448 /* Does this entry refer to another terminal type's entry?
449 If something is found, copy it into heap and null-terminate it. */
450 term = tgetst1 (find_capability (bp2, "tc"), 0);
451 }
452
453 close (fd);
454 xfree (buf.beg);
455
456 if (malloc_size)
457 {
458 bp = (char *) xrealloc (bp, bp1 - bp + 1);
459 }
460
461 ret:
462 term_entry = bp;
463 if (malloc_size)
464 /* #### yuck, why the hell are we casting a pointer to an int? */
465 return (int) (long) bp;
466 return 1;
467 }
468
469 /* Given file open on FD and buffer BUFP,
470 scan the file from the beginning until a line is found
471 that starts the entry for terminal type STRING.
472 Returns 1 if successful, with that line in BUFP,
473 or returns 0 if no entry found in the file. */
474
475 static int
476 scan_file (string, fd, bufp)
477 char *string;
478 int fd;
479 struct buffer *bufp;
480 {
481 char *end;
482
483 bufp->ptr = bufp->beg;
484 bufp->full = 0;
485 bufp->ateof = 0;
486 *bufp->ptr = 0;
487
488 lseek (fd, 0L, 0);
489
490 while (!bufp->ateof)
491 {
492 /* Read a line into the buffer. */
493 end = 0;
494 do
495 {
496 /* if it is continued, append another line to it,
497 until a non-continued line ends. */
498 end = gobble_line (fd, bufp, end);
499 }
500 while (!bufp->ateof && end[-2] == '\\');
501
502 if (*bufp->ptr != '#'
503 && name_match (bufp->ptr, string))
504 return 1;
505
506 /* Discard the line just processed. */
507 bufp->ptr = end;
508 }
509 return 0;
510 }
511
512 /* Return nonzero if NAME is one of the names specified
513 by termcap entry LINE. */
514
515 static int
516 name_match (line, name)
517 char *line, *name;
518 {
519 char *tem;
520
521 if (!compare_contin (line, name))
522 return 1;
523 /* This line starts an entry. Is it the right one? */
524 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
525 if (*tem == '|' && !compare_contin (tem + 1, name))
526 return 1;
527
528 return 0;
529 }
530
531 static int
532 compare_contin (str1, str2)
533 char *str1, *str2;
534 {
535 int c1, c2;
536 while (1)
537 {
538 c1 = *str1++;
539 c2 = *str2++;
540 while (c1 == '\\' && *str1 == '\n')
541 {
542 str1++;
543 while ((c1 = *str1++) == ' ' || c1 == '\t');
544 }
545 if (c2 == '\0')
546 {
547 /* End of type being looked up. */
548 if (c1 == '|' || c1 == ':')
549 /* If end of name in data base, we win. */
550 return 0;
551 else
552 return 1;
553 }
554 else if (c1 != c2)
555 return 1;
556 }
557 }
558
559 /* Make sure that the buffer <- BUFP contains a full line
560 of the file open on FD, starting at the place BUFP->ptr
561 points to. Can read more of the file, discard stuff before
562 BUFP->ptr, or make the buffer bigger.
563
564 Returns the pointer to after the newline ending the line,
565 or to the end of the file, if there is no newline to end it.
566
567 Can also merge on continuation lines. If APPEND_END is
568 nonzero, it points past the newline of a line that is
569 continued; we add another line onto it and regard the whole
570 thing as one line. The caller decides when a line is continued. */
571
572 static char *
573 gobble_line (fd, bufp, append_end)
574 int fd;
575 struct buffer *bufp;
576 char *append_end;
577 {
578 char *end;
579 int nread;
580 char *buf = bufp->beg;
581 char *tem;
582
583 if (append_end == 0)
584 append_end = bufp->ptr;
585
586 while (1)
587 {
588 end = append_end;
589 while (*end && *end != '\n') end++;
590 if (*end)
591 break;
592 if (bufp->ateof)
593 return buf + bufp->full;
594 if (bufp->ptr == buf)
595 {
596 if (bufp->full == bufp->size)
597 {
598 bufp->size *= 2;
599 /* Add 1 to size to ensure room for terminating null. */
600 tem = (char *) xrealloc (buf, bufp->size + 1);
601 bufp->ptr = (bufp->ptr - buf) + tem;
602 append_end = (append_end - buf) + tem;
603 bufp->beg = buf = tem;
604 }
605 }
606 else
607 {
608 append_end -= bufp->ptr - buf;
609 memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf);
610 bufp->ptr = buf;
611 }
612 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
613 bufp->ateof = 1;
614 bufp->full += nread;
615 buf[bufp->full] = 0;
616 }
617 return end + 1;
618 }
619
620 #ifdef TEST
621
622 #include <stdio.h>
623
624 main (argc, argv)
625 int argc;
626 char **argv;
627 {
628 char *term;
629 char *buf;
630
631 term = argv[1];
632 printf ("TERM: %s\n", term);
633
634 buf = (char *) tgetent (0, term);
635 if ((int) buf <= 0)
636 {
637 printf ("No entry.\n");
638 return 0;
639 }
640
641 printf ("Entry: %s\n", buf);
642
643 tprint ("cm");
644 tprint ("AL");
645
646 printf ("co: %d\n", tgetnum ("co"));
647 printf ("am: %d\n", tgetflag ("am"));
648 }
649
650 tprint (cap)
651 CONST char *cap;
652 {
653 char *x = tgetstr (cap, 0);
654 char *y;
655
656 printf ("%s: ", cap);
657 if (x)
658 {
659 for (y = x; *y; y++)
660 if (*y <= ' ' || *y == 0177)
661 printf ("\\%0o", *y);
662 else
663 putchar (*y);
664 xfree (x);
665 }
666 else
667 printf ("none");
668 putchar ('\n');
669 }
670
671 #endif /* TEST */
672