Annotation of src/usr.bin/less/ch.c, Revision 1.21
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.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: }