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