68 Commits

Author SHA1 Message Date
6a5739ea9d Merge branch 'dj-formatting' 2024-07-29 22:40:18 -06:00
DTB
6bbccb3776 fop(1): fix minor optind bug, add some comments 2024-07-29 21:14:45 -06:00
DTB
fd13a7f189 swab(1): fix imports to be consistent 2024-07-29 14:43:21 -06:00
6c2b7b68fd dj(1): more better comments 2024-07-29 14:38:33 -06:00
DTB
8d23c7fbac dj(1): better ASV formatting and comments 2024-07-29 13:43:52 -06:00
DTB
7e347bebdf mm(1): interpret retvals 2024-07-29 11:30:41 -06:00
DTB
a7b27f0d6a strcmp(1): interpret all retvals 2024-07-29 11:14:48 -06:00
DTB
c554b96722 swab(1): fix type errors 2024-07-29 11:13:58 -06:00
DTB
7b930363bf npc(1): fix syntax errors 2024-07-29 11:12:02 -06:00
DTB
5545846c92 dj(1): fix fprintio signature 2024-07-29 11:10:17 -06:00
DTB
162c6411b3 mm(1): remove terminate macro 2024-07-29 11:08:30 -06:00
DTB
2e172d93e8 dj(1): interpret all retvals 2024-07-29 11:04:00 -06:00
DTB
549fa98bdb npc(1): interpret all retvals 2024-07-29 10:53:58 -06:00
361b34c50f npc(1): improves program_name resolution 2024-07-29 02:35:59 -06:00
DTB
8bb57bf2e4 Merge branch 'dj-formatting' of git.tebibyte.media:bonsai/harakit into dj-formatting 2024-07-28 21:26:08 -06:00
DTB
ca6148e11f intcmp(1): tweak comment 2024-07-28 21:06:53 -06:00
44d461fb16 scrut(1): return program_name ternary to former position 2024-07-28 18:33:49 -06:00
338a3e7155 intcmp(1): move program_name ternary 2024-07-28 18:31:01 -06:00
6b9d13b8a0 fop(1): better opt matching 2024-07-28 18:27:22 -06:00
68e31058a8 intcmp(1): move program_name ternary 2024-07-28 17:49:39 -06:00
d2ae35eac9 fop(1): fixes glaring issue with newline handling 2024-07-28 01:15:08 -06:00
0f121cbac7 fop(1): more descriptive command arguments variable name 2024-07-28 00:40:16 -06:00
09193a14d5 fop(1): fixes unhandled i/o error 2024-07-28 00:35:55 -06:00
d5d13b84a7 STYLE: repition & Kernighan quote 2024-07-28 00:34:42 -06:00
aed64840ea STYLE: fixes some concerns 2024-07-28 00:12:34 -06:00
d45fa19d5c dj(1): fixes extraneous ternary for program_name 2024-07-27 23:59:32 -06:00
DTB
970b25dee2 str(1): fix brackets 2024-07-27 18:35:02 -06:00
DTB
5d48114083 dj(1), intcmp(1), mm(1), npc(1), scrut(1), str(1): make usage function consistent 2024-07-27 18:32:04 -06:00
DTB
429d064209 mm(1): fix argv use 2024-07-27 18:22:43 -06:00
DTB
c5c0e543e4 mm(1): remove useless extern errno 2024-07-27 18:21:17 -06:00
DTB
cba8394d95 str(1): fix argv parsing and add comments 2024-07-27 18:18:29 -06:00
DTB
199d48d85b swab(1): comment code 2024-07-27 17:59:47 -06:00
c0a5e11eef mm(1): revert changes to program_name 2024-07-25 21:28:42 -06:00
0641a487d7 intcmp(1): fixes pointless condition; formatting 2024-07-25 21:17:30 -06:00
2fe3aa894c dj(1), intcmp(1), mm(1), npc(1), scrut(1), strcmp(1): changes to use and modify program_name 2024-07-24 19:37:07 -06:00
0282b60e65 dj(1), mm(1), npc(1), scrut(1), str(1): consistent argv[0] handling 2024-07-20 07:18:59 -06:00
DTB
f96ed9c1f3 scrut(1): fix syntax error 2024-07-19 19:34:37 -06:00
DTB
19eee6b4e5 scrut(1): replace do/while loop 2024-07-19 19:31:34 -06:00
DTB
9086bf0d08 dj(1): remove do/while statement in read loop 2024-07-19 19:18:04 -06:00
DTB
e90d25e30f Merge branch 'strcmp' 2024-07-15 19:52:26 -06:00
DTB
71f372d2c2 Merge branch 'intcmp' 2024-07-15 15:21:31 -06:00
DTB
27ff64dffa intcmp(1): add comments 2024-07-15 14:51:54 -06:00
DTB
becb3bac4e strcmp(1): code clarification 2024-07-15 14:40:26 -06:00
e7a6632b41 mm(1): improves comments 2024-07-15 12:52:02 -06:00
48dbea0228 mm(1): imports full enum 2024-07-15 12:52:02 -06:00
64f3f73d96 mm(1): formatting 2024-07-15 12:52:02 -06:00
eda5385058 mm.1: small correction 2024-07-15 12:52:02 -06:00
7a0ad78000 mm.1: reflects new changes 2024-07-15 12:52:02 -06:00
DTB
8dc763f05e mm(1): treats trailing arguments as optargs for the last opt 2024-07-15 12:52:01 -06:00
20692d581a mm(1): makes -e block inferring stdout as an output, mm.1: reflects changes to -e 2024-07-15 12:52:01 -06:00
8d693b6664 mm(1): removes debug print 2024-07-15 12:52:01 -06:00
37804aab6b mm(1): fixes comment about flags -a & -t 2024-07-15 12:52:01 -06:00
3b76254599 mm(1): fixes creating files 2024-07-15 12:52:01 -06:00
e972ff468a mm(1): added -t for disabling truncation; mm.1: updated docs 2024-07-15 12:52:01 -06:00
1b3b03cae0 mm(1): rewritten in Rust 2024-07-15 12:51:48 -06:00
DTB
efb3ce626d strcmp(1): fix program_name type 2024-07-15 04:29:43 -06:00
DTB
16f23e11c0 strcmp.1: update docs to match utility 2024-07-15 04:26:57 -06:00
DTB
d87c278be5 strcmp(1): re-style, tweak exits 2024-07-15 04:21:50 -06:00
DTB
5caefbb465 strcmp(1): note used sysexit 2024-07-15 03:45:36 -06:00
DTB
8d743dab7a strcmp(1): add copyright header
I could trace strcmp(1) as far back as
<293436c5ad/src/streq.c>
in my repo.
2024-07-15 03:43:25 -06:00
DTB
2f2270322a intcmp(1): rewrite in rust 2024-07-15 03:14:57 -06:00
DTB
699893af89 intcmp(1): initial rust impl 2024-07-15 03:09:00 -06:00
DTB
9addfc9284 Merge branch 'dj' 2024-07-15 02:36:07 -06:00
DTB
b7bc1f16ad swab(1): use the getopt error message 2024-07-08 14:34:42 -06:00
ca6865688a swab(1): updates getopt usage 2024-07-08 13:23:23 -06:00
DTB
1fd768057c swab(1): don't accept positional arguments 2024-07-08 11:45:01 -06:00
DTB
35d54d84b0 swab(1): don't use the getopt error message 2024-07-08 11:31:35 -06:00
DTB
a141b95293 swab(1): remove -f 2024-07-08 11:30:21 -06:00
16 changed files with 513 additions and 564 deletions

View File

