From d3f5246242984a1ff03982d92adb0d2b8fb465af Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 11:36:52 -0600 Subject: [PATCH 01/51] dj(1), dj.1: remove the unnecessary -d and -q --- docs/dj.1 | 29 +++-------------------------- src/dj.c | 41 ++++++++--------------------------------- 2 files changed, 11 insertions(+), 59 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 7d28ac8..5de56eb 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -11,7 +11,7 @@ dj \(en disk jockey .SH SYNOPSIS dj -.RB ( -AdHnq ) +.RB ( -AHn ) .RB ( -a .RB [ byte ]) .RB ( -c @@ -76,18 +76,11 @@ with a null byte instead of a character. .IP \fB-c\fP Specifies a number of reads to make. The default is 0, in which case the input is read until a partial or empty read is made. -.IP \fB-d\fP -Prints invocation information before program execution as described in the -DIAGNOSTICS section. Each invocation increments the debug level of the -program. .IP \fB-H\fP Prints diagnostics messages in a human-readable manner as described in the DIAGNOSTICS section. .IP \fB-n\fP Retries failed reads once before exiting. -.IP \fB-q\fP -Suppresses error messages which print when a read or write is partial or -empty. Each invocation decrements the debug level of the program. .\" .SH STANDARD INPUT @@ -100,10 +93,8 @@ one or more of the input files is \(lq-\(rq. .\" .SH DIAGNOSTICS -On a partial or empty read, unless the -.B -q -option is specified, a diagnostic message is printed. Then, the program exits -unless the +On a partial or empty read a diagnostic message is printed. Then, the program +exits unless the .B -n option is specified. @@ -128,20 +119,6 @@ option may be specified. In this event, the following format is used instead: {ASCII line feed} .RE -If the -.B -d -option is specified, debug information will be printed at the beginning of -execution. This output contains information regarding how the program was -invoked. The following example is the result of running the program with -.B -d -as the only argument: - -.RS -argv0=dj -in= ibs=1024 skip=0 align=ff count=0 -out= obs=1024 seek=0 debug= 3 noerror=0 -.RE - In non-recoverable errors that don\(cqt pertain to the read-write cycle, a diagnostic message is printed and the program exits with the appropriate .BR sysexits.h (3) diff --git a/src/dj.c b/src/dj.c index 8a6732c..27c160a 100644 --- a/src/dj.c +++ b/src/dj.c @@ -59,14 +59,6 @@ struct Io{ /* pointer to chosen formatting */ /* (-H) */ static char *fmt_output; /* fmt_asv (default) or fmt_human (-H) */ -/* (-dq) */ static char debug; /* - * -d increments dj -qq | 0 - no diagnostic output whatsoever - * -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 @@ -84,7 +76,6 @@ static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */ #define setdefaults do{ \ align = -1; \ count = 0; \ - debug = 2; \ fmt_output = fmt_asv; \ noerror = 0; \ ep[0].fl = read_flags; \ @@ -284,14 +275,13 @@ oserr(char *s){ } /* Prints statistics regarding the use of dj, particularly partially and - * completely read and written records, accessing debug, ep, and fmt_output. */ + * completely read and written records, accessing 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); + fprintf(stderr, fmt_output, + ep[0].rec, ep[0].prec, ep[1].rec, ep[1].prec, + ep[0].bytes, ep[1].bytes); return; } @@ -342,10 +332,8 @@ int main(int argc, char *argv[]){ terminate(ep); return oserr(optarg); case 'A': align = '\0'; break; - case 'd': ++debug; break; case 'n': noerror = 1; break; case 'H': fmt_output = fmt_human; break; - case 'q': --debug; break; case 'a': if(optarg[0] != '\0' && optarg[1] == '\0'){ align = optarg[0]; @@ -367,15 +355,6 @@ int main(int argc, char *argv[]){ } } - if(debug >= 3) - fprintf(stderr, - "argv0=%s\n" - "in=%s\tibs=%d\tskip=%ld\talign=%hhx\tcount=%d\n" - "out=%s\tobs=%d\tseek=%ld\tdebug=%2d\tnoerror=%d\n", - program_name, - ep[0].fn, ep[0].bs, ep[0].seek, align, count, - ep[1].fn, ep[1].bs, ep[1].seek, debug, noerror); - if(argc > optind){ terminate(ep); return usage(); @@ -404,10 +383,8 @@ int main(int argc, char *argv[]){ break; else if(ep[0].bufuse < ep[0].bs){ ++ep[0].prec; - if(debug >= 2){ - fprintf(stderr, "%s: Partial read:\n\t", program_name); - output(); - } + fprintf(stderr, "%s: Partial read:\n\t", program_name); + output(); if(!noerror) count = 1; if(align >= 0) @@ -432,10 +409,8 @@ int main(int argc, char *argv[]){ break; }else if(c > ep[1].bufuse && ep[1].bufuse > 0){ ep[1].prec += 1; - if(debug >= 2){ - fprintf(stderr, "%s: Partial write:\n\t", program_name); - output(); - } + fprintf(stderr, "%s: Partial write:\n\t", program_name); + output(); if(!noerror) count = 1; }else if(ep[1].bufuse == 0 && c < ep[1].bs) From 95f7992e0fbdbabac78c397c357523e85c2cc576 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 11:39:34 -0600 Subject: [PATCH 02/51] dj(1): fix usage text to be consistent with man page --- src/dj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dj.c b/src/dj.c index 27c160a..d4462e8 100644 --- a/src/dj.c +++ b/src/dj.c @@ -303,7 +303,7 @@ parse(char *s){ static int usage(void){ - fprintf(stderr, "Usage: %s (-AdfHqQ) (-a [byte]) (-c [count])\n" + fprintf(stderr, "Usage: %s (-AHn) (-a [byte]) (-c [count])\n" "\t(-i [input file]) (-b [input block size]) (-s [input offset])\n" "\t(-o [output file]) (-B [output block size]) (-S [output offset])\n", program_name); From 45a880455d440deb05034f7562cb756eae5287d7 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 12:22:33 -0600 Subject: [PATCH 03/51] dj(1): refactor to build again and to get rid of globals --- src/dj.c | 81 +++++++++++++++++++++++--------------------------------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/src/dj.c b/src/dj.c index d4462e8..e41f350 100644 --- a/src/dj.c +++ b/src/dj.c @@ -25,6 +25,8 @@ #include /* EX_OK, EX_USAGE */ #include /* close(2), getopt(3), lseek(2), read(2), write(2), * optarg, optind, STDIN_FILENO, STDOUT_FILENO */ +#include /* S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, + S_IWUSR */ extern int errno; /* dj uses two structures that respectively correspond to the reading and @@ -41,47 +43,17 @@ struct Io{ int prec; /* partial records processed */ int rec; /* records processed */ long seek; /* bytes to seek/skip (will be 0 after skippage) (-sS) */ -} ep[2]; /* "engineered pipe"; also "extended play", for the deejay */ +}; -/* Additionally, the following global variables are used to store user options. - */ +/* To be assigned to main:fmt_output and used with output(). */ +static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034"; +static char *fmt_human = "%d+%d > %d+%d; %d > %d\n"; -/* (-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 */ - 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) */ - -/* (-n) */ static char noerror; /* 0 - exits on partial reads or writes - * (default) - * 1 - retries on partial reads/writes - * (-n) */ - -/* Non-configurable defaults. */ -#define bs_default 1024 /* GNU dd(1) default; twice POSIX but a neat 2^10 */ static char *program_name = ""; static char *stdin_name = ""; static char *stdout_name = ""; -static int read_flags = O_RDONLY; /* These flags are consistent with Busybox */ -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; \ - 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) +static int read_flags = O_RDONLY; /* Consistent with Busybox dd(1). */ +static int write_flags = O_WRONLY | O_CREAT; #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -238,7 +210,7 @@ Io_read(struct Io *io){ static struct Io * Io_setdefaults(struct Io *io){ - io->bs = bs_default; + io->bs = 1024 /* bytes; 1 KiB */; /* GNU dd(1) default; POSIX says 512B */ io->buf = NULL; io->bytes = 0; io->fd = (io->fl == read_flags) ? STDIN_FILENO : STDOUT_FILENO; @@ -275,13 +247,13 @@ oserr(char *s){ } /* Prints statistics regarding the use of dj, particularly partially and - * completely read and written records, accessing ep and fmt_output. */ + * completely read and written records. */ static void -output(void){ +output(struct Io io[2], char *fmt){ - fprintf(stderr, fmt_output, - ep[0].rec, ep[0].prec, ep[1].rec, ep[1].prec, - ep[0].bytes, ep[1].bytes); + fprintf(stderr, fmt, + io[0].rec, io[0].prec, io[1].rec, io[1].prec, + io[0].bytes, io[1].bytes); return; } @@ -312,14 +284,27 @@ usage(void){ } int main(int argc, char *argv[]){ + int align; /* low 8b used, negative if no alignment is being done */ + int count; /* 0 if dj(1) runs until no more reads are possible */ + struct Io ep[2]; /* "engineered pipe"; also "extended play", for the DJ */ + char *fmt_output; /* fmt_asv (default) or fmt_human (-H) */ + char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ int c; int i; - setdefaults; + /* Set defaults. */ + align = -1; + count = 0; + fmt_output = fmt_asv; + noerror = 0; + ep[0].fl = read_flags; + Io_setdefaults(&ep[0]); + ep[1].fl = write_flags; + Io_setdefaults(&ep[1]); if(argc > 0){ program_name = argv[0]; - while((c = getopt(argc, argv, "a:Ab:B:c:di:hHnqs:S:o:")) != -1) + while((c = getopt(argc, argv, "a:Ab:B:c:i:hHns:S:o:")) != -1) switch(c){ case 'i': case 'o': i = (c == 'o'); @@ -369,7 +354,7 @@ int main(int argc, char *argv[]){ }else if(ep[i].seek > 0) switch(Io_fdseek(&ep[i])){ case EX_OK: - output(); + output(ep, fmt_output); terminate(ep); return EX_OK; } @@ -384,7 +369,7 @@ int main(int argc, char *argv[]){ else if(ep[0].bufuse < ep[0].bs){ ++ep[0].prec; fprintf(stderr, "%s: Partial read:\n\t", program_name); - output(); + output(ep, fmt_output); if(!noerror) count = 1; if(align >= 0) @@ -410,7 +395,7 @@ int main(int argc, char *argv[]){ }else if(c > ep[1].bufuse && ep[1].bufuse > 0){ ep[1].prec += 1; fprintf(stderr, "%s: Partial write:\n\t", program_name); - output(); + output(ep, fmt_output); if(!noerror) count = 1; }else if(ep[1].bufuse == 0 && c < ep[1].bs) @@ -420,7 +405,7 @@ int main(int argc, char *argv[]){ }while(ep[0].bufuse > 0); }while(count == 0 || --count > 0); - output(); + output(ep, fmt_output); terminate(ep); return EX_OK; From 66f54982320eb6ca2f47da26c8dac78aaf4aaecc Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 12:40:36 -0600 Subject: [PATCH 04/51] dj(1): refactor Io_fdseek --- src/dj.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/dj.c b/src/dj.c index e41f350..7c0b7c3 100644 --- a/src/dj.c +++ b/src/dj.c @@ -156,18 +156,15 @@ Io_fdopen(struct Io *io, char *fn){ return fd; } -/* Seeks io->seek bytes through *io's file descriptor, (counter-intuitively) - * returning -1 if successful and a sysexits.h exit code if an unrecoverable - * error occurred. io->buf will be cleared of useful bytes and io->seek will - * be set to zero to indicate the seek occurred. */ -static int +/* 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){ - int (*op)(int, void *, size_t); - if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1) - return -1; + if(io->seek != 0 + || (!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)) + return; - /* repeated code to get the condition out of the loop */ 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), @@ -186,12 +183,11 @@ Io_fdseek(struct Io *io){ /* second chance */ io->bufuse = read(io->fd, io->buf, MIN(io->bs, io->seek)); }while((io->seek -= io->bufuse) > 0 && io->bufuse != 0); - }else - return EX_SOFTWARE; + } io->bufuse = 0; - return -1; + return; } /* Reads io->bs bytes from *io's file descriptor into io->buf, storing the @@ -352,11 +348,10 @@ int main(int argc, char *argv[]){ terminate(ep); return EX_OSERR; }else if(ep[i].seek > 0) - switch(Io_fdseek(&ep[i])){ - case EX_OK: - output(ep, fmt_output); + Io_fdseek(&ep[i]); + if(ep[i].seek > 0){ terminate(ep); - return EX_OK; + return oserr(ep[i].fn); } } From b70b356ce5ec6a78f029a8db0df6b5a3d3944827 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 12:45:50 -0600 Subject: [PATCH 05/51] dj(1): remove Io_buffree --- src/dj.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/dj.c b/src/dj.c index 7c0b7c3..01832a8 100644 --- a/src/dj.c +++ b/src/dj.c @@ -67,8 +67,8 @@ static int write_flags = O_WRONLY | O_CREAT; * 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). */ #define terminate(io) do{ \ - Io_buffree(&(io)[0]); \ - Io_buffree(&(io)[1]); \ + free((io[0]).buf); \ + free((io[1]).buf); \ Io_fdclose(&(io)[0]); \ Io_fdclose(&(io)[1]); }while(0) @@ -79,15 +79,6 @@ 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 * From e65f6b650df5323d66ec94e3b8fe5b792f6e5fb5 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 13:41:24 -0600 Subject: [PATCH 06/51] dj(1): more refactor (get rid of ep pun) --- src/dj.c | 101 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/src/dj.c b/src/dj.c index 01832a8..af842a4 100644 --- a/src/dj.c +++ b/src/dj.c @@ -156,6 +156,9 @@ Io_fdseek(struct Io *io){ || (!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), @@ -273,7 +276,7 @@ usage(void){ int main(int argc, char *argv[]){ int align; /* low 8b used, negative if no alignment is being done */ int count; /* 0 if dj(1) runs until no more reads are possible */ - struct Io ep[2]; /* "engineered pipe"; also "extended play", for the DJ */ + struct Io io[2]; char *fmt_output; /* fmt_asv (default) or fmt_human (-H) */ char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ int c; @@ -284,10 +287,10 @@ int main(int argc, char *argv[]){ count = 0; fmt_output = fmt_asv; noerror = 0; - ep[0].fl = read_flags; - Io_setdefaults(&ep[0]); - ep[1].fl = write_flags; - Io_setdefaults(&ep[1]); + io[0].fl = read_flags; + Io_setdefaults(&io[0]); + io[1].fl = write_flags; + Io_setdefaults(&io[1]); if(argc > 0){ program_name = argv[0]; @@ -296,12 +299,12 @@ int main(int argc, char *argv[]){ case 'i': case 'o': i = (c == 'o'); if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */ - ep[i].fd = (i == 0) ? STDIN_FILENO : STDOUT_FILENO; - ep[i].fn = (i == 0) ? stdin_name : stdout_name; + io[i].fd = (i == 0) ? STDIN_FILENO : STDOUT_FILENO; + io[i].fn = (i == 0) ? stdin_name : stdout_name; break; - }else if(Io_fdopen(&ep[i], optarg) != -1) + }else if(Io_fdopen(&io[i], optarg) != -1) break; - terminate(ep); + terminate(io); return oserr(optarg); case 'A': align = '\0'; break; case 'n': noerror = 1; break; @@ -317,82 +320,82 @@ int main(int argc, char *argv[]){ break; i = isupper(c); c = tolower(c); - if((c == 'b' && (ep[i].bs = parse(optarg)) > 0) - || (c == 's' && (ep[i].seek = parse(optarg)) >= 0)) + if((c == 'b' && (io[i].bs = parse(optarg)) > 0) + || (c == 's' && (io[i].seek = parse(optarg)) >= 0)) break; /* FALLTHROUGH */ default: - terminate(ep); + terminate(io); return usage(); } } if(argc > optind){ - terminate(ep); + terminate(io); return usage(); } - for(i = 0; i <= 1; ++i){ - if(Io_bufalloc(&ep[i]) == NULL){ + for(i = 0; i < 2; ++i){ + if(Io_bufalloc(&io[i]) == NULL){ fprintf(stderr, "%s: Failed to allocate %d bytes\n", - program_name, ep[i].bs); - terminate(ep); + program_name, io[i].bs); + terminate(io); return EX_OSERR; - }else if(ep[i].seek > 0) - Io_fdseek(&ep[i]); - if(ep[i].seek > 0){ - terminate(ep); - return oserr(ep[i].fn); + }else if(io[i].seek > 0) + Io_fdseek(&io[i]); + if(io[i].seek > 0){ + terminate(io); + return oserr(io[i].fn); } } do{ /* read */ - Io_read(&ep[0]); - if(!noerror && ep[0].bufuse == 0) - Io_read(&ep[0]); /* second chance */ - if(ep[0].bufuse == 0) /* that's all she wrote */ + Io_read(&io[0]); + if(!noerror && io[0].bufuse == 0) + Io_read(&io[0]); /* second chance */ + if(io[0].bufuse == 0) /* that's all she wrote */ break; - else if(ep[0].bufuse < ep[0].bs){ - ++ep[0].prec; + else if(io[0].bufuse < io[0].bs){ + ++io[0].prec; fprintf(stderr, "%s: Partial read:\n\t", program_name); - output(ep, fmt_output); + output(io, fmt_output); if(!noerror) count = 1; if(align >= 0) - Io_bufrpad(&ep[0], align); + Io_bufrpad(&io[0], align); }else - ++ep[0].rec; + ++io[0].rec; /* write */ - do{ if(ep[1].bs > ep[0].bs){ /* io[1].bs > io[0].bs */ - Io_bufxapp(&ep[1], &ep[0]); - if(ep[0].bs + ep[1].bufuse <= ep[1].bs && count != 1) + do{ if(io[1].bs > io[0].bs){ /* io[1].bs > io[0].bs */ + Io_bufxapp(&io[1], &io[0]); + if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) continue; /* we could write more */ }else - Io_bufxfer(&ep[1], &ep[0], MIN(ep[0].bufuse, ep[1].bs)); + Io_bufxfer(&io[1], &io[0], MIN(io[0].bufuse, io[1].bs)); - c = ep[1].bufuse; - Io_write(&ep[1]); - if(!noerror && ep[1].bufuse == c) - Io_write(&ep[1]); /* second chance */ - if(c == ep[1].bufuse){ /* no more love */ + c = io[1].bufuse; + Io_write(&io[1]); + if(!noerror && io[1].bufuse == c) + Io_write(&io[1]); /* second chance */ + if(c == io[1].bufuse){ /* no more love */ count = 1; break; - }else if(c > ep[1].bufuse && ep[1].bufuse > 0){ - ep[1].prec += 1; + }else if(c > io[1].bufuse && io[1].bufuse > 0){ + io[1].prec += 1; fprintf(stderr, "%s: Partial write:\n\t", program_name); - output(ep, fmt_output); + output(io, fmt_output); if(!noerror) count = 1; - }else if(ep[1].bufuse == 0 && c < ep[1].bs) - ++ep[1].prec; + }else if(io[1].bufuse == 0 && c < io[1].bs) + ++io[1].prec; else - ++ep[1].rec; - }while(ep[0].bufuse > 0); + ++io[1].rec; + }while(io[0].bufuse > 0); }while(count == 0 || --count > 0); - output(ep, fmt_output); - terminate(ep); + output(io, fmt_output); + terminate(io); return EX_OK; } From fb74e7bef036d266b34fc93621d6889bfa31b61d Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 13:45:36 -0600 Subject: [PATCH 07/51] dj(1), dj.1: Remove -A (use -a "\0") (see #101) --- docs/dj.1 | 10 +++------- src/dj.c | 7 +++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 5de56eb..1494827 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -11,7 +11,7 @@ dj \(en disk jockey .SH SYNOPSIS dj -.RB ( -AHn ) +.RB ( -Hn ) .RB ( -a .RB [ byte ]) .RB ( -c @@ -67,12 +67,8 @@ Seeks a number of bytes through the output before starting to write from the input. If the output is a stream, null characters are printed. .IP \fB-a\fP Accepts a single literal byte with which the input buffer is padded in the event -of an incomplete read from the input file. -.IP \fB-A\fP -Specifying this option pads the input buffer with null bytes in the event of an -incomplete read. This is equivalent to specifying -.B -a -with a null byte instead of a character. +of an incomplete read from the input file. If the option argument is empty, the +null byte is used. .IP \fB-c\fP Specifies a number of reads to make. The default is 0, in which case the input is read until a partial or empty read is made. diff --git a/src/dj.c b/src/dj.c index af842a4..b45513e 100644 --- a/src/dj.c +++ b/src/dj.c @@ -265,7 +265,7 @@ parse(char *s){ static int usage(void){ - fprintf(stderr, "Usage: %s (-AHn) (-a [byte]) (-c [count])\n" + fprintf(stderr, "Usage: %s (-Hn) (-a [byte]) (-c [count])\n" "\t(-i [input file]) (-b [input block size]) (-s [input offset])\n" "\t(-o [output file]) (-B [output block size]) (-S [output offset])\n", program_name); @@ -294,7 +294,7 @@ int main(int argc, char *argv[]){ if(argc > 0){ program_name = argv[0]; - while((c = getopt(argc, argv, "a:Ab:B:c:i:hHns:S:o:")) != -1) + while((c = getopt(argc, argv, "a:b:B:c:i:hHns:S:o:")) != -1) switch(c){ case 'i': case 'o': i = (c == 'o'); @@ -306,11 +306,10 @@ int main(int argc, char *argv[]){ break; terminate(io); return oserr(optarg); - case 'A': align = '\0'; break; case 'n': noerror = 1; break; case 'H': fmt_output = fmt_human; break; case 'a': - if(optarg[0] != '\0' && optarg[1] == '\0'){ + if(optarg[0] == '\0' || optarg[1] == '\0'){ align = optarg[0]; break; } From 2cfae0e8d7595964a492bf03f601512f0c7d15be Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 15:15:37 -0600 Subject: [PATCH 08/51] dj(1): refactor (remove Io_setdefaults and other stuff) --- src/dj.c | 66 ++++++++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/dj.c b/src/dj.c index b45513e..5632645 100644 --- a/src/dj.c +++ b/src/dj.c @@ -16,7 +16,6 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -#include /* isupper(3), tolower(3) */ #include /* errno */ #include /* open(2) */ #include /* fprintf(3), stderr */ @@ -29,6 +28,8 @@ S_IWUSR */ extern int errno; +char *program_name = "dj"; + /* dj uses two structures that respectively correspond to the reading and * writing ends of its jockeyed "pipe". User-configurable members are noted * with their relevant options. */ @@ -49,9 +50,9 @@ struct Io{ static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034"; static char *fmt_human = "%d+%d > %d+%d; %d > %d\n"; -static char *program_name = ""; static char *stdin_name = ""; static char *stdout_name = ""; + static int read_flags = O_RDONLY; /* Consistent with Busybox dd(1). */ static int write_flags = O_WRONLY | O_CREAT; @@ -195,23 +196,6 @@ Io_read(struct Io *io){ return io; } -/* Sets the variables in a struct *io to the defaults. Identifies the read/ - * write ends of the "pipe" by checking io->fl. Returns io. */ -static struct Io * -Io_setdefaults(struct Io *io){ - - io->bs = 1024 /* bytes; 1 KiB */; /* GNU dd(1) default; POSIX says 512B */ - io->buf = NULL; - io->bytes = 0; - io->fd = (io->fl == read_flags) ? STDIN_FILENO : STDOUT_FILENO; - io->fn = (io->fl == read_flags) ? stdin_name : stdout_name; - io->prec = 0; - io->rec = 0; - io->seek = 0; - - return io; -} - /* Writes io->bufuse units from io->buf to io->fd, permuting any unwritten * bytes to the start of io->buf and updating io->bufuse. If io->bufuse doesn't * change, errno will probably be set. Returns io. */ @@ -263,12 +247,12 @@ parse(char *s){ } static int -usage(void){ +usage(char *s){ fprintf(stderr, "Usage: %s (-Hn) (-a [byte]) (-c [count])\n" "\t(-i [input file]) (-b [input block size]) (-s [input offset])\n" "\t(-o [output file]) (-B [output block size]) (-S [output offset])\n", - program_name); + s); return EX_USAGE; } @@ -276,31 +260,37 @@ usage(void){ int main(int argc, char *argv[]){ int align; /* low 8b used, negative if no alignment is being done */ int count; /* 0 if dj(1) runs until no more reads are possible */ + char *fmt_output; /* == fmt_asv (default) or fmt_human (-H) */ + size_t i; /* side of io being modified */ struct Io io[2]; - char *fmt_output; /* fmt_asv (default) or fmt_human (-H) */ char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ - int c; - int i; /* Set defaults. */ align = -1; count = 0; fmt_output = fmt_asv; noerror = 0; - io[0].fl = read_flags; - Io_setdefaults(&io[0]); - io[1].fl = write_flags; - Io_setdefaults(&io[1]); + for(i = 0; i < 2; ++i){ + io[i].bs = 1024 /* 1 KiB */; /* GNU dd(1) default; POSIX says 512B */ + io[i].bytes = 0; + io[i].fd = i ? STDIN_FILENO : STDOUT_FILENO; + io[i].fn = i ? stdin_name : stdout_name; + io[i].fl = i ? read_flags : write_flags; + io[i].prec = 0; + io[i].rec = 0; + io[i].seek = 0; + } if(argc > 0){ + int c; + program_name = argv[0]; while((c = getopt(argc, argv, "a:b:B:c:i:hHns:S:o:")) != -1) switch(c){ - case 'i': case 'o': - i = (c == 'o'); + case 'i': case 'o': i = (c == 'o') if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */ - io[i].fd = (i == 0) ? STDIN_FILENO : STDOUT_FILENO; - io[i].fn = (i == 0) ? stdin_name : stdout_name; + io[i].fd = i ? STDIN_FILENO : STDOUT_FILENO; + io[i].fn = i ? stdin_name : stdout_name; break; }else if(Io_fdopen(&io[i], optarg) != -1) break; @@ -314,24 +304,24 @@ int main(int argc, char *argv[]){ break; } /* FALLTHROUGH */ - case 'c': case 'b': case 's': case 'B': case 'S': + case 'c': case 'b': case 's': case 'B': case 'S': /* numbers */ if(c == 'c' && (count = parse(optarg)) >= 0) break; - i = isupper(c); - c = tolower(c); + i = (c >= 'A' && c <= 'Z'); /* uppercase changes output */ + c &= 0x20 /* 0b 0010 0000 */; /* (ASCII) make lowercase */ if((c == 'b' && (io[i].bs = parse(optarg)) > 0) || (c == 's' && (io[i].seek = parse(optarg)) >= 0)) break; /* FALLTHROUGH */ default: terminate(io); - return usage(); + return usage(program_name); } } if(argc > optind){ terminate(io); - return usage(); + return usage(program_name); } for(i = 0; i < 2; ++i){ @@ -366,7 +356,7 @@ int main(int argc, char *argv[]){ ++io[0].rec; /* write */ - do{ if(io[1].bs > io[0].bs){ /* io[1].bs > io[0].bs */ + do{ if(io[1].bs > io[0].bs){ Io_bufxapp(&io[1], &io[0]); if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) continue; /* we could write more */ From 3a66022c6d735a3def3e52f4883178dfafe6d24f Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 26 Jun 2024 15:28:02 -0600 Subject: [PATCH 09/51] dj(1): more refactor (get rid of the c scratch variable, use scoping) --- src/dj.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/dj.c b/src/dj.c index 5632645..a9e7cdf 100644 --- a/src/dj.c +++ b/src/dj.c @@ -287,7 +287,7 @@ int main(int argc, char *argv[]){ program_name = argv[0]; while((c = getopt(argc, argv, "a:b:B:c:i:hHns:S:o:")) != -1) switch(c){ - case 'i': case 'o': i = (c == 'o') + case 'i': case 'o': i = (c == 'o'); if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */ io[i].fd = i ? STDIN_FILENO : STDOUT_FILENO; io[i].fn = i ? stdin_name : stdout_name; @@ -356,27 +356,30 @@ int main(int argc, char *argv[]){ ++io[0].rec; /* write */ - do{ if(io[1].bs > io[0].bs){ + do{ + int t; + + if(io[1].bs > io[0].bs){ Io_bufxapp(&io[1], &io[0]); if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) continue; /* we could write more */ }else Io_bufxfer(&io[1], &io[0], MIN(io[0].bufuse, io[1].bs)); - c = io[1].bufuse; + t = io[1].bufuse; Io_write(&io[1]); - if(!noerror && io[1].bufuse == c) + if(!noerror && io[1].bufuse == t) Io_write(&io[1]); /* second chance */ - if(c == io[1].bufuse){ /* no more love */ + if(t == io[1].bufuse){ /* no more love */ count = 1; break; - }else if(c > io[1].bufuse && io[1].bufuse > 0){ + }else if(t > io[1].bufuse && io[1].bufuse > 0){ io[1].prec += 1; fprintf(stderr, "%s: Partial write:\n\t", program_name); output(io, fmt_output); if(!noerror) count = 1; - }else if(io[1].bufuse == 0 && c < io[1].bs) + }else if(io[1].bufuse == 0 && t < io[1].bs) ++io[1].prec; else ++io[1].rec; From e341c38cd63941e65ee32a91c9c9c653d4b761fb Mon Sep 17 00:00:00 2001 From: emma Date: Sat, 29 Jun 2024 05:18:20 -0600 Subject: [PATCH 10/51] docs, src: updates usage text for utilities --- docs/dj.1 | 26 +++++++++----------------- docs/intcmp.1 | 4 +--- docs/mm.1 | 8 +++----- docs/npc.1 | 2 +- docs/scrut.1 | 4 ++-- docs/str.1 | 3 +-- docs/strcmp.1 | 3 +-- docs/swab.1 | 14 +++++--------- src/dj.c | 6 +++--- src/intcmp.c | 2 +- src/mm.c | 2 +- src/npc.c | 2 +- src/scrut.c | 2 +- src/str.c | 2 +- src/strcmp.c | 2 +- src/swab.rs | 2 +- 16 files changed, 33 insertions(+), 51 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 7031ccf..4b0b5b1 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -11,25 +11,17 @@ dj \(en disk jockey .SH SYNOPSIS dj -.RB ( -AdHnq ) -.RB ( -a -.RB [ byte ]) -.RB ( -c -.RB [ count ]) +.RB [ -AdHnq ] +.RB [ -a\ byte ] +.RB [ -c\ count ] -.RB ( -i -[\fBinput file\fP]) -.RB ( -b -[\fBinput block size\fP]) -.RB ( -s -[\fBinput offset\fP]) +.RB [ -i\ input_file ] +.RB [ -b\ input_block_size ] +.RB [ -s\ input_offset ] -.RB ( -o -[\fBoutput file\fP]) -.RB ( -B -[\fBoutput block size\fP]) -.RB ( -S -[\fBoutput offset\fP]) +.RB [ -o\ output_file ] +.RB [ -B\ output_block_size ] +.RB [ -S\ output_offset ] .\" .SH DESCRIPTION diff --git a/docs/intcmp.1 b/docs/intcmp.1 index 034a4fd..b90f23a 100644 --- a/docs/intcmp.1 +++ b/docs/intcmp.1 @@ -11,9 +11,7 @@ intcmp \(en compare integers .SH SYNOPSIS intcmp -.RB ( -egl ) -.RB [ integer ] -.RB [ integer... ] +.RB [ -egl ]\ integer\ integer... .SH DESCRIPTION Compare integers to each other. .\" diff --git a/docs/mm.1 b/docs/mm.1 index 2ff9f44..735f9df 100644 --- a/docs/mm.1 +++ b/docs/mm.1 @@ -10,11 +10,9 @@ mm \(en middleman .SH SYNOPSIS mm -.RB ( -aenu ) -.RB ( -i -.RB [ input ]) -.RB ( -o -.RB [ output ]) +.RB [ -aenu ] +.RB [ -i\ input ] +.RB [ -o\ output ] .\" .SH DESCRIPTION diff --git a/docs/npc.1 b/docs/npc.1 index 3e7af39..5acee9e 100644 --- a/docs/npc.1 +++ b/docs/npc.1 @@ -11,7 +11,7 @@ npc \(en show non-printing characters .SH SYNOPSIS npc -.RB ( -et ) +.RB [ -et ] .\" .SH DESCRIPTION diff --git a/docs/scrut.1 b/docs/scrut.1 index 56383b8..2b95bee 100644 --- a/docs/scrut.1 +++ b/docs/scrut.1 @@ -10,8 +10,8 @@ scrut \(en scrutinize file properties .SH SYNOPSIS scrut -.RB ( -LSbcdefgkprsuwx ) -.RB [ file... ] +.RB [ -LSbcdefgkprsuwx ] +.B file... .\" .SH DESCRIPTION diff --git a/docs/str.1 b/docs/str.1 index 22ffea1..1a4d8e4 100644 --- a/docs/str.1 +++ b/docs/str.1 @@ -11,8 +11,7 @@ str \(en test string arguments .SH SYNOPSIS str -.RB [ type ] -.RB [ string... ] +.B type string... .\" .SH DESCRIPTION diff --git a/docs/strcmp.1 b/docs/strcmp.1 index 0ad21b2..c99c8c8 100644 --- a/docs/strcmp.1 +++ b/docs/strcmp.1 @@ -11,8 +11,7 @@ strcmp \(en compare strings .SH SYNOPSIS strcmp -.RM [ string ] -.RB [ strings... ] +.B string string... .\" .SH DESCRIPTION diff --git a/docs/swab.1 b/docs/swab.1 index 72f0f19..e589c10 100644 --- a/docs/swab.1 +++ b/docs/swab.1 @@ -11,11 +11,8 @@ swab \(en swap bytes .SH SYNOPSIS swab -.RB ( -f ) -.RB ( -w -.R [ -.B word size -.R ]) +.RB [ -f ] +.RB [ -w\ word_size ] .\" .SH DESCRIPTION @@ -26,10 +23,9 @@ Swap the latter and former halves of a block of bytes. .IP \fB-f\fP Ignore SIGINT signal. .IP \fB-w\fP -Configures the word size; that is, the size in bytes of the block size -on which to operate. The default word size is 2. The word size must be -cleanly divisible by 2, otherwise the block of bytes being processed can\(cqt be -halved. +Configures the word size; that is, the size in bytes of the block size on which +to operate. The default word size is 2. The word size must be cleanly divisible +by 2, otherwise the block of bytes being processed can\(cqt be halved. .\" .SH EXAMPLES diff --git a/src/dj.c b/src/dj.c index 8a6732c..3885012 100644 --- a/src/dj.c +++ b/src/dj.c @@ -313,9 +313,9 @@ parse(char *s){ static int usage(void){ - fprintf(stderr, "Usage: %s (-AdfHqQ) (-a [byte]) (-c [count])\n" - "\t(-i [input file]) (-b [input block size]) (-s [input offset])\n" - "\t(-o [output file]) (-B [output block size]) (-S [output offset])\n", + fprintf(stderr, "Usage: %s [-AdfHqQ] [-a byte] [-c count]\n" + "\t[-i input_file] [-b input_block_size] [-s input_offset]\n" + "\t[-o output_file] [-B output_block_size] [-S output_offset]\n", program_name); return EX_USAGE; diff --git a/src/intcmp.c b/src/intcmp.c index 408474b..1fd278d 100644 --- a/src/intcmp.c +++ b/src/intcmp.c @@ -52,7 +52,7 @@ int main(int argc, char *argv[]){ if(optind + 2 /* ref cmp */ > argc){ usage: fprintf(stderr, - "Usage: %s (-eghl) [integer] [integer...]\n", + "Usage: %s [-eghl] integer integer...\n", argv[0] == NULL ? program_name : argv[0]); return EX_USAGE; } diff --git a/src/mm.c b/src/mm.c index dc337b7..e905b35 100644 --- a/src/mm.c +++ b/src/mm.c @@ -110,7 +110,7 @@ oserr(char *s, char *r){ * returns an exit status appropriate for a usage error. */ int usage(char *s){ - fprintf(stderr, "Usage: %s (-aenu) (-i [input])... (-o [output])...\n", s); + fprintf(stderr, "Usage: %s [-aenu] [-i input]... [-o output]...\n", s); return EX_USAGE; } diff --git a/src/npc.c b/src/npc.c index 8b97180..6b5e5f0 100644 --- a/src/npc.c +++ b/src/npc.c @@ -39,7 +39,7 @@ int main(int argc, char *argv[]){ } if(argc > optind){ -usage: fprintf(stderr, "Usage: %s (-eht)\n", argv[0]); +usage: fprintf(stderr, "Usage: %s [-eht]\n", argv[0]); return EX_USAGE; } diff --git a/src/scrut.c b/src/scrut.c index c5b675f..d85d243 100644 --- a/src/scrut.c +++ b/src/scrut.c @@ -66,7 +66,7 @@ int main(int argc, char *argv[]){ if(ops[i] == 'e') continue; else if(ops[i] == 'h'){ -usage: fprintf(stderr, "Usage: %s (-%s) [file...]\n", +usage: fprintf(stderr, "Usage: %s [-%s] file...\n", argv[0] == NULL ? program_name : argv[0], diff --git a/src/str.c b/src/str.c index ae03b1d..b4725eb 100644 --- a/src/str.c +++ b/src/str.c @@ -56,7 +56,7 @@ int main(int argc, char *argv[]){ goto pass; } - fprintf(stderr, "Usage: %s [type] [string...]\n", + fprintf(stderr, "Usage: %s type string...\n", argv[0] == NULL ? program_name : argv[0]); return EX_USAGE; diff --git a/src/strcmp.c b/src/strcmp.c index acb4946..33b73c2 100644 --- a/src/strcmp.c +++ b/src/strcmp.c @@ -8,7 +8,7 @@ int main(int argc, char *argv[]){ int i; if(argc < 3){ - fprintf(stderr, "Usage: %s [string] [string...]\n", + fprintf(stderr, "Usage: %s string string...\n", argv[0] == NULL ? program_name : argv[0]); return EX_USAGE; } diff --git a/src/swab.rs b/src/swab.rs index ca944d9..471f92b 100644 --- a/src/swab.rs +++ b/src/swab.rs @@ -35,7 +35,7 @@ fn oserr(s: &str, e: Error) -> ExitCode { } fn usage(s: &str) -> ExitCode { - eprintln!("Usage: {} (-f) (-w [wordsize])", s); + eprintln!("Usage: {} [-f] [-w word_size]", s); ExitCode::from(EX_USAGE as u8) } From 07a12ba81c29e06775eb129a187a4db358cac4a2 Mon Sep 17 00:00:00 2001 From: emma Date: Sat, 29 Jun 2024 05:28:23 -0600 Subject: [PATCH 11/51] docs, src: fixing man page and usage text readability --- docs/dj.1 | 28 ++++++++++++++-------------- docs/fop.1 | 2 +- docs/mm.1 | 4 ++-- docs/swab.1 | 2 +- src/dj.c | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 4b0b5b1..43c2750 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -15,13 +15,13 @@ dj .RB [ -a\ byte ] .RB [ -c\ count ] -.RB [ -i\ input_file ] -.RB [ -b\ input_block_size ] -.RB [ -s\ input_offset ] +.RB [ -i\ file ] +.RB [ -b\ block_size ] +.RB [ -s\ offset ] -.RB [ -o\ output_file ] -.RB [ -B\ output_block_size ] -.RB [ -S\ output_offset ] +.RB [ -o\ file ] +.RB [ -B\ block_size ] +.RB [ -S\ offset ] .\" .SH DESCRIPTION @@ -39,25 +39,25 @@ immediately subsequent to the specified byte. .\" .SH OPTIONS -.IP \fB-i\fP +.IP \fB-i\fP\ \fIfile\fP Takes a file path as an argument and opens it for use as an input. -.IP \fB-b\fP +.IP \fB-b\fP\ \fIblock_size\fP Takes a numeric argument as the size in bytes of the input buffer, the default being 1024. -.IP \fB-s\fP +.IP \fB-s\fP\ \fIoffset\fP Takes a numeric argument as the number of bytes to skip into the input before starting to read. If the standard input is used, bytes read to this point are discarded. -.IP \fB-o\fP +.IP \fB-o\fP\ \fIfile\fP Takes a file path as an argument and opens it for use as an output. -.IP \fB-B\fP +.IP \fB-B\fP\ \fIblock_size\fP Does the same as .B -b but for the output buffer. -.IP \fB-S\fP +.IP \fB-S\fP\ \fIoffset\fP Seeks a number of bytes through the output before starting to write from the input. If the output is a stream, null characters are printed. -.IP \fB-a\fP +.IP \fB-a\fP\ \fIbyte\fP Accepts a single literal byte with which the input buffer is padded in the event of an incomplete read from the input file. .IP \fB-A\fP @@ -65,7 +65,7 @@ Specifying this option pads the input buffer with null bytes in the event of an incomplete read. This is equivalent to specifying .B -a with a null byte instead of a character. -.IP \fB-c\fP +.IP \fB-c\fP\ \fIcount\fP Specifies a number of reads to make. The default is 0, in which case the input is read until a partial or empty read is made. .IP \fB-d\fP diff --git a/docs/fop.1 b/docs/fop.1 index d777c68..b96033a 100644 --- a/docs/fop.1 +++ b/docs/fop.1 @@ -22,7 +22,7 @@ Performs operations on specified fields in data read from the standard input. .\" .SH OPTIONS -.IP \fB-d\fP +.IP \fB-d\fP\ \fIdelimiter\fP Sets a delimiter by which the input data will be split into fields. The default is an ASCII record separator. .\" diff --git a/docs/mm.1 b/docs/mm.1 index 735f9df..2916aa7 100644 --- a/docs/mm.1 +++ b/docs/mm.1 @@ -24,10 +24,10 @@ Catenate input files and write them to the start of each output file or stream. Opens subsequent outputs for appending rather than updating. .IP \fB-e\fP Use the standard error as an output. -.IP \fB-i\fP +.IP \fB-i\fP\ \fIinput\fP Opens a path as an input. If one or more of the input files is \(lq-\(rq or if no inputs are specified, the standard input shall be used. -.IP \fB-o\fP +.IP \fB-o\fP\ \fIoutput\fP Opens a path as an output. If one or more of the output files is \(lq-\(rq or if no outputs are specified, the standard output shall be used. .IP \fB-u\fP diff --git a/docs/swab.1 b/docs/swab.1 index e589c10..42eef95 100644 --- a/docs/swab.1 +++ b/docs/swab.1 @@ -22,7 +22,7 @@ Swap the latter and former halves of a block of bytes. .IP \fB-f\fP Ignore SIGINT signal. -.IP \fB-w\fP +.IP \fB-w\fP\ \fIword_size\fP Configures the word size; that is, the size in bytes of the block size on which to operate. The default word size is 2. The word size must be cleanly divisible by 2, otherwise the block of bytes being processed can\(cqt be halved. diff --git a/src/dj.c b/src/dj.c index 3885012..beafde8 100644 --- a/src/dj.c +++ b/src/dj.c @@ -314,8 +314,8 @@ static int usage(void){ fprintf(stderr, "Usage: %s [-AdfHqQ] [-a byte] [-c count]\n" - "\t[-i input_file] [-b input_block_size] [-s input_offset]\n" - "\t[-o output_file] [-B output_block_size] [-S output_offset]\n", + "\t[-i file] [-b block_size] [-s offset]\n" + "\t[-o file] [-B block_size] [-S offset]\n", program_name); return EX_USAGE; From 67b60e20cc8bb44dc8069a6b2538629f1bbc22ab Mon Sep 17 00:00:00 2001 From: DTB Date: Sat, 29 Jun 2024 05:55:29 -0600 Subject: [PATCH 12/51] dj.1: Man page fixes --- docs/dj.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 1494827..8fd01f4 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -4,7 +4,7 @@ .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" visit . .\" -.TH DJ 1 2024-06-17 "Bonsai Core Utilites 0.13.11" +.TH DJ 1 2024-06-29 "Harakit X.X.X" .SH NAME dj \(en disk jockey .\" @@ -73,7 +73,7 @@ null byte is used. Specifies a number of reads to make. The default is 0, in which case the input is read until a partial or empty read is made. .IP \fB-H\fP -Prints diagnostics messages in a human-readable manner as described in the +Prints diagnostic messages in a human-readable manner as described in the DIAGNOSTICS section. .IP \fB-n\fP Retries failed reads once before exiting. @@ -89,7 +89,7 @@ one or more of the input files is \(lq-\(rq. .\" .SH DIAGNOSTICS -On a partial or empty read a diagnostic message is printed. Then, the program +On a partial or empty read, a diagnostic message is printed. Then, the program exits unless the .B -n option is specified. From 17455baeab063443a69d1e73848465ebdc25d254 Mon Sep 17 00:00:00 2001 From: emma Date: Sat, 29 Jun 2024 06:38:55 -0600 Subject: [PATCH 13/51] intcmp(1), npc(1): removes vestigial option --- src/intcmp.c | 2 +- src/npc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/intcmp.c b/src/intcmp.c index 1fd278d..d6dff0d 100644 --- a/src/intcmp.c +++ b/src/intcmp.c @@ -52,7 +52,7 @@ int main(int argc, char *argv[]){ if(optind + 2 /* ref cmp */ > argc){ usage: fprintf(stderr, - "Usage: %s [-eghl] integer integer...\n", + "Usage: %s [-egl] integer integer...\n", argv[0] == NULL ? program_name : argv[0]); return EX_USAGE; } diff --git a/src/npc.c b/src/npc.c index 6b5e5f0..1f96668 100644 --- a/src/npc.c +++ b/src/npc.c @@ -39,7 +39,7 @@ int main(int argc, char *argv[]){ } if(argc > optind){ -usage: fprintf(stderr, "Usage: %s [-eht]\n", argv[0]); +usage: fprintf(stderr, "Usage: %s [-et]\n", argv[0]); return EX_USAGE; } From 261c98ad1446c43e82076950eca0597274f9b211 Mon Sep 17 00:00:00 2001 From: emma Date: Sat, 29 Jun 2024 08:24:11 -0600 Subject: [PATCH 14/51] Makefile: docs no longer builds every invocation, normalize PREFIX for setting man dir --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index fb9aa5d..99cda9f 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,10 @@ DESTDIR ?= dist PREFIX ?= /usr/local -MANDIR != [ $(PREFIX) = / ] && printf '/usr/share/man\n' \ +# normalized prefix +PREFIX_N != (test -d $(PREFIX) && [ '-' != $(PREFIX) ] \ + && CDPATH= cd -P -- $(PREFIX) && pwd -P) +MANDIR != [ $(PREFIX_N) = / ] && printf '/usr/share/man\n' \ || printf '/share/man\n' SYSEXITS != printf '\043include \n' | cpp -M - | sed 's/ /\n/g' \ | sed -n 's/sysexits\.h//p' || printf 'include\n' @@ -29,7 +32,7 @@ RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \ CFLAGS += -I$(SYSEXITS) .PHONY: all -all: docs dj false fop hru intcmp mm npc rpn scrut str strcmp swab true +all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true # keep build/include until bindgen(1) has stdin support # https://github.com/rust-lang/rust-bindgen/issues/2703 @@ -40,7 +43,7 @@ build: clean: rm -rf build dist -dist: all +dist: all docs mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/share/man/man1 cp build/bin/* $(DESTDIR)/$(PREFIX)/bin cp build/docs/*.1 $(DESTDIR)/$(PREFIX)/$(MANDIR)/man1 From e38ea5b35d74e79d427b0cc1c6d01f7a90eb7976 Mon Sep 17 00:00:00 2001 From: emma Date: Sat, 29 Jun 2024 08:36:12 -0600 Subject: [PATCH 15/51] Makefile: fixes dist --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 99cda9f..581d2a1 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ clean: rm -rf build dist dist: all docs - mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/share/man/man1 + mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/$(MANDIR)/man1 cp build/bin/* $(DESTDIR)/$(PREFIX)/bin cp build/docs/*.1 $(DESTDIR)/$(PREFIX)/$(MANDIR)/man1 From 6b28a12b731b06abc6691e325d95dbc17523d90a Mon Sep 17 00:00:00 2001 From: DTB Date: Sat, 29 Jun 2024 19:14:08 -0600 Subject: [PATCH 16/51] dj.1: last minute changes --- docs/dj.1 | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 8fd01f4..0e69afc 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -53,9 +53,9 @@ Takes a file path as an argument and opens it for use as an input. Takes a numeric argument as the size in bytes of the input buffer, the default being 1024. .IP \fB-s\fP -Takes a numeric argument as the number of bytes to skip into the input -before starting to read. If the standard input is used, bytes read to this point -are discarded. +Takes a numeric argument as the index of the byte at which reading will +commence; \(lqskips\(rq that number of bytes. If the standard input is used, +bytes read to this point are discarded. .IP \fB-o\fP Takes a file path as an argument and opens it for use as an output. .IP \fB-B\fP @@ -63,8 +63,9 @@ Does the same as .B -b but for the output buffer. .IP \fB-S\fP -Seeks a number of bytes through the output before starting to write from -the input. If the output is a stream, null characters are printed. +Takes a numeric argument as the index of the byte at which writing will +commence; \(lqseeks\(rq that number of bytes. If the standard output is used, +null characters are printed. .IP \fB-a\fP Accepts a single literal byte with which the input buffer is padded in the event of an incomplete read from the input file. If the option argument is empty, the @@ -81,11 +82,11 @@ Retries failed reads once before exiting. .SH STANDARD INPUT The standard input shall be used as an input if no inputs are specified or if -one or more of the input files is \(lq-\(rq. +input file is \(lq-\(rq. .\" .SH STANDARD OUTPUT The standard output shall be used as an output if no inputs are specified or if -one or more of the input files is \(lq-\(rq. +the output file is \(lq-\(rq. .\" .SH DIAGNOSTICS @@ -129,9 +130,7 @@ is specified along with the option and a count, actual byte output is the product of the count and the input block size and therefore may be lower than expected. If the .B -a -or -.B -A -options are specified, this could make written data nonsensical. +option is specified, this could make written data nonsensical. .\" .SH CAVEATS From 984c1c1f9a02033fcf6e6bf3782868aa75118dd2 Mon Sep 17 00:00:00 2001 From: emma Date: Sun, 30 Jun 2024 21:21:02 -0600 Subject: [PATCH 17/51] Makefile: fixes portability issue --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 581d2a1..5e2c70d 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ PREFIX_N != (test -d $(PREFIX) && [ '-' != $(PREFIX) ] \ && CDPATH= cd -P -- $(PREFIX) && pwd -P) MANDIR != [ $(PREFIX_N) = / ] && printf '/usr/share/man\n' \ || printf '/share/man\n' -SYSEXITS != printf '\043include \n' | cpp -M - | sed 's/ /\n/g' \ +SYSEXITS != printf '\043include \n' | cpp -M - | tr ' ' '\n' \ | sed -n 's/sysexits\.h//p' || printf 'include\n' CC ?= cc From 064abb82a6f3e4973f3332937c1a9057e7798fd4 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 13:50:24 -0600 Subject: [PATCH 18/51] dj(1): fix option parsing regression --- src/dj.c | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/src/dj.c b/src/dj.c index d9c38eb..f7e4e9b 100644 --- a/src/dj.c +++ b/src/dj.c @@ -19,7 +19,7 @@ #include /* errno */ #include /* open(2) */ #include /* fprintf(3), stderr */ -#include /* free(3), malloc(3), strtol(3), size_t */ +#include /* malloc(3), strtol(3), size_t */ #include /* memcpy(3), memmove(3), memset(3) */ #include /* EX_OK, EX_USAGE */ #include /* close(2), getopt(3), lseek(2), read(2), write(2), @@ -64,15 +64,6 @@ static int write_flags = O_WRONLY | O_CREAT; || (fd) == STDOUT_FILENO \ || (fd) == STDERR_FILENO) -/* Macro to call the cleanup functions that operate on struct io on the - * 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). */ -#define terminate(io) do{ \ - free((io[0]).buf); \ - free((io[1]).buf); \ - Io_fdclose(&(io)[0]); \ - Io_fdclose(&(io)[1]); }while(0) - /* Allocates *io's buffer. Returns NULL if unsuccessful. */ static void * Io_bufalloc(struct Io *io){ @@ -121,15 +112,6 @@ Io_bufxfer(struct Io *dest, struct Io *src, int n){ return dest; } -/* Closes io->fn and returns -1 on error, otherwise io->fd. */ -static int -Io_fdclose(struct Io *io){ - - return fdisstd(io->fd) - ? 0 - : close(io->fd); -} - /* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd, * which will be -1 if an error occured. */ static int @@ -140,7 +122,7 @@ Io_fdopen(struct Io *io, char *fn){ /* these are the flags used by touch(1p) */ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) != -1 - && Io_fdclose(io) == 0){ + && (fdisstd(io->fd) || close(io->fd) == 0)){ io->fd = fd; io->fn = fn; } @@ -294,7 +276,6 @@ int main(int argc, char *argv[]){ break; }else if(Io_fdopen(&io[i], optarg) != -1) break; - terminate(io); return oserr(optarg); case 'n': noerror = 1; break; case 'H': fmt_output = fmt_human; break; @@ -308,19 +289,18 @@ int main(int argc, char *argv[]){ if(c == 'c' && (count = parse(optarg)) >= 0) break; i = (c >= 'A' && c <= 'Z'); /* uppercase changes output */ - c &= 0x20 /* 0b 0010 0000 */; /* (ASCII) make lowercase */ + c |= 0x20 /* 0b 0010 0000 */; /* (ASCII) make lowercase */ if((c == 'b' && (io[i].bs = parse(optarg)) > 0) || (c == 's' && (io[i].seek = parse(optarg)) >= 0)) break; /* FALLTHROUGH */ default: - terminate(io); return usage(program_name); } } + if(argc > optind){ - terminate(io); return usage(program_name); } @@ -328,12 +308,10 @@ int main(int argc, char *argv[]){ if(Io_bufalloc(&io[i]) == NULL){ fprintf(stderr, "%s: Failed to allocate %d bytes\n", program_name, io[i].bs); - terminate(io); return EX_OSERR; }else if(io[i].seek > 0) Io_fdseek(&io[i]); if(io[i].seek > 0){ - terminate(io); return oserr(io[i].fn); } } @@ -387,7 +365,6 @@ int main(int argc, char *argv[]){ }while(count == 0 || --count > 0); output(io, fmt_output); - terminate(io); return EX_OK; } From 1cf67af28179d46b6da387afb691944cc5637d85 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 14:22:23 -0600 Subject: [PATCH 19/51] dj(1): add a ton of assertions, fix if statement, fix io[i] mixups --- src/dj.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/dj.c b/src/dj.c index f7e4e9b..e5f276b 100644 --- a/src/dj.c +++ b/src/dj.c @@ -16,6 +16,7 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +#include /* assert(3) */ #include /* errno */ #include /* open(2) */ #include /* fprintf(3), stderr */ @@ -68,14 +69,18 @@ static int write_flags = O_WRONLY | O_CREAT; static void * Io_bufalloc(struct Io *io){ - return (io->buf = malloc(io->bs * (sizeof *io->buf))); + return io != NULL + ? (io->buf = malloc(io->bs * (sizeof *io->buf))) + : NULL; } /* 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){ - + + assert(io != NULL); + memset(io->buf + io->bufuse, padding, io->bs - io->bufuse); io->bufuse = io->bs; @@ -90,6 +95,8 @@ static struct Io* Io_bufxapp(struct Io *dest, struct Io *src){ int n; + assert(dest != NULL && src != NULL); + n = MIN(src->bufuse, dest->bs - dest->bufuse); memcpy(dest->buf + dest->bufuse, src->buf, n); dest->bufuse += n; @@ -106,6 +113,8 @@ Io_bufxapp(struct Io *dest, struct Io *src){ static struct Io* Io_bufxfer(struct Io *dest, struct Io *src, int n){ + assert(dest != NULL && src != NULL); + memcpy(dest->buf, src->buf, (dest->bufuse = n)); memmove(src->buf, src->buf + n, (src->bufuse -= n)); @@ -117,7 +126,9 @@ Io_bufxfer(struct Io *dest, struct Io *src, int n){ static int Io_fdopen(struct Io *io, char *fn){ int fd; - + + assert(io != NULL); + if((fd = open(fn, io->fl, /* these are the flags used by touch(1p) */ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) @@ -134,8 +145,15 @@ Io_fdopen(struct Io *io, char *fn){ * of sought bytes from io->seek. This procedure leaves garbage in io->buf. */ static void Io_fdseek(struct Io *io){ - - if(io->seek != 0 + + 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); + + printf("%s\n", io->fn); + + if(io->seek == 0 || (!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)) return; @@ -160,7 +178,8 @@ Io_fdseek(struct Io *io){ /* 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; @@ -255,9 +274,9 @@ int main(int argc, char *argv[]){ for(i = 0; i < 2; ++i){ io[i].bs = 1024 /* 1 KiB */; /* GNU dd(1) default; POSIX says 512B */ io[i].bytes = 0; - io[i].fd = i ? STDIN_FILENO : STDOUT_FILENO; - io[i].fn = i ? stdin_name : stdout_name; - io[i].fl = i ? read_flags : write_flags; + io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO; + io[i].fn = i == 0 ? stdin_name : stdout_name; + io[i].fl = i == 0 ? read_flags : write_flags; io[i].prec = 0; io[i].rec = 0; io[i].seek = 0; @@ -271,8 +290,8 @@ int main(int argc, char *argv[]){ switch(c){ case 'i': case 'o': i = (c == 'o'); if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */ - io[i].fd = i ? STDIN_FILENO : STDOUT_FILENO; - io[i].fn = i ? stdin_name : stdout_name; + io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO; + io[i].fn = i == 0 ? stdin_name : stdout_name; break; }else if(Io_fdopen(&io[i], optarg) != -1) break; @@ -309,11 +328,12 @@ int main(int argc, char *argv[]){ fprintf(stderr, "%s: Failed to allocate %d bytes\n", program_name, io[i].bs); return EX_OSERR; - }else if(io[i].seek > 0) + }else if(io[i].seek > 0){ Io_fdseek(&io[i]); if(io[i].seek > 0){ return oserr(io[i].fn); } + } } do{ /* read */ From 76252305f990c960de08d6793da96a2f0e3a2765 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 14:46:56 -0600 Subject: [PATCH 20/51] dj(1): remove Io_bufalloc --- src/dj.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/dj.c b/src/dj.c index e5f276b..2e565ce 100644 --- a/src/dj.c +++ b/src/dj.c @@ -65,15 +65,6 @@ static int write_flags = O_WRONLY | O_CREAT; || (fd) == STDOUT_FILENO \ || (fd) == STDERR_FILENO) -/* Allocates *io's buffer. Returns NULL if unsuccessful. */ -static void * -Io_bufalloc(struct Io *io){ - - return io != NULL - ? (io->buf = malloc(io->bs * (sizeof *io->buf))) - : NULL; -} - /* Fills the unused portion of io's buffer with padding, updating io->bufuse. * Returns io. */ static struct Io * @@ -324,7 +315,7 @@ int main(int argc, char *argv[]){ } for(i = 0; i < 2; ++i){ - if(Io_bufalloc(&io[i]) == NULL){ + if((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL){ fprintf(stderr, "%s: Failed to allocate %d bytes\n", program_name, io[i].bs); return EX_OSERR; From aff658d611e7b221a58b3177f34c7bd5c323d725 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 14:50:50 -0600 Subject: [PATCH 21/51] dj(1): remove debugging vestige, reflow output into printio --- src/dj.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/dj.c b/src/dj.c index 2e565ce..a20b472 100644 --- a/src/dj.c +++ b/src/dj.c @@ -47,7 +47,7 @@ struct Io{ long seek; /* bytes to seek/skip (will be 0 after skippage) (-sS) */ }; -/* To be assigned to main:fmt_output and used with output(). */ +/* To be assigned to main:fmt and used with printio(). */ static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034"; static char *fmt_human = "%d+%d > %d+%d; %d > %d\n"; @@ -142,8 +142,6 @@ Io_fdseek(struct Io *io){ assert(io->fd != STDOUT_FILENO || io->fl == write_flags); assert(io->fd != STDERR_FILENO || io->fl == write_flags); - printf("%s\n", io->fn); - if(io->seek == 0 || (!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)) return; @@ -215,7 +213,7 @@ oserr(char *s){ /* Prints statistics regarding the use of dj, particularly partially and * completely read and written records. */ static void -output(struct Io io[2], char *fmt){ +printio(char *fmt, struct Io io[2]){ fprintf(stderr, fmt, io[0].rec, io[0].prec, io[1].rec, io[1].prec, @@ -252,7 +250,7 @@ usage(char *s){ int main(int argc, char *argv[]){ int align; /* low 8b used, negative if no alignment is being done */ int count; /* 0 if dj(1) runs until no more reads are possible */ - char *fmt_output; /* == fmt_asv (default) or fmt_human (-H) */ + char *fmt; /* == fmt_asv (default) or fmt_human (-H) */ size_t i; /* side of io being modified */ struct Io io[2]; char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ @@ -260,7 +258,7 @@ int main(int argc, char *argv[]){ /* Set defaults. */ align = -1; count = 0; - fmt_output = fmt_asv; + fmt = fmt_asv; noerror = 0; for(i = 0; i < 2; ++i){ io[i].bs = 1024 /* 1 KiB */; /* GNU dd(1) default; POSIX says 512B */ @@ -288,7 +286,7 @@ int main(int argc, char *argv[]){ break; return oserr(optarg); case 'n': noerror = 1; break; - case 'H': fmt_output = fmt_human; break; + case 'H': fmt = fmt_human; break; case 'a': if(optarg[0] == '\0' || optarg[1] == '\0'){ align = optarg[0]; @@ -298,7 +296,7 @@ int main(int argc, char *argv[]){ case 'c': case 'b': case 's': case 'B': case 'S': /* numbers */ if(c == 'c' && (count = parse(optarg)) >= 0) break; - i = (c >= 'A' && c <= 'Z'); /* uppercase changes output */ + i = (c >= 'A' && c <= 'Z'); c |= 0x20 /* 0b 0010 0000 */; /* (ASCII) make lowercase */ if((c == 'b' && (io[i].bs = parse(optarg)) > 0) || (c == 's' && (io[i].seek = parse(optarg)) >= 0)) @@ -336,7 +334,7 @@ int main(int argc, char *argv[]){ else if(io[0].bufuse < io[0].bs){ ++io[0].prec; fprintf(stderr, "%s: Partial read:\n\t", program_name); - output(io, fmt_output); + printio(fmt, io); if(!noerror) count = 1; if(align >= 0) @@ -365,7 +363,7 @@ int main(int argc, char *argv[]){ }else if(t > io[1].bufuse && io[1].bufuse > 0){ io[1].prec += 1; fprintf(stderr, "%s: Partial write:\n\t", program_name); - output(io, fmt_output); + printio(fmt, io); if(!noerror) count = 1; }else if(io[1].bufuse == 0 && t < io[1].bs) @@ -375,7 +373,7 @@ int main(int argc, char *argv[]){ }while(io[0].bufuse > 0); }while(count == 0 || --count > 0); - output(io, fmt_output); + printio(fmt, io); return EX_OK; } From 6548a448c74d7d226091eff9570dbd34e5f01255 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 15:44:42 -0600 Subject: [PATCH 22/51] dj(1): fix potential skip/seek bug in non-std io --- src/dj.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dj.c b/src/dj.c index a20b472..41fd65f 100644 --- a/src/dj.c +++ b/src/dj.c @@ -133,7 +133,9 @@ Io_fdopen(struct Io *io, char *fn){ } /* 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. */ + * of sought bytes from io->seek. This procedure leaves garbage in io->buf. + * Read/written bytes here aren't counted in the statistics because successful + * seeking is guaranteed. */ static void Io_fdseek(struct Io *io){ @@ -143,8 +145,10 @@ Io_fdseek(struct Io *io){ assert(io->fd != STDERR_FILENO || io->fl == write_flags); if(io->seek == 0 - || (!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)) + || (!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)){ + io->seek = 0; return; + } if(io->fl == write_flags) memset(io->buf, '\0', io->bs); From 3897f44cf8c4379067316415459d2449f014ea70 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 15:46:11 -0600 Subject: [PATCH 23/51] dj(1): prefix getopt optstring with : --- src/dj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dj.c b/src/dj.c index 41fd65f..f938ca9 100644 --- a/src/dj.c +++ b/src/dj.c @@ -279,7 +279,7 @@ int main(int argc, char *argv[]){ int c; 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){ case 'i': case 'o': i = (c == 'o'); if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */ From 66ca4b9a120a8ce4d6e704d0330f9713194b865c Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 15:47:48 -0600 Subject: [PATCH 24/51] dj(1): remove unnecessary stderr checks --- src/dj.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/dj.c b/src/dj.c index f938ca9..8c9f327 100644 --- a/src/dj.c +++ b/src/dj.c @@ -59,11 +59,8 @@ static int write_flags = O_WRONLY | O_CREAT; #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -/* Macro to check if fd is a std* file, e.g. stdin. */ -#define fdisstd(fd) \ - ((fd) == STDIN_FILENO \ - || (fd) == STDOUT_FILENO \ - || (fd) == STDERR_FILENO) +/* Macro to check if fd is stdin or stdout */ +#define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO) /* Fills the unused portion of io's buffer with padding, updating io->bufuse. * Returns io. */ @@ -142,7 +139,6 @@ 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)){ From 944feef43465caf4ffafc8106373bfbd0d9a090f Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 16:07:02 -0600 Subject: [PATCH 25/51] dj(1): Refactor out Io_fdseek entirely --- src/dj.c | 93 +++++++++++++++++++++++++------------------------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/src/dj.c b/src/dj.c index 8c9f327..96085e3 100644 --- a/src/dj.c +++ b/src/dj.c @@ -129,52 +129,6 @@ Io_fdopen(struct Io *io, char *fn){ 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. - * Read/written bytes here aren't counted in the statistics because successful - * seeking is guaranteed. */ -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); - - if(io->seek == 0 - || (!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)){ - io->seek = 0; - 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 * number of read bytes in io->bufuse and updating io->bytes. If io->bufuse is * 0, errno will probably be set. Returns io. */ @@ -307,24 +261,61 @@ 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){ return usage(program_name); } for(i = 0; i < 2; ++i){ + /* buffer allocation */ if((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL){ fprintf(stderr, "%s: Failed to allocate %d bytes\n", program_name, io[i].bs); 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 */ Io_read(&io[0]); if(!noerror && io[0].bufuse == 0) From 7fe122ac3b5ee858007c74879f8ee5b4362ec530 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 16:13:20 -0600 Subject: [PATCH 26/51] dj.1: clarify skip/seek behavior with regards to statistics output --- docs/dj.1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/dj.1 b/docs/dj.1 index 38b8034..dd293f2 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -35,7 +35,9 @@ respectively. This language is inherited from the utility and used here to decrease ambiguity. When seeking or skipping to a byte, writing or reading starts at the byte -immediately subsequent to the specified byte. +immediately subsequent to the specified byte. Seeks and skips aren\(cqt counted +in the output statistics because they're guaranteed to succeed (or the utility +will exit unsuccessfully). .\" .SH OPTIONS From f4b97be1f12d1e212218a4c4e152fb5ca76b7f8c Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 17:50:04 -0600 Subject: [PATCH 27/51] dj(1): iron out Io_bufxapp --- src/dj.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/dj.c b/src/dj.c index 96085e3..3421292 100644 --- a/src/dj.c +++ b/src/dj.c @@ -75,25 +75,6 @@ Io_bufrpad(struct Io *io, int padding){ return io; } -/* 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 - * units in the src buffer to the start of the buffer, modifying both the src - * and dest bufuse and returning dest. */ -static struct Io* -Io_bufxapp(struct Io *dest, struct Io *src){ - int n; - - assert(dest != NULL && src != NULL); - - n = MIN(src->bufuse, dest->bs - dest->bufuse); - memcpy(dest->buf + dest->bufuse, src->buf, n); - dest->bufuse += n; - memmove(src->buf, src->buf + n, src->bs - n); - src->bufuse -= n; - - return dest; -} - /* Copies from the buffer in src to the buffer in dest no more than n units, * 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 @@ -338,7 +319,16 @@ int main(int argc, char *argv[]){ int t; if(io[1].bs > io[0].bs){ - Io_bufxapp(&io[1], &io[0]); + int n; + + /* copy from ibuf as much as possible to the obuf */ + memcpy(io[1].buf + io[1].bufuse, io[0].buf, + (n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse))); + io[1].bufuse += n; + /* permute out the copied units */ + memmove(io[0].buf, io[0].buf + n, io[0].bs - n); + io[0].bufuse -= n; + if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) continue; /* we could write more */ }else From 2167f35f588c4f87635060b60f8f30192cfe3a47 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 17:59:21 -0600 Subject: [PATCH 28/51] dj(1): fix segfault when bses are mismatched --- src/dj.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dj.c b/src/dj.c index 3421292..17c1d59 100644 --- a/src/dj.c +++ b/src/dj.c @@ -35,15 +35,15 @@ char *program_name = "dj"; * writing ends of its jockeyed "pipe". User-configurable members are noted * with their relevant options. */ struct Io{ - int bs; /* buffer size (-bB) */ + int bs; /* buffer size (-bB) */ size_t bufuse; /* buffer usage */ char *buf; /* buffer */ - int bytes; /* bytes processed */ + size_t bytes; /* bytes processed */ int fd; /* file descriptor */ int fl; /* file opening flags */ char *fn; /* file name (may be stdin_name or stdout_name) (-io) */ - int prec; /* partial records processed */ - int rec; /* records processed */ + size_t prec; /* partial records processed */ + size_t rec; /* records processed */ long seek; /* bytes to seek/skip (will be 0 after skippage) (-sS) */ }; @@ -197,6 +197,7 @@ int main(int argc, char *argv[]){ noerror = 0; for(i = 0; i < 2; ++i){ io[i].bs = 1024 /* 1 KiB */; /* GNU dd(1) default; POSIX says 512B */ + io[i].bufuse = 0; io[i].bytes = 0; io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO; io[i].fn = i == 0 ? stdin_name : stdout_name; From adda0d95807085c3685c71accd4bcb4bd4963fc9 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 18:30:54 -0600 Subject: [PATCH 29/51] dj.1: elaborate on skip/seek behavior, provide another example --- docs/dj.1 | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index dd293f2..09f40c9 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -4,7 +4,7 @@ .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" visit . .\" -.TH DJ 1 2024-06-29 "Harakit X.X.X" +.TH DJ 1 2024-07-03 "Harakit X.X.X" .SH NAME dj \(en disk jockey .\" @@ -34,10 +34,17 @@ respectively. This language is inherited from the .BR dd (1p) utility and used here to decrease ambiguity. -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 -in the output statistics because they're guaranteed to succeed (or the utility -will exit unsuccessfully). +The offset used when skipping or seeking refers to how many bytes are skipped +or sought. Running +.BR dj (1) +with a skip offset of 1 skips one byte into the input and reads from the second +byte onwards. A programmer may think of a file as a zero-indexed array of +bytes; in this analogy, the offset given is the index of the byte at which to +start reading or writing. + +Seeks and skips aren\(cqt counted in the output statistics because they're +guaranteed to succeed (or the utility will exit unsuccessfully, before it has +written any data). .\" .SH OPTIONS @@ -82,6 +89,54 @@ input file is \(lq-\(rq. The standard output shall be used as an output if no inputs are specified or if the output file is \(lq-\(rq. .\" +.SH EXAMPLES + +The following +.BR sh (1p) +line: + +.RS +printf 'Hello, world!\(rsn' | dj -c 1 -b 7 -s 7 2>/dev/null +.RE + +Produces the following output: + +.RS +world! +.RE + +The following +.BR sh (1p) +lines run sequentially: + +.RS +tr '\(rs0' 0 Date: Wed, 3 Jul 2024 18:44:42 -0600 Subject: [PATCH 30/51] dj(1): clean up some stray ends --- src/dj.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/dj.c b/src/dj.c index 17c1d59..ac52883 100644 --- a/src/dj.c +++ b/src/dj.c @@ -22,7 +22,7 @@ #include /* fprintf(3), stderr */ #include /* malloc(3), strtol(3), size_t */ #include /* memcpy(3), memmove(3), memset(3) */ -#include /* EX_OK, EX_USAGE */ +#include /* EX_OK, EX_OSERR, EX_USAGE */ #include /* close(2), getopt(3), lseek(2), read(2), write(2), * optarg, optind, STDIN_FILENO, STDOUT_FILENO */ #include /* S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, @@ -246,9 +246,8 @@ 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); - } for(i = 0; i < 2; ++i){ /* buffer allocation */ From b74160fa4e4af5604bb7a3c5278c2b3982094b23 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 19:04:01 -0600 Subject: [PATCH 31/51] dj(1): remove Io_bufxfer --- src/dj.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/dj.c b/src/dj.c index ac52883..22c46a7 100644 --- a/src/dj.c +++ b/src/dj.c @@ -75,21 +75,6 @@ Io_bufrpad(struct Io *io, int padding){ return io; } -/* Copies from the buffer in src to the buffer in dest no more than n units, - * 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 - * bufuse and returning dest. */ -static struct Io* -Io_bufxfer(struct Io *dest, struct Io *src, int n){ - - assert(dest != NULL && src != NULL); - - memcpy(dest->buf, src->buf, (dest->bufuse = n)); - memmove(src->buf, src->buf + n, (src->bufuse -= n)); - - return dest; -} - /* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd, * which will be -1 if an error occured. */ static int @@ -318,10 +303,18 @@ int main(int argc, char *argv[]){ do{ int t; - if(io[1].bs > io[0].bs){ + if(io[0].bs <= io[1].bs){ int n; - /* copy from ibuf as much as possible to the obuf */ + /* saturate obuf */ + memcpy(io[1].buf, io[0].buf, + (io[1].bufuse = (n = MIN(io[0].bufuse, io[1].bs)))); + /* permute the copied units out of ibuf */ + memmove(io[0].buf, io[0].buf + n, (io[0].bufuse -= n)); + }else /* if(io[0].bs < io[1].bs) */ { + int n; + + /* drain what we can from ibuf */ memcpy(io[1].buf + io[1].bufuse, io[0].buf, (n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse))); io[1].bufuse += n; @@ -330,9 +323,8 @@ int main(int argc, char *argv[]){ io[0].bufuse -= n; if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) - continue; /* we could write more */ - }else - Io_bufxfer(&io[1], &io[0], MIN(io[0].bufuse, io[1].bs)); + continue; /* obuf not saturated - we could write more */ + } t = io[1].bufuse; Io_write(&io[1]); From 2b593559afc64ad2a7f80bed0511e19250ade41d Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 19:06:59 -0600 Subject: [PATCH 32/51] dj(1): remove Io_bufrpad --- src/dj.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/dj.c b/src/dj.c index 22c46a7..6815a82 100644 --- a/src/dj.c +++ b/src/dj.c @@ -62,19 +62,6 @@ static int write_flags = O_WRONLY | O_CREAT; /* Macro to check if fd is stdin or stdout */ #define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO) -/* 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){ - - assert(io != NULL); - - memset(io->buf + io->bufuse, padding, io->bs - io->bufuse); - io->bufuse = io->bs; - - return io; -} - /* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd, * which will be -1 if an error occured. */ static int @@ -294,8 +281,12 @@ int main(int argc, char *argv[]){ printio(fmt, io); if(!noerror) count = 1; - if(align >= 0) - Io_bufrpad(&io[0], align); + if(align >= 0){ + /* fill the rest of the ibuf with padding */ + memset(io[0].buf + io[0].bufuse, align, + io[0].bs - io[0].bufuse); + io->bufuse = io->bs; + } }else ++io[0].rec; From 5b1d4fef8828a6057183b95d3828564b4f1811c8 Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 19:22:34 -0600 Subject: [PATCH 33/51] dj(1): remove Io_fdopen --- src/dj.c | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/dj.c b/src/dj.c index 6815a82..974bcf0 100644 --- a/src/dj.c +++ b/src/dj.c @@ -62,26 +62,6 @@ static int write_flags = O_WRONLY | O_CREAT; /* Macro to check if fd is stdin or stdout */ #define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO) -/* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd, - * which will be -1 if an error occured. */ -static int -Io_fdopen(struct Io *io, char *fn){ - int fd; - - assert(io != NULL); - - if((fd = open(fn, io->fl, - /* these are the flags used by touch(1p) */ - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) - != -1 - && (fdisstd(io->fd) || close(io->fd) == 0)){ - io->fd = fd; - io->fn = fn; - } - - return fd; -} - /* 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 * 0, errno will probably be set. Returns io. */ @@ -155,12 +135,12 @@ usage(char *s){ } int main(int argc, char *argv[]){ - int align; /* low 8b used, negative if no alignment is being done */ - int count; /* 0 if dj(1) runs until no more reads are possible */ - char *fmt; /* == fmt_asv (default) or fmt_human (-H) */ - size_t i; /* side of io being modified */ - struct Io io[2]; + int align; /* low 8b used, negative if no alignment is being done */ + int count; /* 0 if dj(1) runs until no more reads are possible */ + char *fmt; /* == fmt_asv (default) or fmt_human (-H) */ + size_t i; /* side of io being modified */ char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ + struct Io io[2]; /* Set defaults. */ align = -1; @@ -190,10 +170,21 @@ int main(int argc, char *argv[]){ io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO; io[i].fn = i == 0 ? stdin_name : stdout_name; break; - }else if(Io_fdopen(&io[i], optarg) != -1) - break; + }else{ + int fd; + + if((fd = open(optarg, io[i].fl, /* touch(1p) flags */ + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP + | S_IROTH | S_IWOTH)) + != -1 + && (fdisstd(io[i].fd) || close(io[i].fd) == 0)){ + io[i].fd = fd; + io[i].fn = optarg; + break; + } + } return oserr(optarg); - case 'n': noerror = 1; break; + case 'n': noerror = 1; break; case 'H': fmt = fmt_human; break; case 'a': if(optarg[0] == '\0' || optarg[1] == '\0'){ From cc645613888960c2784bcfe5c2a90ce17fe6b2bc Mon Sep 17 00:00:00 2001 From: DTB Date: Wed, 3 Jul 2024 20:52:41 -0600 Subject: [PATCH 34/51] dj(1): only include sysexits if they aren't defined --- src/dj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dj.c b/src/dj.c index 974bcf0..5a095ed 100644 --- a/src/dj.c +++ b/src/dj.c @@ -22,7 +22,9 @@ #include /* fprintf(3), stderr */ #include /* malloc(3), strtol(3), size_t */ #include /* memcpy(3), memmove(3), memset(3) */ -#include /* EX_OK, EX_OSERR, EX_USAGE */ +#if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE +# include +#endif #include /* close(2), getopt(3), lseek(2), read(2), write(2), * optarg, optind, STDIN_FILENO, STDOUT_FILENO */ #include /* S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, From 4004a4a0068f173059ed59c5ac6aba10ec0c6ce6 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 18:41:20 -0600 Subject: [PATCH 35/51] dj(1): use the retvals of Io_read and Io_write --- src/dj.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dj.c b/src/dj.c index 5a095ed..fa3d46a 100644 --- a/src/dj.c +++ b/src/dj.c @@ -263,8 +263,7 @@ int main(int argc, char *argv[]){ return oserr(io[i].fn); do{ /* read */ - Io_read(&io[0]); - if(!noerror && io[0].bufuse == 0) + if(Io_read(&io[0])->bufuse == 0 && !noerror) Io_read(&io[0]); /* second chance */ if(io[0].bufuse == 0) /* that's all she wrote */ break; @@ -311,8 +310,7 @@ int main(int argc, char *argv[]){ } t = io[1].bufuse; - Io_write(&io[1]); - if(!noerror && io[1].bufuse == t) + if(Io_write(&io[1])->bufuse == t && !noerror) Io_write(&io[1]); /* second chance */ if(t == io[1].bufuse){ /* no more love */ count = 1; From f49a2d2eb8ec3aed83434f164477ff422b3b6b98 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 19:21:40 -0600 Subject: [PATCH 36/51] dj(1): move prec and rec adjustment into Io_read --- src/dj.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/dj.c b/src/dj.c index fa3d46a..9aed880 100644 --- a/src/dj.c +++ b/src/dj.c @@ -65,13 +65,24 @@ static int write_flags = O_WRONLY | O_CREAT; #define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO) /* 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 - * 0, errno will probably be set. Returns io. */ + * number of read bytes in io->bufuse and updating io->bytes. If the buf isn't + * saturated but is still read into, io->prec will be incremented. If the buf + * is saturated, io->rec will be incremented. If io->bufuse is 0, errno will + * probably be set. Returns io. */ static struct Io * Io_read(struct Io *io){ io->bytes += (io->bufuse = read(io->fd, io->buf, io->bs)); + assert(io->bufuse <= io->bs); + + if(io->bufuse != 0){ + if(io->bufuse < io->bs) + ++io->prec; + else /* if(io->bufuse == io->bs) */ + ++io->rec; + } + return io; } @@ -267,8 +278,8 @@ int main(int argc, char *argv[]){ Io_read(&io[0]); /* second chance */ if(io[0].bufuse == 0) /* that's all she wrote */ break; - else if(io[0].bufuse < io[0].bs){ - ++io[0].prec; + + if(io[0].bufuse < io[0].bs){ fprintf(stderr, "%s: Partial read:\n\t", program_name); printio(fmt, io); if(!noerror) @@ -279,8 +290,7 @@ int main(int argc, char *argv[]){ io[0].bs - io[0].bufuse); io->bufuse = io->bs; } - }else - ++io[0].rec; + } /* write */ do{ From fc0d9e374bb36198c812bab561b894672ca11fa2 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 19:23:09 -0600 Subject: [PATCH 37/51] dj(1): make printio fprintio --- src/dj.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dj.c b/src/dj.c index 9aed880..b81deeb 100644 --- a/src/dj.c +++ b/src/dj.c @@ -113,9 +113,9 @@ oserr(char *s){ /* Prints statistics regarding the use of dj, particularly partially and * completely read and written records. */ static void -printio(char *fmt, struct Io io[2]){ +fprintio(FILE *stream, char *fmt, struct Io io[2]){ - fprintf(stderr, fmt, + fprintf(stream, fmt, io[0].rec, io[0].prec, io[1].rec, io[1].prec, io[0].bytes, io[1].bytes); @@ -281,7 +281,7 @@ int main(int argc, char *argv[]){ if(io[0].bufuse < io[0].bs){ fprintf(stderr, "%s: Partial read:\n\t", program_name); - printio(fmt, io); + fprintio(stderr, fmt, io); if(!noerror) count = 1; if(align >= 0){ @@ -328,7 +328,7 @@ int main(int argc, char *argv[]){ }else if(t > io[1].bufuse && io[1].bufuse > 0){ io[1].prec += 1; fprintf(stderr, "%s: Partial write:\n\t", program_name); - printio(fmt, io); + fprintio(stderr, fmt, io); if(!noerror) count = 1; }else if(io[1].bufuse == 0 && t < io[1].bs) @@ -338,7 +338,7 @@ int main(int argc, char *argv[]){ }while(io[0].bufuse > 0); }while(count == 0 || --count > 0); - printio(fmt, io); + fprintio(stderr, fmt, io); return EX_OK; } From f8c0e0570c8896d2e29f0b8bddf2b44c771ba962 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 19:36:32 -0600 Subject: [PATCH 38/51] dj(1): make Io_write handle prec and rec --- src/dj.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/dj.c b/src/dj.c index b81deeb..4feae19 100644 --- a/src/dj.c +++ b/src/dj.c @@ -72,30 +72,34 @@ static int write_flags = O_WRONLY | O_CREAT; static struct Io * Io_read(struct Io *io){ + assert(io->bs > 0); + io->bytes += (io->bufuse = read(io->fd, io->buf, io->bs)); assert(io->bufuse <= io->bs); - if(io->bufuse != 0){ - if(io->bufuse < io->bs) - ++io->prec; - else /* if(io->bufuse == io->bs) */ - ++io->rec; - } + io->prec += (0 < io->bufuse && io->bufuse < io->bs); + io->rec += (io->bufuse == io->bs); return io; } -/* Writes io->bufuse units from io->buf to io->fd, permuting any unwritten +/* Writes io->bufuse (>0) units from io->buf to io->fd, permuting any unwritten * bytes to the start of io->buf and updating io->bufuse. If io->bufuse doesn't * change, errno will probably be set. Returns io. */ static struct Io * Io_write(struct Io *io){ int t; + assert(io->bufuse > 0); + assert(io->bufuse <= io->bs); + if((t = write(io->fd, io->buf, io->bufuse)) > 0) memmove(io->buf, io->buf + t, (io->bufuse -= t)); + io->bytes += t; + io->prec += (t > 0 && io->bufuse > 0); + io->rec += (t > 0 && io->bufuse == 0); return io; } @@ -322,19 +326,17 @@ int main(int argc, char *argv[]){ t = io[1].bufuse; if(Io_write(&io[1])->bufuse == t && !noerror) Io_write(&io[1]); /* second chance */ - if(t == io[1].bufuse){ /* no more love */ + if(io[1].bufuse == t){ /* no more love */ count = 1; break; - }else if(t > io[1].bufuse && io[1].bufuse > 0){ - io[1].prec += 1; + } + + if(0 < io[1].bufuse && io[1].bufuse < t){ fprintf(stderr, "%s: Partial write:\n\t", program_name); fprintio(stderr, fmt, io); if(!noerror) count = 1; - }else if(io[1].bufuse == 0 && t < io[1].bs) - ++io[1].prec; - else - ++io[1].rec; + } }while(io[0].bufuse > 0); }while(count == 0 || --count > 0); From 8c33f0116c33834a8ffba6f934cdd99a1cb8453e Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 19:45:53 -0600 Subject: [PATCH 39/51] dj(1): move open(2) flags, remove unnecessary comments --- src/dj.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/dj.c b/src/dj.c index 4feae19..d5f209c 100644 --- a/src/dj.c +++ b/src/dj.c @@ -56,6 +56,9 @@ static char *fmt_human = "%d+%d > %d+%d; %d > %d\n"; static char *stdin_name = ""; static char *stdout_name = ""; + +static int creat_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH + | S_IWOTH; /* Consistent with touch(1p). */ static int read_flags = O_RDONLY; /* Consistent with Busybox dd(1). */ static int write_flags = O_WRONLY | O_CREAT; @@ -64,11 +67,6 @@ static int write_flags = O_WRONLY | O_CREAT; /* Macro to check if fd is stdin or stdout */ #define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO) -/* 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 the buf isn't - * saturated but is still read into, io->prec will be incremented. If the buf - * is saturated, io->rec will be incremented. If io->bufuse is 0, errno will - * probably be set. Returns io. */ static struct Io * Io_read(struct Io *io){ @@ -84,9 +82,6 @@ Io_read(struct Io *io){ return io; } -/* Writes io->bufuse (>0) units from io->buf to io->fd, permuting any unwritten - * bytes to the start of io->buf and updating io->bufuse. If io->bufuse doesn't - * change, errno will probably be set. Returns io. */ static struct Io * Io_write(struct Io *io){ int t; @@ -190,10 +185,7 @@ int main(int argc, char *argv[]){ }else{ int fd; - if((fd = open(optarg, io[i].fl, /* touch(1p) flags */ - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP - | S_IROTH | S_IWOTH)) - != -1 + if((fd = open(optarg, io[i].fl, creat_mode)) != -1 && (fdisstd(io[i].fd) || close(io[i].fd) == 0)){ io[i].fd = fd; io[i].fn = optarg; From fe175cab191af00bac56042f338ad3f46f5ff617 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 20:00:40 -0600 Subject: [PATCH 40/51] dj(1): add a variable for skipping in the main loop --- src/dj.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/dj.c b/src/dj.c index d5f209c..9fb55ca 100644 --- a/src/dj.c +++ b/src/dj.c @@ -269,22 +269,29 @@ int main(int argc, char *argv[]){ if(io[i].seek > 0) return oserr(io[i].fn); - do{ /* read */ - if(Io_read(&io[0])->bufuse == 0 && !noerror) - Io_read(&io[0]); /* second chance */ - if(io[0].bufuse == 0) /* that's all she wrote */ - break; + do{ + { /* read */ + static char skipping = 0; - if(io[0].bufuse < io[0].bs){ - fprintf(stderr, "%s: Partial read:\n\t", program_name); - fprintio(stderr, fmt, io); - if(!noerror) - count = 1; - if(align >= 0){ - /* fill the rest of the ibuf with padding */ - memset(io[0].buf + io[0].bufuse, align, - io[0].bs - io[0].bufuse); - io->bufuse = io->bs; + if(io[0].seek > 0) + skipping = 1; + + if(Io_read(&io[0])->bufuse == 0 && !noerror) + Io_read(&io[0]); /* second chance */ + if(io[0].bufuse == 0) /* that's all she wrote */ + break; + + if(io[0].bufuse < io[0].bs){ + fprintf(stderr, "%s: Partial read:\n\t", program_name); + fprintio(stderr, fmt, io); + if(!noerror) + count = 1; + if(align >= 0){ + /* fill the rest of the ibuf with padding */ + memset(io[0].buf + io[0].bufuse, align, + io[0].bs - io[0].bufuse); + io->bufuse = io->bs; + } } } From 1fab60d77939f151627c23f5715fb41633f4ab15 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 20:05:18 -0600 Subject: [PATCH 41/51] dj(1): no more pointer arithmetic --- src/dj.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dj.c b/src/dj.c index 9fb55ca..e3972aa 100644 --- a/src/dj.c +++ b/src/dj.c @@ -90,7 +90,7 @@ Io_write(struct Io *io){ assert(io->bufuse <= io->bs); if((t = write(io->fd, io->buf, io->bufuse)) > 0) - memmove(io->buf, io->buf + t, (io->bufuse -= t)); + memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); io->bytes += t; io->prec += (t > 0 && io->bufuse > 0); @@ -288,7 +288,7 @@ int main(int argc, char *argv[]){ count = 1; if(align >= 0){ /* fill the rest of the ibuf with padding */ - memset(io[0].buf + io[0].bufuse, align, + memset(&(io[0].buf)[io[0].bufuse], align, io[0].bs - io[0].bufuse); io->bufuse = io->bs; } @@ -306,16 +306,16 @@ int main(int argc, char *argv[]){ memcpy(io[1].buf, io[0].buf, (io[1].bufuse = (n = MIN(io[0].bufuse, io[1].bs)))); /* permute the copied units out of ibuf */ - memmove(io[0].buf, io[0].buf + n, (io[0].bufuse -= n)); + memmove(io[0].buf, &(io[0].buf)[n], (io[0].bufuse -= n)); }else /* if(io[0].bs < io[1].bs) */ { int n; /* drain what we can from ibuf */ - memcpy(io[1].buf + io[1].bufuse, io[0].buf, + memcpy(&(io[1].buf)[io[1].bufuse], io[0].buf, (n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse))); io[1].bufuse += n; /* permute out the copied units */ - memmove(io[0].buf, io[0].buf + n, io[0].bs - n); + memmove(io[0].buf, &{io[0].buf)[n], io[0].bs - n); io[0].bufuse -= n; if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) From 9f420131ee591ad97847cc1599c05fbd8b2e0203 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 20:16:54 -0600 Subject: [PATCH 42/51] dj(1): more work adapting hard skipping to the main loop --- src/dj.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/dj.c b/src/dj.c index e3972aa..aee9c38 100644 --- a/src/dj.c +++ b/src/dj.c @@ -270,23 +270,27 @@ int main(int argc, char *argv[]){ return oserr(io[i].fn); do{ + assert(io[0].bufuse == 0); + { /* read */ static char skipping = 0; + int t; if(io[0].seek > 0) skipping = 1; - if(Io_read(&io[0])->bufuse == 0 && !noerror) + t = io[0].bufuse; + if(Io_read(&io[0])->bufuse == t && !noerror) Io_read(&io[0]); /* second chance */ - if(io[0].bufuse == 0) /* that's all she wrote */ + if(io[0].bufuse == t) /* that's all she wrote */ break; - if(io[0].bufuse < io[0].bs){ + if(/* t < io[0].bufuse && */ io[0].bufuse < io[0].bs){ fprintf(stderr, "%s: Partial read:\n\t", program_name); fprintio(stderr, fmt, io); if(!noerror) count = 1; - if(align >= 0){ + if(!skipping && align >= 0){ /* fill the rest of the ibuf with padding */ memset(&(io[0].buf)[io[0].bufuse], align, io[0].bs - io[0].bufuse); @@ -315,7 +319,7 @@ int main(int argc, char *argv[]){ (n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse))); io[1].bufuse += n; /* permute out the copied units */ - memmove(io[0].buf, &{io[0].buf)[n], io[0].bs - n); + memmove(io[0].buf, &(io[0].buf)[n], io[0].bs - n); io[0].bufuse -= n; if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) @@ -330,7 +334,7 @@ int main(int argc, char *argv[]){ break; } - if(0 < io[1].bufuse && io[1].bufuse < t){ + if(0 < io[1].bufuse /* && io[1].bufuse < t */){ fprintf(stderr, "%s: Partial write:\n\t", program_name); fprintio(stderr, fmt, io); if(!noerror) From 906eb92f5a59995a5b5a977ee2667b9a47b3ddec Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 20:27:31 -0600 Subject: [PATCH 43/51] dj(1): (broken) move hard skipping to the main loop --- src/dj.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/dj.c b/src/dj.c index aee9c38..7b3bb0a 100644 --- a/src/dj.c +++ b/src/dj.c @@ -72,7 +72,8 @@ Io_read(struct Io *io){ assert(io->bs > 0); - io->bytes += (io->bufuse = read(io->fd, io->buf, io->bs)); + io->bytes += (io->bufuse = read(io->fd, &(io->buf)[io->bufuse], + io->bs - io->bufuse)); assert(io->bufuse <= io->bs); @@ -233,19 +234,6 @@ int main(int argc, char *argv[]){ 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); @@ -265,9 +253,8 @@ int main(int argc, char *argv[]){ /* 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); + if(io[1].seek > 0) + return oserr(io[1].fn); do{ assert(io[0].bufuse == 0); @@ -279,6 +266,9 @@ int main(int argc, char *argv[]){ if(io[0].seek > 0) skipping = 1; + if(skipping && io[0].seek < io[0].bs) + io[0].bufuse = io[0].bs - io[0].seek; + t = io[0].bufuse; if(Io_read(&io[0])->bufuse == t && !noerror) Io_read(&io[0]); /* second chance */ @@ -297,6 +287,13 @@ int main(int argc, char *argv[]){ io->bufuse = io->bs; } } + + if(skipping){ + skipping = (io[0].seek -= io[0].bufuse - t) > 0; + io[0].bufuse = 0; + count += (count != 0); + continue; + } } /* write */ From 9e8b82c4bbf402c5caec14c0dd4a74acf8ccf35e Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 20:47:30 -0600 Subject: [PATCH 44/51] dj(1): fix inaccurate statistics after Io_read and Io_write --- src/dj.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/dj.c b/src/dj.c index 7b3bb0a..e65274a 100644 --- a/src/dj.c +++ b/src/dj.c @@ -69,16 +69,19 @@ static int write_flags = O_WRONLY | O_CREAT; static struct Io * Io_read(struct Io *io){ + int t; assert(io->bs > 0); + assert(io->bufuse < io->bs); - io->bytes += (io->bufuse = read(io->fd, &(io->buf)[io->bufuse], + io->bufuse += (t = read(io->fd, &(io->buf)[io->bufuse], io->bs - io->bufuse)); assert(io->bufuse <= io->bs); - io->prec += (0 < io->bufuse && io->bufuse < io->bs); - io->rec += (io->bufuse == io->bs); + io->bytes += t; + io->prec += (0 < io->bufuse && io->bufuse < io->bs); + io->rec += (io->bufuse == io->bs); return io; } @@ -94,8 +97,8 @@ Io_write(struct Io *io){ memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); io->bytes += t; - io->prec += (t > 0 && io->bufuse > 0); - io->rec += (t > 0 && io->bufuse == 0); + io->prec += (0 < io->bufuse && 0 < t); + io->rec += (io->bufuse == 0 && 0 < t); return io; } From 571796fe0d6ae440ff849f6a6ccbbba5e7760dd0 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 21:05:15 -0600 Subject: [PATCH 45/51] dj.1: update man page to match behavior --- docs/dj.1 | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 09f40c9..4c60ff7 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -42,9 +42,9 @@ byte onwards. A programmer may think of a file as a zero-indexed array of bytes; in this analogy, the offset given is the index of the byte at which to start reading or writing. -Seeks and skips aren\(cqt counted in the output statistics because they're -guaranteed to succeed (or the utility will exit unsuccessfully, before it has -written any data). +Seeks aren\(cqt counted in the output statistics because they\(cqre guaranteed +to succeed (or the utility will exit unsuccessfully, before it has written any +data). .\" .SH OPTIONS @@ -185,9 +185,19 @@ option is specified, this could make written data nonsensical. Existing files are not truncated on ouput and are instead overwritten. -Many lowercase options have capitalized variants and vice-versa which can be -confusing. Capitalized options tend to affect output or are more intense -versions of lowercase options. +The options +.B -b +and +.B -B +could be confused for each other, and so could +.B -s +and +.BR -S . +The lowercase option affects input and the capitalized option affects output. + +The discarded but read bytes skipped while processing irregular files, such as +streams, are reported in the diagnostic output. Bytes skipped while processing +regular files are not reported, as the bytes weren\(cqt read. .\" .SH RATIONALE @@ -208,3 +218,4 @@ Copyright \(co 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later .\" .SH SEE ALSO .BR dd (1p) +.BR lseek (3p) From 6ed7089b25852f41f882f4a42829e22f107259d3 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 4 Jul 2024 21:32:05 -0600 Subject: [PATCH 46/51] dj(1): statistics now track hard seeks --- docs/dj.1 | 11 ++++------- src/dj.c | 36 ++++++++++++++---------------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/docs/dj.1 b/docs/dj.1 index 4c60ff7..d358e3f 100644 --- a/docs/dj.1 +++ b/docs/dj.1 @@ -41,10 +41,6 @@ with a skip offset of 1 skips one byte into the input and reads from the second byte onwards. A programmer may think of a file as a zero-indexed array of bytes; in this analogy, the offset given is the index of the byte at which to start reading or writing. - -Seeks aren\(cqt counted in the output statistics because they\(cqre guaranteed -to succeed (or the utility will exit unsuccessfully, before it has written any -data). .\" .SH OPTIONS @@ -195,9 +191,10 @@ and .BR -S . The lowercase option affects input and the capitalized option affects output. -The discarded but read bytes skipped while processing irregular files, such as -streams, are reported in the diagnostic output. Bytes skipped while processing -regular files are not reported, as the bytes weren\(cqt read. +The skipped or sought bytes while processing irregular files, such as streams, +are reported in the diagnostic output, because they were actually read or +written. This is as opposed to bytes skipped while processing regular files, +which are not reported. .\" .SH RATIONALE diff --git a/src/dj.c b/src/dj.c index e65274a..08df495 100644 --- a/src/dj.c +++ b/src/dj.c @@ -56,7 +56,6 @@ static char *fmt_human = "%d+%d > %d+%d; %d > %d\n"; static char *stdin_name = ""; static char *stdout_name = ""; - static int creat_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* Consistent with touch(1p). */ static int read_flags = O_RDONLY; /* Consistent with Busybox dd(1). */ @@ -239,37 +238,29 @@ int main(int argc, char *argv[]){ /* 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. */ + size_t t; 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); + memset(io[1].buf, '\0', + (t = io[1].bufuse = MIN(io[1].bs, io[1].seek))); + if(Io_write(&io[1])->bufuse == t && !noerror) + Io_write(&io[1]); /* second chance */ + }while((io[1].seek -= (t - io[1].bufuse)) > 0 && io[1].bufuse != t); io[1].bufuse = 0; } - /* Sought bytes aren't counted in the statistics because successful seeking - * is guaranteed here. */ - if(io[1].seek > 0) + if(io[1].seek > 0){ + fprintio(stderr, fmt, io); return oserr(io[1].fn); + } do{ assert(io[0].bufuse == 0); { /* read */ - static char skipping = 0; + char skipping; int t; - if(io[0].seek > 0) - skipping = 1; - - if(skipping && io[0].seek < io[0].bs) + if((skipping = (io[0].seek > 0)) && io[0].seek < io[0].bs) io[0].bufuse = io[0].bs - io[0].seek; t = io[0].bufuse; @@ -279,11 +270,13 @@ int main(int argc, char *argv[]){ break; if(/* t < io[0].bufuse && */ io[0].bufuse < io[0].bs){ + assert(!skipping); + fprintf(stderr, "%s: Partial read:\n\t", program_name); fprintio(stderr, fmt, io); if(!noerror) count = 1; - if(!skipping && align >= 0){ + if(align >= 0){ /* fill the rest of the ibuf with padding */ memset(&(io[0].buf)[io[0].bufuse], align, io[0].bs - io[0].bufuse); @@ -292,7 +285,6 @@ int main(int argc, char *argv[]){ } if(skipping){ - skipping = (io[0].seek -= io[0].bufuse - t) > 0; io[0].bufuse = 0; count += (count != 0); continue; From abfe7046e77a3103f01f9b1f7ae9dddf082062ef Mon Sep 17 00:00:00 2001 From: DTB Date: Fri, 5 Jul 2024 08:02:09 -0600 Subject: [PATCH 47/51] dj(1): fix some type issues --- src/dj.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/dj.c b/src/dj.c index 08df495..2be2ecb 100644 --- a/src/dj.c +++ b/src/dj.c @@ -73,15 +73,16 @@ Io_read(struct Io *io){ assert(io->bs > 0); assert(io->bufuse < io->bs); - io->bufuse += (t = read(io->fd, &(io->buf)[io->bufuse], - io->bs - io->bufuse)); + if((t = read(io->fd, &(io->buf)[io->bufuse], io->bs - io->bufuse)) < 0) + t = 0; + + io->bufuse += t; + io->bytes += t; + io->prec += (0 < io->bufuse && io->bufuse < io->bs); + io->rec += (io->bufuse == io->bs); assert(io->bufuse <= io->bs); - io->bytes += t; - io->prec += (0 < io->bufuse && io->bufuse < io->bs); - io->rec += (io->bufuse == io->bs); - return io; } @@ -96,8 +97,8 @@ Io_write(struct Io *io){ memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); io->bytes += t; - io->prec += (0 < io->bufuse && 0 < t); - io->rec += (io->bufuse == 0 && 0 < t); + io->prec += (t > 0 && io->bufuse > 0); + io->rec += (t > 0 && io->bufuse == 0); return io; } @@ -258,8 +259,9 @@ int main(int argc, char *argv[]){ { /* read */ char skipping; - int t; + size_t t; + /* hack to intentionally get a partial read from Io_read */ if((skipping = (io[0].seek > 0)) && io[0].seek < io[0].bs) io[0].bufuse = io[0].bs - io[0].seek; @@ -270,8 +272,6 @@ int main(int argc, char *argv[]){ break; if(/* t < io[0].bufuse && */ io[0].bufuse < io[0].bs){ - assert(!skipping); - fprintf(stderr, "%s: Partial read:\n\t", program_name); fprintio(stderr, fmt, io); if(!noerror) From bab3cdd90e8ef97a4aa71593edf67c4bb8fa4c09 Mon Sep 17 00:00:00 2001 From: DTB Date: Sun, 7 Jul 2024 18:14:48 -0600 Subject: [PATCH 48/51] dj(1): Io_write: don't add to bufuse --- src/dj.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dj.c b/src/dj.c index 2be2ecb..4dc4842 100644 --- a/src/dj.c +++ b/src/dj.c @@ -93,7 +93,9 @@ Io_write(struct Io *io){ assert(io->bufuse > 0); assert(io->bufuse <= io->bs); - if((t = write(io->fd, io->buf, io->bufuse)) > 0) + if((t = write(io->fd, io->buf, io->bufuse)) < 0) + t = 0; + else if(t > 0) memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); io->bytes += t; @@ -268,6 +270,7 @@ int main(int argc, char *argv[]){ t = io[0].bufuse; if(Io_read(&io[0])->bufuse == t && !noerror) Io_read(&io[0]); /* second chance */ + assert(io[0].bufuse >= t); if(io[0].bufuse == t) /* that's all she wrote */ break; @@ -321,6 +324,7 @@ int main(int argc, char *argv[]){ t = io[1].bufuse; if(Io_write(&io[1])->bufuse == t && !noerror) Io_write(&io[1]); /* second chance */ + assert(io[1].bufuse <= t); if(io[1].bufuse == t){ /* no more love */ count = 1; break; From cf744efc1b13d7482c2f234c9f05490e443bb8f3 Mon Sep 17 00:00:00 2001 From: emma Date: Sun, 7 Jul 2024 18:21:48 -0600 Subject: [PATCH 49/51] swab(1): fixes not using strerror(3) --- src/swab.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/swab.rs b/src/swab.rs index e723819..5942c38 100644 --- a/src/swab.rs +++ b/src/swab.rs @@ -29,8 +29,11 @@ use getopt::GetOpt; extern crate sysexits; use sysexits::{ EX_OK, EX_OSERR, EX_USAGE }; +extern crate strerror; +use strerror::StrError; + fn oserr(s: &str, e: Error) -> ExitCode { - eprintln!("{}: {}", s, e); + eprintln!("{}: {}", s, e.strerror()); ExitCode::from(EX_OSERR as u8) } From 691e94c0c1542a3cdbe6f683e42e8854b5485bbc Mon Sep 17 00:00:00 2001 From: DTB Date: Sun, 7 Jul 2024 20:33:54 -0600 Subject: [PATCH 50/51] dj(1): error reporting more of the time --- src/dj.c | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/dj.c b/src/dj.c index 4dc4842..88c3979 100644 --- a/src/dj.c +++ b/src/dj.c @@ -37,17 +37,18 @@ char *program_name = "dj"; * writing ends of its jockeyed "pipe". User-configurable members are noted * with their relevant options. */ struct Io{ - int bs; /* buffer size (-bB) */ + char *buf; /* buffer */ + char *fn; /* file name (-io) */ + size_t bs; /* buffer size (-bB) */ size_t bufuse; /* buffer usage */ - char *buf; /* buffer */ - size_t bytes; /* bytes processed */ - int fd; /* file descriptor */ - int fl; /* file opening flags */ - char *fn; /* file name (may be stdin_name or stdout_name) (-io) */ - size_t prec; /* partial records processed */ - size_t rec; /* records processed */ - long seek; /* bytes to seek/skip (will be 0 after skippage) (-sS) */ -}; + size_t bytes; /* bytes processed */ + size_t prec; /* partial records processed */ + size_t rec; /* records processed */ + long seek; /* remaining bytes to seek/skip (-sS) */ + int fd; /* file descriptor */ + int fl; /* file opening flags */ + char error: 1; /* (bool) error status */ +}; /* To be assigned to main:fmt and used with printio(). */ static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034"; @@ -73,8 +74,10 @@ Io_read(struct Io *io){ assert(io->bs > 0); assert(io->bufuse < io->bs); - if((t = read(io->fd, &(io->buf)[io->bufuse], io->bs - io->bufuse)) < 0) + if((t = read(io->fd, &(io->buf)[io->bufuse], io->bs - io->bufuse)) < 0){ + io->error = 1; t = 0; + } io->bufuse += t; io->bytes += t; @@ -93,9 +96,10 @@ Io_write(struct Io *io){ assert(io->bufuse > 0); assert(io->bufuse <= io->bs); - if((t = write(io->fd, io->buf, io->bufuse)) < 0) + if((t = write(io->fd, io->buf, io->bufuse)) < 0){ + io->error = 1; t = 0; - else if(t > 0) + }else if(t > 0) memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); io->bytes += t; @@ -158,20 +162,21 @@ int main(int argc, char *argv[]){ char *fmt; /* == fmt_asv (default) or fmt_human (-H) */ size_t i; /* side of io being modified */ char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ - struct Io io[2]; + struct Io io[2 /* { in, out } */]; /* Set defaults. */ align = -1; count = 0; fmt = fmt_asv; noerror = 0; - for(i = 0; i < 2; ++i){ + for(i = 0; i < (sizeof io) / (sizeof *io); ++i){ io[i].bs = 1024 /* 1 KiB */; /* GNU dd(1) default; POSIX says 512B */ io[i].bufuse = 0; io[i].bytes = 0; io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO; io[i].fn = i == 0 ? stdin_name : stdout_name; io[i].fl = i == 0 ? read_flags : write_flags; + io[i].error = 0; io[i].prec = 0; io[i].rec = 0; io[i].seek = 0; @@ -227,10 +232,10 @@ int main(int argc, char *argv[]){ if(argc > optind) return usage(program_name); - for(i = 0; i < 2; ++i){ + for(i = 0; i < (sizeof io) / (sizeof *io); ++i){ /* buffer allocation */ 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 %zd bytes\n", program_name, io[i].bs); return EX_OSERR; } @@ -245,7 +250,7 @@ int main(int argc, char *argv[]){ do{ memset(io[1].buf, '\0', (t = io[1].bufuse = MIN(io[1].bs, io[1].seek))); - if(Io_write(&io[1])->bufuse == t && !noerror) + if(Io_write(&io[1])->bufuse == t && !noerror && !io[1].error) Io_write(&io[1]); /* second chance */ }while((io[1].seek -= (t - io[1].bufuse)) > 0 && io[1].bufuse != t); io[1].bufuse = 0; @@ -268,7 +273,7 @@ int main(int argc, char *argv[]){ io[0].bufuse = io[0].bs - io[0].seek; t = io[0].bufuse; - if(Io_read(&io[0])->bufuse == t && !noerror) + if(Io_read(&io[0])->bufuse == t && !noerror && io[0].error) Io_read(&io[0]); /* second chance */ assert(io[0].bufuse >= t); if(io[0].bufuse == t) /* that's all she wrote */ @@ -322,7 +327,7 @@ int main(int argc, char *argv[]){ } t = io[1].bufuse; - if(Io_write(&io[1])->bufuse == t && !noerror) + if(Io_write(&io[1])->bufuse == t && !noerror && !io[1].error) Io_write(&io[1]); /* second chance */ assert(io[1].bufuse <= t); if(io[1].bufuse == t){ /* no more love */ @@ -341,5 +346,9 @@ int main(int argc, char *argv[]){ fprintio(stderr, fmt, io); + for(i = 0; i < (sizeof io) / (sizeof *io); ++i) + if(io[i].error) + return oserr(io[i].fn); + return EX_OK; } From 5d5a6d2172a5e33399fe143265a205b5f7ffdffb Mon Sep 17 00:00:00 2001 From: DTB Date: Sun, 7 Jul 2024 21:13:44 -0600 Subject: [PATCH 51/51] dj(1): fix retvals --- src/dj.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/dj.c b/src/dj.c index 88c3979..8fe1af3 100644 --- a/src/dj.c +++ b/src/dj.c @@ -45,9 +45,9 @@ struct Io{ size_t prec; /* partial records processed */ size_t rec; /* records processed */ long seek; /* remaining bytes to seek/skip (-sS) */ + int error; /* errno */ int fd; /* file descriptor */ int fl; /* file opening flags */ - char error: 1; /* (bool) error status */ }; /* To be assigned to main:fmt and used with printio(). */ @@ -75,7 +75,7 @@ Io_read(struct Io *io){ assert(io->bufuse < io->bs); if((t = read(io->fd, &(io->buf)[io->bufuse], io->bs - io->bufuse)) < 0){ - io->error = 1; + io->error = errno; t = 0; } @@ -97,7 +97,7 @@ Io_write(struct Io *io){ assert(io->bufuse <= io->bs); if((t = write(io->fd, io->buf, io->bufuse)) < 0){ - io->error = 1; + io->error = errno; t = 0; }else if(t > 0) memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); @@ -109,13 +109,9 @@ Io_write(struct Io *io){ return io; } -/* Prints an error message suitable for the event of an operating system error, - * with the error itself to be described in the string s. */ static int -oserr(char *s){ - - fprintf(stderr, "%s: %s: %s\n", program_name, s, strerror(errno)); - +oserr(char *e, int n){ + fprintf(stderr, "%s: %s: %s\n", program_name, e, strerror(n)); return EX_OSERR; } @@ -203,7 +199,7 @@ int main(int argc, char *argv[]){ break; } } - return oserr(optarg); + return oserr(optarg, errno); case 'n': noerror = 1; break; case 'H': fmt = fmt_human; break; case 'a': @@ -250,15 +246,17 @@ int main(int argc, char *argv[]){ do{ memset(io[1].buf, '\0', (t = io[1].bufuse = MIN(io[1].bs, io[1].seek))); - if(Io_write(&io[1])->bufuse == t && !noerror && !io[1].error) + if(Io_write(&io[1])->bufuse == t && !noerror && io[1].error == 0) Io_write(&io[1]); /* second chance */ + if(io[1].error != 0) + return oserr(io[1].fn, io[1].error); }while((io[1].seek -= (t - io[1].bufuse)) > 0 && io[1].bufuse != t); io[1].bufuse = 0; } if(io[1].seek > 0){ fprintio(stderr, fmt, io); - return oserr(io[1].fn); + return oserr(io[1].fn, errno); } do{ @@ -273,7 +271,7 @@ int main(int argc, char *argv[]){ io[0].bufuse = io[0].bs - io[0].seek; t = io[0].bufuse; - if(Io_read(&io[0])->bufuse == t && !noerror && io[0].error) + if(Io_read(&io[0])->bufuse == t && !noerror && io[0].error == 0) Io_read(&io[0]); /* second chance */ assert(io[0].bufuse >= t); if(io[0].bufuse == t) /* that's all she wrote */ @@ -327,7 +325,7 @@ int main(int argc, char *argv[]){ } t = io[1].bufuse; - if(Io_write(&io[1])->bufuse == t && !noerror && !io[1].error) + if(Io_write(&io[1])->bufuse == t && !noerror && io[1].error == 0) Io_write(&io[1]); /* second chance */ assert(io[1].bufuse <= t); if(io[1].bufuse == t){ /* no more love */ @@ -348,7 +346,7 @@ int main(int argc, char *argv[]){ for(i = 0; i < (sizeof io) / (sizeof *io); ++i) if(io[i].error) - return oserr(io[i].fn); + return oserr(io[i].fn, io[i].error); return EX_OK; }