version 1.1.1.2, 2003/04/13 18:21:21 |
version 1.1.1.3, 2011/09/16 17:47:00 |
|
|
/* |
/* |
* Copyright (C) 1984-2002 Mark Nudelman |
* Copyright (C) 1984-2011 Mark Nudelman |
* |
* |
* You may distribute under the terms of either the GNU General Public |
* You may distribute under the terms of either the GNU General Public |
* License or the Less License, as specified in the README file. |
* License or the Less License, as specified in the README file. |
|
|
#include <windows.h> |
#include <windows.h> |
#endif |
#endif |
|
|
|
#if HAVE_STAT_INO |
|
#include <sys/stat.h> |
|
extern dev_t curr_dev; |
|
extern ino_t curr_ino; |
|
#endif |
|
|
typedef POSITION BLOCKNUM; |
typedef POSITION BLOCKNUM; |
|
|
public int ignore_eoi; |
public int ignore_eoi; |
|
|
* in order from most- to least-recently used. |
* in order from most- to least-recently used. |
* The circular list is anchored by the file state "thisfile". |
* The circular list is anchored by the file state "thisfile". |
*/ |
*/ |
|
struct bufnode { |
|
struct bufnode *next, *prev; |
|
struct bufnode *hnext, *hprev; |
|
}; |
|
|
#define LBUFSIZE 8192 |
#define LBUFSIZE 8192 |
struct buf { |
struct buf { |
struct buf *next, *prev; |
struct bufnode node; |
struct buf *hnext, *hprev; |
|
BLOCKNUM block; |
BLOCKNUM block; |
unsigned int datasize; |
unsigned int datasize; |
unsigned char data[LBUFSIZE]; |
unsigned char data[LBUFSIZE]; |
}; |
}; |
|
#define bufnode_buf(bn) ((struct buf *) bn) |
|
|
struct buflist { |
|
/* -- Following members must match struct buf */ |
|
struct buf *buf_next, *buf_prev; |
|
struct buf *buf_hnext, *buf_hprev; |
|
}; |
|
|
|
/* |
/* |
* The file state is maintained in a filestate structure. |
* The file state is maintained in a filestate structure. |
* A pointer to the filestate is kept in the ifile structure. |
* A pointer to the filestate is kept in the ifile structure. |
*/ |
*/ |
#define BUFHASH_SIZE 64 |
#define BUFHASH_SIZE 64 |
struct filestate { |
struct filestate { |
struct buf *buf_next, *buf_prev; |
struct bufnode buflist; |
struct buflist hashtbl[BUFHASH_SIZE]; |
struct bufnode hashtbl[BUFHASH_SIZE]; |
int file; |
int file; |
int flags; |
int flags; |
POSITION fpos; |
POSITION fpos; |
|
|
POSITION fsize; |
POSITION fsize; |
}; |
}; |
|
|
#define ch_bufhead thisfile->buf_next |
#define ch_bufhead thisfile->buflist.next |
#define ch_buftail thisfile->buf_prev |
#define ch_buftail thisfile->buflist.prev |
#define ch_nbufs thisfile->nbufs |
#define ch_nbufs thisfile->nbufs |
#define ch_block thisfile->block |
#define ch_block thisfile->block |
#define ch_offset thisfile->offset |
#define ch_offset thisfile->offset |
|
|
#define ch_flags thisfile->flags |
#define ch_flags thisfile->flags |
#define ch_file thisfile->file |
#define ch_file thisfile->file |
|
|
#define END_OF_CHAIN ((struct buf *)&thisfile->buf_next) |
#define END_OF_CHAIN (&thisfile->buflist) |
#define END_OF_HCHAIN(h) ((struct buf *)&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)) |
|
|
#define FOR_BUFS_IN_CHAIN(h,bp) \ |
/* |
for (bp = thisfile->hashtbl[h].buf_hnext; \ |
* Macros to manipulate the list of buffers in thisfile->buflist. |
bp != END_OF_HCHAIN(h); bp = bp->hnext) |
*/ |
|
#define FOR_BUFS(bn) \ |
|
for (bn = ch_bufhead; bn != END_OF_CHAIN; bn = bn->next) |
|
|
#define HASH_RM(bp) \ |
#define BUF_RM(bn) \ |
(bp)->hnext->hprev = (bp)->hprev; \ |
(bn)->next->prev = (bn)->prev; \ |
(bp)->hprev->hnext = (bp)->hnext; |
(bn)->prev->next = (bn)->next; |
|
|
#define HASH_INS(bp,h) \ |
#define BUF_INS_HEAD(bn) \ |
(bp)->hnext = thisfile->hashtbl[h].buf_hnext; \ |
(bn)->next = ch_bufhead; \ |
(bp)->hprev = END_OF_HCHAIN(h); \ |
(bn)->prev = END_OF_CHAIN; \ |
thisfile->hashtbl[h].buf_hnext->hprev = (bp); \ |
ch_bufhead->prev = (bn); \ |
thisfile->hashtbl[h].buf_hnext = (bp); |
ch_bufhead = (bn); |
|
|
|
#define BUF_INS_TAIL(bn) \ |
|
(bn)->next = END_OF_CHAIN; \ |
|
(bn)->prev = ch_buftail; \ |
|
ch_buftail->next = (bn); \ |
|
ch_buftail = (bn); |
|
|
|
/* |
|
* Macros to manipulate the list of buffers in thisfile->hashtbl[n]. |
|
*/ |
|
#define FOR_BUFS_IN_CHAIN(h,bn) \ |
|
for (bn = thisfile->hashtbl[h].hnext; \ |
|
bn != END_OF_HCHAIN(h); bn = bn->hnext) |
|
|
|
#define BUF_HASH_RM(bn) \ |
|
(bn)->hnext->hprev = (bn)->hprev; \ |
|
(bn)->hprev->hnext = (bn)->hnext; |
|
|
|
#define BUF_HASH_INS(bn,h) \ |
|
(bn)->hnext = thisfile->hashtbl[h].hnext; \ |
|
(bn)->hprev = END_OF_HCHAIN(h); \ |
|
thisfile->hashtbl[h].hnext->hprev = (bn); \ |
|
thisfile->hashtbl[h].hnext = (bn); |
|
|
static struct filestate *thisfile; |
static struct filestate *thisfile; |
static int ch_ungotchar = -1; |
static int ch_ungotchar = -1; |
static int maxbufs = -1; |
static int maxbufs = -1; |
|
|
extern int autobuf; |
extern int autobuf; |
extern int sigs; |
extern int sigs; |
extern int secure; |
extern int secure; |
|
extern int screen_trashed; |
|
extern int follow_mode; |
extern constant char helpdata[]; |
extern constant char helpdata[]; |
extern constant int size_helpdata; |
extern constant int size_helpdata; |
extern IFILE curr_ifile; |
extern IFILE curr_ifile; |
|
|
|
|
/* |
/* |
* Get the character pointed to by the read pointer. |
* 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 == ch_bufhead->block && \ |
|
ch_offset < ch_bufhead->datasize) ? \ |
|
ch_bufhead->data[ch_offset] : fch_get()) |
|
int |
int |
fch_get() |
ch_get() |
{ |
{ |
register struct buf *bp; |
register struct buf *bp; |
|
register struct bufnode *bn; |
register int n; |
register int n; |
register int slept; |
register int slept; |
register int h; |
register int h; |
POSITION pos; |
POSITION pos; |
POSITION len; |
POSITION len; |
|
|
|
if (thisfile == NULL) |
|
return (EOI); |
|
|
|
/* |
|
* Quick check for the common case where |
|
* the desired char is in the head buffer. |
|
*/ |
|
if (ch_bufhead != END_OF_CHAIN) |
|
{ |
|
bp = bufnode_buf(ch_bufhead); |
|
if (ch_block == bp->block && ch_offset < bp->datasize) |
|
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, bp) |
FOR_BUFS_IN_CHAIN(h, 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 read_more; |
break; |
goto found; |
goto found; |
} |
} |
} |
} |
/* |
if (bn == END_OF_HCHAIN(h)) |
* 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, |
|
* then maybe allocate a new buffer. |
|
*/ |
|
if (ch_buftail == END_OF_CHAIN || ch_buftail->block != -1) |
|
{ |
{ |
/* |
/* |
* There is no empty buffer to use. |
* Block is not in a buffer. |
* Allocate a new buffer if: |
* Take the least recently used buffer |
* 1. We can't seek on this file and -b is not in effect; or |
* and read the desired block into it. |
* 2. We haven't allocated the max buffers for this file yet. |
* If the LRU buffer has data in it, |
|
* then maybe allocate a new buffer. |
*/ |
*/ |
if ((autobuf && !(ch_flags & CH_CANSEEK)) || |
if (ch_buftail == END_OF_CHAIN || |
(maxbufs < 0 || ch_nbufs < maxbufs)) |
bufnode_buf(ch_buftail)->block != -1) |
if (ch_addbuf()) |
{ |
/* |
/* |
* Allocation failed: turn off autobuf. |
* There is no empty buffer to use. |
*/ |
* Allocate a new buffer if: |
autobuf = OPT_OFF; |
* 1. We can't seek on this file and -b is not in effect; or |
|
* 2. We haven't allocated the max buffers for this file yet. |
|
*/ |
|
if ((autobuf && !(ch_flags & CH_CANSEEK)) || |
|
(maxbufs < 0 || ch_nbufs < maxbufs)) |
|
if (ch_addbuf()) |
|
/* |
|
* Allocation failed: turn off autobuf. |
|
*/ |
|
autobuf = OPT_OFF; |
|
} |
|
bn = ch_buftail; |
|
bp = bufnode_buf(bn); |
|
BUF_HASH_RM(bn); /* Remove from old hash chain. */ |
|
bp->block = ch_block; |
|
bp->datasize = 0; |
|
BUF_HASH_INS(bn, h); /* Insert into new hash chain. */ |
} |
} |
bp = ch_buftail; |
|
HASH_RM(bp); /* Remove from old hash chain. */ |
|
bp->block = ch_block; |
|
bp->datasize = 0; |
|
HASH_INS(bp, h); /* Insert into new hash chain. */ |
|
|
|
read_more: |
read_more: |
pos = (ch_block * LBUFSIZE) + bp->datasize; |
pos = (ch_block * LBUFSIZE) + bp->datasize; |
|
|
*/ |
*/ |
if (!(ch_flags & CH_CANSEEK)) |
if (!(ch_flags & CH_CANSEEK)) |
return ('?'); |
return ('?'); |
if (lseek(ch_file, (off_t)pos, 0) == 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(); |
|
|
#endif |
#endif |
#endif |
#endif |
slept = TRUE; |
slept = TRUE; |
|
|
|
#if HAVE_STAT_INO |
|
if (follow_mode == FOLLOW_NAME) |
|
{ |
|
/* See whether the file's i-number has changed. |
|
* If so, force the file to be closed and |
|
* reopened. */ |
|
struct stat st; |
|
int r = stat(get_filename(curr_ifile), &st); |
|
if (r == 0 && (st.st_ino != curr_ino || |
|
st.st_dev != curr_dev)) |
|
{ |
|
/* screen_trashed=2 causes |
|
* make_display to reopen the file. */ |
|
screen_trashed = 2; |
|
return (EOI); |
|
} |
|
} |
|
#endif |
} |
} |
if (sigs) |
if (sigs) |
return (EOI); |
return (EOI); |
} |
} |
|
|
found: |
found: |
if (ch_bufhead != bp) |
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. |
*/ |
*/ |
bp->next->prev = bp->prev; |
BUF_RM(bn); |
bp->prev->next = bp->next; |
BUF_INS_HEAD(bn); |
bp->next = ch_bufhead; |
|
bp->prev = END_OF_CHAIN; |
|
ch_bufhead->prev = bp; |
|
ch_bufhead = bp; |
|
|
|
/* |
/* |
* Move to head of hash chain too. |
* Move to head of hash chain too. |
*/ |
*/ |
HASH_RM(bp); |
BUF_HASH_RM(bn); |
HASH_INS(bp, h); |
BUF_HASH_INS(bn, h); |
} |
} |
|
|
if (ch_offset >= bp->datasize) |
if (ch_offset >= bp->datasize) |
|
|
sync_logfile() |
sync_logfile() |
{ |
{ |
register struct buf *bp; |
register struct buf *bp; |
|
register 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++) |
{ |
{ |
for (bp = ch_bufhead; ; bp = bp->next) |
int wrote = FALSE; |
|
FOR_BUFS(bn) |
{ |
{ |
if (bp == END_OF_CHAIN) |
bp = bufnode_buf(bn); |
{ |
|
if (!warned) |
|
{ |
|
error("Warning: log file is incomplete", |
|
NULL_PARG); |
|
warned = TRUE; |
|
} |
|
break; |
|
} |
|
if (bp->block == block) |
if (bp->block == block) |
{ |
{ |
write(logfile, (char *) bp->data, bp->datasize); |
write(logfile, (char *) bp->data, bp->datasize); |
|
wrote = TRUE; |
break; |
break; |
} |
} |
} |
} |
|
if (!wrote && !warned) |
|
{ |
|
error("Warning: log file is incomplete", |
|
NULL_PARG); |
|
warned = TRUE; |
|
} |
} |
} |
} |
} |
|
|
|
|
BLOCKNUM block; |
BLOCKNUM block; |
{ |
{ |
register struct buf *bp; |
register struct buf *bp; |
|
register struct bufnode *bn; |
register int h; |
register int h; |
|
|
h = BUFHASH(block); |
h = BUFHASH(block); |
FOR_BUFS_IN_CHAIN(h, bp) |
FOR_BUFS_IN_CHAIN(h, bn) |
{ |
{ |
|
bp = bufnode_buf(bn); |
if (bp->block == block) |
if (bp->block == block) |
return (TRUE); |
return (TRUE); |
} |
} |
|
|
BLOCKNUM new_block; |
BLOCKNUM new_block; |
POSITION len; |
POSITION len; |
|
|
|
if (thisfile == NULL) |
|
return (0); |
|
|
len = ch_length(); |
len = ch_length(); |
if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) |
if (pos < ch_zero() || (len != NULL_POSITION && pos > len)) |
return (1); |
return (1); |
|
|
{ |
{ |
POSITION len; |
POSITION len; |
|
|
|
if (thisfile == NULL) |
|
return (0); |
|
|
if (ch_flags & CH_CANSEEK) |
if (ch_flags & CH_CANSEEK) |
ch_fsize = filesize(ch_file); |
ch_fsize = filesize(ch_file); |
|
|
|
|
public int |
public int |
ch_beg_seek() |
ch_beg_seek() |
{ |
{ |
register struct buf *bp, *firstbp; |
register struct bufnode *bn; |
|
register struct bufnode *firstbn; |
|
|
/* |
/* |
* Try a plain ch_seek first. |
* Try a plain ch_seek first. |
|
|
* Can't get to position 0. |
* Can't get to position 0. |
* Look thru the buffers for the one closest to position 0. |
* Look thru the buffers for the one closest to position 0. |
*/ |
*/ |
firstbp = bp = ch_bufhead; |
firstbn = ch_bufhead; |
if (bp == END_OF_CHAIN) |
if (firstbn == END_OF_CHAIN) |
return (1); |
return (1); |
while ((bp = bp->next) != END_OF_CHAIN) |
FOR_BUFS(bn) |
if (bp->block < firstbp->block) |
{ |
firstbp = bp; |
if (bufnode_buf(bn)->block < bufnode_buf(firstbn)->block) |
ch_block = firstbp->block; |
firstbn = bn; |
|
} |
|
ch_block = bufnode_buf(firstbn)->block; |
ch_offset = 0; |
ch_offset = 0; |
return (0); |
return (0); |
} |
} |
|
|
public POSITION |
public POSITION |
ch_length() |
ch_length() |
{ |
{ |
|
if (thisfile == NULL) |
|
return (NULL_POSITION); |
if (ignore_eoi) |
if (ignore_eoi) |
return (NULL_POSITION); |
return (NULL_POSITION); |
if (ch_flags & CH_HELPFILE) |
if (ch_flags & CH_HELPFILE) |
|
|
public POSITION |
public POSITION |
ch_tell() |
ch_tell() |
{ |
{ |
|
if (thisfile == NULL) |
|
return (NULL_POSITION); |
return (ch_block * LBUFSIZE) + ch_offset; |
return (ch_block * LBUFSIZE) + ch_offset; |
} |
} |
|
|
|
|
{ |
{ |
register int c; |
register int c; |
|
|
|
if (thisfile == NULL) |
|
return (EOI); |
c = ch_get(); |
c = ch_get(); |
if (c == EOI) |
if (c == EOI) |
return (EOI); |
return (EOI); |
|
|
public int |
public int |
ch_back_get() |
ch_back_get() |
{ |
{ |
|
if (thisfile == NULL) |
|
return (EOI); |
if (ch_offset > 0) |
if (ch_offset > 0) |
ch_offset --; |
ch_offset --; |
else |
else |
|
|
public void |
public void |
ch_flush() |
ch_flush() |
{ |
{ |
register struct buf *bp; |
register struct bufnode *bn; |
|
|
|
if (thisfile == NULL) |
|
return; |
|
|
if (!(ch_flags & CH_CANSEEK)) |
if (!(ch_flags & CH_CANSEEK)) |
{ |
{ |
/* |
/* |
|
|
/* |
/* |
* Initialize all the buffers. |
* Initialize all the buffers. |
*/ |
*/ |
for (bp = ch_bufhead; bp != END_OF_CHAIN; bp = bp->next) |
FOR_BUFS(bn) |
bp->block = -1; |
{ |
|
bufnode_buf(bn)->block = -1; |
|
} |
|
|
/* |
/* |
* Figure out the size of the file, if we can. |
* Figure out the size of the file, if we can. |
|
|
} |
} |
#endif |
#endif |
|
|
if (lseek(ch_file, (off_t)0, 0) == 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, |
|
|
ch_addbuf() |
ch_addbuf() |
{ |
{ |
register struct buf *bp; |
register struct buf *bp; |
|
register struct bufnode *bn; |
|
|
/* |
/* |
* Allocate and initialize a new buffer and link it |
* Allocate and initialize a new buffer and link it |
|
|
return (1); |
return (1); |
ch_nbufs++; |
ch_nbufs++; |
bp->block = -1; |
bp->block = -1; |
bp->next = END_OF_CHAIN; |
bn = &bp->node; |
bp->prev = ch_buftail; |
|
ch_buftail->next = bp; |
BUF_INS_TAIL(bn); |
ch_buftail = bp; |
BUF_HASH_INS(bn, 0); |
HASH_INS(bp, 0); |
|
return (0); |
return (0); |
} |
} |
|
|
|
|
|
|
for (h = 0; h < BUFHASH_SIZE; h++) |
for (h = 0; h < BUFHASH_SIZE; h++) |
{ |
{ |
thisfile->hashtbl[h].buf_hnext = END_OF_HCHAIN(h); |
thisfile->hashtbl[h].hnext = END_OF_HCHAIN(h); |
thisfile->hashtbl[h].buf_hprev = END_OF_HCHAIN(h); |
thisfile->hashtbl[h].hprev = END_OF_HCHAIN(h); |
} |
} |
} |
} |
|
|
|
|
static void |
static void |
ch_delbufs() |
ch_delbufs() |
{ |
{ |
register struct buf *bp; |
register struct bufnode *bn; |
|
|
while (ch_bufhead != END_OF_CHAIN) |
while (ch_bufhead != END_OF_CHAIN) |
{ |
{ |
bp = ch_bufhead; |
bn = ch_bufhead; |
bp->next->prev = bp->prev;; |
BUF_RM(bn); |
bp->prev->next = bp->next; |
free(bufnode_buf(bn)); |
free(bp); |
|
} |
} |
ch_nbufs = 0; |
ch_nbufs = 0; |
init_hashtbl(); |
init_hashtbl(); |
|
|
return (0); |
return (0); |
} |
} |
#endif |
#endif |
return (lseek(f, (off_t)1, 0) != BAD_LSEEK); |
return (lseek(f, (off_t)1, SEEK_SET) != BAD_LSEEK); |
} |
} |
|
|
/* |
/* |
|
|
*/ |
*/ |
thisfile = (struct filestate *) |
thisfile = (struct filestate *) |
calloc(1, sizeof(struct filestate)); |
calloc(1, sizeof(struct filestate)); |
thisfile->buf_next = thisfile->buf_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->fpos = 0; |
thisfile->fpos = 0; |
|
|
{ |
{ |
int keepstate = FALSE; |
int keepstate = FALSE; |
|
|
|
if (thisfile == NULL) |
|
return; |
|
|
if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) |
if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE)) |
{ |
{ |
/* |
/* |
|
|
public int |
public int |
ch_getflags() |
ch_getflags() |
{ |
{ |
|
if (thisfile == NULL) |
|
return (0); |
return (ch_flags); |
return (ch_flags); |
} |
} |
|
|
|
|
ch_dump(struct filestate *fs) |
ch_dump(struct filestate *fs) |
{ |
{ |
struct buf *bp; |
struct buf *bp; |
|
struct bufnode *bn; |
unsigned char *s; |
unsigned char *s; |
|
|
if (fs == NULL) |
if (fs == NULL) |
|
|
fs->file, fs->flags, fs->fpos, |
fs->file, fs->flags, fs->fpos, |
fs->fsize, fs->block, fs->offset); |
fs->fsize, fs->block, fs->offset); |
printf(" %d bufs:\n", fs->nbufs); |
printf(" %d bufs:\n", fs->nbufs); |
for (bp = fs->buf_next; bp != (struct buf *)fs; bp = bp->next) |
for (bn = fs->next; bn != &fs->buflist; bn = bn->next) |
{ |
{ |
|
bp = bufnode_buf(bn); |
printf("%x: blk %x, size %x \"", |
printf("%x: blk %x, size %x \"", |
bp, bp->block, bp->datasize); |
bp, bp->block, bp->datasize); |
for (s = bp->data; s < bp->data + 30; s++) |
for (s = bp->data; s < bp->data + 30; s++) |