version 1.10, 2014/04/25 13:38:21 |
version 1.11, 2015/11/05 22:08:43 |
|
|
* |
* |
* For more information, see the README file. |
* For more information, see the README file. |
*/ |
*/ |
|
/* |
|
* Modified for use with illumos. |
|
* Copyright 2014 Garrett D'Amore <garrett@damore.org> |
|
*/ |
|
|
|
|
/* |
/* |
* Low level character input from the input file. |
* Low level character input from the input file. |
* We use these special purpose routines which optimize moving |
* We use these special purpose routines which optimize moving |
|
|
*/ |
*/ |
|
|
#include "less.h" |
#include "less.h" |
#if MSDOS_COMPILER==WIN32C |
|
#include <errno.h> |
|
#include <windows.h> |
|
#endif |
|
|
|
#if HAVE_STAT_INO |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
extern dev_t curr_dev; |
extern dev_t curr_dev; |
extern ino_t curr_ino; |
extern ino_t curr_ino; |
#endif |
extern int less_is_more; |
|
|
typedef POSITION BLOCKNUM; |
typedef off_t BLOCKNUM; |
|
|
public int ignore_eoi; |
int ignore_eoi; |
|
|
/* |
/* |
* Pool of buffers holding the most recently used blocks of the input file. |
* Pool of buffers holding the most recently used blocks of the input file. |
|
|
unsigned int datasize; |
unsigned int datasize; |
unsigned char data[LBUFSIZE]; |
unsigned char data[LBUFSIZE]; |
}; |
}; |
#define bufnode_buf(bn) ((struct buf *) bn) |
#define bufnode_buf(bn) ((struct buf *)bn) |
|
|
/* |
/* |
* The file state is maintained in a filestate structure. |
* The file state is maintained in a filestate structure. |
|
|
struct bufnode hashtbl[BUFHASH_SIZE]; |
struct bufnode hashtbl[BUFHASH_SIZE]; |
int file; |
int file; |
int flags; |
int flags; |
POSITION fpos; |
off_t fpos; |
int nbufs; |
int nbufs; |
BLOCKNUM block; |
BLOCKNUM block; |
unsigned int offset; |
unsigned int offset; |
POSITION fsize; |
off_t fsize; |
}; |
}; |
|
|
#define ch_bufhead thisfile->buflist.next |
#define ch_bufhead thisfile->buflist.next |
|
|
|
|
#define END_OF_CHAIN (&thisfile->buflist) |
#define END_OF_CHAIN (&thisfile->buflist) |
#define END_OF_HCHAIN(h) (&thisfile->hashtbl[h]) |
#define END_OF_HCHAIN(h) (&thisfile->hashtbl[h]) |
#define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) |
#define BUFHASH(blk) ((blk) & (BUFHASH_SIZE-1)) |
|
|
/* |
/* |
* Macros to manipulate the list of buffers in thisfile->buflist. |
* Macros to manipulate the list of buffers in thisfile->buflist. |
|
|
#define FOR_BUFS(bn) \ |
#define FOR_BUFS(bn) \ |
for (bn = ch_bufhead; bn != END_OF_CHAIN; bn = bn->next) |
for (bn = ch_bufhead; bn != END_OF_CHAIN; bn = bn->next) |
|
|
#define BUF_RM(bn) \ |
#define BUF_RM(bn) \ |
(bn)->next->prev = (bn)->prev; \ |
(bn)->next->prev = (bn)->prev; \ |
(bn)->prev->next = (bn)->next; |
(bn)->prev->next = (bn)->next; |
|
|
#define BUF_INS_HEAD(bn) \ |
#define BUF_INS_HEAD(bn) \ |
(bn)->next = ch_bufhead; \ |
(bn)->next = ch_bufhead; \ |
(bn)->prev = END_OF_CHAIN; \ |
(bn)->prev = END_OF_CHAIN; \ |
ch_bufhead->prev = (bn); \ |
ch_bufhead->prev = (bn); \ |
ch_bufhead = (bn); |
ch_bufhead = (bn); |
|
|
#define BUF_INS_TAIL(bn) \ |
#define BUF_INS_TAIL(bn) \ |
(bn)->next = END_OF_CHAIN; \ |
(bn)->next = END_OF_CHAIN; \ |
(bn)->prev = ch_buftail; \ |
(bn)->prev = ch_buftail; \ |
ch_buftail->next = (bn); \ |
ch_buftail->next = (bn); \ |
|
|
/* |
/* |
* Macros to manipulate the list of buffers in thisfile->hashtbl[n]. |
* Macros to manipulate the list of buffers in thisfile->hashtbl[n]. |
*/ |
*/ |
#define FOR_BUFS_IN_CHAIN(h,bn) \ |
#define FOR_BUFS_IN_CHAIN(h, bn) \ |
for (bn = thisfile->hashtbl[h].hnext; \ |
for (bn = thisfile->hashtbl[h].hnext; \ |
bn != END_OF_HCHAIN(h); bn = bn->hnext) |
bn != END_OF_HCHAIN(h); bn = bn->hnext) |
|
|
#define BUF_HASH_RM(bn) \ |
#define BUF_HASH_RM(bn) \ |
(bn)->hnext->hprev = (bn)->hprev; \ |
(bn)->hnext->hprev = (bn)->hprev; \ |
(bn)->hprev->hnext = (bn)->hnext; |
(bn)->hprev->hnext = (bn)->hnext; |
|
|
#define BUF_HASH_INS(bn,h) \ |
#define BUF_HASH_INS(bn, h) \ |
(bn)->hnext = thisfile->hashtbl[h].hnext; \ |
(bn)->hnext = thisfile->hashtbl[h].hnext; \ |
(bn)->hprev = END_OF_HCHAIN(h); \ |
(bn)->hprev = END_OF_HCHAIN(h); \ |
thisfile->hashtbl[h].hnext->hprev = (bn); \ |
thisfile->hashtbl[h].hnext->hprev = (bn); \ |
|
|
extern int screen_trashed; |
extern int screen_trashed; |
extern int follow_mode; |
extern int follow_mode; |
extern IFILE curr_ifile; |
extern IFILE curr_ifile; |
#if LOGFILE |
|
extern int logfile; |
extern int logfile; |
extern char *namelogfile; |
extern char *namelogfile; |
#endif |
|
|
|
static int ch_addbuf(); |
static int ch_addbuf(void); |
|
|
|
|
/* |
/* |
* Get the character pointed to by the read pointer. |
* Get the character pointed to by the read pointer. |
*/ |
*/ |
int |
int |
ch_get() |
ch_get(void) |
{ |
{ |
register struct buf *bp; |
struct buf *bp; |
register struct bufnode *bn; |
struct bufnode *bn; |
register int n; |
int n; |
register int slept; |
int slept; |
register int h; |
int h; |
POSITION pos; |
off_t pos; |
POSITION len; |
off_t len; |
|
|
if (thisfile == NULL) |
if (thisfile == NULL) |
return (EOI); |
return (EOI); |
|
|
/* |
/* |
* Quick check for the common case where |
* Quick check for the common case where |
* the desired char is in the head buffer. |
* the desired char is in the head buffer. |
*/ |
*/ |
if (ch_bufhead != END_OF_CHAIN) |
if (ch_bufhead != END_OF_CHAIN) { |
{ |
|
bp = bufnode_buf(ch_bufhead); |
bp = bufnode_buf(ch_bufhead); |
if (ch_block == bp->block && ch_offset < bp->datasize) |
if (ch_block == bp->block && ch_offset < bp->datasize) |
return bp->data[ch_offset]; |
return (bp->data[ch_offset]); |
} |
} |
|
|
slept = FALSE; |
slept = FALSE; |
|
|
* Look for a buffer holding the desired block. |
* Look for a buffer holding the desired block. |
*/ |
*/ |
h = BUFHASH(ch_block); |
h = BUFHASH(ch_block); |
FOR_BUFS_IN_CHAIN(h, bn) |
FOR_BUFS_IN_CHAIN(h, bn) { |
{ |
|
bp = bufnode_buf(bn); |
bp = bufnode_buf(bn); |
if (bp->block == ch_block) |
if (bp->block == ch_block) { |
{ |
|
if (ch_offset >= bp->datasize) |
if (ch_offset >= bp->datasize) |
/* |
/* |
* Need more data in this buffer. |
* Need more data in this buffer. |
|
|
goto found; |
goto found; |
} |
} |
} |
} |
if (bn == END_OF_HCHAIN(h)) |
if (bn == END_OF_HCHAIN(h)) { |
{ |
|
/* |
/* |
* Block is not in a buffer. |
* Block is not in a buffer. |
* Take the least recently used buffer |
* Take the least recently used buffer |
* and read the desired block into it. |
* and read the desired block into it. |
* If the LRU buffer has data in it, |
* If the LRU buffer has data in it, |
* then maybe allocate a new buffer. |
* then maybe allocate a new buffer. |
*/ |
*/ |
if (ch_buftail == END_OF_CHAIN || |
if (ch_buftail == END_OF_CHAIN || |
bufnode_buf(ch_buftail)->block != -1) |
bufnode_buf(ch_buftail)->block != -1) { |
{ |
|
/* |
/* |
* There is no empty buffer to use. |
* There is no empty buffer to use. |
* Allocate a new buffer if: |
* Allocate a new buffer if: |
* 1. We can't seek on this file and -b is not in effect; or |
* 1. We can't seek on this file and -b is not in |
* 2. We haven't allocated the max buffers for this file yet. |
* effect; or |
|
* 2. We haven't allocated the max buffers for this |
|
* file yet. |
*/ |
*/ |
if ((autobuf && !(ch_flags & CH_CANSEEK)) || |
if ((autobuf && !(ch_flags & CH_CANSEEK)) || |
(maxbufs < 0 || ch_nbufs < maxbufs)) |
(maxbufs < 0 || ch_nbufs < maxbufs)) |
if (ch_addbuf()) |
if (ch_addbuf()) |
/* |
/* |
* Allocation failed: turn off autobuf. |
* Allocation failed: turn off autobuf. |
|
|
BUF_HASH_INS(bn, h); /* Insert into new hash chain. */ |
BUF_HASH_INS(bn, h); /* Insert into new hash chain. */ |
} |
} |
|
|
read_more: |
read_more: |
pos = (ch_block * LBUFSIZE) + bp->datasize; |
pos = (ch_block * LBUFSIZE) + bp->datasize; |
if ((len = ch_length()) != NULL_POSITION && pos >= len) |
if ((len = ch_length()) != -1 && pos >= len) |
/* |
/* |
* At end of file. |
* At end of file. |
*/ |
*/ |
return (EOI); |
return (EOI); |
|
|
if (pos != ch_fpos) |
if (pos != ch_fpos) { |
{ |
|
/* |
/* |
* Not at the correct position: must seek. |
* Not at the correct position: must seek. |
* If input is a pipe, we're in trouble (can't seek on a pipe). |
* If input is a pipe, we're in trouble (can't seek on a pipe). |
|
|
*/ |
*/ |
if (!(ch_flags & CH_CANSEEK)) |
if (!(ch_flags & CH_CANSEEK)) |
return ('?'); |
return ('?'); |
if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK) |
if (lseek(ch_file, (off_t)pos, SEEK_SET) == BAD_LSEEK) { |
{ |
error("seek error", NULL_PARG); |
error("seek error", NULL_PARG); |
|
clear_eol(); |
clear_eol(); |
return (EOI); |
return (EOI); |
} |
} |
ch_fpos = pos; |
ch_fpos = pos; |
} |
} |
|
|
/* |
/* |
* Read the block. |
* Read the block. |
* If we read less than a full block, that's ok. |
* If we read less than a full block, that's ok. |
* We use partial block and pick up the rest next time. |
* We use partial block and pick up the rest next time. |
*/ |
*/ |
if (ch_ungotchar != -1) |
if (ch_ungotchar != -1) { |
{ |
bp->data[bp->datasize] = (unsigned char)ch_ungotchar; |
bp->data[bp->datasize] = ch_ungotchar; |
|
n = 1; |
n = 1; |
ch_ungotchar = -1; |
ch_ungotchar = -1; |
} else |
} else { |
{ |
n = iread(ch_file, &bp->data[bp->datasize], |
n = iread(ch_file, &bp->data[bp->datasize], |
(unsigned int)(LBUFSIZE - bp->datasize)); |
(unsigned int)(LBUFSIZE - bp->datasize)); |
|
} |
} |
|
|
if (n == READ_INTR) |
if (n == READ_INTR) |
return (EOI); |
return (EOI); |
if (n < 0) |
if (n < 0) { |
{ |
error("read error", NULL_PARG); |
#if MSDOS_COMPILER==WIN32C |
clear_eol(); |
if (errno != EPIPE) |
|
#endif |
|
{ |
|
error("read error", NULL_PARG); |
|
clear_eol(); |
|
} |
|
n = 0; |
n = 0; |
} |
} |
|
|
#if LOGFILE |
|
/* |
/* |
* If we have a log file, write the new data to it. |
* If we have a log file, write the new data to it. |
*/ |
*/ |
if (!secure && logfile >= 0 && n > 0) |
if (!secure && logfile >= 0 && n > 0) |
write(logfile, (char *) &bp->data[bp->datasize], n); |
(void) write(logfile, (char *)&bp->data[bp->datasize], n); |
#endif |
|
|
|
ch_fpos += n; |
ch_fpos += n; |
bp->datasize += n; |
bp->datasize += n; |
|
|
* If we have read to end of file, set ch_fsize to indicate |
* If we have read to end of file, set ch_fsize to indicate |
* the position of the end of file. |
* the position of the end of file. |
*/ |
*/ |
if (n == 0) |
if (n == 0) { |
{ |
|
ch_fsize = pos; |
ch_fsize = pos; |
if (ignore_eoi) |
if (ignore_eoi) { |
{ |
|
/* |
/* |
* We are ignoring EOF. |
* We are ignoring EOF. |
* Wait a while, then try again. |
* Wait a while, then try again. |
*/ |
*/ |
if (!slept) |
if (!slept) { |
{ |
|
PARG parg; |
PARG parg; |
parg.p_string = wait_message(); |
parg.p_string = wait_message(); |
ierror("%s", &parg); |
ierror("%s", &parg); |
} |
} |
#if !MSDOS_COMPILER |
sleep(1); |
sleep(1); |
|
#else |
|
#if MSDOS_COMPILER==WIN32C |
|
Sleep(1000); |
|
#endif |
|
#endif |
|
slept = TRUE; |
slept = TRUE; |
|
|
#if HAVE_STAT_INO |
if (follow_mode == FOLLOW_NAME) { |
if (follow_mode == FOLLOW_NAME) |
/* |
{ |
* See whether the file's i-number has changed. |
/* See whether the file's i-number has changed. |
|
* If so, force the file to be closed and |
* If so, force the file to be closed and |
* reopened. */ |
* reopened. |
|
*/ |
struct stat st; |
struct stat st; |
int r = stat(get_filename(curr_ifile), &st); |
int r = stat(get_filename(curr_ifile), &st); |
if (r == 0 && (st.st_ino != curr_ino || |
if (r == 0 && (st.st_ino != curr_ino || |
st.st_dev != curr_dev)) |
st.st_dev != curr_dev)) { |
{ |
/* |
/* screen_trashed=2 causes |
* screen_trashed=2 causes |
* make_display to reopen the file. */ |
* make_display to reopen the file. |
|
*/ |
screen_trashed = 2; |
screen_trashed = 2; |
return (EOI); |
return (EOI); |
} |
} |
} |
} |
#endif |
|
} |
} |
if (sigs) |
if (sigs) |
return (EOI); |
return (EOI); |
} |
} |
|
|
found: |
found: |
if (ch_bufhead != bn) |
if (ch_bufhead != bn) { |
{ |
|
/* |
/* |
* Move the buffer to the head of the buffer chain. |
* Move the buffer to the head of the buffer chain. |
* This orders the buffer chain, most- to least-recently used. |
* This orders the buffer chain, most- to least-recently used. |
|
|
} |
} |
|
|
/* |
/* |
* ch_ungetchar is a rather kludgy and limited way to push |
* ch_ungetchar is a rather kludgy and limited way to push |
* a single char onto an input file descriptor. |
* a single char onto an input file descriptor. |
*/ |
*/ |
public void |
void |
ch_ungetchar(c) |
ch_ungetchar(int c) |
int c; |
|
{ |
{ |
if (c != -1 && ch_ungotchar != -1) |
if (c != -1 && ch_ungotchar != -1) |
error("ch_ungetchar overrun", NULL_PARG); |
error("ch_ungetchar overrun", NULL_PARG); |
ch_ungotchar = c; |
ch_ungotchar = c; |
} |
} |
|
|
#if LOGFILE |
|
/* |
/* |
* Close the logfile. |
* Close the logfile. |
* If we haven't read all of standard input into it, do that now. |
* If we haven't read all of standard input into it, do that now. |
*/ |
*/ |
public void |
void |
end_logfile() |
end_logfile(void) |
{ |
{ |
static int tried = FALSE; |
static int tried = FALSE; |
|
|
if (logfile < 0) |
if (logfile < 0) |
return; |
return; |
if (!tried && ch_fsize == NULL_POSITION) |
if (!tried && ch_fsize == -1) { |
{ |
|
tried = TRUE; |
tried = TRUE; |
ierror("Finishing logfile", NULL_PARG); |
ierror("Finishing logfile", NULL_PARG); |
while (ch_forw_get() != EOI) |
while (ch_forw_get() != EOI) |
|
|
* Invoked from the - command; see toggle_option(). |
* Invoked from the - command; see toggle_option(). |
* Write all the existing buffered data to the log file. |
* Write all the existing buffered data to the log file. |
*/ |
*/ |
public void |
void |
sync_logfile() |
sync_logfile(void) |
{ |
{ |
register struct buf *bp; |
struct buf *bp; |
register struct bufnode *bn; |
struct bufnode *bn; |
int warned = FALSE; |
int warned = FALSE; |
BLOCKNUM block; |
BLOCKNUM block; |
BLOCKNUM nblocks; |
BLOCKNUM nblocks; |
|
|
nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; |
nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE; |
for (block = 0; block < nblocks; block++) |
for (block = 0; block < nblocks; block++) { |
{ |
|
int wrote = FALSE; |
int wrote = FALSE; |
FOR_BUFS(bn) |
FOR_BUFS(bn) { |
{ |
|
bp = bufnode_buf(bn); |
bp = bufnode_buf(bn); |
if (bp->block == block) |
if (bp->block == block) { |
{ |
(void) write(logfile, (char *)bp->data, |
write(logfile, (char *) bp->data, bp->datasize); |
bp->datasize); |
wrote = TRUE; |
wrote = TRUE; |
break; |
break; |
} |
} |
} |
} |
if (!wrote && !warned) |
if (!wrote && !warned) { |
{ |
error("Warning: log file is incomplete", NULL_PARG); |
error("Warning: log file is incomplete", |
|
NULL_PARG); |
|
warned = TRUE; |
warned = TRUE; |
} |
} |
} |
} |
} |
} |
|
|
#endif |
|
|
|
/* |
/* |
* Determine if a specific block is currently in one of the buffers. |
* Determine if a specific block is currently in one of the buffers. |
*/ |
*/ |
static int |
static int |
buffered(block) |
buffered(BLOCKNUM block) |
BLOCKNUM block; |
|
{ |
{ |
register struct buf *bp; |
register struct buf *bp; |
register struct bufnode *bn; |
register struct bufnode *bn; |
register int h; |
register int h; |
|
|
h = BUFHASH(block); |
h = BUFHASH(block); |
FOR_BUFS_IN_CHAIN(h, bn) |
FOR_BUFS_IN_CHAIN(h, bn) { |
{ |
|
bp = bufnode_buf(bn); |
bp = bufnode_buf(bn); |
if (bp->block == block) |
if (bp->block == block) |
return (TRUE); |
return (TRUE); |
|
|
* Seek to a specified position in the file. |
* Seek to a specified position in the file. |
* Return 0 if successful, non-zero if can't seek there. |
* Return 0 if successful, non-zero if can't seek there. |
*/ |
*/ |
public int |
int |
ch_seek(pos) |
ch_seek(off_t pos) |
register POSITION pos; |
|
{ |
{ |
BLOCKNUM new_block; |
BLOCKNUM new_block; |
POSITION len; |
off_t len; |
|
|
if (thisfile == NULL) |
if (thisfile == NULL) |
return (0); |
return (0); |
|
|
len = ch_length(); |
len = ch_length(); |
if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) |
if (pos < ch_zero() || (len != -1 && pos > len)) |
return (1); |
return (1); |
|
|
new_block = pos / LBUFSIZE; |
new_block = pos / LBUFSIZE; |
if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && !buffered(new_block)) |
if (!(ch_flags & CH_CANSEEK) && pos != ch_fpos && |
{ |
!buffered(new_block)) { |
if (ch_fpos > pos) |
if (ch_fpos > pos) |
return (1); |
return (1); |
while (ch_fpos < pos) |
while (ch_fpos < pos) { |
{ |
|
if (ch_forw_get() == EOI) |
if (ch_forw_get() == EOI) |
return (1); |
return (1); |
if (ABORT_SIGS()) |
if (ABORT_SIGS()) |
|
|
/* |
/* |
* Seek to the end of the file. |
* Seek to the end of the file. |
*/ |
*/ |
public int |
int |
ch_end_seek() |
ch_end_seek(void) |
{ |
{ |
POSITION len; |
off_t len; |
|
|
if (thisfile == NULL) |
if (thisfile == NULL) |
return (0); |
return (0); |
|
|
ch_fsize = filesize(ch_file); |
ch_fsize = filesize(ch_file); |
|
|
len = ch_length(); |
len = ch_length(); |
if (len != NULL_POSITION) |
if (len != -1) |
return (ch_seek(len)); |
return (ch_seek(len)); |
|
|
/* |
/* |
|
|
* We may not be able to seek there if input is a pipe and the |
* We may not be able to seek there if input is a pipe and the |
* beginning of the pipe is no longer buffered. |
* beginning of the pipe is no longer buffered. |
*/ |
*/ |
public int |
int |
ch_beg_seek() |
ch_beg_seek(void) |
{ |
{ |
register struct bufnode *bn; |
struct bufnode *bn; |
register struct bufnode *firstbn; |
struct bufnode *firstbn; |
|
|
/* |
/* |
* Try a plain ch_seek first. |
* Try a plain ch_seek first. |
|
|
firstbn = ch_bufhead; |
firstbn = ch_bufhead; |
if (firstbn == END_OF_CHAIN) |
if (firstbn == END_OF_CHAIN) |
return (1); |
return (1); |
FOR_BUFS(bn) |
FOR_BUFS(bn) { |
{ |
|
if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) |
if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) |
firstbn = bn; |
firstbn = bn; |
} |
} |
|
|
/* |
/* |
* Return the length of the file, if known. |
* Return the length of the file, if known. |
*/ |
*/ |
public POSITION |
off_t |
ch_length() |
ch_length(void) |
{ |
{ |
if (thisfile == NULL) |
if (thisfile == NULL) |
return (NULL_POSITION); |
return (-1); |
if (ignore_eoi) |
if (ignore_eoi) |
return (NULL_POSITION); |
return (-1); |
if (ch_flags & CH_NODATA) |
if (ch_flags & CH_NODATA) |
return (0); |
return (0); |
return (ch_fsize); |
return (ch_fsize); |
|
|
/* |
/* |
* Return the current position in the file. |
* Return the current position in the file. |
*/ |
*/ |
public POSITION |
off_t |
ch_tell() |
ch_tell(void) |
{ |
{ |
if (thisfile == NULL) |
if (thisfile == NULL) |
return (NULL_POSITION); |
return (-1); |
return (ch_block * LBUFSIZE) + ch_offset; |
return ((ch_block * LBUFSIZE) + ch_offset); |
} |
} |
|
|
/* |
/* |
* Get the current char and post-increment the read pointer. |
* Get the current char and post-increment the read pointer. |
*/ |
*/ |
public int |
int |
ch_forw_get() |
ch_forw_get(void) |
{ |
{ |
register int c; |
int c; |
|
|
if (thisfile == NULL) |
if (thisfile == NULL) |
return (EOI); |
return (EOI); |
c = ch_get(); |
c = ch_get(); |
if (c == EOI) |
if (c == EOI) |
return (EOI); |
return (EOI); |
if (ch_offset < LBUFSIZE-1) |
if (ch_offset < LBUFSIZE-1) { |
ch_offset++; |
ch_offset++; |
else |
} else { |
{ |
|
ch_block ++; |
ch_block ++; |
ch_offset = 0; |
ch_offset = 0; |
} |
} |
|
|
/* |
/* |
* Pre-decrement the read pointer and get the new current char. |
* Pre-decrement the read pointer and get the new current char. |
*/ |
*/ |
public int |
int |
ch_back_get() |
ch_back_get(void) |
{ |
{ |
if (thisfile == NULL) |
if (thisfile == NULL) |
return (EOI); |
return (EOI); |
if (ch_offset > 0) |
if (ch_offset > 0) { |
ch_offset --; |
ch_offset --; |
else |
} else { |
{ |
|
if (ch_block <= 0) |
if (ch_block <= 0) |
return (EOI); |
return (EOI); |
if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) |
if (!(ch_flags & CH_CANSEEK) && !buffered(ch_block-1)) |
|
|
* Set max amount of buffer space. |
* Set max amount of buffer space. |
* bufspace is in units of 1024 bytes. -1 mean no limit. |
* bufspace is in units of 1024 bytes. -1 mean no limit. |
*/ |
*/ |
public void |
void |
ch_setbufspace(bufspace) |
ch_setbufspace(int bufspace) |
int bufspace; |
|
{ |
{ |
if (bufspace < 0) |
if (bufspace < 0) { |
maxbufs = -1; |
maxbufs = -1; |
else |
} else { |
{ |
|
maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; |
maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE; |
if (maxbufs < 1) |
if (maxbufs < 1) |
maxbufs = 1; |
maxbufs = 1; |
|
|
/* |
/* |
* Flush (discard) any saved file state, including buffer contents. |
* Flush (discard) any saved file state, including buffer contents. |
*/ |
*/ |
public void |
void |
ch_flush() |
ch_flush(void) |
{ |
{ |
register struct bufnode *bn; |
struct bufnode *bn; |
|
|
if (thisfile == NULL) |
if (thisfile == NULL) |
return; |
return; |
|
|
if (!(ch_flags & CH_CANSEEK)) |
if (!(ch_flags & CH_CANSEEK)) { |
{ |
|
/* |
/* |
* If input is a pipe, we don't flush buffer contents, |
* If input is a pipe, we don't flush buffer contents, |
* since the contents can't be recovered. |
* since the contents can't be recovered. |
*/ |
*/ |
ch_fsize = NULL_POSITION; |
ch_fsize = -1; |
return; |
return; |
} |
} |
|
|
/* |
/* |
* Initialize all the buffers. |
* Initialize all the buffers. |
*/ |
*/ |
FOR_BUFS(bn) |
FOR_BUFS(bn) { |
{ |
|
bufnode_buf(bn)->block = -1; |
bufnode_buf(bn)->block = -1; |
} |
} |
|
|
|
|
#if 1 |
#if 1 |
/* |
/* |
* This is a kludge to workaround a Linux kernel bug: files in |
* This is a kludge to workaround a Linux kernel bug: files in |
* /proc have a size of 0 according to fstat() but have readable |
* /proc have a size of 0 according to fstat() but have readable |
* data. They are sometimes, but not always, seekable. |
* data. They are sometimes, but not always, seekable. |
* Force them to be non-seekable here. |
* Force them to be non-seekable here. |
*/ |
*/ |
if (ch_fsize == 0) |
if (ch_fsize == 0) { |
{ |
ch_fsize = -1; |
ch_fsize = NULL_POSITION; |
|
ch_flags &= ~CH_CANSEEK; |
ch_flags &= ~CH_CANSEEK; |
} |
} |
#endif |
#endif |
|
|
if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) |
if (lseek(ch_file, (off_t)0, SEEK_SET) == BAD_LSEEK) { |
{ |
|
/* |
/* |
* Warning only; even if the seek fails for some reason, |
* Warning only; even if the seek fails for some reason, |
* there's a good chance we're at the beginning anyway. |
* there's a good chance we're at the beginning anyway. |
|
|
* Allocate a new buffer. |
* Allocate a new buffer. |
* The buffer is added to the tail of the buffer chain. |
* The buffer is added to the tail of the buffer chain. |
*/ |
*/ |
static int |
static int |
ch_addbuf() |
ch_addbuf(void) |
{ |
{ |
register struct buf *bp; |
struct buf *bp; |
register struct bufnode *bn; |
struct bufnode *bn; |
|
|
/* |
/* |
* Allocate and initialize a new buffer and link it |
* Allocate and initialize a new buffer and link it |
* onto the tail of the buffer list. |
* onto the tail of the buffer list. |
*/ |
*/ |
bp = (struct buf *) calloc(1, sizeof(struct buf)); |
bp = calloc(1, sizeof (struct buf)); |
if (bp == NULL) |
if (bp == NULL) |
return (1); |
return (1); |
ch_nbufs++; |
ch_nbufs++; |
|
|
/* |
/* |
* |
* |
*/ |
*/ |
static void |
static void |
init_hashtbl() |
init_hashtbl(void) |
{ |
{ |
register int h; |
int h; |
|
|
for (h = 0; h < BUFHASH_SIZE; h++) |
for (h = 0; h < BUFHASH_SIZE; h++) { |
{ |
|
thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); |
thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); |
thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); |
thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); |
} |
} |
|
|
/* |
/* |
* Delete all buffers for this file. |
* Delete all buffers for this file. |
*/ |
*/ |
static void |
static void |
ch_delbufs() |
ch_delbufs(void) |
{ |
{ |
register struct bufnode *bn; |
struct bufnode *bn; |
|
|
while (ch_bufhead != END_OF_CHAIN) |
while (ch_bufhead != END_OF_CHAIN) { |
{ |
|
bn = ch_bufhead; |
bn = ch_bufhead; |
BUF_RM(bn); |
BUF_RM(bn); |
free(bufnode_buf(bn)); |
free(bufnode_buf(bn)); |
|
|
/* |
/* |
* Is it possible to seek on a file descriptor? |
* Is it possible to seek on a file descriptor? |
*/ |
*/ |
public int |
int |
seekable(f) |
seekable(int f) |
int f; |
|
{ |
{ |
#if MSDOS_COMPILER |
|
extern int fd0; |
|
if (f == fd0 && !isatty(fd0)) |
|
{ |
|
/* |
|
* In MS-DOS, pipes are seekable. Check for |
|
* standard input, and pretend it is not seekable. |
|
*/ |
|
return (0); |
|
} |
|
#endif |
|
return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); |
return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); |
} |
} |
|
|
|
|
* Force EOF to be at the current read position. |
* Force EOF to be at the current read position. |
* This is used after an ignore_eof read, during which the EOF may change. |
* This is used after an ignore_eof read, during which the EOF may change. |
*/ |
*/ |
public void |
void |
ch_set_eof() |
ch_set_eof(void) |
{ |
{ |
ch_fsize = ch_fpos; |
ch_fsize = ch_fpos; |
} |
} |
|
|
/* |
/* |
* Initialize file state for a new file. |
* Initialize file state for a new file. |
*/ |
*/ |
public void |
void |
ch_init(f, flags) |
ch_init(int f, int flags) |
int f; |
|
int flags; |
|
{ |
{ |
/* |
/* |
* See if we already have a filestate for this file. |
* See if we already have a filestate for this file. |
*/ |
*/ |
thisfile = (struct filestate *) get_filestate(curr_ifile); |
thisfile = get_filestate(curr_ifile); |
if (thisfile == NULL) |
if (thisfile == NULL) { |
{ |
|
/* |
/* |
* Allocate and initialize a new filestate. |
* Allocate and initialize a new filestate. |
*/ |
*/ |
thisfile = (struct filestate *) |
thisfile = calloc(1, sizeof (struct filestate)); |
calloc(1, sizeof(struct filestate)); |
|
thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; |
thisfile->buflist.next = thisfile->buflist.prev = END_OF_CHAIN; |
thisfile->nbufs = 0; |
thisfile->nbufs = 0; |
thisfile->flags = 0; |
thisfile->flags = 0; |
|
|
thisfile->block = 0; |
thisfile->block = 0; |
thisfile->offset = 0; |
thisfile->offset = 0; |
thisfile->file = -1; |
thisfile->file = -1; |
thisfile->fsize = NULL_POSITION; |
thisfile->fsize = -1; |
ch_flags = flags; |
ch_flags = flags; |
init_hashtbl(); |
init_hashtbl(); |
/* |
/* |
|
|
/* |
/* |
* Close a filestate. |
* Close a filestate. |
*/ |
*/ |
public void |
void |
ch_close() |
ch_close(void) |
{ |
{ |
int keepstate = FALSE; |
int keepstate = FALSE; |
|
|
if (thisfile == NULL) |
if (thisfile == NULL) |
return; |
return; |
|
|
if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) |
if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) { |
{ |
|
/* |
/* |
* We can seek or re-open, so we don't need to keep buffers. |
* We can seek or re-open, so we don't need to keep buffers. |
*/ |
*/ |
ch_delbufs(); |
ch_delbufs(); |
} else |
} else { |
keepstate = TRUE; |
keepstate = TRUE; |
if (!(ch_flags & CH_KEEPOPEN)) |
} |
{ |
if (!(ch_flags & CH_KEEPOPEN)) { |
/* |
/* |
* We don't need to keep the file descriptor open |
* We don't need to keep the file descriptor open |
* (because we can re-open it.) |
* (because we can re-open it.) |
|
|
if (!(ch_flags & CH_POPENED)) |
if (!(ch_flags & CH_POPENED)) |
close(ch_file); |
close(ch_file); |
ch_file = -1; |
ch_file = -1; |
} else |
} else { |
keepstate = TRUE; |
keepstate = TRUE; |
if (!keepstate) |
} |
{ |
if (!keepstate) { |
/* |
/* |
* We don't even need to keep the filestate structure. |
* We don't even need to keep the filestate structure. |
*/ |
*/ |
free(thisfile); |
free(thisfile); |
thisfile = NULL; |
thisfile = NULL; |
set_filestate(curr_ifile, (void *) NULL); |
set_filestate(curr_ifile, NULL); |
} |
} |
} |
} |
|
|
/* |
/* |
* Return ch_flags for the current file. |
* Return ch_flags for the current file. |
*/ |
*/ |
public int |
int |
ch_getflags() |
ch_getflags(void) |
{ |
{ |
if (thisfile == NULL) |
if (thisfile == NULL) |
return (0); |
return (0); |
return (ch_flags); |
return (ch_flags); |
} |
} |
|
|
#if 0 |
|
public void |
|
ch_dump(struct filestate *fs) |
|
{ |
|
struct buf *bp; |
|
struct bufnode *bn; |
|
unsigned char *s; |
|
|
|
if (fs == NULL) |
|
{ |
|
printf(" --no filestate\n"); |
|
return; |
|
} |
|
printf(" file %d, flags %x, fpos %x, fsize %x, blk/off %x/%x\n", |
|
fs->file, fs->flags, fs->fpos, |
|
fs->fsize, fs->block, fs->offset); |
|
printf(" %d bufs:\n", fs->nbufs); |
|
for (bn = fs->next; bn != &fs->buflist; bn = bn->next) |
|
{ |
|
bp = bufnode_buf(bn); |
|
printf("%x: blk %x, size %x \"", |
|
bp, bp->block, bp->datasize); |
|
for (s = bp->data; s < bp->data + 30; s++) |
|
if (*s >= ' ' && *s < 0x7F) |
|
printf("%c", *s); |
|
else |
|
printf("."); |
|
printf("\"\n"); |
|
} |
|
} |
|
#endif |
|