blob: 7311349b798e78e62d079df2507f488761b197c0 [file] [log] [blame]
Jari Aalto31859422009-01-12 13:36:28 +00001/* termcap.c - Work-alike for termcap, plus extra features. */
Jari Aalto726f6381996-08-26 18:22:31 +00002
Jari Aalto31859422009-01-12 13:36:28 +00003/* Copyright (C) 1985, 1986, 1993,1994, 1995, 1998, 2001,2003,2005,2006,2008,2009 Free Software Foundation, Inc.
Jari Aalto726f6381996-08-26 18:22:31 +00004
Jari Aalto31859422009-01-12 13:36:28 +00005 This file is part of GNU Bash, the Bourne Again SHell.
Jari Aalto726f6381996-08-26 18:22:31 +00006
Jari Aalto31859422009-01-12 13:36:28 +00007 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19*/
Jari Aalto726f6381996-08-26 18:22:31 +000020
21/* Emacs config.h may rename various library functions such as malloc. */
22#ifdef HAVE_CONFIG_H
Jari Aalto726f6381996-08-26 18:22:31 +000023
Jari Aaltoccc6cda1996-12-23 17:02:34 +000024#include <config.h>
25
26/* Get the O_* definitions for open et al. */
Jari Aaltob80f6442004-07-27 13:29:18 +000027#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
28# include <sys/file.h>
Jari Aalto726f6381996-08-26 18:22:31 +000029#endif
30
Jari Aaltocce855b1998-04-17 19:52:44 +000031#include <fcntl.h>
32
Jari Aalto06285672006-10-10 14:15:34 +000033#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36
Jari Aaltobb706242000-03-17 21:46:59 +000037#ifdef HAVE_STDLIB_H
38# include <stdlib.h>
39#else
40extern char *getenv ();
41extern char *malloc ();
42extern char *realloc ();
43#endif
44
Jari Aalto06285672006-10-10 14:15:34 +000045#if defined (HAVE_STRING_H)
46#include <string.h>
47#endif
48
49#if !defined (HAVE_BCOPY) && (defined (HAVE_STRING_H) || defined (STDC_HEADERS))
50# define bcopy(s, d, n) memcpy ((d), (s), (n))
51#endif
52
Jari Aaltoccc6cda1996-12-23 17:02:34 +000053#else /* not HAVE_CONFIG_H */
54
Jari Aalto726f6381996-08-26 18:22:31 +000055#ifdef STDC_HEADERS
56#include <stdlib.h>
57#include <string.h>
58#else
59char *getenv ();
60char *malloc ();
61char *realloc ();
62#endif
63
Jari Aaltoccc6cda1996-12-23 17:02:34 +000064/* Do this after the include, in case string.h prototypes bcopy. */
65#if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy)
66#define bcopy(s, d, n) memcpy ((d), (s), (n))
67#endif
68
Jari Aalto726f6381996-08-26 18:22:31 +000069#ifdef HAVE_UNISTD_H
70#include <unistd.h>
71#endif
72#ifdef _POSIX_VERSION
73#include <fcntl.h>
74#endif
75
76#endif /* not HAVE_CONFIG_H */
77
78#ifndef NULL
79#define NULL (char *) 0
80#endif
81
Jari Aaltoccc6cda1996-12-23 17:02:34 +000082#ifndef O_RDONLY
83#define O_RDONLY 0
84#endif
85
Jari Aalto726f6381996-08-26 18:22:31 +000086/* BUFSIZE is the initial size allocated for the buffer
87 for reading the termcap file.
88 It is not a limit.
89 Make it large normally for speed.
90 Make it variable when debugging, so can exercise
91 increasing the space dynamically. */
92
93#ifndef BUFSIZE
94#ifdef DEBUG
95#define BUFSIZE bufsize
96
97int bufsize = 128;
98#else
99#define BUFSIZE 2048
100#endif
101#endif
102
Jari Aaltobb706242000-03-17 21:46:59 +0000103#include "ltcap.h"
104
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000105#ifndef TERMCAP_FILE
106#define TERMCAP_FILE "/etc/termcap"
107#endif
108
Jari Aalto726f6381996-08-26 18:22:31 +0000109#ifndef emacs
110static void
111memory_out ()
112{
113 write (2, "virtual memory exhausted\n", 25);
114 exit (1);
115}
116
117static char *
118xmalloc (size)
119 unsigned size;
120{
121 register char *tem = malloc (size);
122
123 if (!tem)
124 memory_out ();
125 return tem;
126}
127
128static char *
129xrealloc (ptr, size)
130 char *ptr;
131 unsigned size;
132{
133 register char *tem = realloc (ptr, size);
134
135 if (!tem)
136 memory_out ();
137 return tem;
138}
139#endif /* not emacs */
140
141/* Looking up capabilities in the entry already found. */
142
143/* The pointer to the data made by tgetent is left here
144 for tgetnum, tgetflag and tgetstr to find. */
145static char *term_entry;
146
147static char *tgetst1 ();
148
149/* Search entry BP for capability CAP.
150 Return a pointer to the capability (in BP) if found,
151 0 if not found. */
152
153static char *
154find_capability (bp, cap)
155 register char *bp, *cap;
156{
157 for (; *bp; bp++)
158 if (bp[0] == ':'
159 && bp[1] == cap[0]
160 && bp[2] == cap[1])
161 return &bp[4];
162 return NULL;
163}
164
Jari Aaltobb706242000-03-17 21:46:59 +0000165__private_extern__
Jari Aalto726f6381996-08-26 18:22:31 +0000166int
167tgetnum (cap)
168 char *cap;
169{
170 register char *ptr = find_capability (term_entry, cap);
171 if (!ptr || ptr[-1] != '#')
172 return -1;
173 return atoi (ptr);
174}
175
Jari Aaltobb706242000-03-17 21:46:59 +0000176__private_extern__
Jari Aalto726f6381996-08-26 18:22:31 +0000177int
178tgetflag (cap)
179 char *cap;
180{
181 register char *ptr = find_capability (term_entry, cap);
182 return ptr && ptr[-1] == ':';
183}
184
185/* Look up a string-valued capability CAP.
186 If AREA is non-null, it points to a pointer to a block in which
187 to store the string. That pointer is advanced over the space used.
188 If AREA is null, space is allocated with `malloc'. */
189
Jari Aaltobb706242000-03-17 21:46:59 +0000190__private_extern__
Jari Aalto726f6381996-08-26 18:22:31 +0000191char *
192tgetstr (cap, area)
193 char *cap;
194 char **area;
195{
196 register char *ptr = find_capability (term_entry, cap);
197 if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~'))
198 return NULL;
199 return tgetst1 (ptr, area);
200}
201
202/* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted,
203 gives meaning of character following \, or a space if no special meaning.
204 Eight characters per line within the string. */
205
206static char esctab[]
207 = " \007\010 \033\014 \
208 \012 \
209 \015 \011 \013 \
210 ";
211
212/* PTR points to a string value inside a termcap entry.
213 Copy that value, processing \ and ^ abbreviations,
214 into the block that *AREA points to,
215 or to newly allocated storage if AREA is NULL.
216 Return the address to which we copied the value,
217 or NULL if PTR is NULL. */
218
219static char *
220tgetst1 (ptr, area)
221 char *ptr;
222 char **area;
223{
224 register char *p, *r;
225 register int c;
226 register int size;
227 char *ret;
228 register int c1;
229
230 if (!ptr)
231 return NULL;
232
233 /* `ret' gets address of where to store the string. */
234 if (!area)
235 {
236 /* Compute size of block needed (may overestimate). */
237 p = ptr;
238 while ((c = *p++) && c != ':' && c != '\n')
239 ;
240 ret = (char *) xmalloc (p - ptr + 1);
241 }
242 else
243 ret = *area;
244
245 /* Copy the string value, stopping at null or colon.
246 Also process ^ and \ abbreviations. */
247 p = ptr;
248 r = ret;
249 while ((c = *p++) && c != ':' && c != '\n')
250 {
251 if (c == '^')
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000252 {
253 c = *p++;
254 if (c == '?')
255 c = 0177;
256 else
257 c &= 037;
258 }
Jari Aalto726f6381996-08-26 18:22:31 +0000259 else if (c == '\\')
260 {
261 c = *p++;
262 if (c >= '0' && c <= '7')
263 {
264 c -= '0';
265 size = 0;
266
267 while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7')
268 {
269 c *= 8;
270 c += c1 - '0';
271 p++;
272 }
273 }
274 else if (c >= 0100 && c < 0200)
275 {
276 c1 = esctab[(c & ~040) - 0100];
277 if (c1 != ' ')
278 c = c1;
279 }
280 }
281 *r++ = c;
282 }
283 *r = '\0';
284 /* Update *AREA. */
285 if (area)
286 *area = r + 1;
287 return ret;
288}
289
290/* Outputting a string with padding. */
291
292short ospeed;
293/* If OSPEED is 0, we use this as the actual baud rate. */
294int tputs_baud_rate;
Jari Aaltobb706242000-03-17 21:46:59 +0000295__private_extern__ char PC = '\0';
Jari Aalto726f6381996-08-26 18:22:31 +0000296
297/* Actual baud rate if positive;
298 - baud rate / 100 if negative. */
299
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000300static int speeds[] =
Jari Aalto726f6381996-08-26 18:22:31 +0000301 {
302#ifdef VMS
303 0, 50, 75, 110, 134, 150, -3, -6, -12, -18,
304 -20, -24, -36, -48, -72, -96, -192
305#else /* not VMS */
306 0, 50, 75, 110, 135, 150, -2, -3, -6, -12,
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000307 -18, -24, -48, -96, -192, -288, -384, -576, -1152
Jari Aalto726f6381996-08-26 18:22:31 +0000308#endif /* not VMS */
309 };
310
Jari Aaltobb706242000-03-17 21:46:59 +0000311__private_extern__
Jari Aalto726f6381996-08-26 18:22:31 +0000312void
313tputs (str, nlines, outfun)
314 register char *str;
315 int nlines;
316 register int (*outfun) ();
317{
318 register int padcount = 0;
319 register int speed;
320
321#ifdef emacs
322 extern baud_rate;
323 speed = baud_rate;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000324 /* For quite high speeds, convert to the smaller
325 units to avoid overflow. */
326 if (speed > 10000)
327 speed = - speed / 100;
Jari Aalto726f6381996-08-26 18:22:31 +0000328#else
329 if (ospeed == 0)
330 speed = tputs_baud_rate;
Jari Aaltobb706242000-03-17 21:46:59 +0000331 else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0]))
Jari Aalto726f6381996-08-26 18:22:31 +0000332 speed = speeds[ospeed];
Jari Aaltobb706242000-03-17 21:46:59 +0000333 else
334 speed = 0;
Jari Aalto726f6381996-08-26 18:22:31 +0000335#endif
336
337 if (!str)
338 return;
339
340 while (*str >= '0' && *str <= '9')
341 {
342 padcount += *str++ - '0';
343 padcount *= 10;
344 }
345 if (*str == '.')
346 {
347 str++;
348 padcount += *str++ - '0';
349 }
350 if (*str == '*')
351 {
352 str++;
353 padcount *= nlines;
354 }
355 while (*str)
356 (*outfun) (*str++);
357
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000358 /* PADCOUNT is now in units of tenths of msec.
359 SPEED is measured in characters per 10 seconds
360 or in characters per .1 seconds (if negative).
361 We use the smaller units for larger speeds to avoid overflow. */
362 padcount *= speed;
Jari Aalto726f6381996-08-26 18:22:31 +0000363 padcount += 500;
364 padcount /= 1000;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000365 if (speed < 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000366 padcount = -padcount;
367 else
368 {
369 padcount += 50;
370 padcount /= 100;
371 }
372
373 while (padcount-- > 0)
374 (*outfun) (PC);
375}
376
377/* Finding the termcap entry in the termcap data base. */
378
379struct buffer
380 {
381 char *beg;
382 int size;
383 char *ptr;
384 int ateof;
385 int full;
386 };
387
388/* Forward declarations of static functions. */
389
390static int scan_file ();
391static char *gobble_line ();
392static int compare_contin ();
393static int name_match ();
394
395#ifdef VMS
396
397#include <rmsdef.h>
398#include <fab.h>
399#include <nam.h>
400
401static int
402valid_filename_p (fn)
403 char *fn;
404{
405 struct FAB fab = cc$rms_fab;
406 struct NAM nam = cc$rms_nam;
407 char esa[NAM$C_MAXRSS];
408
409 fab.fab$l_fna = fn;
410 fab.fab$b_fns = strlen(fn);
411 fab.fab$l_nam = &nam;
412 fab.fab$l_fop = FAB$M_NAM;
413
414 nam.nam$l_esa = esa;
415 nam.nam$b_ess = sizeof esa;
416
417 return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL;
418}
419
420#else /* !VMS */
421
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000422#ifdef MSDOS /* MW, May 1993 */
423static int
424valid_filename_p (fn)
425 char *fn;
426{
Jari Aaltobb706242000-03-17 21:46:59 +0000427 return *fn == '\\' || *fn == '/' ||
428 (*fn >= 'A' && *fn <= 'z' && fn[1] == ':');
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000429}
430#else
Jari Aalto726f6381996-08-26 18:22:31 +0000431#define valid_filename_p(fn) (*(fn) == '/')
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000432#endif
Jari Aalto726f6381996-08-26 18:22:31 +0000433
434#endif /* !VMS */
435
436/* Find the termcap entry data for terminal type NAME
437 and store it in the block that BP points to.
438 Record its address for future use.
439
440 If BP is null, space is dynamically allocated.
441
442 Return -1 if there is some difficulty accessing the data base
443 of terminal types,
444 0 if the data base is accessible but the type NAME is not defined
445 in it, and some other value otherwise. */
446
Jari Aaltobb706242000-03-17 21:46:59 +0000447__private_extern__
Jari Aalto726f6381996-08-26 18:22:31 +0000448int
449tgetent (bp, name)
450 char *bp, *name;
451{
452 register char *termcap_name;
453 register int fd;
454 struct buffer buf;
455 register char *bp1;
456 char *bp2;
457 char *term;
458 int malloc_size = 0;
459 register int c;
460 char *tcenv; /* TERMCAP value, if it contains :tc=. */
461 char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */
462 int filep;
463
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000464#ifdef INTERNAL_TERMINAL
465 /* For the internal terminal we don't want to read any termcap file,
466 so fake it. */
467 if (!strcmp (name, "internal"))
468 {
469 term = INTERNAL_TERMINAL;
470 if (!bp)
471 {
472 malloc_size = 1 + strlen (term);
473 bp = (char *) xmalloc (malloc_size);
474 }
475 strcpy (bp, term);
476 goto ret;
477 }
478#endif /* INTERNAL_TERMINAL */
479
480 /* For compatibility with programs like `less' that want to
481 put data in the termcap buffer themselves as a fallback. */
482 if (bp)
483 term_entry = bp;
484
Jari Aalto726f6381996-08-26 18:22:31 +0000485 termcap_name = getenv ("TERMCAP");
486 if (termcap_name && *termcap_name == '\0')
487 termcap_name = NULL;
Jari Aaltobb706242000-03-17 21:46:59 +0000488#if 0
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000489#if defined (MSDOS) && !defined (TEST)
490 if (termcap_name && (*termcap_name == '\\'
491 || *termcap_name == '/'
492 || termcap_name[1] == ':'))
493 dostounix_filename(termcap_name);
494#endif
Jari Aaltobb706242000-03-17 21:46:59 +0000495#endif
Jari Aalto726f6381996-08-26 18:22:31 +0000496
497 filep = termcap_name && valid_filename_p (termcap_name);
498
499 /* If termcap_name is non-null and starts with / (in the un*x case, that is),
500 it is a file name to use instead of /etc/termcap.
501 If it is non-null and does not start with /,
502 it is the entry itself, but only if
503 the name the caller requested matches the TERM variable. */
504
505 if (termcap_name && !filep && !strcmp (name, getenv ("TERM")))
506 {
507 indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0);
508 if (!indirect)
509 {
510 if (!bp)
511 bp = termcap_name;
512 else
513 strcpy (bp, termcap_name);
514 goto ret;
515 }
516 else
517 { /* It has tc=. Need to read /etc/termcap. */
518 tcenv = termcap_name;
519 termcap_name = NULL;
520 }
521 }
522
523 if (!termcap_name || !filep)
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000524 termcap_name = TERMCAP_FILE;
Jari Aalto726f6381996-08-26 18:22:31 +0000525
526 /* Here we know we must search a file and termcap_name has its name. */
527
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000528#ifdef MSDOS
529 fd = open (termcap_name, O_RDONLY|O_TEXT, 0);
530#else
531 fd = open (termcap_name, O_RDONLY, 0);
532#endif
Jari Aalto726f6381996-08-26 18:22:31 +0000533 if (fd < 0)
534 return -1;
535
536 buf.size = BUFSIZE;
537 /* Add 1 to size to ensure room for terminating null. */
538 buf.beg = (char *) xmalloc (buf.size + 1);
539 term = indirect ? indirect : name;
540
541 if (!bp)
542 {
543 malloc_size = indirect ? strlen (tcenv) + 1 : buf.size;
544 bp = (char *) xmalloc (malloc_size);
545 }
546 bp1 = bp;
547
548 if (indirect)
549 /* Copy the data from the environment variable. */
550 {
551 strcpy (bp, tcenv);
552 bp1 += strlen (tcenv);
553 }
554
555 while (term)
556 {
557 /* Scan the file, reading it via buf, till find start of main entry. */
558 if (scan_file (term, fd, &buf) == 0)
559 {
560 close (fd);
561 free (buf.beg);
562 if (malloc_size)
563 free (bp);
564 return 0;
565 }
566
567 /* Free old `term' if appropriate. */
568 if (term != name)
569 free (term);
570
571 /* If BP is malloc'd by us, make sure it is big enough. */
572 if (malloc_size)
573 {
574 malloc_size = bp1 - bp + buf.size;
575 termcap_name = (char *) xrealloc (bp, malloc_size);
576 bp1 += termcap_name - bp;
577 bp = termcap_name;
578 }
579
580 bp2 = bp1;
581
582 /* Copy the line of the entry from buf into bp. */
583 termcap_name = buf.ptr;
584 while ((*bp1++ = c = *termcap_name++) && c != '\n')
585 /* Drop out any \ newline sequence. */
586 if (c == '\\' && *termcap_name == '\n')
587 {
588 bp1--;
589 termcap_name++;
590 }
591 *bp1 = '\0';
592
593 /* Does this entry refer to another terminal type's entry?
594 If something is found, copy it into heap and null-terminate it. */
595 term = tgetst1 (find_capability (bp2, "tc"), (char **) 0);
596 }
597
598 close (fd);
599 free (buf.beg);
600
601 if (malloc_size)
602 bp = (char *) xrealloc (bp, bp1 - bp + 1);
603
604 ret:
605 term_entry = bp;
Jari Aalto726f6381996-08-26 18:22:31 +0000606 return 1;
607}
608
609/* Given file open on FD and buffer BUFP,
610 scan the file from the beginning until a line is found
611 that starts the entry for terminal type STR.
612 Return 1 if successful, with that line in BUFP,
613 or 0 if no entry is found in the file. */
614
615static int
616scan_file (str, fd, bufp)
617 char *str;
618 int fd;
619 register struct buffer *bufp;
620{
621 register char *end;
622
623 bufp->ptr = bufp->beg;
624 bufp->full = 0;
625 bufp->ateof = 0;
626 *bufp->ptr = '\0';
627
628 lseek (fd, 0L, 0);
629
630 while (!bufp->ateof)
631 {
632 /* Read a line into the buffer. */
633 end = NULL;
634 do
635 {
636 /* if it is continued, append another line to it,
637 until a non-continued line ends. */
638 end = gobble_line (fd, bufp, end);
639 }
640 while (!bufp->ateof && end[-2] == '\\');
641
642 if (*bufp->ptr != '#'
643 && name_match (bufp->ptr, str))
644 return 1;
645
646 /* Discard the line just processed. */
647 bufp->ptr = end;
648 }
649 return 0;
650}
651
652/* Return nonzero if NAME is one of the names specified
653 by termcap entry LINE. */
654
655static int
656name_match (line, name)
657 char *line, *name;
658{
659 register char *tem;
660
661 if (!compare_contin (line, name))
662 return 1;
663 /* This line starts an entry. Is it the right one? */
664 for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++)
665 if (*tem == '|' && !compare_contin (tem + 1, name))
666 return 1;
667
668 return 0;
669}
670
671static int
672compare_contin (str1, str2)
673 register char *str1, *str2;
674{
675 register int c1, c2;
676 while (1)
677 {
678 c1 = *str1++;
679 c2 = *str2++;
680 while (c1 == '\\' && *str1 == '\n')
681 {
682 str1++;
683 while ((c1 = *str1++) == ' ' || c1 == '\t');
684 }
685 if (c2 == '\0')
686 {
687 /* End of type being looked up. */
688 if (c1 == '|' || c1 == ':')
689 /* If end of name in data base, we win. */
690 return 0;
691 else
692 return 1;
693 }
694 else if (c1 != c2)
695 return 1;
696 }
697}
698
699/* Make sure that the buffer <- BUFP contains a full line
700 of the file open on FD, starting at the place BUFP->ptr
701 points to. Can read more of the file, discard stuff before
702 BUFP->ptr, or make the buffer bigger.
703
704 Return the pointer to after the newline ending the line,
705 or to the end of the file, if there is no newline to end it.
706
707 Can also merge on continuation lines. If APPEND_END is
708 non-null, it points past the newline of a line that is
709 continued; we add another line onto it and regard the whole
710 thing as one line. The caller decides when a line is continued. */
711
712static char *
713gobble_line (fd, bufp, append_end)
714 int fd;
715 register struct buffer *bufp;
716 char *append_end;
717{
718 register char *end;
719 register int nread;
720 register char *buf = bufp->beg;
721 register char *tem;
722
723 if (!append_end)
724 append_end = bufp->ptr;
725
726 while (1)
727 {
728 end = append_end;
729 while (*end && *end != '\n') end++;
730 if (*end)
731 break;
732 if (bufp->ateof)
733 return buf + bufp->full;
734 if (bufp->ptr == buf)
735 {
736 if (bufp->full == bufp->size)
737 {
738 bufp->size *= 2;
739 /* Add 1 to size to ensure room for terminating null. */
740 tem = (char *) xrealloc (buf, bufp->size + 1);
741 bufp->ptr = (bufp->ptr - buf) + tem;
742 append_end = (append_end - buf) + tem;
743 bufp->beg = buf = tem;
744 }
745 }
746 else
747 {
748 append_end -= bufp->ptr - buf;
749 bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf);
750 bufp->ptr = buf;
751 }
752 if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full)))
753 bufp->ateof = 1;
754 bufp->full += nread;
755 buf[bufp->full] = '\0';
756 }
757 return end + 1;
758}
759
760#ifdef TEST
761
762#ifdef NULL
763#undef NULL
764#endif
765
766#include <stdio.h>
767
768main (argc, argv)
769 int argc;
770 char **argv;
771{
772 char *term;
773 char *buf;
774
775 term = argv[1];
776 printf ("TERM: %s\n", term);
777
778 buf = (char *) tgetent (0, term);
779 if ((int) buf <= 0)
780 {
781 printf ("No entry.\n");
782 return 0;
783 }
784
785 printf ("Entry: %s\n", buf);
786
787 tprint ("cm");
788 tprint ("AL");
789
790 printf ("co: %d\n", tgetnum ("co"));
791 printf ("am: %d\n", tgetflag ("am"));
792}
793
794tprint (cap)
795 char *cap;
796{
797 char *x = tgetstr (cap, 0);
798 register char *y;
799
800 printf ("%s: ", cap);
801 if (x)
802 {
803 for (y = x; *y; y++)
804 if (*y <= ' ' || *y == 0177)
805 printf ("\\%0o", *y);
806 else
807 putchar (*y);
808 free (x);
809 }
810 else
811 printf ("none");
812 putchar ('\n');
813}
814
815#endif /* TEST */