Annotation of src/usr.bin/less/ch.c, Revision 1.18
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.16 mmcc 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.17 mmcc 107: for ((bn) = thisfile->hashtbl[h].hnext; \
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.9 millert 125: extern volatile sig_atomic_t sigs;
1.4 millert 126: extern int secure;
1.8 shadchin 127: extern int screen_trashed;
128: extern int follow_mode;
1.1 etheisen 129: extern IFILE curr_ifile;
130: extern int logfile;
131: extern char *namelogfile;
132:
1.11 nicm 133: static int ch_addbuf(void);
1.1 etheisen 134:
135:
136: /*
137: * Get the character pointed to by the read pointer.
1.8 shadchin 138: */
1.11 nicm 139: int
140: ch_get(void)
1.1 etheisen 141: {
1.11 nicm 142: struct buf *bp;
143: struct bufnode *bn;
144: int n;
145: int slept;
146: int h;
147: off_t pos;
148: off_t len;
1.1 etheisen 149:
1.8 shadchin 150: if (thisfile == NULL)
151: return (EOI);
152:
153: /*
1.11 nicm 154: * Quick check for the common case where
1.8 shadchin 155: * the desired char is in the head buffer.
156: */
1.11 nicm 157: if (ch_bufhead != END_OF_CHAIN) {
1.8 shadchin 158: bp = bufnode_buf(ch_bufhead);
159: if (ch_block == bp->block && ch_offset < bp->datasize)
1.11 nicm 160: return (bp->data[ch_offset]);
1.8 shadchin 161: }
162:
1.1 etheisen 163: slept = FALSE;
164:
165: /*
166: * Look for a buffer holding the desired block.
167: */
1.4 millert 168: h = BUFHASH(ch_block);
1.11 nicm 169: FOR_BUFS_IN_CHAIN(h, bn) {
1.8 shadchin 170: bp = bufnode_buf(bn);
1.11 nicm 171: if (bp->block == ch_block) {
1.1 etheisen 172: if (ch_offset >= bp->datasize)
173: /*
174: * Need more data in this buffer.
175: */
1.8 shadchin 176: break;
1.1 etheisen 177: goto found;
178: }
1.4 millert 179: }
1.11 nicm 180: if (bn == END_OF_HCHAIN(h)) {
1.8 shadchin 181: /*
1.11 nicm 182: * Block is not in a buffer.
183: * Take the least recently used buffer
1.8 shadchin 184: * and read the desired block into it.
1.11 nicm 185: * If the LRU buffer has data in it,
1.8 shadchin 186: * then maybe allocate a new buffer.
187: */
1.11 nicm 188: if (ch_buftail == END_OF_CHAIN ||
189: bufnode_buf(ch_buftail)->block != -1) {
1.8 shadchin 190: /*
191: * There is no empty buffer to use.
192: * Allocate a new buffer if:
1.11 nicm 193: * 1. We can't seek on this file and -b is not in
194: * effect; or
195: * 2. We haven't allocated the max buffers for this
196: * file yet.
1.8 shadchin 197: */
198: if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
1.11 nicm 199: (maxbufs < 0 || ch_nbufs < maxbufs))
1.8 shadchin 200: if (ch_addbuf())
201: /*
202: * Allocation failed: turn off autobuf.
203: */
204: autobuf = OPT_OFF;
205: }
206: bn = ch_buftail;
207: bp = bufnode_buf(bn);
208: BUF_HASH_RM(bn); /* Remove from old hash chain. */
209: bp->block = ch_block;
210: bp->datasize = 0;
211: BUF_HASH_INS(bn, h); /* Insert into new hash chain. */
1.1 etheisen 212: }
213:
1.11 nicm 214: read_more:
1.1 etheisen 215: pos = (ch_block * LBUFSIZE) + bp->datasize;
1.11 nicm 216: if ((len = ch_length()) != -1 && pos >= len)
1.1 etheisen 217: /*
218: * At end of file.
219: */
220: return (EOI);
221:
1.11 nicm 222: if (pos != ch_fpos) {
1.1 etheisen 223: /*
224: * Not at the correct position: must seek.
225: * If input is a pipe, we're in trouble (can't seek on a pipe).
226: * Some data has been lost: just return "?".
227: */
228: if (!(ch_flags & CH_CANSEEK))
229: return ('?');
1.15 deraadt 230: if (lseek(ch_file, (off_t)pos, SEEK_SET) == (off_t)-1) {
1.14 deraadt 231: error("seek error", NULL);
1.1 etheisen 232: clear_eol();
233: return (EOI);
1.11 nicm 234: }
235: ch_fpos = pos;
236: }
1.1 etheisen 237:
238: /*
239: * Read the block.
240: * If we read less than a full block, that's ok.
241: * We use partial block and pick up the rest next time.
242: */
1.11 nicm 243: if (ch_ungotchar != -1) {
244: bp->data[bp->datasize] = (unsigned char)ch_ungotchar;
1.1 etheisen 245: n = 1;
246: ch_ungotchar = -1;
1.11 nicm 247: } else {
248: n = iread(ch_file, &bp->data[bp->datasize],
249: (unsigned int)(LBUFSIZE - bp->datasize));
1.1 etheisen 250: }
251:
252: if (n == READ_INTR)
253: return (EOI);
1.11 nicm 254: if (n < 0) {
1.14 deraadt 255: error("read error", NULL);
1.11 nicm 256: clear_eol();
1.1 etheisen 257: n = 0;
258: }
259:
260: /*
261: * If we have a log file, write the new data to it.
262: */
1.4 millert 263: if (!secure && logfile >= 0 && n > 0)
1.11 nicm 264: (void) write(logfile, (char *)&bp->data[bp->datasize], n);
1.1 etheisen 265:
266: ch_fpos += n;
267: bp->datasize += n;
268:
269: /*
270: * If we have read to end of file, set ch_fsize to indicate
271: * the position of the end of file.
272: */
1.11 nicm 273: if (n == 0) {
1.1 etheisen 274: ch_fsize = pos;
1.11 nicm 275: if (ignore_eoi) {
1.1 etheisen 276: /*
277: * We are ignoring EOF.
278: * Wait a while, then try again.
279: */
1.11 nicm 280: if (!slept) {
1.4 millert 281: PARG parg;
282: parg.p_string = wait_message();
283: ierror("%s", &parg);
284: }
1.11 nicm 285: sleep(1);
1.1 etheisen 286: slept = TRUE;
1.8 shadchin 287:
1.11 nicm 288: if (follow_mode == FOLLOW_NAME) {
289: /*
290: * See whether the file's i-number has changed.
1.8 shadchin 291: * If so, force the file to be closed and
1.11 nicm 292: * reopened.
293: */
1.8 shadchin 294: struct stat st;
295: int r = stat(get_filename(curr_ifile), &st);
296: if (r == 0 && (st.st_ino != curr_ino ||
1.11 nicm 297: st.st_dev != curr_dev)) {
298: /*
299: * screen_trashed=2 causes
300: * make_display to reopen the file.
301: */
1.8 shadchin 302: screen_trashed = 2;
303: return (EOI);
304: }
305: }
1.1 etheisen 306: }
1.4 millert 307: if (sigs)
1.1 etheisen 308: return (EOI);
309: }
310:
1.11 nicm 311: found:
312: if (ch_bufhead != bn) {
1.1 etheisen 313: /*
314: * Move the buffer to the head of the buffer chain.
315: * This orders the buffer chain, most- to least-recently used.
316: */
1.8 shadchin 317: BUF_RM(bn);
318: BUF_INS_HEAD(bn);
1.4 millert 319:
320: /*
321: * Move to head of hash chain too.
322: */
1.8 shadchin 323: BUF_HASH_RM(bn);
324: BUF_HASH_INS(bn, h);
1.1 etheisen 325: }
326:
327: if (ch_offset >= bp->datasize)
328: /*
329: * After all that, we still don't have enough data.
330: * Go back and try again.
331: */
332: goto read_more;
333:
334: return (bp->data[ch_offset]);
335: }
336:
337: /*
1.11 nicm 338: * ch_ungetchar is a rather kludgy and limited way to push
1.1 etheisen 339: * a single char onto an input file descriptor.
340: */
1.11 nicm 341: void
342: ch_ungetchar(int c)
1.1 etheisen 343: {
344: if (c != -1 && ch_ungotchar != -1)
1.14 deraadt 345: error("ch_ungetchar overrun", NULL);
1.1 etheisen 346: ch_ungotchar = c;
347: }
348:
349: /*
350: * Close the logfile.
351: * If we haven't read all of standard input into it, do that now.
352: */
1.11 nicm 353: void
354: end_logfile(void)
1.1 etheisen 355: {
356: static int tried = FALSE;
357:
358: if (logfile < 0)
359: return;
1.11 nicm 360: if (!tried && ch_fsize == -1) {
1.1 etheisen 361: tried = TRUE;
1.14 deraadt 362: ierror("Finishing logfile", NULL);
1.1 etheisen 363: while (ch_forw_get() != EOI)
364: if (ABORT_SIGS())
365: break;
366: }
367: close(logfile);
368: logfile = -1;
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.11 nicm 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);
449: if (ABORT_SIGS())
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)
484: if (ABORT_SIGS())
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.4 millert 646: #if 1
647: /*
648: * This is a kludge to workaround a Linux kernel bug: files in
1.11 nicm 649: * /proc have a size of 0 according to fstat() but have readable
1.4 millert 650: * data. They are sometimes, but not always, seekable.
651: * Force them to be non-seekable here.
652: */
1.11 nicm 653: if (ch_fsize == 0) {
654: ch_fsize = -1;
1.4 millert 655: ch_flags &= ~CH_CANSEEK;
656: }
657: #endif
658:
1.15 deraadt 659: if (lseek(ch_file, (off_t)0, SEEK_SET) == (off_t)-1) {
1.1 etheisen 660: /*
661: * Warning only; even if the seek fails for some reason,
662: * there's a good chance we're at the beginning anyway.
663: * {{ I think this is bogus reasoning. }}
664: */
1.14 deraadt 665: error("seek error to 0", NULL);
1.1 etheisen 666: }
667: }
668:
669: /*
670: * Allocate a new buffer.
671: * The buffer is added to the tail of the buffer chain.
672: */
1.11 nicm 673: static int
674: ch_addbuf(void)
1.1 etheisen 675: {
1.11 nicm 676: struct buf *bp;
677: struct bufnode *bn;
1.1 etheisen 678:
679: /*
1.11 nicm 680: * Allocate and initialize a new buffer and link it
1.1 etheisen 681: * onto the tail of the buffer list.
682: */
1.11 nicm 683: bp = calloc(1, sizeof (struct buf));
1.1 etheisen 684: if (bp == NULL)
685: return (1);
686: ch_nbufs++;
1.4 millert 687: bp->block = -1;
1.8 shadchin 688: bn = &bp->node;
689:
690: BUF_INS_TAIL(bn);
691: BUF_HASH_INS(bn, 0);
1.1 etheisen 692: return (0);
693: }
694:
695: /*
1.4 millert 696: *
697: */
1.11 nicm 698: static void
699: init_hashtbl(void)
1.4 millert 700: {
1.11 nicm 701: int h;
1.4 millert 702:
1.11 nicm 703: for (h = 0; h < BUFHASH_SIZE; h++) {
1.8 shadchin 704: thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h);
705: thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h);
1.4 millert 706: }
707: }
708:
709: /*
1.1 etheisen 710: * Delete all buffers for this file.
711: */
1.11 nicm 712: static void
713: ch_delbufs(void)
1.1 etheisen 714: {
1.11 nicm 715: struct bufnode *bn;
1.1 etheisen 716:
1.11 nicm 717: while (ch_bufhead != END_OF_CHAIN) {
1.8 shadchin 718: bn = ch_bufhead;
719: BUF_RM(bn);
720: free(bufnode_buf(bn));
1.1 etheisen 721: }
722: ch_nbufs = 0;
1.4 millert 723: init_hashtbl();
1.1 etheisen 724: }
725:
726: /*
727: * Is it possible to seek on a file descriptor?
728: */
1.11 nicm 729: int
730: seekable(int f)
731: {
1.15 deraadt 732: return (lseek(f, (off_t)1, SEEK_SET) != (off_t)-1);
1.1 etheisen 733: }
1.10 shadchin 734:
735: /*
736: * Force EOF to be at the current read position.
737: * This is used after an ignore_eof read, during which the EOF may change.
738: */
1.11 nicm 739: void
740: ch_set_eof(void)
1.10 shadchin 741: {
742: ch_fsize = ch_fpos;
743: }
744:
1.1 etheisen 745:
746: /*
747: * Initialize file state for a new file.
748: */
1.11 nicm 749: void
750: ch_init(int f, int flags)
1.1 etheisen 751: {
752: /*
753: * See if we already have a filestate for this file.
754: */
1.11 nicm 755: thisfile = get_filestate(curr_ifile);
756: if (thisfile == NULL) {
1.1 etheisen 757: /*
758: * Allocate and initialize a new filestate.
759: */
1.11 nicm 760: thisfile = calloc(1, sizeof (struct filestate));
1.8 shadchin 761: thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN;
1.1 etheisen 762: thisfile->nbufs = 0;
763: thisfile->flags = 0;
764: thisfile->fpos = 0;
765: thisfile->block = 0;
766: thisfile->offset = 0;
767: thisfile->file = -1;
1.11 nicm 768: thisfile->fsize = -1;
1.1 etheisen 769: ch_flags = flags;
1.4 millert 770: init_hashtbl();
1.1 etheisen 771: /*
772: * Try to seek; set CH_CANSEEK if it works.
773: */
1.4 millert 774: if ((flags & CH_CANSEEK) && !seekable(f))
775: ch_flags &= ~CH_CANSEEK;
1.1 etheisen 776: set_filestate(curr_ifile, (void *) thisfile);
777: }
778: if (thisfile->file == -1)
779: thisfile->file = f;
780: ch_flush();
781: }
782:
783: /*
784: * Close a filestate.
785: */
1.11 nicm 786: void
787: ch_close(void)
1.1 etheisen 788: {
789: int keepstate = FALSE;
790:
1.8 shadchin 791: if (thisfile == NULL)
792: return;
793:
1.11 nicm 794: if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) {
1.1 etheisen 795: /*
796: * We can seek or re-open, so we don't need to keep buffers.
797: */
798: ch_delbufs();
1.11 nicm 799: } else {
1.1 etheisen 800: keepstate = TRUE;
1.11 nicm 801: }
802: if (!(ch_flags & CH_KEEPOPEN)) {
1.1 etheisen 803: /*
804: * We don't need to keep the file descriptor open
805: * (because we can re-open it.)
806: * But don't really close it if it was opened via popen(),
807: * because pclose() wants to close it.
808: */
1.5 millert 809: if (!(ch_flags & CH_POPENED))
1.1 etheisen 810: close(ch_file);
811: ch_file = -1;
1.11 nicm 812: } else {
1.1 etheisen 813: keepstate = TRUE;
1.11 nicm 814: }
815: if (!keepstate) {
1.1 etheisen 816: /*
817: * We don't even need to keep the filestate structure.
818: */
819: free(thisfile);
820: thisfile = NULL;
1.11 nicm 821: set_filestate(curr_ifile, NULL);
1.1 etheisen 822: }
823: }
824:
825: /*
826: * Return ch_flags for the current file.
827: */
1.11 nicm 828: int
829: ch_getflags(void)
1.1 etheisen 830: {
1.8 shadchin 831: if (thisfile == NULL)
832: return (0);
1.1 etheisen 833: return (ch_flags);
834: }