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