* Low level character input from the input file.
* We use these special purpose routines which optimize moving
* both forward and backward from the current read pointer.
public int file
= -1; /* File descriptor of the input file */
* Pool of buffers holding the most recently used blocks of the input file.
struct buf
*next
, *prev
; /* Must be first to match struct filestate */
unsigned char data
[BUFSIZ
];
* The buffer pool is kept as a doubly-linked circular list,
* in order from most- to least-recently used.
* The circular list is anchored by the file state "thisfile".
* The file state is maintained in a filestate structure.
* There are two such structures, one used when input is a pipe
* and the other when input is an ordinary file.
* This is so that we can leave a pipe, look and other files,
* and return to the pipe without losing buffered data.
* Buffered data can be reconstructed for a non-pipe file by
* simply re-reading the file, but a pipe cannot be re-read.
struct buf
*next
, *prev
; /* Must be first to match struct buf */
#define END_OF_CHAIN ((struct buf *)thisfile)
#define buf_head thisfile->next
#define buf_tail thisfile->prev
#define ch_nbufs thisfile->nbufs
#define ch_block thisfile->block
#define ch_offset thisfile->offset
#define ch_fpos thisfile->fpos
#define ch_fsize thisfile->fsize
static struct filestate pipefile
=
{ (struct buf
*)&pipefile
, (struct buf
*)&pipefile
};
static struct filestate nonpipefile
=
{ (struct buf
*)&nonpipefile
, (struct buf
*)&nonpipefile
};
static struct filestate
*thisfile
;
extern char *namelogfile
;
* Get the character pointed to by the read pointer.
* ch_get() is a macro which is more efficient to call
* than fch_get (the function), in the usual case
* that the block desired is at the head of the chain.
#define ch_get() ((ch_block == buf_head->block && \
ch_offset < buf_head->datasize) ? \
buf_head->data[ch_offset] : fch_get())
* Look for a buffer holding the desired block.
for (bp
= buf_head
; bp
!= END_OF_CHAIN
; bp
= bp
->next
)
if (bp
->block
== ch_block
)
if (ch_offset
>= bp
->datasize
)
* Need more data in this buffer.
* Block is not in a buffer.
* Take the least recently used buffer
* and read the desired block into it.
* If the LRU buffer has data in it,
* and autobuf is true, and input is a pipe,
* then try to allocate a new buffer first.
if (autobuf
&& ispipe
&& buf_tail
->block
!= (long)(-1))
* Allocation failed: turn off autobuf.
pos
= (ch_block
* BUFSIZ
) + bp
->datasize
;
if ((len
= ch_length()) != NULL_POSITION
&& pos
>= len
)
* Not at the correct position: must seek.
* If input is a pipe, we're in trouble (can't seek on a pipe).
* Some data has been lost: just return "?".
if (lseek(file
, (offset_t
)pos
, 0) == BAD_LSEEK
)
error("seek error", NULL_PARG
);
* If we read less than a full block, that's ok.
* We use partial block and pick up the rest next time.
n
= iread(file
, &bp
->data
[bp
->datasize
],
(unsigned int)(BUFSIZ
- bp
->datasize
));
error("read error", NULL_PARG
);
* If we have a log file, write the new data to it.
if (logfile
>= 0 && n
> 0)
write(logfile
, (char *) &bp
->data
[bp
->datasize
], n
);
* If we have read to end of file, set ch_fsize to indicate
* the position of the end of file.
* Wait a while, then try again.
ierror("Waiting for data", NULL_PARG
);
* Move the buffer to the head of the buffer chain.
* This orders the buffer chain, most- to least-recently used.
bp
->next
->prev
= bp
->prev
;
bp
->prev
->next
= bp
->next
;
if (ch_offset
>= bp
->datasize
)
* After all that, we still don't have enough data.
return (bp
->data
[ch_offset
]);
* If we haven't read all of standard input into it, do that now.
if (!tried
&& ch_fsize
== NULL_POSITION
)
ierror("Finishing logfile", NULL_PARG
);
while (ch_forw_get() != EOI
)
* Start a log file AFTER less has already been running.
* Invoked from the - command; see toggle_option().
* Write all the existing buffered data to the log file.
last_block
= (ch_fpos
+ BUFSIZ
- 1) / BUFSIZ
;
for (block
= 0; block
<= last_block
; block
++)
for (bp
= buf_head
; bp
!= END_OF_CHAIN
; bp
= bp
->next
)
write(logfile
, (char *) bp
->data
, bp
->datasize
);
* Determine if a specific block is currently in one of the buffers.
for (bp
= buf_head
; bp
!= END_OF_CHAIN
; bp
= bp
->next
)
* Seek to a specified position in the file.
* Return 0 if successful, non-zero if can't seek there.
if (pos
< ch_zero() || (len
!= NULL_POSITION
&& pos
> len
))
new_block
= pos
/ BUFSIZ
;
if (ispipe
&& pos
!= ch_fpos
&& !buffered(new_block
))
ch_offset
= pos
% BUFSIZ
;
* Seek to the end of the file.
ch_fsize
= filesize(file
);
if (len
!= NULL_POSITION
)
* Do it the slow way: read till end of data.
while (ch_forw_get() != EOI
)
* Seek to the beginning of the file, or as close to it as we can get.
* We may not be able to seek there if input is a pipe and the
* beginning of the pipe is no longer buffered.
register struct buf
*bp
, *firstbp
;
* Try a plain ch_seek first.
if (ch_seek(ch_zero()) == 0)
* Can't get to position 0.
* Look thru the buffers for the one closest to position 0.
while ((bp
= bp
->next
) != END_OF_CHAIN
)
if (bp
->block
< firstbp
->block
)
ch_block
= firstbp
->block
;
* Return the length of the file, if known.
* Return the current position in the file.
#define tellpos(blk,off) ((POSITION)((((long)(blk)) * BUFSIZ) + (off)))
return (tellpos(ch_block
, ch_offset
));
* Get the current char and post-increment the read pointer.
if (ch_offset
< BUFSIZ
-1)
#if __ZOFFSET /* NOT WORKING */
if (ch_fsize
!= NULL_POSITION
&&
tellpos(ch_block
+1, 0) >= ch_fsize
)
* Pre-decrement the read pointer and get the new current char.
#if __ZOFFSET /* NOT WORKING */
if (tellpos(ch_block
-1, BUFSIZ
-1) < ch_zero())
if (ispipe
&& !buffered(ch_block
-1))
* Caller wants us to have a total of at least want_nbufs buffers.
if (ch_nbufs
< want_nbufs
&& ch_addbuf(want_nbufs
- ch_nbufs
))
* Cannot allocate enough buffers.
* If we don't have ANY, then quit.
* Otherwise, just report the error and return.
parg
.p_int
= want_nbufs
- ch_nbufs
;
error("Cannot allocate %d buffers", &parg
);
* Flush any saved file state, including buffer contents.
* If input is a pipe, we don't flush buffer contents,
* since the contents can't be recovered.
ch_fsize
= NULL_POSITION
;
* Initialize all the buffers.
for (bp
= buf_head
; bp
!= END_OF_CHAIN
; bp
= bp
->next
)
* Figure out the size of the file, if we can.
ch_fsize
= filesize(file
);
* Seek to a known position: the beginning of the file.
ch_block
= ch_fpos
/ BUFSIZ
;
ch_offset
= ch_fpos
% BUFSIZ
;
if (lseek(file
, (offset_t
)0, 0) == BAD_LSEEK
)
* Warning only; even if the seek fails for some reason,
* there's a good chance we're at the beginning anyway.
* {{ I think this is bogus reasoning. }}
error("seek error to 0", NULL_PARG
);
* Allocate some new buffers.
* The buffers are added to the tail of the buffer chain.
register struct buf
*newbufs
;
* We don't have enough buffers.
* Allocate some new ones.
newbufs
= (struct buf
*) calloc(nnew
, sizeof(struct buf
));
* Initialize the new buffers and link them together.
* Link them all onto the tail of the buffer list.
for (bp
= &newbufs
[0]; bp
< &newbufs
[nnew
]; bp
++)
newbufs
[nnew
-1].next
= END_OF_CHAIN
;
newbufs
[0].prev
= buf_tail
;
buf_tail
->next
= &newbufs
[0];
buf_tail
= &newbufs
[nnew
-1];
* Use the pipe file state.
* Use the non-pipe file state.