Compare commits

...

1 Commits
main ... dj

Author SHA1 Message Date
DTB
fe1216868b
dj(1): refactor source 2024-03-28 21:22:36 -06:00

182
src/dj.c
View File

@ -46,31 +46,11 @@ struct Io{
/* Additionally, the following global variables are used to store user options. /* Additionally, the following global variables are used to store user options.
*/ */
/* (-a) */ static int align; /* Only the lower 8b are used but align is
* negative if no alignment is being done. */
/* (-c) */ static int count; /* 0 if dj(1) runs until no more reads are
* possible. */
/* ASCII field separator delimited statistics */ /* ASCII field separator delimited statistics */
static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034"; static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034";
/* human-readable statistics */
static char *fmt_human = "%d+%d > %d+%d; %d > %d\n";
/* pointer to chosen formatting */
/* (-H) */ static char *fmt_output; /* fmt_asv (default) or fmt_human (-H) */
/* (-dq) */ static char debug; /* /* (-H) Human-readable statistics */
* -d increments dj -qq | 0 - no diagnostic output whatsoever static char *fmt_human = "%d+%d > %d+%d; %d > %d\n";
* -q decrements dj -q | 1 - typical output without
* | notifications on partial reads or
* | writes
* dj | 2 - typical output (default)
* dj -d | 3 - verbose status messages */
/* (-n) */ static char noerror; /* 0 - exits on partial reads or writes
* (default)
* 1 - retries on partial reads/writes
* (-n) */
/* Non-configurable defaults. */ /* Non-configurable defaults. */
#define bs_default 1024 /* GNU dd(1) default; twice POSIX but a neat 2^10 */ #define bs_default 1024 /* GNU dd(1) default; twice POSIX but a neat 2^10 */
@ -80,19 +60,8 @@ static char *stdout_name = "<stdout>";
static int read_flags = O_RDONLY; /* These flags are consistent with Busybox */ static int read_flags = O_RDONLY; /* These flags are consistent with Busybox */
static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */ static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */
/* Macro to set defaults for user-configurable options. */
#define setdefaults do{ \
align = -1; \
count = 0; \
debug = 2; \
fmt_output = fmt_asv; \
noerror = 0; \
ep[0].fl = read_flags; \
Io_setdefaults(&ep[0]); \
ep[1].fl = write_flags; \
Io_setdefaults(&ep[1]); }while(0)
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
/* Macro to check if fd is a std* file, e.g. stdin. */ /* Macro to check if fd is a std* file, e.g. stdin. */
#define fdisstd(fd) \ #define fdisstd(fd) \
@ -104,43 +73,16 @@ static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */
* particular io[2] used in main. Error conditions are not checked because this * particular io[2] used in main. Error conditions are not checked because this
* is only used when the program is about to terminate (hence its name). */ * is only used when the program is about to terminate (hence its name). */
#define terminate(io) do{ \ #define terminate(io) do{ \
Io_buffree(&(io)[0]); \ free((io)[0].buf); \
Io_buffree(&(io)[1]); \ free((io)[1].buf); \
Io_fdclose(&(io)[0]); \ Io_fdclose(&(io)[0]); \
Io_fdclose(&(io)[1]); }while(0) Io_fdclose(&(io)[1]); }while(0)
/* Allocates *io's buffer. Returns NULL if unsuccessful. */
static void *
Io_bufalloc(struct Io *io){
return (io->buf = malloc(io->bs * (sizeof *io->buf)));
}
/* Frees *io's buffer. Returns io. */
static struct Io *
Io_buffree(struct Io *io){
free(io->buf);
return io;
}
/* Fills the unused portion of io's buffer with padding, updating io->bufuse.
* Returns io. */
static struct Io *
Io_bufrpad(struct Io *io, int padding){
memset(io->buf + io->bufuse, padding, io->bs - io->bufuse);
io->bufuse = io->bs;
return io;
}
/* Copies from the buffer in src as much as possible to the free space in the /* Copies from the buffer in src as much as possible to the free space in the
* dest buffer, removing the copied units from src and permuting the remaining * dest buffer, removing the copied units from src and permuting the remaining
* units in the src buffer to the start of the buffer, modifying both the src * units in the src buffer to the start of the buffer, modifying both the src
* and dest bufuse and returning dest. */ * and dest bufuse and returning dest. */
static struct Io* static struct Io *
Io_bufxapp(struct Io *dest, struct Io *src){ Io_bufxapp(struct Io *dest, struct Io *src){
int n; int n;
@ -157,7 +99,7 @@ Io_bufxapp(struct Io *dest, struct Io *src){
* removing the copied units from src and permuting the remaining units in the * removing the copied units from src and permuting the remaining units in the
* src buffer to the start of the buffer, modifying both the src and dest * src buffer to the start of the buffer, modifying both the src and dest
* bufuse and returning dest. */ * bufuse and returning dest. */
static struct Io* static struct Io *
Io_bufxfer(struct Io *dest, struct Io *src, int n){ Io_bufxfer(struct Io *dest, struct Io *src, int n){
memcpy(dest->buf, src->buf, (dest->bufuse = n)); memcpy(dest->buf, src->buf, (dest->bufuse = n));
@ -167,13 +109,10 @@ Io_bufxfer(struct Io *dest, struct Io *src, int n){
} }
/* Closes io->fn and returns -1 on error, otherwise io->fd. */ /* Closes io->fn and returns -1 on error, otherwise io->fd. */
static int #define Io_fdclose(io) \
Io_fdclose(struct Io *io){ (fdisstd((io)->fd) \
? 0 \
return fdisstd(io->fd) : close((io)->fd))
? 0
: close(io->fd);
}
/* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd, /* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd,
* which will be -1 if an error occured. */ * which will be -1 if an error occured. */
@ -199,8 +138,7 @@ Io_fdopen(struct Io *io, char *fn){
* be set to zero to indicate the seek occurred. */ * be set to zero to indicate the seek occurred. */
static int static int
Io_fdseek(struct Io *io){ Io_fdseek(struct Io *io){
int (*op)(int, void *, size_t);
if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1) if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)
return -1; return -1;
@ -283,19 +221,6 @@ oserr(char *s){
return EX_OSERR; return EX_OSERR;
} }
/* Prints statistics regarding the use of dj, particularly partially and
* completely read and written records, accessing debug, ep, and fmt_output. */
static void
output(void){
if(debug >= 1)
fprintf(stderr, fmt_output,
ep[0].rec, ep[0].prec, ep[1].rec, ep[1].prec,
ep[0].bytes, ep[1].bytes);
return;
}
/* Parses the string s to an integer, returning either the integer or in the /* Parses the string s to an integer, returning either the integer or in the
* case of an error a negative integer. This is used for argument parsing * case of an error a negative integer. This is used for argument parsing
* (e.g. -B [int]) in dj and no negative integer would be valid anyway. */ * (e.g. -B [int]) in dj and no negative integer would be valid anyway. */
@ -321,11 +246,30 @@ usage(void){
return EX_USAGE; return EX_USAGE;
} }
int main(int argc, char *argv[]){ /* For use in main only.
int c; * Prints statistics regarding the use of dj, particularly partially and
int i; * completely read and written records, accessing ep, and fmt_output. */
#define output fprintf(stderr, fmt_output, \
ep[0].rec, ep[0].prec, ep[1].rec, ep[1].prec, ep[0].bytes, ep[1].bytes \
)
setdefaults; int main(int argc, char *argv[]){
int align; /* Only the lower 8b are used, negative if no alignment. */
int count;
int c, i;
enum { QUIETER = 0, TYPICAL = 1, VERBOSE = 2 } debug;
enum { GIVEUP = 0, RETRY = 1 } error;
char *fmt_output = fmt_asv;
ep[0].fl = read_flags;
Io_setdefaults(&ep[0]);
ep[1].fl = write_flags;
Io_setdefaults(&ep[1]);
align = -1;
count = 0;
debug = TYPICAL;
error = GIVEUP;
if(argc > 0){ if(argc > 0){
program_name = argv[0]; program_name = argv[0];
@ -342,10 +286,10 @@ int main(int argc, char *argv[]){
terminate(ep); terminate(ep);
return oserr(optarg); return oserr(optarg);
case 'A': align = '\0'; break; case 'A': align = '\0'; break;
case 'd': ++debug; break; case 'd': debug = VERBOSE; break;
case 'n': noerror = 1; break; case 'n': error = RETRY; break;
case 'H': fmt_output = fmt_human; break; case 'H': fmt_output = fmt_human; break;
case 'q': --debug; break; case 'q': debug = QUIETER; break;
case 'a': case 'a':
if(optarg[0] != '\0' && optarg[1] == '\0'){ if(optarg[0] != '\0' && optarg[1] == '\0'){
align = optarg[0]; align = optarg[0];
@ -367,14 +311,15 @@ int main(int argc, char *argv[]){
} }
} }
if(debug >= 3) if(debug >= VERBOSE)
fprintf(stderr, fprintf(stderr,
"argv0=%s\n" "argv0=%s\n"
"in=%s\tibs=%d\tskip=%ld\talign=%hhx\tcount=%d\n" "in=%s\tibs=%d\tskip=%ld\talign=%hhx\tcount=%d\n"
"out=%s\tobs=%d\tseek=%ld\tdebug=%2d\tnoerror=%d\n", "out=%s\tobs=%d\tseek=%ld\tdebug=%2d\terror=%s\n",
program_name, program_name,
ep[0].fn, ep[0].bs, ep[0].seek, align, count, ep[0].fn, ep[0].bs, ep[0].seek, align, count,
ep[1].fn, ep[1].bs, ep[1].seek, debug, noerror); ep[1].fn, ep[1].bs, ep[1].seek, debug,
error == GIVEUP ? "GIVEUP" : "RETRY");
if(argc > optind){ if(argc > optind){
terminate(ep); terminate(ep);
@ -382,36 +327,37 @@ int main(int argc, char *argv[]){
} }
for(i = 0; i <= 1; ++i){ for(i = 0; i <= 1; ++i){
if(Io_bufalloc(&ep[i]) == NULL){ if((ep[i].buf = malloc(ep[i].bs * (sizeof ep[i].buf))) == NULL){
fprintf(stderr, "%s: Failed to allocate %d bytes\n", fprintf(stderr, "%s: Failed to allocate %d bytes\n",
program_name, ep[i].bs); program_name, ep[i].bs * (sizeof ep[i].buf));
terminate(ep); terminate(ep);
return EX_OSERR; return EX_OSERR;
}else if(ep[i].seek > 0) }else if(ep[i].seek > 0 && (c = Io_fdseek(&ep[i])) != -1){
switch(Io_fdseek(&ep[i])){ output;
case EX_OK: terminate(ep);
output(); return c;
terminate(ep); }
return EX_OK;
}
} }
do{ /* read */ do{ /* read */
Io_read(&ep[0]); Io_read(&ep[0]);
if(!noerror && ep[0].bufuse == 0) if(error == RETRY && ep[0].bufuse == 0)
Io_read(&ep[0]); /* second chance */ Io_read(&ep[0]); /* second chance */
if(ep[0].bufuse == 0) /* that's all she wrote */ if(ep[0].bufuse == 0) /* that's all she wrote */
break; break;
else if(ep[0].bufuse < ep[0].bs){ else if(ep[0].bufuse < ep[0].bs){
++ep[0].prec; ++ep[0].prec;
if(debug >= 2){ if(debug >= TYPICAL){
fprintf(stderr, "%s: Partial read:\n\t", program_name); fprintf(stderr, "%s: Partial read:\n\t", program_name);
output(); output;
} }
if(!noerror) if(error == GIVEUP)
count = 1; count = 1;
if(align >= 0) if(align >= 0){
Io_bufrpad(&ep[0], align); memset(ep[0].buf + ep[0].bufuse, align,
ep[0].bs - ep[0].bufuse);
ep[0].bufuse = ep[0].bs;
}
}else }else
++ep[0].rec; ++ep[0].rec;
@ -425,18 +371,18 @@ int main(int argc, char *argv[]){
c = ep[1].bufuse; c = ep[1].bufuse;
Io_write(&ep[1]); Io_write(&ep[1]);
if(!noerror && ep[1].bufuse == c) if(error == GIVEUP && ep[1].bufuse == c)
Io_write(&ep[1]); /* second chance */ Io_write(&ep[1]); /* second chance */
if(c == ep[1].bufuse){ /* no more love */ if(c == ep[1].bufuse){ /* no more love */
count = 1; count = 1;
break; break;
}else if(c > ep[1].bufuse && ep[1].bufuse > 0){ }else if(c > ep[1].bufuse && ep[1].bufuse > 0){
ep[1].prec += 1; ep[1].prec += 1;
if(debug >= 2){ if(debug >= TYPICAL){
fprintf(stderr, "%s: Partial write:\n\t", program_name); fprintf(stderr, "%s: Partial write:\n\t", program_name);
output(); output;
} }
if(!noerror) if(error == GIVEUP)
count = 1; count = 1;
}else if(ep[1].bufuse == 0 && c < ep[1].bs) }else if(ep[1].bufuse == 0 && c < ep[1].bs)
++ep[1].prec; ++ep[1].prec;
@ -445,7 +391,7 @@ int main(int argc, char *argv[]){
}while(ep[0].bufuse > 0); }while(ep[0].bufuse > 0);
}while(count == 0 || --count > 0); }while(count == 0 || --count > 0);
output(); output;
terminate(ep); terminate(ep);
return EX_OK; return EX_OK;