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