@@ -109,13 +109,13 @@ build/bin/hru: src/hru.rs build rustlibs
.PHONY: intcmp .PHONY: intcmp
intcmp: build/bin/intcmp intcmp: build/bin/intcmp
build/bin/intcmp: src/intcmp.c build build/bin/intcmp: src/intcmp.rs build rustlibs
$(CC) $(CFLAGS) -o $@ src/intcmp.c $(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/intcmp.rs
.PHONY: mm .PHONY: mm
mm: build/bin/mm mm: build/bin/mm
build/bin/mm: src/mm.c build build/bin/mm: src/mm.rs build rustlibs
$(CC) $(CFLAGS) -o $@ src/mm.c $(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/mm.rs
.PHONY: npc .PHONY: npc
npc: build/bin/npc npc: build/bin/npc

22
STYLE
View File

@@ -65,7 +65,7 @@ opening curly brace and before a closing one:
8. If a control flow statement is short enough to be easily understood in a 8. If a control flow statement is short enough to be easily understood in a
glance, it may be placed on a single line: glance, it may be placed on a single line:
if (!argc < 0) { usage(program_name); } if !(argc < 0) { usage(program_name); }
9. In C, note everything you use from a library in a comment subsequent to its 9. In C, note everything you use from a library in a comment subsequent to its
#include statement: #include statement:
@@ -86,21 +86,29 @@ library crates. Group alike statements:
11. Do not use do while loops in C. 11. Do not use do while loops in C.
12. Follow the rules from the paper The Power of 10: Rules for Developing 12. Adhere to the following rules from the paper The Power of 10: Rules for
Safety-Critical Code [0]: Developing Safety-Critical Code [0]:
1. Avoid complex flow constructs, such as goto and recursion. 1. Avoid complex flow constructs, such as goto and recursion.
2. All loops must have fixed bounds. This prevents runaway code. 2. All loops must have fixed bounds. This prevents runaway code.
3. Avoid heap memory allocation. 3. Avoid heap memory allocation.
4. Restrict functions to a single printed page. 4. Restrict functions to the length of a single printed page.
5. Use a minimum of two runtime assertions per function.
6. Restrict the scope of data to the smallest possible. 6. Restrict the scope of data to the smallest possible.
7. Check the return value of all non-void functions, or cast to void to 7. Check the return value of all non-void functions, or cast to void to
indicate the return value is useless. indicate the return value is useless (such as in the case of using
fprintf(3p) to print to the standard error).
8. Use the preprocessor sparingly. 8. Use the preprocessor sparingly.
9. Limit pointer use to a single dereference, and do not use function 9. Limit pointer use to a single dereference, and do not use function
pointers. pointers.
10. Compile with all possible warnings active; all warnings should then be 10. Compile with all possible warnings active; all warnings should then be
addressed before release of the software. addressed before release of the software (for C compilers, compile with
-Wpedantic).
13. Remember this quote from The Elements of Programming Style by Brian
Kernighan:
Everyone knows that debugging is twice as hard as writing a program in the
first place. So if you're as clever as you can be when you write it, how
will you ever debug it?
References References

View File

@@ -3,14 +3,14 @@
.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license,
.\" visit <http://creativecommons.org/licenses/by-sa/4.0/>. .\" visit <http://creativecommons.org/licenses/by-sa/4.0/>.
.\" .\"
.TH MM 1 2024-06-17 "Harakit X.X.X" .TH MM 1 2024-07-14 "Harakit X.X.X"
.SH NAME .SH NAME
mm \(en middleman mm \(en middleman
.\" .\"
.SH SYNOPSIS .SH SYNOPSIS
mm mm
.RB [ -aenu ] .RB [ -aetu ]
.RB [ -i\ input ] .RB [ -i\ input ]
.RB [ -o\ output ] .RB [ -o\ output ]
.\" .\"
@@ -21,19 +21,25 @@ Catenate input files and write them to the start of each output file or stream.
.SH OPTIONS .SH OPTIONS
.IP \fB-a\fP .IP \fB-a\fP
Opens subsequent outputs for appending rather than updating. Opens outputs for appending rather than updating.
.IP \fB-e\fP .IP \fB-e\fP
Use the standard error as an output. Use the standard error as an output.
.IP \fB-i\fP\ \fIinput\fP .IP \fB-t\fP
Opens a path as an input. If one or more of the input files is \(lq-\(rq or if Causes outputs to be overwritten instead of being truncated.
no inputs are specified, the standard input shall be used.
.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 .IP \fB-u\fP
Ensures neither input or output will be buffered. Ensures neither input or output will be buffered.
.IP \fB-n\fP .IP \fB-i\fP\ \fIinput\fP
Causes SIGINT signals to be ignored. 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. If specified as the
last option and if there are trailing arguments to the program, they shall be
appended to the list of files to use as inputs.
.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 and the
.B -e
option is not specified, the standard output shall be used. If specified as the
last option and if there are trailing arguments to the program, they shall be
appended to the list of files to use as outputs.
.\" .\"
.SH DIAGNOSTICS .SH DIAGNOSTICS
@@ -45,10 +51,6 @@ exits with the appropriate
.BR sysexits.h (3) .BR sysexits.h (3)
status. status.
.\" .\"
.SH CAVEATS
Existing files are not truncated on ouput and are instead overwritten.
.\"
.SH RATIONALE .SH RATIONALE
The The

View File

@@ -4,7 +4,7 @@
.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license,
.\" visit <http://creativecommons.org/licenses/by-sa/4.0/>. .\" visit <http://creativecommons.org/licenses/by-sa/4.0/>.
.\" .\"
.TH STRCMP 1 2024-06-17 "Harakit X.X.X" .TH STRCMP 1 2024-07-15 "Harakit X.X.X"
.SH NAME .SH NAME
strcmp \(en compare strings strcmp \(en compare strings
.\" .\"
@@ -20,15 +20,15 @@ Check whether string arguments are the same.
.SH DIAGNOSTICS .SH DIAGNOSTICS
The program will exit successfully if the strings are identical. Otherwise, it The program will exit successfully if the strings are identical. Otherwise, it
will exit with an error code of 1 if a string passed has a lesser byte value will exit with an error code less than 128 if a string passed has a lesser byte
than one of the prior strings: value than one of the prior strings:
.RS .RS
strcmp b a strcmp b a
.RE .RE
or with an error code of 255 if it has a greater byte value than one of the or with an error code greater than 128 if it has a greater byte value than one
prior strings: of the prior strings:
.RS .RS
strcmp a b strcmp a b

View File

@@ -11,7 +11,6 @@ swab \(en swap bytes
.SH SYNOPSIS .SH SYNOPSIS
swab swab
.RB [ -f ]
.RB [ -w\ word_size ] .RB [ -w\ word_size ]
.\" .\"
.SH DESCRIPTION .SH DESCRIPTION
@@ -20,8 +19,6 @@ Swap the latter and former halves of a block of bytes.
.\" .\"
.SH OPTIONS .SH OPTIONS
.IP \fB-f\fP
Ignore SIGINT signal.
.IP \fB-w\fP\ \fIword_size\fP .IP \fB-w\fP\ \fIword_size\fP
Configures the word size; that is, the size in bytes of the block size on which 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 to operate. The default word size is 2. The word size must be cleanly divisible

120
src/dj.c
View File

@@ -20,6 +20,7 @@
#include <assert.h> /* assert(3) */ #include <assert.h> /* assert(3) */
#include <errno.h> /* errno */ #include <errno.h> /* errno */
#include <fcntl.h> /* open(2) */ #include <fcntl.h> /* open(2) */
#include <stdbool.h> /* bool */
#include <stdio.h> /* fprintf(3), stderr */ #include <stdio.h> /* fprintf(3), stderr */
#include <stdlib.h> /* malloc(3), strtol(3), size_t */ #include <stdlib.h> /* malloc(3), strtol(3), size_t */
#include <string.h> /* memcpy(3), memmove(3), memset(3) */ #include <string.h> /* memcpy(3), memmove(3), memset(3) */
@@ -28,8 +29,6 @@
* optarg, optind, STDIN_FILENO, STDOUT_FILENO */ * optarg, optind, STDIN_FILENO, STDOUT_FILENO */
#include <sys/stat.h> /* S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR */ #include <sys/stat.h> /* S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR */
extern int errno;
char *program_name = "dj"; char *program_name = "dj";
/* dj uses two structures that respectively correspond to the reading and /* dj uses two structures that respectively correspond to the reading and
@@ -49,10 +48,6 @@ struct Io {
int fl; /* file opening flags */ int fl; /* file opening flags */
}; };
/* 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";
static char *stdin_name = "<stdin>"; static char *stdin_name = "<stdin>";
static char *stdout_name = "<stdout>"; static char *stdout_name = "<stdout>";
@@ -66,6 +61,7 @@ static int write_flags = O_WRONLY | O_CREAT;
/* Macro to check if fd is stdin or stdout */ /* Macro to check if fd is stdin or stdout */
#define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO) #define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO)
/* Completes one Io block read */
static struct Io * static struct Io *
Io_read(struct Io *io) { Io_read(struct Io *io) {
int t; int t;
@@ -88,6 +84,7 @@ Io_read(struct Io *io) {
return io; return io;
} }
/* Completes one Io block write */
static struct Io * static struct Io *
Io_write(struct Io *io) { Io_write(struct Io *io) {
int t; int t;
@@ -99,7 +96,7 @@ Io_write(struct Io *io) {
io->error = errno; io->error = errno;
t = 0; t = 0;
} else if (t > 0) { } else if (t > 0) {
memmove(io->buf, &(io->buf)[t], (io->bufuse -= t)); (void)memmove(io->buf, &(io->buf)[t], (io->bufuse -= t));
} }
io->bytes += t; io->bytes += t;
@@ -111,15 +108,16 @@ Io_write(struct Io *io) {
static int static int
oserr(char *e, int n) { oserr(char *e, int n) {
fprintf(stderr, "%s: %s: %s\n", program_name, e, strerror(n)); (void)fprintf(stderr, "%s: %s: %s\n", program_name, e, strerror(n));
return EX_OSERR; return EX_OSERR;
} }
/* Prints statistics regarding the use of dj, particularly partially and /* Prints statistics regarding the use of dj, particularly partially and
* completely read and written records. */ * completely read and written records. */
static void static int
fprintio(FILE *stream, char *fmt, struct Io io[2]) { fprintio(FILE *stream, char *fmt, struct Io io[2]) {
fprintf( return fprintf(
stream, stream,
fmt, fmt,
io[0].rec, io[0].rec,
@@ -129,10 +127,20 @@ fprintio(FILE *stream, char *fmt, struct Io io[2]) {
io[0].bytes, io[0].bytes,
io[1].bytes io[1].bytes
); );
return;
} }
/* To be assigned to main:fmt and used with printio(). */
static char *fmt_asv =
"%d" /* io[0].rec */ "\037" /* ASCII US */
"%d" /* io[0].prec */ "\036" /* ASCII RS */
"%d" /* io[1].rec */ "\037" /* ASCII US */
"%d" /* io[1].prec */ "\035" /* ASCII GS */
"%d" /* io[0].bytes */ "\036" /* ASCII RS */
"%d" /* io[1].bytes */ "\034" /* ASCII FS */
"\n"
;
static char *fmt_human = "%d+%d > %d+%d; %d > %d\n";
/* Parses the string s to an integer, returning either the integer or in the /* Parses the string s to an integer, returning either the integer or in the
* case of an error a negative integer. This is used for argument parsing * case of an error a negative integer. This is used for argument parsing
* (e.g. -B [int]) in dj and no negative integer would be valid anyway. */ * (e.g. -B [int]) in dj and no negative integer would be valid anyway. */
@@ -146,11 +154,13 @@ parse(char *s) {
} }
static int static int
usage(char *s) { usage(char *argv0) {
fprintf( (void)fprintf(
stderr, "Usage: %s [-Hn] [-a byte] [-c count]\n" stderr,
"Usage: %s [-Hn] [-a byte] [-c count]\n"
"\t[-i file] [-b block_size] [-s offset]\n" "\t[-i file] [-b block_size] [-s offset]\n"
"\t[-o file] [-B block_size] [-S offset]\n", program_name "\t[-o file] [-B block_size] [-S offset]\n",
argv0
); );
return EX_USAGE; return EX_USAGE;
@@ -158,17 +168,17 @@ usage(char *s) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int align; /* low 8b used, negative if no alignment is being done */ 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 */ int count; /* -1 if dj(1) runs until no more reads are possible */
char *fmt; /* == fmt_asv (default) or fmt_human (-H) */ char *fmt; /* set to fmt_asv (default) or fmt_human (-H) */
size_t i; /* side of io being modified */ size_t i; /* side of io (in or out) being modified */
char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ bool retry; /* false if exits on partial reads or writes */
struct Io io[2 /* { in, out } */]; struct Io io[2 /* { in, out } */];
/* Set defaults. */ /* Set defaults. */
align = -1; align = -1;
count = 0; count = -1;
fmt = fmt_asv; fmt = fmt_asv;
noerror = 0; retry = 0;
for (i = 0; i < (sizeof io) / (sizeof *io); ++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].bs = 1024 /* 1 KiB */; /* GNU dd(1) default; POSIX says 512B */
io[i].bufuse = 0; io[i].bufuse = 0;
@@ -209,8 +219,9 @@ int main(int argc, char *argv[]) {
} }
} }
return oserr(optarg, errno); /* break; */ return oserr(optarg, errno);
case 'n': noerror = 1; break; /* retry failed reads once */ /* UNREACHABLE */
case 'n': retry = 1; break; /* retry failed reads once */
case 'H': fmt = fmt_human; break; /* human-readable output */ case 'H': fmt = fmt_human; break; /* human-readable output */
case 'a': /* input buffer padding */ case 'a': /* input buffer padding */
if (optarg[0] == '\0' || optarg[1] == '\0') { if (optarg[0] == '\0' || optarg[1] == '\0') {
@@ -224,12 +235,12 @@ int main(int argc, char *argv[]) {
if (c == 'c' && (count = parse(optarg)) >= 0) { break; } if (c == 'c' && (count = parse(optarg)) >= 0) { break; }
i = (c >= 'A' && c <= 'Z'); i = (c >= 'A' && c <= 'Z');
c |= 0x20; /* 0b 0010 0000 (ASCII make lowercase) */ c |= 0x20; /* 0b 0010 0000 (ASCII) make lowercase */
if ( if ( /* if -b or -s is parsed out correctly */
(c == 'b' && (io[i].bs = parse(optarg)) > 0) (c == 'b' && (io[i].bs = parse(optarg)) > 0)
|| (c == 's' && (io[i].seek = parse(optarg)) >= 0) || (c == 's' && (io[i].seek = parse(optarg)) >= 0)
) { break; } ) { break; } /* don't error */
/* FALLTHROUGH */ /* FALLTHROUGH */
default: default:
@@ -246,7 +257,7 @@ int main(int argc, char *argv[]) {
for (i = 0; i < (sizeof io) / (sizeof *io); ++i) { for (i = 0; i < (sizeof io) / (sizeof *io); ++i) {
/* buffer allocation */ /* buffer allocation */
if ((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL) { if ((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL) {
fprintf( (void)fprintf(
stderr, "%s: Failed to allocate %zd bytes\n", stderr, "%s: Failed to allocate %zd bytes\n",
program_name, io[i].bs program_name, io[i].bs
); );
@@ -263,13 +274,13 @@ int main(int argc, char *argv[]) {
/* hard seeking; t is io[1].bufuse, before Io_write subtracts from it */ /* hard seeking; t is io[1].bufuse, before Io_write subtracts from it */
for(size_t t; io[1].seek > 0; io[1].seek -= (t - io[1].bufuse)) { for(size_t t; io[1].seek > 0; io[1].seek -= (t - io[1].bufuse)) {
memset( (void)memset(
io[1].buf, '\0', /* set buf to all nulls */ io[1].buf, '\0', /* set buf to all nulls */
(t = io[1].bufuse = MIN(io[1].bs, io[1].seek)) /* saturate block */ (t = io[1].bufuse = MIN(io[1].bs, io[1].seek)) /* saturate block */
); );
if (Io_write(&io[1])->bufuse == t && !noerror && io[1].error == 0) { if (Io_write(&io[1])->bufuse == t && !retry && io[1].error == 0) {
Io_write(&io[1]); /* second chance */ (void)Io_write(&io[1]); /* second chance */
} }
if (io[1].error != 0) { return oserr(io[1].fn, io[1].error); } if (io[1].error != 0) { return oserr(io[1].fn, io[1].error); }
@@ -277,14 +288,17 @@ int main(int argc, char *argv[]) {
if (io[1].bufuse == t) { break; } /* all writes failed! */ if (io[1].bufuse == t) { break; } /* all writes failed! */
} }
io[1].bufuse = 0; io[1].bufuse = 0; /* reset after hard seek */
if (io[1].seek > 0) { /* hard seeking failed */ if (io[1].seek > 0) { /* hard seeking failed */
fprintio(stderr, fmt, io); (void)fprintio(stderr, fmt, io);
return oserr(io[1].fn, errno); return oserr(io[1].fn, errno);
} }
do { /* while(count == 0 || --count > 0); */ for ( ;
count == -1 || count > 0;
count -= (count != -1) /* decrement if counting */
) {
assert(io[0].bufuse == 0); assert(io[0].bufuse == 0);
{ /* read */ { /* read */
@@ -297,23 +311,23 @@ int main(int argc, char *argv[]) {
} }
t = io[0].bufuse; t = io[0].bufuse;
if (Io_read(&io[0])->bufuse == t && !noerror && io[0].error == 0) { if (Io_read(&io[0])->bufuse == t && !retry && io[0].error == 0) {
Io_read(&io[0]); /* second chance */ (void)Io_read(&io[0]); /* second chance */
} }
assert(io[0].bufuse >= t); assert(io[0].bufuse >= t);
if (io[0].bufuse == t) /* that's all she wrote */ { break; } if (io[0].bufuse == t) { break; } /* that's all she wrote */
if (/* t < io[0].bufuse && */ 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); (void)fprintf(stderr, "%s: Partial read:\n\t", program_name);
fprintio(stderr, fmt, io); (void)fprintio(stderr, fmt, io);
if (!noerror) { count = 1; } if (!retry) { count = 1; }
if (align >= 0) { if (align >= 0) {
/* fill the rest of the ibuf with padding */ /* fill the rest of the ibuf with padding */
memset( (void)memset(
&(io[0].buf)[io[0].bufuse], &(io[0].buf)[io[0].bufuse],
align, align,
io[0].bs - io[0].bufuse io[0].bs - io[0].bufuse
@@ -326,7 +340,7 @@ int main(int argc, char *argv[]) {
if (skipping > 0) { if (skipping > 0) {
io[0].seek -= skipping; io[0].seek -= skipping;
io[0].bufuse = 0; io[0].bufuse = 0;
count += (count != 0); count += (count != -1); /* increment if counting */
continue; continue;
} }
} }
@@ -337,25 +351,25 @@ int main(int argc, char *argv[]) {
if (io[0].bs <= io[1].bs) { if (io[0].bs <= io[1].bs) {
int n; int n;
memcpy( /* saturate obuf */ (void)memcpy( /* saturate obuf */
io[1].buf, io[0].buf, io[1].buf, io[0].buf,
(io[1].bufuse = (n = MIN(io[0].bufuse, io[1].bs))) (io[1].bufuse = (n = MIN(io[0].bufuse, io[1].bs)))
); );
/* permute the copied units out of ibuf */ /* permute the copied units out of ibuf */
memmove(io[0].buf, &(io[0].buf)[n], (io[0].bufuse -= n)); (void)memmove(io[0].buf, &(io[0].buf)[n], (io[0].bufuse -= n));
} else /* if(io[0].bs > io[1].bs) */ { } else /* if(io[0].bs > io[1].bs) */ {
int n; int n;
/* drain what we can from ibuf */ /* drain what we can from ibuf */
memcpy( (void)memcpy(
&(io[1].buf)[io[1].bufuse], io[0].buf, &(io[1].buf)[io[1].bufuse], io[0].buf,
(n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse)) (n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse))
); );
io[1].bufuse += n; io[1].bufuse += n;
/* permute out the copied units */ /* permute out the copied units */
memmove(io[0].buf, &(io[0].buf)[n], io[0].bs - n); (void)memmove(io[0].buf, &(io[0].buf)[n], io[0].bs - n);
io[0].bufuse -= n; io[0].bufuse -= n;
if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) { if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) {
@@ -368,9 +382,9 @@ int main(int argc, char *argv[]) {
t = io[1].bufuse; t = io[1].bufuse;
if (Io_write(&io[1])->bufuse == t if (Io_write(&io[1])->bufuse == t
&& !noerror && !retry
&& io[1].error == 0) { && io[1].error == 0) {
Io_write(&io[1]); /* second chance */ (void)Io_write(&io[1]); /* second chance */
} }
assert(io[1].error == 0 || io[1].bufuse == t); assert(io[1].error == 0 || io[1].bufuse == t);
@@ -387,15 +401,15 @@ int main(int argc, char *argv[]) {
} }
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); (void)fprintf(stderr, "%s: Partial write:\n\t", program_name);
fprintio(stderr, fmt, io); (void)fprintio(stderr, fmt, io);
if(!noerror) { count = 1; } if(!retry) { count = 1; }
}
} }
} }
} while(count == 0 || --count > 0);
fprintio(stderr, fmt, io); (void)fprintio(stderr, fmt, io);
for (i = 0; i < (sizeof io) / (sizeof *io); ++i) { for (i = 0; i < (sizeof io) / (sizeof *io); ++i) {
if (io[i].error) { return oserr(io[i].fn, io[i].error); } if (io[i].error) { return oserr(io[i].fn, io[i].error); }

View File

@@ -33,7 +33,7 @@ use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
fn main() { fn main() {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
let mut d = '\u{1E}'.to_string(); /* ASCII record separator */ let mut d = '\u{1E}'.to_string(); /* ASCII record separator */
let mut optind = 0; let mut optind = 1;
let usage = format!( let usage = format!(
"Usage: {} [-d delimiter] index command [args...]", "Usage: {} [-d delimiter] index command [args...]",
@@ -42,18 +42,24 @@ fn main() {
while let Some(opt) = argv.getopt("d:") { while let Some(opt) = argv.getopt("d:") {
match opt.opt() { match opt.opt() {
Ok(_) => { Ok("d") => {
/* delimiter */ /* delimiter */
d = opt.arg().unwrap(); d = opt.arg().unwrap();
optind = opt.ind(); optind = opt.ind();
}, },
Err(_) => { _ => {
eprintln!("{}", usage); eprintln!("{}", usage);
exit(EX_USAGE); exit(EX_USAGE);
} }
}; };
} }
/* parse the specified index as a number we can use */
let index = argv[optind].parse::<usize>().unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[1], e);
exit(EX_DATAERR);
});
/* index of the argv[0] for the operator command */ /* index of the argv[0] for the operator command */
let command_arg = optind as usize + 1; let command_arg = optind as usize + 1;
@@ -63,20 +69,18 @@ fn main() {
exit(EX_USAGE); exit(EX_USAGE);
}); });
/* parse the specified index as a number we can use */ /* read entire standard input into memory */
let index = argv[optind].parse::<usize>().unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[1], e);
exit(EX_DATAERR);
});
let mut buf = String::new(); let mut buf = String::new();
let _ = stdin().read_to_string(&mut buf); if let Err(e) = stdin().read_to_string(&mut buf) {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
};
/* split the buffer by the delimiter (by default, '\u{1E}') */ /* split the buffer by the delimiter (by default, '\u{1E}') */
let mut fields = buf.split(&d).collect::<Vec<&str>>(); let mut fields = buf.split(&d).collect::<Vec<&str>>();
/* collect arguments for the operator command */ /* collect arguments for the operator command */
let opts = argv let command_args = argv
.iter() .iter()
.clone() .clone()
.skip(command_arg + 1) /* skip the command name */ .skip(command_arg + 1) /* skip the command name */
@@ -84,7 +88,7 @@ fn main() {
/* spawn the command to operate on the field */ /* spawn the command to operate on the field */
let mut spawned = Command::new(operator) let mut spawned = Command::new(operator)
.args(opts) /* spawn with the specified arguments */ .args(command_args) /* spawn with the specified arguments */
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) /* piped stdout to handle output ourselves */ .stdout(Stdio::piped()) /* piped stdout to handle output ourselves */
.spawn() .spawn()
@@ -117,8 +121,13 @@ fn main() {
/* get the output with which the original field will be replaced */ /* get the output with which the original field will be replaced */
let mut replace = output.stdout.clone(); let mut replace = output.stdout.clone();
/* as long as its not a newline, set the replacement to the output */ /* pop trailing newline out if the input did not contain it */
if replace.pop() != Some(b'\n') { replace = output.stdout; } if fields[index].chars().last() != Some('\n') /* no newline */
&& replace.pop() != Some(b'\n') { /* pop last char of replacement */
/* restore replacement to original command output if popped char was not
* a newline */
replace = output.stdout;
}
/* convert the output of the program to UTF-8 */ /* convert the output of the program to UTF-8 */
let new_field = String::from_utf8(replace).unwrap_or_else(|e| { let new_field = String::from_utf8(replace).unwrap_or_else(|e| {

View File

@@ -1,87 +0,0 @@
/*
* Copyright (c) 2023 DTB <trinity@trinity.moe>
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <errno.h> /* errno */
#include <stdio.h> /* fprintf(3), stderr */
#include <stdlib.h> /* strtol(3), size_t, EXIT_FAILURE */
#include <unistd.h> /* getopt(3), optind */
#include <sysexits.h> /* EX_OK, EX_USAGE */
/* 0b00? */ /* Equal | -e | 0b001 | 1 */
#define EQUAL 0x01 /* Greater | -g | 0b010 | 2 */
/* 0b0?0 */ /* Greater or Equal | -ge | 0b011 | 3 */
#define GREATER 0x02 /* Lesser | -l | 0b100 | 4 */
/* 0b?00 */ /* Lesser or Equal | -le | 0b101 | 5 */
#define LESSER 0x04 /* Inequal (Greater or Lesser) | -gl | 0b110 | 6 */
static char *program_name = "intcmp";
int usage(char *s) {
fprintf(stderr, "Usage: %s [-egl] integer integer...\n", s);
return EX_USAGE;
}
int main(int argc, char *argv[]) {
int c;
size_t i;
unsigned char mode;
int r; /* reference integer */
char *s = (argv[0] == NULL ? program_name : argv[0]);
mode = 0;
if (argc == 0 | argc < 3) { return usage(s); }
while ((c = getopt(argc, argv, "egl")) != -1) {
switch (c) {
case 'e': mode |= EQUAL; break;
case 'g': mode |= GREATER; break;
case 'l': mode |= LESSER; break;
default: return usage(s);
}
}
if (optind + 2 /* ref cmp */ > argc) { return usage(s); }
i = optind;
do {
r = c;
c = strtol(argv[i], &argv[i], 10);
if (*argv[i] != '\0' || errno != 0) {
fprintf(
stderr, "%s: argument #%d: Invalid integer\n", argv[0], (int)i
);
return EX_USAGE;
}
if (i == optind) { continue; }
/* rule enforcement; if a mode isn't permitted and the numbers
* correspond to it, return 1 */
if ( (!(mode & EQUAL) && r == c)
|| (!(mode & GREATER) && r > c)
|| (!(mode & LESSER) && r < c)
) { return 1; }
} while (++i < argc);
return EX_OK;
}

79
src/intcmp.rs Normal file
View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 20232024 DTB <trinity@trinity.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
use std::{
env::args,
process::ExitCode
};
extern crate getopt;
use getopt::GetOpt;
extern crate sysexits;
use sysexits::EX_USAGE;
fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} [-egl] integer integer...", s);
ExitCode::from(EX_USAGE as u8)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
let mut e = false; /* args can be == */
let mut g = false; /* args can be > */
let mut l = false; /* args can be < */
let mut optind = 0;
if argv.len() < 3 { return usage(&argv[0]); }
while let Some(opt) = argv.getopt("egl") {
match opt.opt() {
Ok("e") => e = true,
Ok("g") => g = true,
Ok("l") => l = true,
_ => { return usage(&argv[0]); },
}
optind = opt.ind();
}
if argv.len() - optind < 2 /* see usage */ { return usage(&argv[0]); }
let mut prev: Option<usize> = None; /* no previous operand */
let mut currn: usize;
for arg in argv.iter().skip(optind) { /* iterate operands */
match arg.parse::<usize>() { /* parse current operand */
Ok(n) => currn = n,
_ => {
eprintln!("{}: {}: Invalid integer", &argv[0], arg);
return ExitCode::from(EX_USAGE as u8);
}
}
if let Some(prevn) = prev { /* if there was a previous opr., test */
if (!e && prevn == currn)
|| (!g && prevn > currn)
|| (!l && prevn < currn)
{ return ExitCode::FAILURE; }
}
prev = Some(currn); /* there is a previous operand */
}
ExitCode::SUCCESS
}

268
src/mm.c
View File

@@ -1,268 +0,0 @@
/*
* Copyright (c) 2024 DTB <trinity@trinity.moe>
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <errno.h> /* errno */
#include <signal.h> /* signal(2), SIG_ERR, SIG_IGN, SIGINT */
#include <stdio.h> /* fclose(3), fopen(3), fprintf(3), getc(3), putc(3),
* setvbuf(3), size_t, _IONBF, NULL */
#include <stdlib.h> /* free(3), realloc(3) */
#include <string.h> /* strcmp(3), strerror(3) */
#include <unistd.h> /* getopt(3) */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */
extern int errno;
/* This structure is how open files are tracked. */
struct Files{
size_t a; /* allocation */
size_t s; /* used size */
char *mode; /* file opening mode */
char **names; /* file names */
FILE **files; /* file pointers */
};
/* How much to grow the allocation when it's saturated. */
#ifndef ALLOC_INCREMENT
# define ALLOC_INCREMENT 1
#endif
/* How much to grow the allocation at program start. */
#ifndef ALLOC_INITIAL
# define ALLOC_INITIAL 10
#endif
/* pre-allocated strings */
static char *program_name = "<no argv[0]>";
static char *stdin_name = "<stdin>";
static char *stdout_name = "<stdout>";
static char *stderr_name = "<stderr>";
static char *(fmode[]) = { (char []){"rb"}, (char []){"rb+"} };
static char *wharsh = "wb";
/* Adds the open FILE pointer for the file at the path s to the files struct,
* returning the FILE if successful and NULL if not, allocating more memory in
* the files buffers as needed. */
static FILE *
Files_append(struct Files *files, FILE *file, char *name) {
if (file == NULL || (files->s == files->a
&& ((files->files = realloc(files->files,
(files->a += (files->a == 0)
? ALLOC_INITIAL
: ALLOC_INCREMENT)
* sizeof *(files->files))) == NULL
|| (files->names = realloc(files->names,
files->a * sizeof *(files->names))) == NULL)))
return NULL;
files->names[files->s] = name;
return files->files[files->s++] = file;
}
/* Opens the file at the path p and puts it in the files struct, returning NULL
* if either the opening or the placement of the open FILE pointer fail. */
#define Files_open(files, p) \
Files_append((files), fopen((p), (files)->mode), (p))
/* Prints a diagnostic message based on errno and returns an exit status
* appropriate for an OS error. */
static int
oserr(char *s, char *r) {
fprintf(stderr, "%s: %s: %s\n", s, r, strerror(errno));
return EX_OSERR;
}
/* Hijacks i and j from main and destructs the files[2] struct used by main by
* closing its files and freeing its files and names arrays, returning retval
* from main. */
#define terminate \
for (i = 0; i < 2; ++i) { \
for (j = 0; j < files[i].s; ++j) { \
if ( \
files[i].files[j] != stdin \
&& files[i].files[j] != stdout \
&& files[i].files[j] != stderr \
) { \
fclose(files[i].files[j]); \
} \
} \
free(files[i].files); \
free(files[i].names); \
} \
return retval
/* Prints a usage text, in which s is the program being run (i.e. argv[0]), and
* returns an exit status appropriate for a usage error. */
int usage(char *s) {
fprintf(stderr, "Usage: %s [-aenu] [-i input]... [-o output]...\n", s);
return EX_USAGE;
}
int main(int argc, char *argv[]) {
int c;
struct Files files[2]; /* {read, write} */
size_t i;
size_t j;
size_t k; /* loop index but also unbuffer status */
int retval;
/* Initializes the files structs with their default values, standard
* input and standard output. If an input or an output is specified
* these initial values will be overwritten, so to, say, use mm(1)
* equivalently to tee(1p), -o - will need to be specified before
* additional files to ensure standard output is still written. */
for (i = 0; i < 2; ++i) {
files[i].a = 0;
files[i].s = 0;
files[i].mode = fmode[i];
files[i].files = NULL;
files[i].names = NULL;
Files_append(
&files[i],
i == 0 ? stdin : stdout,
i == 0 ? stdin_name : stdout_name
);
files[i].s = 0;
}
k = 0;
if (argc > 0) { program_name = argv[0]; }
if (argc > 1) {
while ((c = getopt(argc, argv, "aehi:no:u")) != -1) {
switch (c){
case 'a': /* "rb+" -> "ab" */
files[1].mode[0] = 'a';
files[1].mode[2] = '\0';
break;
case 'e':
if (Files_append(&files[1], stderr, stderr_name) != NULL) {
break;
}
retval = oserr(argv[0], "-e");
terminate;
case 'i':
if (
(strcmp(optarg, "-") == 0
&& Files_append(&files[0], stdin, stdin_name) != NULL)
|| Files_open(&files[0], optarg) != NULL
) { break; }
retval = oserr(argv[0], optarg);
terminate;
case 'o':
if (
(strcmp(optarg, "-") == 0
&& Files_append(&files[1], stdout, stdout_name) != NULL)
|| Files_open(&files[1], optarg) != NULL
) { break; }
/* does not exist, so try to create it */
if (errno == ENOENT) {
files[1].mode = wharsh;
if (Files_open(&files[1], optarg) != NULL) {
files[1].mode = fmode[1];
break;
}
}
retval = oserr(argv[0], optarg);
terminate;
case 'n':
if (signal(SIGINT, SIG_IGN) != SIG_ERR) { break; }
retval = oserr(argv[0], "-n");
terminate;
case 'u':
k = 1;
break;
default:
retval = usage(argv[0]);
terminate;
}
}
}
if (optind != argc) {
retval = usage(argv[0]);
terminate;
}
files[0].s += files[0].s == 0;
files[1].s += files[1].s == 0;
/* Unbuffer files. */
if (k) {
for (
i = 0; i < files[0].s; setvbuf(files[0].files[i++], NULL, _IONBF, 0)
);
for (
i = 0; i < files[1].s; setvbuf(files[1].files[i++], NULL, _IONBF, 0)
);
}
retval = EX_OK;
/* Actual program loop. */
for (i = 0; i < files[0].s; ++i) { /* iterate ins */
while ((c = getc(files[0].files[i])) != EOF) { /* iterate chars */
for (j = 0; j < files[1].s; ++j) { /* iterate outs */
if (putc(c, files[1].files[j]) == EOF) {
/* notebook's full */
retval = EX_IOERR;
fprintf(
stderr,
"%s: %s: %s\n",
program_name,
files[1].names[j],
strerror(errno)
);
if (fclose(files[1].files[j]) == EOF) {
fprintf(
stderr,
"%s: %s: %s\n",
program_name,
files[1].names[j],
strerror(errno)
);
}
/* massage out the tense muscle */
for(k = j--; k < files[1].s - 1; ++k){
files[1].files[k] = files[1].files[k+1];
files[1].names[k] = files[1].names[k+1];
}
if(--files[1].s == 0) { terminate; }
}
}
}
}
terminate;
}

185
src/mm.rs Normal file
View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* Copyright (c) 2024 DTB <trinity@trinity.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
use std::{
env::args,
fs::File,
io::{ stdin, stdout, stderr, BufWriter, Read, Write },
os::fd::{ AsRawFd, FromRawFd },
process::{ exit, ExitCode },
};
extern crate getopt;
extern crate strerror;
extern crate sysexits;
use getopt::GetOpt;
use strerror::StrError;
use sysexits::{ EX_IOERR, EX_USAGE };
use ArgMode::*;
enum ArgMode { In, Out }
fn main() -> ExitCode {
let argv = args().collect::<Vec<_>>();
let usage = format!("Usage: {} [-aetu] [-i input] [-o output]", argv[0]);
let mut a = false; /* append to the file */
let mut e = false; /* use stderr as an output */
let mut t = true; /* do not truncate the file before writing */
let mut u = false; /* unbuffer i/o */
let mut ins = Vec::new(); /* initial input file path vector */
let mut outs = Vec::new(); /* initial output file path vector */
let mut mode: Option<ArgMode> = None; /* mode set by last-used option */
let mut optind = 0;
while let Some(opt) = argv.getopt("aei:o:tu") {
match opt.opt() {
Ok("a") => a = true,
Ok("e") => e = true,
Ok("u") => u = true,
Ok("t") => t = false,
Ok("i") => { /* add inputs */
let input = opt.arg().unwrap();
ins.push(input);
mode = Some(In); /* latest argument == -i */
},
Ok("o") => { /* add output */
let output = opt.arg().unwrap();
outs.push(output);
mode = Some(Out); /* latest argument == -o */
},
Err(_) | Ok(_) => {
eprintln!("{}", usage);
return ExitCode::from(EX_USAGE as u8);
},
};
optind = opt.ind();
}
let remaining = argv.iter().skip(optind);
/* check the last flag specified */
if let Some(m) = mode {
for arg in remaining {
/* move the subsequent arguments to the list of inputs or outputs */
match m {
In => ins.push(arg.to_string()),
Out => outs.push(arg.to_string()),
};
}
} else {
eprintln!("{}", usage);
return ExitCode::from(EX_USAGE as u8);
}
/* use stdin if no inputs are specified */
if ins.is_empty() { ins.push("-".to_string()); }
/* use stdout if no outputs are specified */
if outs.is_empty() && !e { outs.push("-".to_string()); }
/* map all path strings to files */
let inputs = ins.iter().map(|file| {
/* if a file is “-”, it is stdin */
if *file == "-" {
/* portable way to access stdin as a file */
return unsafe { File::from_raw_fd(stdin().as_raw_fd()) };
}
match File::open(file) {
Ok(f) => f,
Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, e.strerror());
exit(EX_IOERR);
},
}
}).collect::<Vec<_>>();
/* map all path strings to files */
let mut outputs = outs.iter().map(|file| {
/* of a file is “-”, it is stdout */
if *file == "-" {
/* portable way to access stdout as a file */
return unsafe { File::from_raw_fd(stdout().as_raw_fd()) };
}
let options = File::options()
/* dont truncate if -t is specified, append if -a is specified */
.truncate(t)
.append(a)
/* enable the ability to create and write to files */
.create(true)
.write(true)
/* finally, open the file! */
.open(file);
match options {
Ok(f) => return f,
Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, e.strerror());
exit(EX_IOERR);
},
};
}).collect::<Vec<_>>();
/* if -e is specified, use stderr */
if e {
/* portable way to access stderr as a file */
outputs.push(unsafe { File::from_raw_fd(stderr().as_raw_fd()) });
}
let mut outputs = outputs.iter().map(|o| {
if u {
/* unbuffered writing through a buffer of capacity 0 */
BufWriter::with_capacity(0, o)
} else {
/* theoretically buffered writing */
BufWriter::new(o)
}
}).collect::<Vec<_>>();
for file in inputs {
for byte in file.bytes().map(|b| {
b.unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
})
}) {
for out in &mut outputs {
if let Err(e) = out.write(&[byte]) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_IOERR as u8);
}
if u {
/* immediately flush the output for -u */
if let Err(e) = out.flush() {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_IOERR as u8);
}
}
}
}
}
ExitCode::SUCCESS
}

View File

@@ -17,14 +17,24 @@
* along with this program. If not, see https://www.gnu.org/licenses/. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), putc(3), stdin, stdout, #include <stdio.h> /* fprintf(3), fputs(3), getc(3), perror(3), putc(3), stdin,
* EOF */ * stdout, EOF */
#include <stdlib.h> /* EXIT_FAILURE */ #include <sysexits.h> /* EX_IOERR, EX_OK, EX_USAGE */
#include <unistd.h> /* getopt(3) */ #include <unistd.h> /* getopt(3) */
#include <sysexits.h> /* EX_OK, EX_USAGE */
int usage(char *s) { char *program_name = "npc";
fprintf(stderr, "Usage: %s [-et]\n", s);
static int
ioerr(char *argv0) {
perror(argv0);
return EX_IOERR;
}
static int
usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s [-et]\n", argv0);
return EX_USAGE; return EX_USAGE;
} }
@@ -34,28 +44,37 @@ int main(int argc, char *argv[]) {
char showtab = 0; /* prints tab characters in caret notation */ char showtab = 0; /* prints tab characters in caret notation */
if (argc > 0) { if (argc > 0) {
program_name = argv[0];
while ((c = getopt(argc, argv, "et")) != -1) { while ((c = getopt(argc, argv, "et")) != -1) {
switch (c){ switch (c){
case 'e': showend = 1; break; case 'e': showend = 1; break;
case 't': showtab = 1; break; case 't': showtab = 1; break;
default: return usage(argv[0]); default: return usage(program_name);
} }
} }
} }
if (argc > optind) { return usage(argv[0]); } if (argc > optind) { return usage(program_name); }
while ((c = getc(stdin)) != EOF) { while ((c = getc(stdin)) != EOF) {
if ((c & 0x80) != 0) { fputs("M-", stdout); } if ((c & 0x80) != 0 && fputs("M-", stdout) == EOF) {
return ioerr(argv[0]);
}
switch (c ^ 0x80 /* 0b 1000 0000 */) { switch (c ^ 0x80 /* 0b 1000 0000 */) {
case 0x7f: fputs("^?", stdout); break; /* delete character */ case 0x7f: /* ASCII DEL (127d) */
case '\n': if (showend) { putc('$', stdout); } if(fputs("^?", stdout) == EOF) { return ioerr(argv[0]); }
break;
case '\n':
if (showend && fputc('$', stdout) == EOF) {
return ioerr(argv[0]);
}
default: default:
if (c >= ' ' || c == '\n' || (!showtab && c == '\t')) { if (c >= ' ' || c == '\n' || (!showtab && c == '\t')) {
putc(c, stdout); if (fputc(c, stdout) == EOF) { return ioerr(argv[0]); }
} else { } else if (fprintf(stdout, "^%c", c + '@') < 0) {
fprintf(stdout, "^%c", c + '@'); return ioerr(argv[0]);
} }
} }
} }

View File

@@ -26,87 +26,69 @@
* S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK, * S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
* S_ISUID, S_ISVTX */ * S_ISUID, S_ISVTX */
static char args[] = "bcdefghkprsuwxLS"; char *program_name = "scrut";
static char ops[(sizeof args) / (sizeof *args)]; #define OPTS "bcdefgkprsuwxLS"
static char *program_name = "scrut"; static char *opts = OPTS;
int usage(char *s) { static int
fprintf( usage(char *argv0) {
stderr, (void)fprintf(stderr, "Usage: %s [-" OPTS "] file...\n", argv0);
"Usage: %s [-%s] file...\n",
s == NULL ? program_name : s, args
);
return EX_USAGE; return EX_USAGE;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
struct stat buf; char sel[(sizeof opts) / (sizeof *opts)];
int c;
size_t i; if (argc < 2) { return usage(argv[0] == NULL ? program_name : argv[0]); }
{ /* option parsing */
char *p; char *p;
if (argc < 2) { return usage(argv[0]); } memset(sel, '\0', sizeof sel);
for (int c; (c = getopt(argc, argv, opts)) != -1;) {
memset(ops, '\0', sizeof ops); if ((p = strchr(opts, c)) == NULL) { return usage(argv[0]); }
while ((c = getopt(argc, argv, args)) != -1) { else { sel[p - opts] = c; }
if ((p = strchr(args, c)) == NULL) {
return usage(argv[0]);
} else {
ops[p - args] = c;
}
} }
/* straighten out ops */ /* straighten out selections; permute out nulls */
for (i = 0, p = ops; i < (sizeof ops) / (sizeof *ops); ++i) { p = sel;
if (ops[i] != '\0') { for (size_t i = 0; i < (sizeof sel) / (sizeof *sel); ++i) {
*p = ops[i]; if (sel[i] != '\0') {
if (&ops[i] != p++) { ops[i] = '\0'; } *p = sel[i];
if (&sel[i] != p++) { sel[i] = '\0'; }
}
} }
} }
if (optind == argc) { return usage(argv[0]); } if (optind == argc) { return usage(argv[0]); }
argv += optind; for (argv += optind ; *argv != NULL; ++argv) {
do { /* while(*++argv != NULL); */ struct stat buf;
if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1) { if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1) {
return EXIT_FAILURE; /* doesn't exist or isn't stattable */ return EXIT_FAILURE; /* doesn't exist or isn't stattable */
} }
for (i = 0; ops[i] != '\0'; ++i) for (size_t i = 0; sel[i] != '\0'; ++i) {
if (ops[i] == 'e') { if (
continue; (sel[i] == 'b' && !S_ISBLK(buf.st_mode))
} else if (ops[i] == 'h') { || (sel[i] == 'c' && !S_ISCHR(buf.st_mode))
return usage(argv[0]); || (sel[i] == 'd' && !S_ISDIR(buf.st_mode))
} else if ( || (sel[i] == 'e' && 0)
(ops[i] == 'b' || (sel[i] == 'f' && !S_ISREG(buf.st_mode))
&& !S_ISBLK(buf.st_mode)) || (sel[i] == 'g' && !(buf.st_mode & S_ISGID))
|| (ops[i] == 'c' || (sel[i] == 'k' && !(buf.st_mode & S_ISVTX))
&& !S_ISCHR(buf.st_mode)) || (sel[i] == 'p' && !S_ISFIFO(buf.st_mode))
|| (ops[i] == 'd' || (sel[i] == 'r' && access(*argv, R_OK) != 0)
&& !S_ISDIR(buf.st_mode)) || (sel[i] == 'u' && !(buf.st_mode & S_ISUID))
|| (ops[i] == 'f' || (sel[i] == 'w' && access(*argv, W_OK) != 0)
&& !S_ISREG(buf.st_mode)) || (sel[i] == 'x' && access(*argv, X_OK) != 0)
|| (ops[i] == 'g' || (sel[i] == 'L' && !S_ISLNK(buf.st_mode))
&& !(buf.st_mode & S_ISGID)) || (sel[i] == 'S' && !S_ISSOCK(buf.st_mode))
|| (ops[i] == 'k'
&& !(buf.st_mode & S_ISVTX))
|| (ops[i] == 'p'
&& !S_ISFIFO(buf.st_mode))
|| (ops[i] == 'r'
&& access(*argv, R_OK) != 0)
|| (ops[i] == 'u'
&& !(buf.st_mode & S_ISUID))
|| (ops[i] == 'w'
&& access(*argv, W_OK) != 0)
|| (ops[i] == 'x'
&& access(*argv, X_OK) != 0)
|| (ops[i] == 'L'
&& !S_ISLNK(buf.st_mode))
|| (ops[i] == 'S'
&& !S_ISSOCK(buf.st_mode))
) { return EXIT_FAILURE; } ) { return EXIT_FAILURE; }
} while(*++argv != NULL); }
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@@ -45,14 +45,16 @@ static struct {
{ NULL, NULL } /* marks end */ { NULL, NULL } /* marks end */
}; };
int usage(char *s){ static int
fprintf(stderr, "Usage: %s type string...\n", s); usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s type string...\n", argv0);
return EX_USAGE; return EX_USAGE;
} }
int main(int argc, char *argv[]){ int main(int argc, char *argv[]) {
size_t ctype; /* selected from ctypes.h; index of ctype */ size_t ctype; // selected from ctypes.h; index of ctype
int retval; int retval; // initially fail but becomes success on the first valid char
if (argc < 3) { return usage(argv[0] == NULL ? program_name : argv[0]); } if (argc < 3) { return usage(argv[0] == NULL ? program_name : argv[0]); }
@@ -71,7 +73,8 @@ int main(int argc, char *argv[]){
/* First checks if argv[0][i] is valid ASCII; ctypes(3) don't /* First checks if argv[0][i] is valid ASCII; ctypes(3) don't
* handle non-ASCII. This is bad. */ * handle non-ASCII. This is bad. */
if( if(
(unsigned char)argv[0][i] < 0x80 && !ctypes[ctype].f(argv[0][i]) (unsigned char)argv[0][i] < 0x80 // argv[0][i] is ASCII,
&& !ctypes[ctype].f(argv[0][i]) // so use ctypes(3)
) { return EXIT_FAILURE; } ) { return EXIT_FAILURE; }
else { retval = EXIT_SUCCESS; } else { retval = EXIT_SUCCESS; }
} }

View File

@@ -16,17 +16,16 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
#include <stdio.h> /* fprintf(3), stderr */ #include <stdio.h> /* fprintf(3), stderr */
#include <sysexits.h> /* EX_OK, EX_USAGE */ #include <sysexits.h> /* EX_OK, EX_USAGE */
static char *program_name = "strcmp"; char *program_name = "strcmp";
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int i; int i;
if (argc < 3) { if (argc < 3) {
fprintf( (void)fprintf(
stderr, stderr,
"Usage: %s string string...\n", "Usage: %s string string...\n",
argv[0] == NULL ? program_name : argv[0] argv[0] == NULL ? program_name : argv[0]

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2024 DTB <trinity@trinity.moe> * Copyright (c) 2024 DTB <trinity@trinity.moe>
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
* *
* This program is free software: you can redistribute it and/or modify it under * This program is free software: you can redistribute it and/or modify it under
@@ -18,72 +19,78 @@
use std::{ use std::{
env::args, env::args,
io::{ stdin, stdout, Error, ErrorKind, Read, Write }, io::{ stdin, stdout, Error, Read, Write },
process::ExitCode, process::ExitCode,
vec::Vec vec::Vec
}; };
extern crate getopt; extern crate getopt;
use getopt::GetOpt;
extern crate sysexits; extern crate sysexits;
use sysexits::{ EX_OK, EX_OSERR, EX_USAGE };
extern crate strerror; extern crate strerror;
use getopt::GetOpt;
use sysexits::{ EX_IOERR, EX_OK, EX_OSERR, EX_USAGE };
use strerror::StrError; use strerror::StrError;
fn oserr(s: &str, e: Error) -> ExitCode { fn oserr(argv0: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", s, e.strerror()); eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_OSERR as u8) ExitCode::from(EX_OSERR as u8)
} }
fn ioerr(argv0: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_IOERR as u8)
}
fn usage(s: &str) -> ExitCode { fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} [-f] [-w word_size]", s); eprintln!("Usage: {} [-w word_size]", s);
ExitCode::from(EX_USAGE as u8) ExitCode::from(EX_USAGE as u8)
} }
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
let mut buf: Vec<u8> = Vec::new(); let mut buf: Vec<u8> = Vec::new(); // holds the sequence getting swabbed
let mut input = stdin(); let mut input = stdin();
let mut output = stdout().lock(); let mut output = stdout().lock();
let mut optind: usize = 1; // argv[0]
let mut wordsize: usize = 2; // default; mimics dd(1p) conv=swab
let mut force = false; while let Some(opt) = argv.getopt("w:") {
let mut wordsize: usize = 2;
while let Some(opt) = argv.getopt("fw:") {
match opt.opt() { match opt.opt() {
Ok("f") => force = true,
Ok("w") => { Ok("w") => {
if let Some(arg) = opt.arg() { match opt.arg().unwrap().parse::<usize>() {
match arg.parse::<usize>() { Ok(w) if w % 2 == 0 => { wordsize = w; },
Ok(w) if w % 2 == 0 => { wordsize = w; () },
_ => { return usage(&argv[0]); }, _ => { return usage(&argv[0]); },
} }
} optind = opt.ind();
}, },
_ => { return usage(&argv[0]); } _ => { return usage(&argv[0]); }
} }
} }
if optind < argv.len() {
return usage(&argv[0]);
}
buf.resize(wordsize, 0); buf.resize(wordsize, 0);
loop { loop {
match input.read(&mut buf) { match input.read(&mut buf) {
Ok(0) => break ExitCode::from(EX_OK as u8), Ok(0) => break ExitCode::from(EX_OK as u8), // read nothing; bye
Ok(v) if v == wordsize => { Ok(v) if v == wordsize => { // read full block; swab
let (left, right) = buf.split_at(v/2); let (left, right) = buf.split_at(v/2);
if let Err(e) = output.write(&right) if let Err(e) = output.write(&right)
.and_then(|_| output.write(&left)) { .and_then(|_| output.write(&left)) {
break oserr(&argv[0], e) break ioerr(&argv[0], e);
} }
}, },
Ok(v) => { Ok(v) => { // partial read; partially write
if let Err(e) = output.write(&buf[..v]) { if let Err(e) = output.write(&buf[..v]) {
break oserr(&argv[0], e) break ioerr(&argv[0], e);
} }
}, },
Err(e) if e.kind() == ErrorKind::Interrupted && force => continue,
Err(e) => break oserr(&argv[0], e) Err(e) => break oserr(&argv[0], e)
} }
} }