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