comparison src/termcap.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 41ff10fd062f
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
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 #ifdef VMS
257 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
258 -20, -24, -36, -48, -72, -96, -192
259 #else /* not VMS */
260 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
261 -18, -24, -48, -96, -192, -288, -384, -576, -1152
262 #endif /* not VMS */
263 };
264
265 void
266 tputs (string, nlines, outfun)
267 CONST char *string;
268 int nlines;
269 void (*outfun) (int);
270 {
271 int padcount = 0;
272 int speed;
273
274 #ifdef emacs
275 speed = DEVICE_BAUD_RATE (XDEVICE (Fselected_device (Qnil)));
276 #else
277 if (ospeed == 0)
278 speed = tputs_baud_rate;
279 else
280 speed = speeds[ospeed];
281 #endif
282
283 if (string == (char *) 0)
284 return;
285
286 while (isdigit (* (CONST unsigned char *) string))
287 {
288 padcount += *string++ - '0';
289 padcount *= 10;
290 }
291 if (*string == '.')
292 {
293 string++;
294 padcount += *string++ - '0';
295 }
296 if (*string == '*')
297 {
298 string++;
299 padcount *= nlines;
300 }
301 while (*string)
302 (*outfun) (*string++);
303
304 /* padcount is now in units of tenths of msec. */
305 padcount *= speeds[ospeed];
306 padcount += 500;
307 padcount /= 1000;
308 if (speeds[ospeed] < 0)
309 padcount = -padcount;
310 else
311 {
312 padcount += 50;
313 padcount /= 100;
314 }
315
316 while (padcount-- > 0)
317 (*outfun) (PC);
318 }
319
320 /* Finding the termcap entry in the termcap data base. */
321
322 struct buffer
323 {
324 char *beg;
325 int size;
326 char *ptr;
327 int ateof;
328 int full;
329 };
330
331 /* Forward declarations of static functions. */
332
333 static int scan_file ();
334 static char *gobble_line ();
335 static int compare_contin ();
336 static int name_match ();
337
338 #ifdef VMS
339
340 #include <rmsdef.h>
341 #include <fab.h>
342 #include <nam.h>
343
344 static int
345 legal_filename_p (fn)
346 char *fn;
347 {
348 struct FAB fab = cc$rms_fab;
349 struct NAM nam = cc$rms_nam;
350 char esa[NAM$C_MAXRSS];
351
352 fab.fab$l_fna = fn;
353 fab.fab$b_fns = strlen(fn);
354 fab.fab$l_nam = &nam;
355 fab.fab$l_fop = FAB$M_NAM;
356
357 nam.nam$l_esa = esa;
358 nam.nam$b_ess = sizeof esa;
359
360 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
361 }
362
363 #endif /* VMS */
364
365 /* Find the termcap entry data for terminal type NAME
366 and store it in the block that BP points to.
367 Record its address for future use.
368
369 If BP is zero, space is dynamically allocated. */
370
371 extern char *getenv ();
372
373 int
374 tgetent (bp, name)
375 char *bp;
376 CONST char *name;
377 {
378 char *tem;
379 int fd;
380 struct buffer buf;
381 char *bp1;
382 char *bp2;
383 CONST char *term;
384 int malloc_size = 0;
385 int c;
386 char *tcenv; /* TERMCAP value, if it contais :tc=. */
387 CONST char *indirect = 0; /* Terminal type in :tc= in TERMCAP value. */
388 int filep;
389
390 tem = getenv ("TERMCAP");
391 if (tem && *tem == 0) tem = 0;
392
393 #ifdef VMS
394 filep = tem && legal_filename_p (tem);
395 #else
396 filep = tem && (*tem == '/');
397 #endif /* VMS */
398
399 /* If tem is non-null and starts with / (in the un*x case, that is),
400 it is a file name to use instead of /etc/termcap.
401 If it is non-null and does not start with /,
402 it is the entry itself, but only if
403 the name the caller requested matches the TERM variable. */
404
405 if (tem && !filep && !strcmp (name, (char *) getenv ("TERM")))
406 {
407 indirect = tgetst1 (find_capability (tem, "tc"), 0);
408 if (!indirect)
409 {
410 if (!bp)
411 bp = tem;
412 else
413 strcpy (bp, tem);
414 goto ret;
415 }
416 else
417 { /* We will need to read /etc/termcap. */
418 tcenv = tem;
419 tem = 0;
420 }
421 }
422 else
423 indirect = (char *) 0;
424
425 if (!tem)
426 #ifdef VMS
427 tem = "emacs_library:[etc]termcap.dat";
428 #else
429 tem = "/etc/termcap";
430 #endif
431
432 /* Here we know we must search a file and tem has its name. */
433
434 fd = open (tem, 0, 0);
435 if (fd < 0)
436 return -1;
437
438 buf.size = BUFSIZE;
439 /* Add 1 to size to ensure room for terminating null. */
440 buf.beg = (char *) xmalloc (buf.size + 1);
441 term = indirect ? indirect : name;
442
443 if (!bp)
444 {
445 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
446 bp = (char *) xmalloc (malloc_size);
447 }
448 bp1 = bp;
449
450 if (indirect)
451 /* Copy the data from the environment variable. */
452 {
453 strcpy (bp, tcenv);
454 bp1 += strlen (tcenv);
455 }
456
457 while (term)
458 {
459 /* Scan the file, reading it via buf, till find start of main entry. */
460 if (scan_file (term, fd, &buf) == 0)
461 return 0;
462
463 /* Free old `term' if appropriate. */
464 if (term != name)
465 xfree (term);
466
467 /* If BP is malloc'd by us, make sure it is big enough. */
468 if (malloc_size)
469 {
470 malloc_size = bp1 - bp + buf.size;
471 tem = (char *) xrealloc (bp, malloc_size);
472 bp1 += tem - bp;
473 bp = tem;
474 }
475
476 bp2 = bp1;
477
478 /* Copy the line of the entry from buf into bp. */
479 tem = buf.ptr;
480 while ((*bp1++ = c = *tem++) && c != '\n')
481 /* Drop out any \ newline sequence. */
482 if (c == '\\' && *tem == '\n')
483 {
484 bp1--;
485 tem++;
486 }
487 *bp1 = 0;
488
489 /* Does this entry refer to another terminal type's entry?
490 If something is found, copy it into heap and null-terminate it. */
491 term = tgetst1 (find_capability (bp2, "tc"), 0);
492 }
493
494 close (fd);
495 xfree (buf.beg);
496
497 if (malloc_size)
498 {
499 bp = (char *) xrealloc (bp, bp1 - bp + 1);
500 }
501
502 ret:
503 term_entry = bp;
504 if (malloc_size)
505 /* #### yuck, why the hell are we casting a pointer to an int? */
506 return (int) (long) bp;
507 return 1;
508 }
509
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 STRING.
513 Returns 1 if successful, with that line in BUFP,
514 or returns 0 if no entry found in the file. */
515
516 static int
517 scan_file (string, fd, bufp)
518 char *string;
519 int fd;
520 struct buffer *bufp;
521 {
522 char *end;
523
524 bufp->ptr = bufp->beg;
525 bufp->full = 0;
526 bufp->ateof = 0;
527 *bufp->ptr = 0;
528
529 lseek (fd, 0L, 0);
530
531 while (!bufp->ateof)
532 {
533 /* Read a line into the buffer. */
534 end = 0;
535 do
536 {
537 /* if it is continued, append another line to it,
538 until a non-continued line ends. */
539 end = gobble_line (fd, bufp, end);
540 }
541 while (!bufp->ateof && end[-2] == '\\');
542
543 if (*bufp->ptr != '#'
544 && name_match (bufp->ptr, string))
545 return 1;
546
547 /* Discard the line just processed. */
548 bufp->ptr = end;
549 }
550 return 0;
551 }
552
553 /* Return nonzero if NAME is one of the names specified
554 by termcap entry LINE. */
555
556 static int
557 name_match (line, name)
558 char *line, *name;
559 {
560 char *tem;
561
562 if (!compare_contin (line, name))
563 return 1;
564 /* This line starts an entry. Is it the right one? */
565 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
566 if (*tem == '|' && !compare_contin (tem + 1, name))
567 return 1;
568
569 return 0;
570 }
571
572 static int
573 compare_contin (str1, str2)
574 char *str1, *str2;
575 {
576 int c1, c2;
577 while (1)
578 {
579 c1 = *str1++;
580 c2 = *str2++;
581 while (c1 == '\\' && *str1 == '\n')
582 {
583 str1++;
584 while ((c1 = *str1++) == ' ' || c1 == '\t');
585 }
586 if (c2 == '\0')
587 {
588 /* End of type being looked up. */
589 if (c1 == '|' || c1 == ':')
590 /* If end of name in data base, we win. */
591 return 0;
592 else
593 return 1;
594 }
595 else if (c1 != c2)
596 return 1;
597 }
598 }
599
600 /* Make sure that the buffer <- BUFP contains a full line
601 of the file open on FD, starting at the place BUFP->ptr
602 points to. Can read more of the file, discard stuff before
603 BUFP->ptr, or make the buffer bigger.
604
605 Returns the pointer to after the newline ending the line,
606 or to the end of the file, if there is no newline to end it.
607
608 Can also merge on continuation lines. If APPEND_END is
609 nonzero, it points past the newline of a line that is
610 continued; we add another line onto it and regard the whole
611 thing as one line. The caller decides when a line is continued. */
612
613 static char *
614 gobble_line (fd, bufp, append_end)
615 int fd;
616 struct buffer *bufp;
617 char *append_end;
618 {
619 char *end;
620 int nread;
621 char *buf = bufp->beg;
622 char *tem;
623
624 if (append_end == 0)
625 append_end = bufp->ptr;
626
627 while (1)
628 {
629 end = append_end;
630 while (*end && *end != '\n') end++;
631 if (*end)
632 break;
633 if (bufp->ateof)
634 return buf + bufp->full;
635 if (bufp->ptr == buf)
636 {
637 if (bufp->full == bufp->size)
638 {
639 bufp->size *= 2;
640 /* Add 1 to size to ensure room for terminating null. */
641 tem = (char *) xrealloc (buf, bufp->size + 1);
642 bufp->ptr = (bufp->ptr - buf) + tem;
643 append_end = (append_end - buf) + tem;
644 bufp->beg = buf = tem;
645 }
646 }
647 else
648 {
649 append_end -= bufp->ptr - buf;
650 memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf);
651 bufp->ptr = buf;
652 }
653 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
654 bufp->ateof = 1;
655 bufp->full += nread;
656 buf[bufp->full] = 0;
657 }
658 return end + 1;
659 }
660
661 #ifdef TEST
662
663 #include <stdio.h>
664
665 main (argc, argv)
666 int argc;
667 char **argv;
668 {
669 char *term;
670 char *buf;
671
672 term = argv[1];
673 printf ("TERM: %s\n", term);
674
675 buf = (char *) tgetent (0, term);
676 if ((int) buf <= 0)
677 {
678 printf ("No entry.\n");
679 return 0;
680 }
681
682 printf ("Entry: %s\n", buf);
683
684 tprint ("cm");
685 tprint ("AL");
686
687 printf ("co: %d\n", tgetnum ("co"));
688 printf ("am: %d\n", tgetflag ("am"));
689 }
690
691 tprint (cap)
692 CONST char *cap;
693 {
694 char *x = tgetstr (cap, 0);
695 char *y;
696
697 printf ("%s: ", cap);
698 if (x)
699 {
700 for (y = x; *y; y++)
701 if (*y <= ' ' || *y == 0177)
702 printf ("\\%0o", *y);
703 else
704 putchar (*y);
705 xfree (x);
706 }
707 else
708 printf ("none");
709 putchar ('\n');
710 }
711
712 #endif /* TEST */
713