Annotation of src/usr.bin/less/ch.c, Revision 1.11
1.1 etheisen 1: /*
1.10 shadchin 2: * Copyright (C) 1984-2012 Mark Nudelman
1.1 etheisen 3: *
1.4 millert 4: * You may distribute under the terms of either the GNU General Public
5: * License or the Less License, as specified in the README file.
1.1 etheisen 6: *
1.10 shadchin 7: * For more information, see the README file.
1.1 etheisen 8: */
1.11 ! nicm 9: /*
! 10: * Modified for use with illumos.
! 11: * Copyright 2014 Garrett D'Amore <garrett@damore.org>
! 12: */
1.1 etheisen 13:
14: /*
15: * Low level character input from the input file.
16: * We use these special purpose routines which optimize moving
17: * both forward and backward from the current read pointer.
18: */
19:
20: #include "less.h"
1.4 millert 21:
1.8 shadchin 22: #include <sys/stat.h>
23: extern dev_t curr_dev;
24: extern ino_t curr_ino;
1.11 ! nicm 25: extern int less_is_more;
1.8 shadchin 26:
1.11 ! nicm 27: typedef off_t BLOCKNUM;
1.1 etheisen 28:
1.11 ! nicm 29: int ignore_eoi;
1.1 etheisen 30:
31: /*
32: * Pool of buffers holding the most recently used blocks of the input file.
33: * The buffer pool is kept as a doubly-linked circular list,
34: * in order from most- to least-recently used.
35: * The circular list is anchored by the file state "thisfile".
36: */
1.8 shadchin 37: struct bufnode {
38: struct bufnode *next, *prev;
39: struct bufnode *hnext, *hprev;
40: };
41:
1.4 millert 42: #define LBUFSIZE 8192
1.1 etheisen 43: struct buf {
1.8 shadchin 44: struct bufnode node;
1.4 millert 45: BLOCKNUM block;
1.1 etheisen 46: unsigned int datasize;
47: unsigned char data[LBUFSIZE];
48: };
1.11 ! nicm 49: #define bufnode_buf(bn) ((struct buf *)bn)
1.4 millert 50:
1.1 etheisen 51: /*
52: * The file state is maintained in a filestate structure.
53: * A pointer to the filestate is kept in the ifile structure.
54: */
1.4 millert 55: #define BUFHASH_SIZE 64
1.1 etheisen 56: struct filestate {
1.8 shadchin 57: struct bufnode buflist;
58: struct bufnode hashtbl[BUFHASH_SIZE];
1.1 etheisen 59: int file;
60: int flags;
1.11 ! nicm 61: off_t fpos;
1.1 etheisen 62: int nbufs;
1.4 millert 63: BLOCKNUM block;
64: unsigned int offset;
1.11 ! nicm 65: off_t fsize;
1.1 etheisen 66: };
67:
1.8 shadchin 68: #define ch_bufhead thisfile->buflist.next
69: #define ch_buftail thisfile->buflist.prev
1.1 etheisen 70: #define ch_nbufs thisfile->nbufs
71: #define ch_block thisfile->block
72: #define ch_offset thisfile->offset
73: #define ch_fpos thisfile->fpos
74: #define ch_fsize thisfile->fsize
75: #define ch_flags thisfile->flags
76: #define ch_file thisfile->file
77:
1.8 shadchin 78: #define END_OF_CHAIN (&thisfile->buflist)
79: #define END_OF_HCHAIN(h) (&thisfile->hashtbl[h])
1.11 ! nicm 80: #define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1))
1.4 millert 81:
1.8 shadchin 82: /*
83: * Macros to manipulate the list of buffers in thisfile->buflist.
84: */
85: #define FOR_BUFS(bn) \
86: for (bn = ch_bufhead; bn != END_OF_CHAIN; bn = bn->next)
87:
1.11 ! nicm 88: #define BUF_RM(bn) \
1.8 shadchin 89: (bn)->next->prev = (bn)->prev; \
90: (bn)->prev->next = (bn)->next;
91:
1.11 ! nicm 92: #define BUF_INS_HEAD(bn) \
1.8 shadchin 93: (bn)->next = ch_bufhead; \
94: (bn)->prev = END_OF_CHAIN; \
95: ch_bufhead->prev = (bn); \
96: ch_bufhead = (bn);
97:
1.11 ! nicm 98: #define BUF_INS_TAIL(bn) \
1.8 shadchin 99: (bn)->next = END_OF_CHAIN; \
100: (bn)->prev = ch_buftail; \
101: ch_buftail->next = (bn); \
102: ch_buftail = (bn);
103:
104: /*
105: * Macros to manipulate the list of buffers in thisfile->hashtbl[n].
106: */
1.11 ! nicm 107: #define FOR_BUFS_IN_CHAIN(h, bn) \
1.8 shadchin 108: for (bn = thisfile->hashtbl[h].hnext; \
1.11 ! nicm 109: bn != END_OF_HCHAIN(h); bn = bn->hnext)
1.8 shadchin 110:
111: #define BUF_HASH_RM(bn) \
112: (bn)->hnext->hprev = (bn)->hprev; \
113: (bn)->hprev->hnext = (bn)->hnext;
114:
1.11 ! nicm 115: #define BUF_HASH_INS(bn, h) \
1.8 shadchin 116: (bn)->hnext = thisfile->hashtbl[h].hnext; \
117: (bn)->hprev = END_OF_HCHAIN(h); \
118: thisfile->hashtbl[h].hnext->hprev = (bn); \
119: thisfile->hashtbl[h].hnext = (bn);
1.4 millert 120:
1.1 etheisen 121: static struct filestate *thisfile;
122: static int ch_ungotchar = -1;
1.4 millert 123: static int maxbufs = -1;
1.1 etheisen 124:
125: extern int autobuf;
1.9 millert 126: extern volatile sig_atomic_t sigs;
1.4 millert 127: extern int secure;
1.8 shadchin 128: extern int screen_trashed;
129: extern int follow_mode;
1.1 etheisen 130: extern IFILE curr_ifile;
131: extern int logfile;
132: extern char *namelogfile;
133:
1.11 ! nicm 134: static int ch_addbuf(void);
1.1 etheisen 135:
136:
137: /*
138: * Get the character pointed to by the read pointer.
1.8 shadchin 139: */
1.11 ! nicm 140: int
! 141: ch_get(void)
1.1 etheisen 142: {
1.11 ! nicm 143: struct buf *bp;
! 144: struct bufnode *bn;
! 145: int n;
! 146: int slept;
! 147: int h;
! 148: off_t pos;
! 149: off_t len;
1.1 etheisen 150:
1.8 shadchin 151: if (thisfile == NULL)
152: return (EOI);
153:
154: /*
1.11 ! nicm 155: * Quick check for the common case where
1.8 shadchin 156: * the desired char is in the head buffer.
157: */
1.11 ! nicm 158: if (ch_bufhead != END_OF_CHAIN) {
1.8 shadchin 159: bp = bufnode_buf(ch_bufhead);
160: if (ch_block == bp->block && ch_offset < bp->datasize)
1.11 ! nicm 161: return (bp->data[ch_offset]);
1.8 shadchin 162: }
163:
1.1 etheisen 164: slept = FALSE;
165:
166: /*
167: * Look for a buffer holding the desired block.
168: */
1.4 millert 169: h = BUFHASH(ch_block);
1.11 ! nicm 170: FOR_BUFS_IN_CHAIN(h, bn) {
1.8 shadchin 171: bp = bufnode_buf(bn);
1.11 ! nicm 172: if (bp->block == ch_block) {
1.1 etheisen 173: if (ch_offset >= bp->datasize)
174: /*
175: * Need more data in this buffer.
176: */
1.8 shadchin 177: break;
1.1 etheisen 178: goto found;
179: }
1.4 millert 180: }
1.11 ! nicm 181: if (bn == END_OF_HCHAIN(h)) {
1.8 shadchin 182: /*
1.11 ! nicm 183: * Block is not in a buffer.
! 184: * Take the least recently used buffer
1.8 shadchin 185: * and read the desired block into it.
1.11 ! nicm 186: * If the LRU buffer has data in it,
1.8 shadchin 187: * then maybe allocate a new buffer.
188: */
1.11 ! nicm 189: if (ch_buftail == END_OF_CHAIN ||
! 190: bufnode_buf(ch_buftail)->block != -1) {
1.8 shadchin 191: /*
192: * There is no empty buffer to use.
193: * Allocate a new buffer if:
1.11 ! nicm 194: * 1. We can't seek on this file and -b is not in
! 195: * effect; or
! 196: * 2. We haven't allocated the max buffers for this
! 197: * file yet.
1.8 shadchin 198: */
199: if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
1.11 ! nicm 200: (maxbufs < 0 || ch_nbufs < maxbufs))
1.8 shadchin 201: if (ch_addbuf())
202: /*
203: * Allocation failed: turn off autobuf.
204: */
205: autobuf = OPT_OFF;
206: }
207: bn = ch_buftail;
208: bp = bufnode_buf(bn);
209: BUF_HASH_RM(bn); /* Remove from old hash chain. */
210: bp->block = ch_block;
211: bp->datasize = 0;
212: BUF_HASH_INS(bn, h); /* Insert into new hash chain. */
1.1 etheisen 213: }
214:
1.11 ! nicm 215: read_more:
1.1 etheisen 216: pos = (ch_block * LBUFSIZE) + bp->datasize;
1.11 ! nicm 217: if ((len = ch_length()) != -1 && pos >= len)
1.1 etheisen 218: /*
219: * At end of file.
220: */
221: return (EOI);
222:
1.11 ! nicm 223: if (pos != ch_fpos) {
1.1 etheisen 224: /*
225: * Not at the correct position: must seek.
226: * If input is a pipe, we're in trouble (can't seek on a pipe).
227: * Some data has been lost: just return "?".
228: */
229: if (!(ch_flags & CH_CANSEEK))
230: return ('?');
1.11 ! nicm 231: if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK) {
! 232: error("seek error", NULL_PARG);
1.1 etheisen 233: clear_eol();
234: return (EOI);
1.11 ! nicm 235: }
! 236: ch_fpos = pos;
! 237: }
1.1 etheisen 238:
239: /*
240: * Read the block.
241: * If we read less than a full block, that's ok.
242: * We use partial block and pick up the rest next time.
243: */
1.11 ! nicm 244: if (ch_ungotchar != -1) {
! 245: bp->data[bp->datasize] = (unsigned char)ch_ungotchar;
1.1 etheisen 246: n = 1;
247: ch_ungotchar = -1;
1.11 ! nicm 248: } else {
! 249: n = iread(ch_file, &bp->data[bp->datasize],
! 250: (unsigned int)(LBUFSIZE - bp->datasize));
1.1 etheisen 251: }
252:
253: if (n == READ_INTR)
254: return (EOI);
1.11 ! nicm 255: if (n < 0) {
! 256: error("read error", NULL_PARG);
! 257: clear_eol();
1.1 etheisen 258: n = 0;
259: }
260:
261: /*
262: * If we have a log file, write the new data to it.
263: */
1.4 millert 264: if (!secure && logfile >= 0 && n > 0)
1.11 ! nicm 265: (void) write(logfile, (char *)&bp->data[bp->datasize], n);
1.1 etheisen 266:
267: ch_fpos += n;
268: bp->datasize += n;
269:
270: /*
271: * If we have read to end of file, set ch_fsize to indicate
272: * the position of the end of file.
273: */
1.11 ! nicm 274: if (n == 0) {
1.1 etheisen 275: ch_fsize = pos;
1.11 ! nicm 276: if (ignore_eoi) {
1.1 etheisen 277: /*
278: * We are ignoring EOF.
279: * Wait a while, then try again.
280: */
1.11 ! nicm 281: if (!slept) {
1.4 millert 282: PARG parg;
283: parg.p_string = wait_message();
284: ierror("%s", &parg);
285: }
1.11 ! nicm 286: sleep(1);
1.1 etheisen 287: slept = TRUE;
1.8 shadchin 288:
1.11 ! nicm 289: if (follow_mode == FOLLOW_NAME) {
! 290: /*
! 291: * See whether the file's i-number has changed.
1.8 shadchin 292: * If so, force the file to be closed and
1.11 ! nicm 293: * reopened.
! 294: */
1.8 shadchin 295: struct stat st;
296: int r = stat(get_filename(curr_ifile), &st);
297: if (r == 0 && (st.st_ino != curr_ino ||
1.11 ! nicm 298: st.st_dev != curr_dev)) {
! 299: /*
! 300: * screen_trashed=2 causes
! 301: * make_display to reopen the file.
! 302: */
1.8 shadchin 303: screen_trashed = 2;
304: return (EOI);
305: }
306: }
1.1 etheisen 307: }
1.4 millert 308: if (sigs)
1.1 etheisen 309: return (EOI);
310: }
311:
1.11 ! nicm 312: found:
! 313: if (ch_bufhead != bn) {
1.1 etheisen 314: /*
315: * Move the buffer to the head of the buffer chain.
316: * This orders the buffer chain, most- to least-recently used.
317: */
1.8 shadchin 318: BUF_RM(bn);
319: BUF_INS_HEAD(bn);
1.4 millert 320:
321: /*
322: * Move to head of hash chain too.
323: */
1.8 shadchin 324: BUF_HASH_RM(bn);
325: BUF_HASH_INS(bn, h);
1.1 etheisen 326: }
327:
328: if (ch_offset >= bp->datasize)
329: /*
330: * After all that, we still don't have enough data.
331: * Go back and try again.
332: */
333: goto read_more;
334:
335: return (bp->data[ch_offset]);
336: }
337:
338: /*
1.11 ! nicm 339: * ch_ungetchar is a rather kludgy and limited way to push
1.1 etheisen 340: * a single char onto an input file descriptor.
341: */
1.11 ! nicm 342: void
! 343: ch_ungetchar(int c)
1.1 etheisen 344: {
345: if (c != -1 && ch_ungotchar != -1)
346: error("ch_ungetchar overrun", NULL_PARG);
347: ch_ungotchar = c;
348: }
349:
350: /*
351: * Close the logfile.
352: * If we haven't read all of standard input into it, do that now.
353: */
1.11 ! nicm 354: void
! 355: end_logfile(void)
1.1 etheisen 356: {
357: static int tried = FALSE;
358:
359: if (logfile < 0)
360: return;
1.11 ! nicm 361: if (!tried && ch_fsize == -1) {
1.1 etheisen 362: tried = TRUE;
363: ierror("Finishing logfile", NULL_PARG);
364: while (ch_forw_get() != EOI)
365: if (ABORT_SIGS())
366: break;
367: }
368: close(logfile);
369: logfile = -1;
370: namelogfile = NULL;
371: }
372:
373: /*
374: * Start a log file AFTER less has already been running.
375: * Invoked from the - command; see toggle_option().
376: * Write all the existing buffered data to the log file.
377: */
1.11 ! nicm 378: void
! 379: sync_logfile(void)
1.1 etheisen 380: {
1.11 ! nicm 381: struct buf *bp;
! 382: struct bufnode *bn;
1.1 etheisen 383: int warned = FALSE;
1.4 millert 384: BLOCKNUM block;
385: BLOCKNUM nblocks;
1.1 etheisen 386:
387: nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
1.11 ! nicm 388: for (block = 0; block < nblocks; block++) {
1.8 shadchin 389: int wrote = FALSE;
1.11 ! nicm 390: FOR_BUFS(bn) {
1.8 shadchin 391: bp = bufnode_buf(bn);
1.11 ! nicm 392: if (bp->block == block) {
! 393: (void) write(logfile, (char *)bp->data,
! 394: bp->datasize);
1.8 shadchin 395: wrote = TRUE;
1.1 etheisen 396: break;
397: }
398: }
1.11 ! nicm 399: if (!wrote && !warned) {
! 400: error("Warning: log file is incomplete", NULL_PARG);
1.8 shadchin 401: warned = TRUE;
402: }
1.1 etheisen 403: }
404: }
405:
406: /*
407: * Determine if a specific block is currently in one of the buffers.
408: */
1.11 ! nicm 409: static int
! 410: buffered(BLOCKNUM block)
1.1 etheisen 411: {
1.4 millert 412: register struct buf *bp;
1.8 shadchin 413: register struct bufnode *bn;
1.4 millert 414: register int h;
1.1 etheisen 415:
1.4 millert 416: h = BUFHASH(block);
1.11 ! nicm 417: FOR_BUFS_IN_CHAIN(h, bn) {
1.8 shadchin 418: bp = bufnode_buf(bn);
1.1 etheisen 419: if (bp->block == block)
420: return (TRUE);
1.4 millert 421: }
1.1 etheisen 422: return (FALSE);
423: }
424:
425: /*
426: * Seek to a specified position in the file.
427: * Return 0 if successful, non-zero if can't seek there.
428: */
1.11 ! nicm 429: int
! 430: ch_seek(off_t pos)
1.1 etheisen 431: {
1.4 millert 432: BLOCKNUM new_block;
1.11 ! nicm 433: off_t len;
1.1 etheisen 434:
1.8 shadchin 435: if (thisfile == NULL)
436: return (0);
437:
1.1 etheisen 438: len = ch_length();
1.11 ! nicm 439: if (pos < ch_zero() || (len != -1 && pos > len))
1.1 etheisen 440: return (1);
441:
442: new_block = pos / LBUFSIZE;
1.11 ! nicm 443: if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos &&
! 444: !buffered(new_block)) {
1.1 etheisen 445: if (ch_fpos > pos)
446: return (1);
1.11 ! nicm 447: while (ch_fpos < pos) {
1.1 etheisen 448: if (ch_forw_get() == EOI)
449: return (1);
450: if (ABORT_SIGS())
451: return (1);
452: }
453: return (0);
454: }
455: /*
456: * Set read pointer.
457: */
458: ch_block = new_block;
459: ch_offset = pos % LBUFSIZE;
460: return (0);
461: }
462:
463: /*
464: * Seek to the end of the file.
465: */
1.11 ! nicm 466: int
! 467: ch_end_seek(void)
1.1 etheisen 468: {
1.11 ! nicm 469: off_t len;
1.1 etheisen 470:
1.8 shadchin 471: if (thisfile == NULL)
472: return (0);
473:
1.1 etheisen 474: if (ch_flags & CH_CANSEEK)
475: ch_fsize = filesize(ch_file);
476:
477: len = ch_length();
1.11 ! nicm 478: if (len != -1)
1.1 etheisen 479: return (ch_seek(len));
480:
481: /*
482: * Do it the slow way: read till end of data.
483: */
484: while (ch_forw_get() != EOI)
485: if (ABORT_SIGS())
486: return (1);
487: return (0);
488: }
489:
490: /*
491: * Seek to the beginning of the file, or as close to it as we can get.
492: * We may not be able to seek there if input is a pipe and the
493: * beginning of the pipe is no longer buffered.
494: */
1.11 ! nicm 495: int
! 496: ch_beg_seek(void)
1.1 etheisen 497: {
1.11 ! nicm 498: struct bufnode *bn;
! 499: struct bufnode *firstbn;
1.1 etheisen 500:
501: /*
502: * Try a plain ch_seek first.
503: */
504: if (ch_seek(ch_zero()) == 0)
505: return (0);
506:
507: /*
508: * Can't get to position 0.
509: * Look thru the buffers for the one closest to position 0.
510: */
1.8 shadchin 511: firstbn = ch_bufhead;
512: if (firstbn == END_OF_CHAIN)
1.1 etheisen 513: return (1);
1.11 ! nicm 514: FOR_BUFS(bn) {
1.8 shadchin 515: if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block)
516: firstbn = bn;
517: }
518: ch_block = bufnode_buf(firstbn)->block;
1.1 etheisen 519: ch_offset = 0;
520: return (0);
521: }
522:
523: /*
524: * Return the length of the file, if known.
525: */
1.11 ! nicm 526: off_t
! 527: ch_length(void)
1.1 etheisen 528: {
1.8 shadchin 529: if (thisfile == NULL)
1.11 ! nicm 530: return (-1);
1.1 etheisen 531: if (ignore_eoi)
1.11 ! nicm 532: return (-1);
1.10 shadchin 533: if (ch_flags & CH_NODATA)
534: return (0);
1.1 etheisen 535: return (ch_fsize);
536: }
537:
538: /*
539: * Return the current position in the file.
540: */
1.11 ! nicm 541: off_t
! 542: ch_tell(void)
1.1 etheisen 543: {
1.8 shadchin 544: if (thisfile == NULL)
1.11 ! nicm 545: return (-1);
! 546: return ((ch_block * LBUFSIZE) + ch_offset);
1.1 etheisen 547: }
548:
549: /*
550: * Get the current char and post-increment the read pointer.
551: */
1.11 ! nicm 552: int
! 553: ch_forw_get(void)
1.1 etheisen 554: {
1.11 ! nicm 555: int c;
1.1 etheisen 556:
1.8 shadchin 557: if (thisfile == NULL)
558: return (EOI);
1.1 etheisen 559: c = ch_get();
560: if (c == EOI)
561: return (EOI);
1.11 ! nicm 562: if (ch_offset < LBUFSIZE-1) {
1.1 etheisen 563: ch_offset++;
1.11 ! nicm 564: } else {
1.1 etheisen 565: ch_block ++;
566: ch_offset = 0;
567: }
568: return (c);
569: }
570:
571: /*
572: * Pre-decrement the read pointer and get the new current char.
573: */
1.11 ! nicm 574: int
! 575: ch_back_get(void)
1.1 etheisen 576: {
1.8 shadchin 577: if (thisfile == NULL)
578: return (EOI);
1.11 ! nicm 579: if (ch_offset > 0) {
1.1 etheisen 580: ch_offset --;
1.11 ! nicm 581: } else {
1.1 etheisen 582: if (ch_block <= 0)
583: return (EOI);
584: if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
585: return (EOI);
586: ch_block--;
587: ch_offset = LBUFSIZE-1;
588: }
589: return (ch_get());
590: }
591:
592: /*
1.4 millert 593: * Set max amount of buffer space.
594: * bufspace is in units of 1024 bytes. -1 mean no limit.
1.1 etheisen 595: */
1.11 ! nicm 596: void
! 597: ch_setbufspace(int bufspace)
1.1 etheisen 598: {
1.11 ! nicm 599: if (bufspace < 0) {
1.4 millert 600: maxbufs = -1;
1.11 ! nicm 601: } else {
1.4 millert 602: maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE;
603: if (maxbufs < 1)
604: maxbufs = 1;
1.1 etheisen 605: }
606: }
607:
608: /*
609: * Flush (discard) any saved file state, including buffer contents.
610: */
1.11 ! nicm 611: void
! 612: ch_flush(void)
1.1 etheisen 613: {
1.11 ! nicm 614: struct bufnode *bn;
1.8 shadchin 615:
616: if (thisfile == NULL)
617: return;
1.1 etheisen 618:
1.11 ! nicm 619: if (!(ch_flags & CH_CANSEEK)) {
1.1 etheisen 620: /*
621: * If input is a pipe, we don't flush buffer contents,
622: * since the contents can't be recovered.
623: */
1.11 ! nicm 624: ch_fsize = -1;
1.1 etheisen 625: return;
626: }
627:
628: /*
629: * Initialize all the buffers.
630: */
1.11 ! nicm 631: FOR_BUFS(bn) {
1.8 shadchin 632: bufnode_buf(bn)->block = -1;
633: }
1.1 etheisen 634:
635: /*
636: * Figure out the size of the file, if we can.
637: */
638: ch_fsize = filesize(ch_file);
639:
640: /*
641: * Seek to a known position: the beginning of the file.
642: */
643: ch_fpos = 0;
644: ch_block = 0; /* ch_fpos / LBUFSIZE; */
645: ch_offset = 0; /* ch_fpos % LBUFSIZE; */
646:
1.4 millert 647: #if 1
648: /*
649: * This is a kludge to workaround a Linux kernel bug: files in
1.11 ! nicm 650: * /proc have a size of 0 according to fstat() but have readable
1.4 millert 651: * data. They are sometimes, but not always, seekable.
652: * Force them to be non-seekable here.
653: */
1.11 ! nicm 654: if (ch_fsize == 0) {
! 655: ch_fsize = -1;
1.4 millert 656: ch_flags &= ~CH_CANSEEK;
657: }
658: #endif
659:
1.11 ! nicm 660: if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) {
1.1 etheisen 661: /*
662: * Warning only; even if the seek fails for some reason,
663: * there's a good chance we're at the beginning anyway.
664: * {{ I think this is bogus reasoning. }}
665: */
666: error("seek error to 0", NULL_PARG);
667: }
668: }
669:
670: /*
671: * Allocate a new buffer.
672: * The buffer is added to the tail of the buffer chain.
673: */
1.11 ! nicm 674: static int
! 675: ch_addbuf(void)
1.1 etheisen 676: {
1.11 ! nicm 677: struct buf *bp;
! 678: struct bufnode *bn;
1.1 etheisen 679:
680: /*
1.11 ! nicm 681: * Allocate and initialize a new buffer and link it
1.1 etheisen 682: * onto the tail of the buffer list.
683: */
1.11 ! nicm 684: bp = calloc(1, sizeof (struct buf));
1.1 etheisen 685: if (bp == NULL)
686: return (1);
687: ch_nbufs++;
1.4 millert 688: bp->block = -1;
1.8 shadchin 689: bn = &bp->node;
690:
691: BUF_INS_TAIL(bn);
692: BUF_HASH_INS(bn, 0);
1.1 etheisen 693: return (0);
694: }
695:
696: /*
1.4 millert 697: *
698: */
1.11 ! nicm 699: static void
! 700: init_hashtbl(void)
1.4 millert 701: {
1.11 ! nicm 702: int h;
1.4 millert 703:
1.11 ! nicm 704: for (h = 0; h < BUFHASH_SIZE; h++) {
1.8 shadchin 705: thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
706: thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
1.4 millert 707: }
708: }
709:
710: /*
1.1 etheisen 711: * Delete all buffers for this file.
712: */
1.11 ! nicm 713: static void
! 714: ch_delbufs(void)
1.1 etheisen 715: {
1.11 ! nicm 716: struct bufnode *bn;
1.1 etheisen 717:
1.11 ! nicm 718: while (ch_bufhead != END_OF_CHAIN) {
1.8 shadchin 719: bn = ch_bufhead;
720: BUF_RM(bn);
721: free(bufnode_buf(bn));
1.1 etheisen 722: }
723: ch_nbufs = 0;
1.4 millert 724: init_hashtbl();
1.1 etheisen 725: }
726:
727: /*
728: * Is it possible to seek on a file descriptor?
729: */
1.11 ! nicm 730: int
! 731: seekable(int f)
! 732: {
1.7 deraadt 733: return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK);
1.1 etheisen 734: }
1.10 shadchin 735:
736: /*
737: * Force EOF to be at the current read position.
738: * This is used after an ignore_eof read, during which the EOF may change.
739: */
1.11 ! nicm 740: void
! 741: ch_set_eof(void)
1.10 shadchin 742: {
743: ch_fsize = ch_fpos;
744: }
745:
1.1 etheisen 746:
747: /*
748: * Initialize file state for a new file.
749: */
1.11 ! nicm 750: void
! 751: ch_init(int f, int flags)
1.1 etheisen 752: {
753: /*
754: * See if we already have a filestate for this file.
755: */
1.11 ! nicm 756: thisfile = get_filestate(curr_ifile);
! 757: if (thisfile == NULL) {
1.1 etheisen 758: /*
759: * Allocate and initialize a new filestate.
760: */
1.11 ! nicm 761: thisfile = calloc(1, sizeof (struct filestate));
1.8 shadchin 762: thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
1.1 etheisen 763: thisfile->nbufs = 0;
764: thisfile->flags = 0;
765: thisfile->fpos = 0;
766: thisfile->block = 0;
767: thisfile->offset = 0;
768: thisfile->file = -1;
1.11 ! nicm 769: thisfile->fsize = -1;
1.1 etheisen 770: ch_flags = flags;
1.4 millert 771: init_hashtbl();
1.1 etheisen 772: /*
773: * Try to seek; set CH_CANSEEK if it works.
774: */
1.4 millert 775: if ((flags & CH_CANSEEK) && !seekable(f))
776: ch_flags &= ~CH_CANSEEK;
1.1 etheisen 777: set_filestate(curr_ifile, (void *) thisfile);
778: }
779: if (thisfile->file == -1)
780: thisfile->file = f;
781: ch_flush();
782: }
783:
784: /*
785: * Close a filestate.
786: */
1.11 ! nicm 787: void
! 788: ch_close(void)
1.1 etheisen 789: {
790: int keepstate = FALSE;
791:
1.8 shadchin 792: if (thisfile == NULL)
793: return;
794:
1.11 ! nicm 795: if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) {
1.1 etheisen 796: /*
797: * We can seek or re-open, so we don't need to keep buffers.
798: */
799: ch_delbufs();
1.11 ! nicm 800: } else {
1.1 etheisen 801: keepstate = TRUE;
1.11 ! nicm 802: }
! 803: if (!(ch_flags & CH_KEEPOPEN)) {
1.1 etheisen 804: /*
805: * We don't need to keep the file descriptor open
806: * (because we can re-open it.)
807: * But don't really close it if it was opened via popen(),
808: * because pclose() wants to close it.
809: */
1.5 millert 810: if (!(ch_flags & CH_POPENED))
1.1 etheisen 811: close(ch_file);
812: ch_file = -1;
1.11 ! nicm 813: } else {
1.1 etheisen 814: keepstate = TRUE;
1.11 ! nicm 815: }
! 816: if (!keepstate) {
1.1 etheisen 817: /*
818: * We don't even need to keep the filestate structure.
819: */
820: free(thisfile);
821: thisfile = NULL;
1.11 ! nicm 822: set_filestate(curr_ifile, NULL);
1.1 etheisen 823: }
824: }
825:
826: /*
827: * Return ch_flags for the current file.
828: */
1.11 ! nicm 829: int
! 830: ch_getflags(void)
1.1 etheisen 831: {
1.8 shadchin 832: if (thisfile == NULL)
833: return (0);
1.1 etheisen 834: return (ch_flags);
835: }