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