Commit | Line | Data |
---|---|---|
f02902f7 | 1 | /* Work-alike for termcap, plus extra features. |
ab422c4d PE |
2 | Copyright (C) 1985-1986, 1993-1995, 2000-2008, 2011, 2013 Free |
3 | Software Foundation, Inc. | |
f02902f7 RM |
4 | |
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. | |
9 | ||
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. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
fee0bd5f | 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
f02902f7 RM |
17 | |
18 | /* Emacs config.h may rename various library functions such as malloc. */ | |
f02902f7 | 19 | #include <config.h> |
8fde62c1 | 20 | #include <sys/file.h> |
8fde62c1 | 21 | #include <fcntl.h> |
e608805f | 22 | #include <unistd.h> |
8fde62c1 | 23 | |
3e6ae1a4 | 24 | #include "lisp.h" |
058e5dad EZ |
25 | #include "tparam.h" |
26 | #ifdef MSDOS | |
27 | #include "msdos.h" | |
28 | #endif | |
3e6ae1a4 | 29 | |
f02902f7 RM |
30 | /* BUFSIZE is the initial size allocated for the buffer |
31 | for reading the termcap file. | |
32 | It is not a limit. | |
33 | Make it large normally for speed. | |
34 | Make it variable when debugging, so can exercise | |
35 | increasing the space dynamically. */ | |
36 | ||
37 | #ifndef BUFSIZE | |
38 | #ifdef DEBUG | |
39 | #define BUFSIZE bufsize | |
40 | ||
41 | int bufsize = 128; | |
42 | #else | |
43 | #define BUFSIZE 2048 | |
44 | #endif | |
45 | #endif | |
46 | ||
aa63d440 DM |
47 | #ifndef TERMCAP_FILE |
48 | #define TERMCAP_FILE "/etc/termcap" | |
f02902f7 RM |
49 | #endif |
50 | ||
f02902f7 RM |
51 | \f |
52 | /* Looking up capabilities in the entry already found. */ | |
53 | ||
54 | /* The pointer to the data made by tgetent is left here | |
55 | for tgetnum, tgetflag and tgetstr to find. */ | |
56 | static char *term_entry; | |
57 | ||
971de7fb | 58 | static char *tgetst1 (char *ptr, char **area); |
f02902f7 RM |
59 | |
60 | /* Search entry BP for capability CAP. | |
61 | Return a pointer to the capability (in BP) if found, | |
62 | 0 if not found. */ | |
63 | ||
64 | static char * | |
058e5dad | 65 | find_capability (register char *bp, register const char *cap) |
f02902f7 RM |
66 | { |
67 | for (; *bp; bp++) | |
68 | if (bp[0] == ':' | |
69 | && bp[1] == cap[0] | |
70 | && bp[2] == cap[1]) | |
71 | return &bp[4]; | |
72 | return NULL; | |
73 | } | |
74 | ||
75 | int | |
058e5dad | 76 | tgetnum (const char *cap) |
f02902f7 RM |
77 | { |
78 | register char *ptr = find_capability (term_entry, cap); | |
79 | if (!ptr || ptr[-1] != '#') | |
80 | return -1; | |
81 | return atoi (ptr); | |
82 | } | |
83 | ||
84 | int | |
058e5dad | 85 | tgetflag (const char *cap) |
f02902f7 RM |
86 | { |
87 | register char *ptr = find_capability (term_entry, cap); | |
88 | return ptr && ptr[-1] == ':'; | |
89 | } | |
90 | ||
91 | /* Look up a string-valued capability CAP. | |
92 | If AREA is non-null, it points to a pointer to a block in which | |
93 | to store the string. That pointer is advanced over the space used. | |
94 | If AREA is null, space is allocated with `malloc'. */ | |
95 | ||
96 | char * | |
058e5dad | 97 | tgetstr (const char *cap, char **area) |
f02902f7 RM |
98 | { |
99 | register char *ptr = find_capability (term_entry, cap); | |
100 | if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~')) | |
101 | return NULL; | |
102 | return tgetst1 (ptr, area); | |
103 | } | |
104 | ||
1ddd1e34 RS |
105 | #ifdef IS_EBCDIC_HOST |
106 | /* Table, indexed by a character in range 0200 to 0300 with 0200 subtracted, | |
107 | gives meaning of character following \, or a space if no special meaning. | |
108 | Sixteen characters per line within the string. */ | |
109 | ||
91433552 | 110 | static const char esctab[] |
1ddd1e34 RS |
111 | = " \057\026 \047\014 \ |
112 | \025 \015 \ | |
113 | \005 \013 \ | |
114 | "; | |
115 | #else | |
f02902f7 RM |
116 | /* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted, |
117 | gives meaning of character following \, or a space if no special meaning. | |
118 | Eight characters per line within the string. */ | |
119 | ||
91433552 | 120 | static const char esctab[] |
f02902f7 RM |
121 | = " \007\010 \033\014 \ |
122 | \012 \ | |
123 | \015 \011 \013 \ | |
124 | "; | |
1ddd1e34 | 125 | #endif |
f02902f7 RM |
126 | |
127 | /* PTR points to a string value inside a termcap entry. | |
128 | Copy that value, processing \ and ^ abbreviations, | |
129 | into the block that *AREA points to, | |
130 | or to newly allocated storage if AREA is NULL. | |
131 | Return the address to which we copied the value, | |
132 | or NULL if PTR is NULL. */ | |
133 | ||
134 | static char * | |
971de7fb | 135 | tgetst1 (char *ptr, char **area) |
f02902f7 RM |
136 | { |
137 | register char *p, *r; | |
138 | register int c; | |
139 | register int size; | |
140 | char *ret; | |
141 | register int c1; | |
142 | ||
143 | if (!ptr) | |
144 | return NULL; | |
145 | ||
146 | /* `ret' gets address of where to store the string. */ | |
147 | if (!area) | |
148 | { | |
149 | /* Compute size of block needed (may overestimate). */ | |
150 | p = ptr; | |
151 | while ((c = *p++) && c != ':' && c != '\n') | |
152 | ; | |
23f86fce | 153 | ret = xmalloc (p - ptr + 1); |
f02902f7 RM |
154 | } |
155 | else | |
156 | ret = *area; | |
157 | ||
158 | /* Copy the string value, stopping at null or colon. | |
159 | Also process ^ and \ abbreviations. */ | |
160 | p = ptr; | |
161 | r = ret; | |
162 | while ((c = *p++) && c != ':' && c != '\n') | |
163 | { | |
164 | if (c == '^') | |
d80bd4a0 RS |
165 | { |
166 | c = *p++; | |
167 | if (c == '?') | |
168 | c = 0177; | |
169 | else | |
170 | c &= 037; | |
171 | } | |
f02902f7 RM |
172 | else if (c == '\\') |
173 | { | |
174 | c = *p++; | |
175 | if (c >= '0' && c <= '7') | |
176 | { | |
177 | c -= '0'; | |
178 | size = 0; | |
179 | ||
180 | while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7') | |
181 | { | |
182 | c *= 8; | |
183 | c += c1 - '0'; | |
184 | p++; | |
185 | } | |
186 | } | |
1ddd1e34 RS |
187 | #ifdef IS_EBCDIC_HOST |
188 | else if (c >= 0200 && c < 0360) | |
189 | { | |
190 | c1 = esctab[(c & ~0100) - 0200]; | |
191 | if (c1 != ' ') | |
192 | c = c1; | |
193 | } | |
194 | #else | |
f02902f7 RM |
195 | else if (c >= 0100 && c < 0200) |
196 | { | |
197 | c1 = esctab[(c & ~040) - 0100]; | |
198 | if (c1 != ' ') | |
199 | c = c1; | |
200 | } | |
1ddd1e34 | 201 | #endif |
f02902f7 RM |
202 | } |
203 | *r++ = c; | |
204 | } | |
56ffd194 TTN |
205 | |
206 | /* Sometimes entries have "%pN" which means use parameter N in the | |
207 | next %-substitution. If all such N are continuous in the range | |
208 | [1,9] we can remove each "%pN" because they are redundant, thus | |
209 | reducing bandwidth requirements. True, Emacs is well beyond the | |
210 | days of 150baud teletypes, but some of its users aren't much so. | |
211 | ||
212 | This pass could probably be integrated into the one above but | |
213 | abbreviation expansion makes that effort a little more hairy than | |
214 | its worth; this is cleaner. */ | |
215 | { | |
653d4f43 PE |
216 | int last_p_param = 0; |
217 | bool remove_p_params = 1; | |
56ffd194 TTN |
218 | struct { char *beg; int len; } cut[11]; |
219 | ||
220 | for (cut[0].beg = p = ret; p < r - 3; p++) | |
221 | { | |
222 | if (!remove_p_params) | |
223 | break; | |
224 | if (*p == '%' && *(p + 1) == 'p') | |
225 | { | |
226 | if (*(p + 2) - '0' == 1 + last_p_param) | |
227 | { | |
228 | cut[last_p_param].len = p - cut[last_p_param].beg; | |
229 | last_p_param++; | |
230 | p += 3; | |
231 | cut[last_p_param].beg = p; | |
232 | } | |
233 | else /* not continuous: bail */ | |
234 | remove_p_params = 0; | |
235 | if (last_p_param > 10) /* too many: bail */ | |
236 | remove_p_params = 0; | |
237 | } | |
238 | } | |
239 | if (remove_p_params && last_p_param) | |
240 | { | |
241 | register int i; | |
242 | char *wp; | |
243 | ||
244 | cut[last_p_param].len = r - cut[last_p_param].beg; | |
245 | for (i = 0, wp = ret; i <= last_p_param; wp += cut[i++].len) | |
72af86bd | 246 | memcpy (wp, cut[i].beg, cut[i].len); |
56ffd194 TTN |
247 | r = wp; |
248 | } | |
249 | } | |
250 | ||
f02902f7 RM |
251 | *r = '\0'; |
252 | /* Update *AREA. */ | |
253 | if (area) | |
254 | *area = r + 1; | |
255 | return ret; | |
256 | } | |
257 | \f | |
258 | /* Outputting a string with padding. */ | |
259 | ||
f02902f7 RM |
260 | char PC; |
261 | ||
f02902f7 | 262 | void |
058e5dad | 263 | tputs (register const char *str, int nlines, int (*outfun) (int)) |
f02902f7 RM |
264 | { |
265 | register int padcount = 0; | |
266 | register int speed; | |
267 | ||
f02902f7 | 268 | speed = baud_rate; |
0c0b6c79 RS |
269 | /* For quite high speeds, convert to the smaller |
270 | units to avoid overflow. */ | |
271 | if (speed > 10000) | |
272 | speed = - speed / 100; | |
f02902f7 RM |
273 | |
274 | if (!str) | |
275 | return; | |
276 | ||
277 | while (*str >= '0' && *str <= '9') | |
278 | { | |
279 | padcount += *str++ - '0'; | |
280 | padcount *= 10; | |
281 | } | |
282 | if (*str == '.') | |
283 | { | |
284 | str++; | |
285 | padcount += *str++ - '0'; | |
286 | } | |
287 | if (*str == '*') | |
288 | { | |
289 | str++; | |
290 | padcount *= nlines; | |
291 | } | |
292 | while (*str) | |
293 | (*outfun) (*str++); | |
294 | ||
0c0b6c79 | 295 | /* PADCOUNT is now in units of tenths of msec. |
95c74a10 | 296 | SPEED is measured in characters per 10 seconds |
0c0b6c79 RS |
297 | or in characters per .1 seconds (if negative). |
298 | We use the smaller units for larger speeds to avoid overflow. */ | |
299 | padcount *= speed; | |
f02902f7 RM |
300 | padcount += 500; |
301 | padcount /= 1000; | |
0c0b6c79 | 302 | if (speed < 0) |
f02902f7 RM |
303 | padcount = -padcount; |
304 | else | |
305 | { | |
306 | padcount += 50; | |
307 | padcount /= 100; | |
308 | } | |
309 | ||
310 | while (padcount-- > 0) | |
311 | (*outfun) (PC); | |
312 | } | |
313 | \f | |
314 | /* Finding the termcap entry in the termcap data base. */ | |
315 | ||
df4cb2b7 | 316 | struct termcap_buffer |
f02902f7 RM |
317 | { |
318 | char *beg; | |
171e2a58 | 319 | ptrdiff_t size; |
f02902f7 | 320 | char *ptr; |
653d4f43 | 321 | bool ateof; |
171e2a58 | 322 | ptrdiff_t full; |
f02902f7 RM |
323 | }; |
324 | ||
325 | /* Forward declarations of static functions. */ | |
326 | ||
653d4f43 PE |
327 | static bool scan_file (char *, int, struct termcap_buffer *); |
328 | static char *gobble_line (int, struct termcap_buffer *, char *); | |
329 | static bool compare_contin (char *, char *); | |
330 | static bool name_match (char *, char *); | |
f02902f7 | 331 | |
653d4f43 | 332 | static bool |
1dae0f0a | 333 | valid_filename_p (char *fn) |
f02902f7 | 334 | { |
653d4f43 | 335 | #ifdef MSDOS |
f02902f7 | 336 | return *fn == '/' || fn[1] == ':'; |
f02902f7 | 337 | #else |
653d4f43 | 338 | return *fn == '/'; |
f02902f7 | 339 | #endif |
653d4f43 | 340 | } |
f02902f7 | 341 | |
f02902f7 RM |
342 | /* Find the termcap entry data for terminal type NAME |
343 | and store it in the block that BP points to. | |
344 | Record its address for future use. | |
345 | ||
346 | If BP is null, space is dynamically allocated. | |
347 | ||
348 | Return -1 if there is some difficulty accessing the data base | |
349 | of terminal types, | |
350 | 0 if the data base is accessible but the type NAME is not defined | |
351 | in it, and some other value otherwise. */ | |
352 | ||
353 | int | |
058e5dad | 354 | tgetent (char *bp, const char *name) |
f02902f7 RM |
355 | { |
356 | register char *termcap_name; | |
357 | register int fd; | |
df4cb2b7 | 358 | struct termcap_buffer buf; |
f02902f7 | 359 | register char *bp1; |
f21b7e24 | 360 | char *tc_search_point; |
f02902f7 | 361 | char *term; |
171e2a58 | 362 | ptrdiff_t malloc_size = 0; |
653d4f43 | 363 | int c; |
20ee9f08 | 364 | char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */ |
f02902f7 | 365 | char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */ |
653d4f43 | 366 | bool filep; |
f02902f7 RM |
367 | |
368 | #ifdef INTERNAL_TERMINAL | |
369 | /* For the internal terminal we don't want to read any termcap file, | |
370 | so fake it. */ | |
371 | if (!strcmp (name, "internal")) | |
372 | { | |
373 | term = INTERNAL_TERMINAL; | |
374 | if (!bp) | |
375 | { | |
376 | malloc_size = 1 + strlen (term); | |
23f86fce | 377 | bp = xmalloc (malloc_size); |
f02902f7 RM |
378 | } |
379 | strcpy (bp, term); | |
380 | goto ret; | |
381 | } | |
382 | #endif /* INTERNAL_TERMINAL */ | |
383 | ||
3e19e687 DM |
384 | /* For compatibility with programs like `less' that want to |
385 | put data in the termcap buffer themselves as a fallback. */ | |
386 | if (bp) | |
387 | term_entry = bp; | |
388 | ||
f02902f7 RM |
389 | termcap_name = getenv ("TERMCAP"); |
390 | if (termcap_name && *termcap_name == '\0') | |
391 | termcap_name = NULL; | |
392 | #if defined (MSDOS) && !defined (TEST) | |
393 | if (termcap_name && (*termcap_name == '\\' | |
394 | || *termcap_name == '/' | |
395 | || termcap_name[1] == ':')) | |
e7ac588e | 396 | dostounix_filename (termcap_name, 0); |
f02902f7 RM |
397 | #endif |
398 | ||
399 | filep = termcap_name && valid_filename_p (termcap_name); | |
400 | ||
401 | /* If termcap_name is non-null and starts with / (in the un*x case, that is), | |
402 | it is a file name to use instead of /etc/termcap. | |
403 | If it is non-null and does not start with /, | |
404 | it is the entry itself, but only if | |
405 | the name the caller requested matches the TERM variable. */ | |
406 | ||
407 | if (termcap_name && !filep && !strcmp (name, getenv ("TERM"))) | |
408 | { | |
7d652d97 | 409 | indirect = tgetst1 (find_capability (termcap_name, "tc"), 0); |
f02902f7 RM |
410 | if (!indirect) |
411 | { | |
412 | if (!bp) | |
413 | bp = termcap_name; | |
414 | else | |
415 | strcpy (bp, termcap_name); | |
416 | goto ret; | |
417 | } | |
418 | else | |
419 | { /* It has tc=. Need to read /etc/termcap. */ | |
420 | tcenv = termcap_name; | |
421 | termcap_name = NULL; | |
422 | } | |
423 | } | |
424 | ||
425 | if (!termcap_name || !filep) | |
aa63d440 | 426 | termcap_name = TERMCAP_FILE; |
f02902f7 RM |
427 | |
428 | /* Here we know we must search a file and termcap_name has its name. */ | |
429 | ||
406af475 | 430 | fd = emacs_open (termcap_name, O_RDONLY | O_TEXT, 0); |
f02902f7 RM |
431 | if (fd < 0) |
432 | return -1; | |
433 | ||
434 | buf.size = BUFSIZE; | |
435 | /* Add 1 to size to ensure room for terminating null. */ | |
23f86fce | 436 | buf.beg = xmalloc (buf.size + 1); |
058e5dad | 437 | term = indirect ? indirect : (char *)name; |
f02902f7 RM |
438 | |
439 | if (!bp) | |
440 | { | |
441 | malloc_size = indirect ? strlen (tcenv) + 1 : buf.size; | |
23f86fce | 442 | bp = xmalloc (malloc_size); |
f02902f7 | 443 | } |
f21b7e24 | 444 | tc_search_point = bp1 = bp; |
f02902f7 RM |
445 | |
446 | if (indirect) | |
447 | /* Copy the data from the environment variable. */ | |
448 | { | |
449 | strcpy (bp, tcenv); | |
450 | bp1 += strlen (tcenv); | |
451 | } | |
452 | ||
453 | while (term) | |
454 | { | |
455 | /* Scan the file, reading it via buf, till find start of main entry. */ | |
456 | if (scan_file (term, fd, &buf) == 0) | |
457 | { | |
bacba3c2 | 458 | emacs_close (fd); |
baad03f0 | 459 | xfree (buf.beg); |
f02902f7 | 460 | if (malloc_size) |
baad03f0 | 461 | xfree (bp); |
f02902f7 RM |
462 | return 0; |
463 | } | |
464 | ||
465 | /* Free old `term' if appropriate. */ | |
466 | if (term != name) | |
baad03f0 | 467 | xfree (term); |
f02902f7 RM |
468 | |
469 | /* If BP is malloc'd by us, make sure it is big enough. */ | |
470 | if (malloc_size) | |
471 | { | |
0d8f2df7 | 472 | ptrdiff_t offset1 = bp1 - bp, offset2 = tc_search_point - bp; |
49484b28 | 473 | malloc_size = offset1 + buf.size; |
38182d90 | 474 | bp = termcap_name = xrealloc (bp, malloc_size); |
49484b28 GM |
475 | bp1 = termcap_name + offset1; |
476 | tc_search_point = termcap_name + offset2; | |
f02902f7 RM |
477 | } |
478 | ||
f02902f7 RM |
479 | /* Copy the line of the entry from buf into bp. */ |
480 | termcap_name = buf.ptr; | |
481 | while ((*bp1++ = c = *termcap_name++) && c != '\n') | |
482 | /* Drop out any \ newline sequence. */ | |
483 | if (c == '\\' && *termcap_name == '\n') | |
484 | { | |
485 | bp1--; | |
486 | termcap_name++; | |
487 | } | |
488 | *bp1 = '\0'; | |
489 | ||
490 | /* Does this entry refer to another terminal type's entry? | |
491 | If something is found, copy it into heap and null-terminate it. */ | |
f21b7e24 | 492 | tc_search_point = find_capability (tc_search_point, "tc"); |
7d652d97 | 493 | term = tgetst1 (tc_search_point, 0); |
f02902f7 RM |
494 | } |
495 | ||
bacba3c2 | 496 | emacs_close (fd); |
baad03f0 | 497 | xfree (buf.beg); |
f02902f7 RM |
498 | |
499 | if (malloc_size) | |
38182d90 | 500 | bp = xrealloc (bp, bp1 - bp + 1); |
f02902f7 RM |
501 | |
502 | ret: | |
503 | term_entry = bp; | |
f02902f7 RM |
504 | return 1; |
505 | } | |
506 | ||
507 | /* Given file open on FD and buffer BUFP, | |
508 | scan the file from the beginning until a line is found | |
509 | that starts the entry for terminal type STR. | |
510 | Return 1 if successful, with that line in BUFP, | |
511 | or 0 if no entry is found in the file. */ | |
512 | ||
653d4f43 PE |
513 | static bool |
514 | scan_file (char *str, int fd, struct termcap_buffer *bufp) | |
f02902f7 | 515 | { |
653d4f43 | 516 | char *end; |
f02902f7 RM |
517 | |
518 | bufp->ptr = bufp->beg; | |
519 | bufp->full = 0; | |
520 | bufp->ateof = 0; | |
521 | *bufp->ptr = '\0'; | |
522 | ||
523 | lseek (fd, 0L, 0); | |
524 | ||
525 | while (!bufp->ateof) | |
526 | { | |
527 | /* Read a line into the buffer. */ | |
528 | end = NULL; | |
529 | do | |
530 | { | |
531 | /* if it is continued, append another line to it, | |
532 | until a non-continued line ends. */ | |
533 | end = gobble_line (fd, bufp, end); | |
534 | } | |
535 | while (!bufp->ateof && end[-2] == '\\'); | |
536 | ||
537 | if (*bufp->ptr != '#' | |
538 | && name_match (bufp->ptr, str)) | |
539 | return 1; | |
540 | ||
541 | /* Discard the line just processed. */ | |
542 | bufp->ptr = end; | |
543 | } | |
544 | return 0; | |
545 | } | |
546 | ||
653d4f43 | 547 | /* Return true if NAME is one of the names specified |
f02902f7 RM |
548 | by termcap entry LINE. */ |
549 | ||
653d4f43 | 550 | static bool |
971de7fb | 551 | name_match (char *line, char *name) |
f02902f7 | 552 | { |
653d4f43 | 553 | char *tem; |
f02902f7 RM |
554 | |
555 | if (!compare_contin (line, name)) | |
556 | return 1; | |
557 | /* This line starts an entry. Is it the right one? */ | |
558 | for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++) | |
559 | if (*tem == '|' && !compare_contin (tem + 1, name)) | |
560 | return 1; | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
653d4f43 PE |
565 | static bool |
566 | compare_contin (char *str1, char *str2) | |
f02902f7 | 567 | { |
f02902f7 RM |
568 | while (1) |
569 | { | |
653d4f43 PE |
570 | int c1 = *str1++; |
571 | int c2 = *str2++; | |
f02902f7 RM |
572 | while (c1 == '\\' && *str1 == '\n') |
573 | { | |
574 | str1++; | |
653d4f43 PE |
575 | while ((c1 = *str1++) == ' ' || c1 == '\t') |
576 | continue; | |
f02902f7 RM |
577 | } |
578 | if (c2 == '\0') | |
579 | { | |
580 | /* End of type being looked up. */ | |
581 | if (c1 == '|' || c1 == ':') | |
582 | /* If end of name in data base, we win. */ | |
583 | return 0; | |
584 | else | |
585 | return 1; | |
586 | } | |
587 | else if (c1 != c2) | |
588 | return 1; | |
589 | } | |
590 | } | |
591 | ||
592 | /* Make sure that the buffer <- BUFP contains a full line | |
593 | of the file open on FD, starting at the place BUFP->ptr | |
594 | points to. Can read more of the file, discard stuff before | |
595 | BUFP->ptr, or make the buffer bigger. | |
596 | ||
597 | Return the pointer to after the newline ending the line, | |
598 | or to the end of the file, if there is no newline to end it. | |
599 | ||
600 | Can also merge on continuation lines. If APPEND_END is | |
601 | non-null, it points past the newline of a line that is | |
602 | continued; we add another line onto it and regard the whole | |
603 | thing as one line. The caller decides when a line is continued. */ | |
604 | ||
605 | static char * | |
971de7fb | 606 | gobble_line (int fd, register struct termcap_buffer *bufp, char *append_end) |
f02902f7 RM |
607 | { |
608 | register char *end; | |
609 | register int nread; | |
610 | register char *buf = bufp->beg; | |
f02902f7 RM |
611 | |
612 | if (!append_end) | |
613 | append_end = bufp->ptr; | |
614 | ||
615 | while (1) | |
616 | { | |
617 | end = append_end; | |
618 | while (*end && *end != '\n') end++; | |
619 | if (*end) | |
620 | break; | |
621 | if (bufp->ateof) | |
622 | return buf + bufp->full; | |
623 | if (bufp->ptr == buf) | |
624 | { | |
625 | if (bufp->full == bufp->size) | |
626 | { | |
0d8f2df7 PE |
627 | ptrdiff_t ptr_offset = bufp->ptr - buf; |
628 | ptrdiff_t append_end_offset = append_end - buf; | |
f02902f7 | 629 | /* Add 1 to size to ensure room for terminating null. */ |
0065d054 PE |
630 | ptrdiff_t size = bufp->size + 1; |
631 | bufp->beg = buf = xpalloc (buf, &size, 1, -1, 1); | |
632 | bufp->size = size - 1; | |
0d8f2df7 PE |
633 | bufp->ptr = buf + ptr_offset; |
634 | append_end = buf + append_end_offset; | |
f02902f7 RM |
635 | } |
636 | } | |
637 | else | |
638 | { | |
639 | append_end -= bufp->ptr - buf; | |
72af86bd | 640 | memcpy (buf, bufp->ptr, bufp->full -= bufp->ptr - buf); |
f02902f7 RM |
641 | bufp->ptr = buf; |
642 | } | |
643 | if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full))) | |
644 | bufp->ateof = 1; | |
645 | bufp->full += nread; | |
646 | buf[bufp->full] = '\0'; | |
647 | } | |
648 | return end + 1; | |
649 | } |