Compare commits

..

No commits in common. "7fe122ac3b5ee858007c74879f8ee5b4362ec530" and "aff658d611e7b221a58b3177f34c7bd5c323d725" have entirely different histories.

2 changed files with 55 additions and 48 deletions

View File

@ -35,9 +35,7 @@ respectively. This language is inherited from the
utility and used here to decrease ambiguity. utility and used here to decrease ambiguity.
When seeking or skipping to a byte, writing or reading starts at the byte When seeking or skipping to a byte, writing or reading starts at the byte
immediately subsequent to the specified byte. Seeks and skips aren\(cqt counted immediately subsequent to the specified byte.
in the output statistics because they're guaranteed to succeed (or the utility
will exit unsuccessfully).
.\" .\"
.SH OPTIONS .SH OPTIONS

View File

@ -59,8 +59,11 @@ static int write_flags = O_WRONLY | O_CREAT;
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))
/* Macro to check if fd is stdin or stdout */ /* Macro to check if fd is a std* file, e.g. stdin. */
#define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO) #define fdisstd(fd) \
((fd) == STDIN_FILENO \
|| (fd) == STDOUT_FILENO \
|| (fd) == STDERR_FILENO)
/* Fills the unused portion of io's buffer with padding, updating io->bufuse. /* Fills the unused portion of io's buffer with padding, updating io->bufuse.
* Returns io. */ * Returns io. */
@ -129,6 +132,49 @@ Io_fdopen(struct Io *io, char *fn){
return fd; return fd;
} }
/* Seeks io->seek bytes through *io's file descriptor, subtracting the number
* of sought bytes from io->seek. This procedure leaves garbage in io->buf. */
static void
Io_fdseek(struct Io *io){
assert(io != NULL);
assert(io->fd != STDIN_FILENO || io->fl == read_flags);
assert(io->fd != STDOUT_FILENO || io->fl == write_flags);
assert(io->fd != STDERR_FILENO || io->fl == write_flags);
if(io->seek == 0
|| (!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1))
return;
if(io->fl == write_flags)
memset(io->buf, '\0', io->bs);
if(io->fl == write_flags){
memset(io->buf, '\0', io->bs);
/* We're going to cheat and use bufuse as the retval for write(2),
* which is fine because it'll be zeroed as this function returns
* anyway. */
do{
if((io->bufuse = write(io->fd, io->buf, MIN(io->bs, io->seek)))
== 0)
/* second chance */
io->bufuse = write(io->fd, io->buf, MIN(io->bs, io->seek));
}while((io->seek -= io->bufuse) > 0 && io->bufuse != 0);
}else if(io->fl == read_flags){
do{
if((io->bufuse = read(io->fd, io->buf, MIN(io->bs, io->seek)))
== 0)
/* second chance */
io->bufuse = read(io->fd, io->buf, MIN(io->bs, io->seek));
}while((io->seek -= io->bufuse) > 0 && io->bufuse != 0);
}else
assert(0); /* UNREACHABLE */
io->bufuse = 0;
return;
}
/* Reads io->bs bytes from *io's file descriptor into io->buf, storing the /* Reads io->bs bytes from *io's file descriptor into io->buf, storing the
* number of read bytes in io->bufuse and updating io->bytes. If io->bufuse is * number of read bytes in io->bufuse and updating io->bytes. If io->bufuse is
* 0, errno will probably be set. Returns io. */ * 0, errno will probably be set. Returns io. */
@ -229,7 +275,7 @@ int main(int argc, char *argv[]){
int c; int c;
program_name = argv[0]; program_name = argv[0];
while((c = getopt(argc, argv, ":a:b:B:c:i:hHns:S:o:")) != -1) while((c = getopt(argc, argv, "a:b:B:c:i:hHns:S:o:")) != -1)
switch(c){ switch(c){
case 'i': case 'o': i = (c == 'o'); case 'i': case 'o': i = (c == 'o');
if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */ if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */
@ -261,61 +307,24 @@ int main(int argc, char *argv[]){
} }
} }
assert(io->fd != STDIN_FILENO || io->fl == read_flags);
assert(io->fd != STDOUT_FILENO || io->fl == write_flags);
if(argc > optind){ if(argc > optind){
return usage(program_name); return usage(program_name);
} }
for(i = 0; i < 2; ++i){ for(i = 0; i < 2; ++i){
/* buffer allocation */
if((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL){ if((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL){
fprintf(stderr, "%s: Failed to allocate %d bytes\n", fprintf(stderr, "%s: Failed to allocate %d bytes\n",
program_name, io[i].bs); program_name, io[i].bs);
return EX_OSERR; return EX_OSERR;
}else if(io[i].seek > 0){
Io_fdseek(&io[i]);
if(io[i].seek > 0){
return oserr(io[i].fn);
}
} }
/* easy seeking */
if(!fdisstd(io[i].fd) && lseek(io[i].fd, io[i].seek, SEEK_SET) != -1)
io[i].seek = 0;
} }
/* hard skipping */
if(io[0].seek > 0){
do{
if((io[0].bufuse = read(
io[0].fd, io[0].buf, MIN(io[0].bs, io[0].seek)))
== 0)
/* second chance */
io->bufuse = read(
io[0].fd, io[0].buf, MIN(io[0].bs, io[0].seek));
}while((io[0].seek -= io[0].bufuse) > 0 && io[0].bufuse != 0);
io[0].bufuse = 0;
}
/* hard seeking */
if(io[1].seek > 0){
memset(io[1].buf, '\0', io[1].bs);
/* We're going to cheat and use bufuse as the retval for write(2),
* which is fine because it'll be zeroed as this function returns
* anyway. */
do{
if((io[1].bufuse = write(
io[1].fd, io[1].buf, MIN(io[1].bs, io[1].seek)))
== 0)
/* second chance */
io[1].bufuse = write(
io[1].fd, io[1].buf, MIN(io[1].bs, io[1].seek));
}while((io[1].seek -= io[1].bufuse) > 0 && io[1].bufuse != 0);
io[1].bufuse = 0;
}
/* Sought bytes aren't counted in the statistics because successful seeking
* is guaranteed here. */
for(i = 0; i < 2; ++i)
if(io[i].seek > 0)
return oserr(io[i].fn);
do{ /* read */ do{ /* read */
Io_read(&io[0]); Io_read(&io[0]);
if(!noerror && io[0].bufuse == 0) if(!noerror && io[0].bufuse == 0)