Annotation of src/usr.bin/less/ch.c, Revision 1.23
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:
1.18 mmcc 18: #include <sys/stat.h>
19:
1.1 etheisen 20: #include "less.h"
1.4 millert 21:
1.8 shadchin 22: extern dev_t curr_dev;
23: extern ino_t curr_ino;
1.11 nicm 24: extern int less_is_more;
1.8 shadchin 25:
1.11 nicm 26: typedef off_t BLOCKNUM;
1.1 etheisen 27:
1.11 nicm 28: int ignore_eoi;
1.1 etheisen 29:
30: /*
31: * Pool of buffers holding the most recently used blocks of the input file.
32: * The buffer pool is kept as a doubly-linked circular list,
33: * in order from most- to least-recently used.
34: * The circular list is anchored by the file state "thisfile".
35: */
1.8 shadchin 36: struct bufnode {
37: struct bufnode *next, *prev;
38: struct bufnode *hnext, *hprev;
39: };
40:
1.4 millert 41: #define LBUFSIZE 8192
1.1 etheisen 42: struct buf {
1.8 shadchin 43: struct bufnode node;
1.4 millert 44: BLOCKNUM block;
1.1 etheisen 45: unsigned int datasize;
46: unsigned char data[LBUFSIZE];
47: };
1.11 nicm 48: #define bufnode_buf(bn) ((struct buf *)bn)
1.4 millert 49:
1.1 etheisen 50: /*
51: * The file state is maintained in a filestate structure.
52: * A pointer to the filestate is kept in the ifile structure.
53: */
1.4 millert 54: #define BUFHASH_SIZE 64
1.1 etheisen 55: struct filestate {
1.8 shadchin 56: struct bufnode buflist;
57: struct bufnode hashtbl[BUFHASH_SIZE];
1.1 etheisen 58: int file;
59: int flags;
1.11 nicm 60: off_t fpos;
1.1 etheisen 61: int nbufs;
1.4 millert 62: BLOCKNUM block;
63: unsigned int offset;
1.11 nicm 64: off_t fsize;
1.1 etheisen 65: };
66:
1.8 shadchin 67: #define ch_bufhead thisfile->buflist.next
68: #define ch_buftail thisfile->buflist.prev
1.1 etheisen 69: #define ch_nbufs thisfile->nbufs
70: #define ch_block thisfile->block
71: #define ch_offset thisfile->offset
72: #define ch_fpos thisfile->fpos
73: #define ch_fsize thisfile->fsize
74: #define ch_flags thisfile->flags
75: #define ch_file thisfile->file
76:
1.8 shadchin 77: #define END_OF_CHAIN (&thisfile->buflist)
78: #define END_OF_HCHAIN(h) (&thisfile->hashtbl[h])
1.11 nicm 79: #define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1))
1.4 millert 80:
1.8 shadchin 81: /*
82: * Macros to manipulate the list of buffers in thisfile->buflist.
83: */
84: #define FOR_BUFS(bn) \
1.19 deraadt 85: for ((bn) = ch_bufhead; (bn) != END_OF_CHAIN; (bn) = (bn)->next)
1.8 shadchin 86:
1.11 nicm 87: #define BUF_RM(bn) \
1.8 shadchin 88: (bn)->next->prev = (bn)->prev; \
89: (bn)->prev->next = (bn)->next;
90:
1.11 nicm 91: #define BUF_INS_HEAD(bn) \
1.8 shadchin 92: (bn)->next = ch_bufhead; \
93: (bn)->prev = END_OF_CHAIN; \
94: ch_bufhead->prev = (bn); \
95: ch_bufhead = (bn);
96:
1.11 nicm 97: #define BUF_INS_TAIL(bn) \
1.8 shadchin 98: (bn)->next = END_OF_CHAIN; \
99: (bn)->prev = ch_buftail; \
100: ch_buftail->next = (bn); \
101: ch_buftail = (bn);
102:
103: /*
104: * Macros to manipulate the list of buffers in thisfile->hashtbl[n].
105: */
1.11 nicm 106: #define FOR_BUFS_IN_CHAIN(h, bn) \
1.19 deraadt 107: for ((bn) = thisfile->hashtbl[h].hnext; \
1.17 mmcc 108: (bn) != END_OF_HCHAIN(h); (bn) = (bn)->hnext)
1.8 shadchin 109:
110: #define BUF_HASH_RM(bn) \
111: (bn)->hnext->hprev = (bn)->hprev; \
112: (bn)->hprev->hnext = (bn)->hnext;
113:
1.11 nicm 114: #define BUF_HASH_INS(bn, h) \
1.8 shadchin 115: (bn)->hnext = thisfile->hashtbl[h].hnext; \
116: (bn)->hprev = END_OF_HCHAIN(h); \
117: thisfile->hashtbl[h].hnext->hprev = (bn); \
118: thisfile->hashtbl[h].hnext = (bn);
1.4 millert 119:
1.1 etheisen 120: static struct filestate *thisfile;
121: static int ch_ungotchar = -1;
1.4 millert 122: static int maxbufs = -1;
1.1 etheisen 123:
124: extern int autobuf;
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.21 deraadt 306: if (any_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)
1.21 deraadt 363: if (abort_sigs())
1.1 etheisen 364: break;
365: }
366: close(logfile);
367: logfile = -1;
1.20 krw 368: free(namelogfile);
1.1 etheisen 369: namelogfile = NULL;
370: }
371:
372: /*
373: * Start a log file AFTER less has already been running.
374: * Invoked from the - command; see toggle_option().
375: * Write all the existing buffered data to the log file.
376: */
1.11 nicm 377: void
378: sync_logfile(void)
1.1 etheisen 379: {
1.11 nicm 380: struct buf *bp;
381: struct bufnode *bn;
1.1 etheisen 382: int warned = FALSE;
1.4 millert 383: BLOCKNUM block;
384: BLOCKNUM nblocks;
1.1 etheisen 385:
386: nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
1.19 deraadt 387: for (block = 0; block < nblocks; block++) {
1.8 shadchin 388: int wrote = FALSE;
1.11 nicm 389: FOR_BUFS(bn) {
1.8 shadchin 390: bp = bufnode_buf(bn);
1.11 nicm 391: if (bp->block == block) {
392: (void) write(logfile, (char *)bp->data,
393: bp->datasize);
1.8 shadchin 394: wrote = TRUE;
1.1 etheisen 395: break;
396: }
397: }
1.11 nicm 398: if (!wrote && !warned) {
1.14 deraadt 399: error("Warning: log file is incomplete", NULL);
1.8 shadchin 400: warned = TRUE;
401: }
1.1 etheisen 402: }
403: }
404:
405: /*
406: * Determine if a specific block is currently in one of the buffers.
407: */
1.11 nicm 408: static int
409: buffered(BLOCKNUM block)
1.1 etheisen 410: {
1.12 tedu 411: struct buf *bp;
412: struct bufnode *bn;
413: int h;
1.1 etheisen 414:
1.4 millert 415: h = BUFHASH(block);
1.11 nicm 416: FOR_BUFS_IN_CHAIN(h, bn) {
1.8 shadchin 417: bp = bufnode_buf(bn);
1.1 etheisen 418: if (bp->block == block)
419: return (TRUE);
1.4 millert 420: }
1.1 etheisen 421: return (FALSE);
422: }
423:
424: /*
425: * Seek to a specified position in the file.
426: * Return 0 if successful, non-zero if can't seek there.
427: */
1.11 nicm 428: int
429: ch_seek(off_t pos)
1.1 etheisen 430: {
1.4 millert 431: BLOCKNUM new_block;
1.11 nicm 432: off_t len;
1.1 etheisen 433:
1.8 shadchin 434: if (thisfile == NULL)
435: return (0);
436:
1.1 etheisen 437: len = ch_length();
1.11 nicm 438: if (pos < ch_zero() || (len != -1 && pos > len))
1.1 etheisen 439: return (1);
440:
441: new_block = pos / LBUFSIZE;
1.11 nicm 442: if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos &&
443: !buffered(new_block)) {
1.1 etheisen 444: if (ch_fpos > pos)
445: return (1);
1.11 nicm 446: while (ch_fpos < pos) {
1.1 etheisen 447: if (ch_forw_get() == EOI)
448: return (1);
1.21 deraadt 449: if (abort_sigs())
1.1 etheisen 450: return (1);
451: }
452: return (0);
453: }
454: /*
455: * Set read pointer.
456: */
457: ch_block = new_block;
458: ch_offset = pos % LBUFSIZE;
459: return (0);
460: }
461:
462: /*
463: * Seek to the end of the file.
464: */
1.11 nicm 465: int
466: ch_end_seek(void)
1.1 etheisen 467: {
1.11 nicm 468: off_t len;
1.1 etheisen 469:
1.8 shadchin 470: if (thisfile == NULL)
471: return (0);
472:
1.1 etheisen 473: if (ch_flags & CH_CANSEEK)
474: ch_fsize = filesize(ch_file);
475:
476: len = ch_length();
1.11 nicm 477: if (len != -1)
1.1 etheisen 478: return (ch_seek(len));
479:
480: /*
481: * Do it the slow way: read till end of data.
482: */
483: while (ch_forw_get() != EOI)
1.21 deraadt 484: if (abort_sigs())
1.1 etheisen 485: return (1);
486: return (0);
487: }
488:
489: /*
490: * Seek to the beginning of the file, or as close to it as we can get.
491: * We may not be able to seek there if input is a pipe and the
492: * beginning of the pipe is no longer buffered.
493: */
1.11 nicm 494: int
495: ch_beg_seek(void)
1.1 etheisen 496: {
1.11 nicm 497: struct bufnode *bn;
498: struct bufnode *firstbn;
1.1 etheisen 499:
500: /*
501: * Try a plain ch_seek first.
502: */
503: if (ch_seek(ch_zero()) == 0)
504: return (0);
505:
506: /*
507: * Can't get to position 0.
508: * Look thru the buffers for the one closest to position 0.
509: */
1.8 shadchin 510: firstbn = ch_bufhead;
511: if (firstbn == END_OF_CHAIN)
1.1 etheisen 512: return (1);
1.11 nicm 513: FOR_BUFS(bn) {
1.8 shadchin 514: if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block)
515: firstbn = bn;
516: }
517: ch_block = bufnode_buf(firstbn)->block;
1.1 etheisen 518: ch_offset = 0;
519: return (0);
520: }
521:
522: /*
523: * Return the length of the file, if known.
524: */
1.11 nicm 525: off_t
526: ch_length(void)
1.1 etheisen 527: {
1.8 shadchin 528: if (thisfile == NULL)
1.11 nicm 529: return (-1);
1.1 etheisen 530: if (ignore_eoi)
1.11 nicm 531: return (-1);
1.10 shadchin 532: if (ch_flags & CH_NODATA)
533: return (0);
1.1 etheisen 534: return (ch_fsize);
535: }
536:
537: /*
538: * Return the current position in the file.
539: */
1.11 nicm 540: off_t
541: ch_tell(void)
1.1 etheisen 542: {
1.8 shadchin 543: if (thisfile == NULL)
1.11 nicm 544: return (-1);
545: return ((ch_block * LBUFSIZE) + ch_offset);
1.1 etheisen 546: }
547:
548: /*
549: * Get the current char and post-increment the read pointer.
550: */
1.11 nicm 551: int
552: ch_forw_get(void)
1.1 etheisen 553: {
1.11 nicm 554: int c;
1.1 etheisen 555:
1.8 shadchin 556: if (thisfile == NULL)
557: return (EOI);
1.1 etheisen 558: c = ch_get();
559: if (c == EOI)
560: return (EOI);
1.11 nicm 561: if (ch_offset < LBUFSIZE-1) {
1.1 etheisen 562: ch_offset++;
1.11 nicm 563: } else {
1.1 etheisen 564: ch_block ++;
565: ch_offset = 0;
566: }
567: return (c);
568: }
569:
570: /*
571: * Pre-decrement the read pointer and get the new current char.
572: */
1.11 nicm 573: int
574: ch_back_get(void)
1.1 etheisen 575: {
1.8 shadchin 576: if (thisfile == NULL)
577: return (EOI);
1.11 nicm 578: if (ch_offset > 0) {
1.1 etheisen 579: ch_offset --;
1.11 nicm 580: } else {
1.1 etheisen 581: if (ch_block <= 0)
582: return (EOI);
583: if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1))
584: return (EOI);
585: ch_block--;
586: ch_offset = LBUFSIZE-1;
587: }
588: return (ch_get());
589: }
590:
591: /*
1.4 millert 592: * Set max amount of buffer space.
593: * bufspace is in units of 1024 bytes. -1 mean no limit.
1.1 etheisen 594: */
1.11 nicm 595: void
596: ch_setbufspace(int bufspace)
1.1 etheisen 597: {
1.11 nicm 598: if (bufspace < 0) {
1.4 millert 599: maxbufs = -1;
1.11 nicm 600: } else {
1.4 millert 601: maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE;
602: if (maxbufs < 1)
603: maxbufs = 1;
1.1 etheisen 604: }
605: }
606:
607: /*
608: * Flush (discard) any saved file state, including buffer contents.
609: */
1.11 nicm 610: void
611: ch_flush(void)
1.1 etheisen 612: {
1.11 nicm 613: struct bufnode *bn;
1.8 shadchin 614:
615: if (thisfile == NULL)
616: return;
1.1 etheisen 617:
1.11 nicm 618: if (!(ch_flags & CH_CANSEEK)) {
1.1 etheisen 619: /*
620: * If input is a pipe, we don't flush buffer contents,
621: * since the contents can't be recovered.
622: */
1.11 nicm 623: ch_fsize = -1;
1.1 etheisen 624: return;
625: }
626:
627: /*
628: * Initialize all the buffers.
629: */
1.11 nicm 630: FOR_BUFS(bn) {
1.8 shadchin 631: bufnode_buf(bn)->block = -1;
632: }
1.1 etheisen 633:
634: /*
635: * Figure out the size of the file, if we can.
636: */
637: ch_fsize = filesize(ch_file);
638:
639: /*
640: * Seek to a known position: the beginning of the file.
641: */
642: ch_fpos = 0;
643: ch_block = 0; /* ch_fpos / LBUFSIZE; */
644: ch_offset = 0; /* ch_fpos % LBUFSIZE; */
645:
1.15 deraadt 646: if (lseek(ch_file, (off_t)0, SEEK_SET) == (off_t)-1) {
1.1 etheisen 647: /*
648: * Warning only; even if the seek fails for some reason,
649: * there's a good chance we're at the beginning anyway.
650: * {{ I think this is bogus reasoning. }}
651: */
1.14 deraadt 652: error("seek error to 0", NULL);
1.1 etheisen 653: }
654: }
655:
656: /*
657: * Allocate a new buffer.
658: * The buffer is added to the tail of the buffer chain.
659: */
1.11 nicm 660: static int
661: ch_addbuf(void)
1.1 etheisen 662: {
1.11 nicm 663: struct buf *bp;
664: struct bufnode *bn;
1.1 etheisen 665:
666: /*
1.11 nicm 667: * Allocate and initialize a new buffer and link it
1.1 etheisen 668: * onto the tail of the buffer list.
669: */
1.11 nicm 670: bp = calloc(1, sizeof (struct buf));
1.1 etheisen 671: if (bp == NULL)
672: return (1);
673: ch_nbufs++;
1.4 millert 674: bp->block = -1;
1.8 shadchin 675: bn = &bp->node;
676:
677: BUF_INS_TAIL(bn);
678: BUF_HASH_INS(bn, 0);
1.1 etheisen 679: return (0);
680: }
681:
682: /*
1.4 millert 683: *
684: */
1.11 nicm 685: static void
686: init_hashtbl(void)
1.4 millert 687: {
1.11 nicm 688: int h;
1.4 millert 689:
1.11 nicm 690: for (h = 0; h < BUFHASH_SIZE; h++) {
1.8 shadchin 691: thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
692: thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
1.4 millert 693: }
694: }
695:
696: /*
1.1 etheisen 697: * Delete all buffers for this file.
698: */
1.11 nicm 699: static void
700: ch_delbufs(void)
1.1 etheisen 701: {
1.11 nicm 702: struct bufnode *bn;
1.1 etheisen 703:
1.11 nicm 704: while (ch_bufhead != END_OF_CHAIN) {
1.8 shadchin 705: bn = ch_bufhead;
706: BUF_RM(bn);
707: free(bufnode_buf(bn));
1.1 etheisen 708: }
709: ch_nbufs = 0;
1.4 millert 710: init_hashtbl();
1.1 etheisen 711: }
712:
713: /*
714: * Is it possible to seek on a file descriptor?
715: */
1.11 nicm 716: int
717: seekable(int f)
718: {
1.15 deraadt 719: return (lseek(f, (off_t)1, SEEK_SET) != (off_t)-1);
1.1 etheisen 720: }
1.10 shadchin 721:
722: /*
723: * Force EOF to be at the current read position.
724: * This is used after an ignore_eof read, during which the EOF may change.
725: */
1.11 nicm 726: void
727: ch_set_eof(void)
1.10 shadchin 728: {
729: ch_fsize = ch_fpos;
730: }
731:
1.1 etheisen 732:
733: /*
734: * Initialize file state for a new file.
735: */
1.11 nicm 736: void
737: ch_init(int f, int flags)
1.1 etheisen 738: {
739: /*
740: * See if we already have a filestate for this file.
741: */
1.11 nicm 742: thisfile = get_filestate(curr_ifile);
743: if (thisfile == NULL) {
1.1 etheisen 744: /*
745: * Allocate and initialize a new filestate.
746: */
1.11 nicm 747: thisfile = calloc(1, sizeof (struct filestate));
1.8 shadchin 748: thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
1.1 etheisen 749: thisfile->nbufs = 0;
750: thisfile->flags = 0;
751: thisfile->fpos = 0;
752: thisfile->block = 0;
753: thisfile->offset = 0;
754: thisfile->file = -1;
1.11 nicm 755: thisfile->fsize = -1;
1.1 etheisen 756: ch_flags = flags;
1.4 millert 757: init_hashtbl();
1.1 etheisen 758: /*
759: * Try to seek; set CH_CANSEEK if it works.
760: */
1.4 millert 761: if ((flags & CH_CANSEEK) && !seekable(f))
762: ch_flags &= ~CH_CANSEEK;
1.1 etheisen 763: set_filestate(curr_ifile, (void *) thisfile);
764: }
765: if (thisfile->file == -1)
766: thisfile->file = f;
767: ch_flush();
768: }
769:
770: /*
771: * Close a filestate.
772: */
1.11 nicm 773: void
774: ch_close(void)
1.1 etheisen 775: {
776: int keepstate = FALSE;
777:
1.8 shadchin 778: if (thisfile == NULL)
779: return;
780:
1.23 ! guenther 781: if (ch_flags & (CH_CANSEEK|CH_HELPFILE)) {
1.1 etheisen 782: /*
783: * We can seek or re-open, so we don't need to keep buffers.
784: */
785: ch_delbufs();
1.11 nicm 786: } else {
1.1 etheisen 787: keepstate = TRUE;
1.11 nicm 788: }
789: if (!(ch_flags & CH_KEEPOPEN)) {
1.1 etheisen 790: /*
791: * We don't need to keep the file descriptor open
792: * (because we can re-open it.)
793: */
1.23 ! guenther 794: close(ch_file);
1.1 etheisen 795: ch_file = -1;
1.11 nicm 796: } else {
1.1 etheisen 797: keepstate = TRUE;
1.11 nicm 798: }
799: if (!keepstate) {
1.1 etheisen 800: /*
801: * We don't even need to keep the filestate structure.
802: */
803: free(thisfile);
804: thisfile = NULL;
1.11 nicm 805: set_filestate(curr_ifile, NULL);
1.1 etheisen 806: }
807: }
808:
809: /*
810: * Return ch_flags for the current file.
811: */
1.11 nicm 812: int
813: ch_getflags(void)
1.1 etheisen 814: {
1.8 shadchin 815: if (thisfile == NULL)
816: return (0);
1.1 etheisen 817: return (ch_flags);
818: }