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