52 Commits

Author SHA1 Message Date
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
c8b4f7a8b3 str(1): edit out goto 2024-07-19 19:03:23 -06:00
DTB
71f4a411b6 dj(1): replace do/while in write loop 2024-07-19 18:40:24 -06:00
DTB
a01cea572d dj(1): replace do/while on hard seeking 2024-07-19 18:26:59 -06:00
f7ebe7cf57 STYLE: added 10 rules reference 2024-07-19 17:31:51 -06:00
22bb26b9cb STYLE: added introduction statement 2024-07-19 17:04:49 -06:00
3ba6682ab3 STYLE: extern and use statements rules 2024-07-19 16:41:02 -06:00
9f2447ce94 STYLE: code block indentation edits and example 2024-07-15 13:38:26 -06:00
fbdf4f9c45 intcmp(1): switch formatting 2024-07-15 13:38:08 -06:00
806ddac8da dj(1): whitespace formatting 2024-07-15 13:31:38 -06:00
a0ed14a089 STYLE: example for trailing comma & add includes guideline 2024-07-15 13:29:12 -06:00
dc2a4a39ba swab(1): note what sysexits are being used 2024-07-15 13:17:00 -06:00
7ff14214c3 npc(1): move bit comment to be next to hex 2024-07-15 13:16:41 -06:00
a3ceb845e3 dj(1): revert some formatting changes 2024-07-15 13:16:05 -06:00
c2e6744e2b dj(1): revert changes to function return type formatting 2024-07-15 13:09:09 -06:00
71e98dbde7 dj.1: fixes more ambiguity 2024-07-15 13:06:34 -06:00
e9496cb4a5 dj.1: fixes ambiguity and false information 2024-07-15 13:04:23 -06:00
789046f694 STYLE: removes do while constraint & reword indentation rule 2024-07-15 13:03:53 -06:00
ab003f7d4a strcmp(1): commenting 2024-07-14 03:25:52 -06:00
3c243e4a09 str(1): formatting 2024-07-14 03:20:15 -06:00
49031102f2 npc(1): commenting 2024-07-14 03:17:21 -06:00
fd1ed79329 dj(1): return top-of-scope variable 2024-07-14 02:43:01 -06:00
fe0c631d42 dj.1: reverts change to hex literals 2024-07-14 02:41:59 -06:00
aa074ad9b6 dj.1: fixes ambiguity in block size options 2024-07-14 02:37:37 -06:00
b22ded9e98 STYLE: adds do while rule 2024-07-14 02:15:07 -06:00
579bf3b622 STYLE: initial commit 2024-07-14 02:10:18 -06:00
b0602388e7 rpn(1): better comments 2024-07-13 18:29:27 -06:00
a9b388fe4b hru(1): adds more descriptive comments 2024-07-13 18:18:32 -06:00
e4e823a309 fop(1): adds more comments 2024-07-13 18:03:49 -06:00
c7c6ca2c60 mm(1): formatting 2024-07-13 17:23:31 -06:00
26b0c93f4d strcmp(1): returns -1, specifies sysexits imports 2024-07-13 17:04:38 -06:00
35a20dca79 npc(1): specifies sysexits imports, formatting 2024-07-13 17:03:41 -06:00
8421f8be87 mm(1): specifies sysexits imports 2024-07-13 17:02:23 -06:00
0c530dffbf intcmp(1): formatting, fixes argv[0] ternary and usage function 2024-07-13 17:01:53 -06:00
d9dd4e6057 intcmp(1): formatting, lists sysexits imports, allows no args 2024-07-13 16:57:52 -06:00
1dfad87e87 dj(1): lists sysexits imports, fixes negation 2024-07-13 16:56:56 -06:00
99f2b2963a dj(1): more formatting 2024-07-12 16:39:11 -06:00
666c621a02 strcmp(1): more formatting 2024-07-12 16:38:50 -06:00
34cd715e37 mm(1): removes unnecessary macros 2024-07-12 16:22:56 -06:00
958bfa52ed scrut(1): more formatting 2024-07-12 16:18:48 -06:00
59de0262bd strcmp(1): adds copyright header, formatting, removes unused #include 2024-07-12 16:15:41 -06:00
5d2872d050 scrut(1): formatting, removes gotos 2024-07-12 16:04:07 -06:00
6e1e3db6c8 npc(1): forgot to update copyright 2024-07-12 15:55:36 -06:00
9cfc48c960 intcmp(1): formatting, removed gotos 2024-07-12 15:54:30 -06:00
acc3cf3e90 swab(1): formatting, remove gotos 2024-07-12 15:43:00 -06:00
1ad4114882 dj(1): remove unnecessary macros 2024-07-12 15:42:24 -06:00
db0dd02d55 dj(1): more formatting 2024-07-12 15:32:30 -06:00
6cf7fd9794 dj(1): reformatting 2024-07-12 15:23:57 -06:00
DTB
8f8de5de2b dj(1): fix io[0].bufuse underflow 2024-07-08 22:53:44 -06:00
DTB
0df2c9f566 dj(1): fix infiniskipping 2024-07-08 22:48:16 -06:00
12 changed files with 699 additions and 443 deletions

116
STYLE Normal file
View File

@@ -0,0 +1,116 @@
The following guidelines are conducive to clear and readable code that is
consistent with the style of the rest of the Bonsai Computer System.
0. Braces are mandatory for all control flow.
1. Nested indentation should be kept to a minimum.
2. Empty lines should be placed between different kinds of statements:
int t;
assert(io->bufuse > 0);
assert(io->bufuse <= io->bs);
if ((t = write(io->fd, io->buf, io->bufuse)) < 0) {
io->error = errno;
t = 0;
} else if (t > 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;
3. Each block of code should be indented once more than the keyword which
initiated the block:
switch (c) {
case 'e': mode |= EQUAL; break;
case 'g': mode |= GREATER; break;
case 'l': mode |= LESS; break;
default: return usage(s);
}
4. In C, spaces should be placed in control flow statements after the keyword
and before the opening brace:
for (i = 2; i < argc; ++i) {
5. If a function, a C control flow statement, or a Rust macro has arguments that
cause the statement to be broken into multiple lines, this should be done by
placing the arguments on a new line inside the parentheses:
let usage = format!(
"Usage: {} [-d delimiter] index command [args...]",
argv[0],
);
6. If Rust function arguments or fields are on their own lines, they should
always have a trailing comma:
return Err(EvaluationError {
message: format!("{}: Invalid token", i),
code: EX_DATAERR,
})
7. If text is on the same line as a brace, spaces should be placed after an
opening curly brace and before a closing one:
use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
8. If a control flow statement is short enough to be easily understood in a
glance, it may be placed on a single line:
if (!argc < 0) { usage(program_name); }
9. In C, note everything you use from a library in a comment subsequent to its
#include statement:
#include <unistd.h> /* close(2), getopt(3), lseek(2), read(2), write(2),
* optarg, optind, STDIN_FILENO, STDOUT_FILENO */
10. In Rust, place extern statements after use statements that include standard
library crates. Group alike statements:
use std::fs::Path;
extern crate strerror;
extern crate sysexits;
use strerror::StrError;
use sysexits::{ EX_OSERR, EX_USAGE };
11. Do not use do while loops in C.
12. Follow the rules from the paper The Power of 10: Rules for Developing
Safety-Critical Code [0]:
1. Avoid complex flow constructs, such as goto and recursion.
2. All loops must have fixed bounds. This prevents runaway code.
3. Avoid heap memory allocation.
4. Restrict functions to a single printed page.
5. Use a minimum of two runtime assertions per function.
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
indicate the return value is useless.
8. Use the preprocessor sparingly.
9. Limit pointer use to a single dereference, and do not use function
pointers.
10. Compile with all possible warnings active; all warnings should then be
addressed before release of the software.
References
==========
[0] <https://web.eecs.umich.edu/~imarkov/10rules.pdf>
--
Copyright © 2024 Emma Tebibyte <emma@tebibyte.media>
Copyright © Wikipedia contributors
This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit
<http://creativecommons.org/licenses/by-sa/4.0/>.

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 DJ 1 2024-07-03 "Harakit X.X.X" .TH DJ 1 2024-07-14 "Harakit X.X.X"
.SH NAME .SH NAME
dj \(en disk jockey dj \(en disk jockey
.\" .\"
@@ -56,9 +56,9 @@ bytes read to this point are discarded.
.IP \fB-o\fP .IP \fB-o\fP
Takes a file path as an argument and opens it for use as an output. Takes a file path as an argument and opens it for use as an output.
.IP \fB-B\fP\ \fIblock_size\fP .IP \fB-B\fP\ \fIblock_size\fP
Does the same as Takes a numeric argument as the size in bytes of the output buffer, the default
.B -b being 1024. Note that this option only affects the size of output writes and not
but for the output buffer. the amount of output data itself. See the CAVEATS section.
.IP \fB-S\fP .IP \fB-S\fP
Takes a numeric argument as the index of the byte at which writing will 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, commence; \(lqseeks\(rq that number of bytes. If the standard output is used,
@@ -68,8 +68,8 @@ 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 of an incomplete read from the input file. If the option argument is empty, the
null byte is used. null byte is used.
.IP \fB-c\fP .IP \fB-c\fP
Specifies a number of reads to make. The default is 0, in which case the Specifies a number of blocks to read. The default is 0, in which case the input
input is read until a partial or empty read is made. is read until a partial or empty read is made.
.IP \fB-H\fP .IP \fB-H\fP
Prints diagnostic messages in a human-readable manner as described in the Prints diagnostic messages in a human-readable manner as described in the
DIAGNOSTICS section. DIAGNOSTICS section.
@@ -181,15 +181,22 @@ option is specified, this could make written data nonsensical.
Existing files are not truncated on ouput and are instead overwritten. Existing files are not truncated on ouput and are instead overwritten.
The options Option variants that have lowercase and uppercase forms could be confused for
.B -b each other. The former affects input and the latter affects output.
and
The
.B -B .B -B
could be confused for each other, and so could option could be mistaken for the count in bytes of data written to the output.
.B -s This conception is intuitive but incorrect, as the
and .B -c
.BR -S . option controls the number of blocks to read and the
The lowercase option affects input and the capitalized option affects output. .B -b
option sets the size of the blocks. The
.B -B
option is similar to the latter but sets the size of blocks to be written,
regardless of the amount of data that will actually be written. In practice,
this means the input buffer should be very large to make use of modern hardware
input and output speeds.
The skipped or sought bytes while processing irregular files, such as streams, The skipped or sought bytes while processing irregular files, such as streams,
are reported in the diagnostic output, because they were actually read or are reported in the diagnostic output, because they were actually read or
@@ -216,3 +223,4 @@ Copyright \(co 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
.SH SEE ALSO .SH SEE ALSO
.BR dd (1p) .BR dd (1p)
.BR lseek (3p) .BR lseek (3p)
.BR mm (1)

226
src/dj.c
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
@@ -22,13 +23,11 @@
#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) */
#if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE #include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */
# include <sysexits.h>
#endif
#include <unistd.h> /* close(2), getopt(3), lseek(2), read(2), write(2), #include <unistd.h> /* close(2), getopt(3), lseek(2), read(2), write(2),
* 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, #include <sys/stat.h> /* S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR */
S_IWUSR */
extern int errno; extern int errno;
char *program_name = "dj"; char *program_name = "dj";
@@ -99,8 +98,9 @@ Io_write(struct Io *io){
if ((t = write(io->fd, io->buf, io->bufuse)) < 0) { if ((t = write(io->fd, io->buf, io->bufuse)) < 0) {
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)); memmove(io->buf, &(io->buf)[t], (io->bufuse -= t));
}
io->bytes += t; io->bytes += t;
io->prec += (t > 0 && io->bufuse > 0); io->prec += (t > 0 && io->bufuse > 0);
@@ -119,10 +119,16 @@ oserr(char *e, int n){
* completely read and written records. */ * completely read and written records. */
static void static void
fprintio(FILE *stream, char *fmt, struct Io io[2]) { fprintio(FILE *stream, char *fmt, struct Io io[2]) {
fprintf(
fprintf(stream, fmt, stream,
io[0].rec, io[0].prec, io[1].rec, io[1].prec, fmt,
io[0].bytes, io[1].bytes); io[0].rec,
io[0].prec,
io[1].rec,
io[1].prec,
io[0].bytes,
io[1].bytes
);
return; return;
} }
@@ -136,25 +142,23 @@ parse(char *s){
errno = 0; errno = 0;
r = strtol(s, &s, 0); r = strtol(s, &s, 0);
return (*s == '\0' /* no chars left unparsed */ && errno == 0) return (*s == '\0' /* no chars left unparsed */ && errno == 0) ? r : -1;
? r
: -1;
} }
static int static int
usage(char *s) { usage(char *s) {
fprintf(
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", "\t[-o file] [-B block_size] [-S offset]\n", program_name
program_name); );
return EX_USAGE; return EX_USAGE;
} }
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; /* == fmt_asv (default) or fmt_human (-H) */
size_t i; /* side of io being modified */ size_t i; /* side of io being modified */
char noerror; /* 0=exits (default) 1=retries on partial reads or writes */ char noerror; /* 0=exits (default) 1=retries on partial reads or writes */
@@ -162,7 +166,7 @@ int main(int argc, char *argv[]){
/* Set defaults. */ /* Set defaults. */
align = -1; align = -1;
count = 0; count = -1;
fmt = fmt_asv; fmt = fmt_asv;
noerror = 0; noerror = 0;
for (i = 0; i < (sizeof io) / (sizeof *io); ++i) { for (i = 0; i < (sizeof io) / (sizeof *io); ++i) {
@@ -182,171 +186,223 @@ int main(int argc, char *argv[]){
int c; int c;
program_name = argv[0]; program_name = argv[0];
while((c = getopt(argc, argv, ":a:b:B:c:i:hHns:S:o:")) != -1) while ((c = getopt(argc, argv, "a:b:B:c:i:hHns:S:o:")) != -1) {
switch (c) { switch (c) {
case 'i': case 'o': i = (c == 'o'); case 'i': case 'o': /* input, output */
if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */ i = (c == 'o');
/* optarg == "-" (stdin/stdout) */
if (optarg[0] == '-' && optarg[1] == '\0') {
io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO; io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO;
io[i].fn = i == 0 ? stdin_name : stdout_name; io[i].fn = i == 0 ? stdin_name : stdout_name;
break; break;
} else { } else {
int fd; int fd;
if((fd = open(optarg, io[i].fl, creat_mode)) != -1 if (
&& (fdisstd(io[i].fd) || close(io[i].fd) == 0)){ (fd = open(optarg, io[i].fl, creat_mode)) != -1
&& (fdisstd(io[i].fd) || close(io[i].fd) == 0)
) {
io[i].fd = fd; io[i].fd = fd;
io[i].fn = optarg; io[i].fn = optarg;
break; break;
} }
} }
return oserr(optarg, errno);
case 'n': noerror = 1; break; return oserr(optarg, errno); /* break; */
case 'H': fmt = fmt_human; break; case 'n': noerror = 1; break; /* retry failed reads once */
case 'a': case 'H': fmt = fmt_human; break; /* human-readable output */
case 'a': /* input buffer padding */
if (optarg[0] == '\0' || optarg[1] == '\0') { if (optarg[0] == '\0' || optarg[1] == '\0') {
align = optarg[0]; align = optarg[0];
break; break;
} }
/* FALLTHROUGH */ /* FALLTHROUGH */
case 'c': case 'b': case 's': case 'B': case 'S': /* numbers */ case 'c': /* number of reads */
if(c == 'c' && (count = parse(optarg)) >= 0) case 'b': case 'B': /* input/output block size */
break; case 's': case 'S': /* (s)kip/(S)eek in input/output */
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((c == 'b' && (io[i].bs = parse(optarg)) > 0)
|| (c == 's' && (io[i].seek = parse(optarg)) >= 0)) if (
break; (c == 'b' && (io[i].bs = parse(optarg)) > 0)
|| (c == 's' && (io[i].seek = parse(optarg)) >= 0)
) { break; }
/* FALLTHROUGH */ /* FALLTHROUGH */
default: default:
return usage(program_name); return usage(program_name);
} }
} }
}
assert(io->fd != STDIN_FILENO || io->fl == read_flags); assert(io->fd != STDIN_FILENO || io->fl == read_flags);
assert(io->fd != STDOUT_FILENO || io->fl == write_flags); assert(io->fd != STDOUT_FILENO || io->fl == write_flags);
if(argc > optind) if (argc > optind) { return usage(program_name); }
return usage(program_name);
for (i = 0; i < (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(stderr, "%s: Failed to allocate %zd bytes\n", fprintf(
program_name, io[i].bs); stderr, "%s: Failed to allocate %zd bytes\n",
program_name, io[i].bs
);
return EX_OSERR; return EX_OSERR;
} }
/* easy seeking */ /* easy seeking */
if(!fdisstd(io[i].fd) && lseek(io[i].fd, io[i].seek, SEEK_SET) != -1) if (!fdisstd(io[i].fd) && lseek(io[i].fd, io[i].seek, SEEK_SET) != -1) {
io[i].seek = 0; io[i].seek = 0;
} }
/* hard seeking */
if(io[1].seek > 0){
size_t t;
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 == 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){ assert(io[1].bufuse == 0); /* requirement for hard seeking */
/* 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)) {
memset(
io[1].buf, '\0', /* set buf to all nulls */
(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) {
Io_write(&io[1]); /* second chance */
}
if (io[1].error != 0) { return oserr(io[1].fn, io[1].error); }
if (io[1].bufuse == t) { break; } /* all writes failed! */
}
io[1].bufuse = 0;
if (io[1].seek > 0) { /* hard seeking failed */
fprintio(stderr, fmt, io); fprintio(stderr, fmt, io);
return oserr(io[1].fn, errno); return oserr(io[1].fn, errno);
} }
do{ for ( ;
count == -1 || count > 0;
count -= (count != -1) /* decrement if counting */
) {
assert(io[0].bufuse == 0); assert(io[0].bufuse == 0);
{ /* read */ { /* read */
char skipping; long skipping;
size_t t; size_t t;
/* hack to intentionally get a partial read from Io_read */ /* hack to intentionally get a partial read from Io_read */
if((skipping = (io[0].seek > 0)) && io[0].seek < io[0].bs) if ((skipping = MIN(io[0].seek, io[0].bs)) > 0) {
io[0].bufuse = io[0].bs - io[0].seek; io[0].bufuse = io[0].bs - (size_t)skipping;
}
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 && !noerror && io[0].error == 0) {
Io_read(&io[0]); /* second chance */ 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); fprintf(stderr, "%s: Partial read:\n\t", program_name);
fprintio(stderr, fmt, io); fprintio(stderr, fmt, io);
if(!noerror)
count = 1; if (!noerror) { 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(&(io[0].buf)[io[0].bufuse], align, memset(
io[0].bs - io[0].bufuse); &(io[0].buf)[io[0].bufuse],
align,
io[0].bs - io[0].bufuse
);
io->bufuse = io->bs; io->bufuse = io->bs;
} }
} }
if(skipping){ if (skipping > 0) {
io[0].seek -= skipping;
io[0].bufuse = 0; io[0].bufuse = 0;
count += (count != 0); count += (count != -1); /* increment if counting */
continue; continue;
} }
} }
/* write */ assert(io[0].bufuse > 0);
do{
int t;
while (io[0].bufuse > 0) { /* write */
if (io[0].bs <= io[1].bs) { if (io[0].bs <= io[1].bs) {
int n; int n;
/* saturate obuf */ memcpy( /* saturate obuf */
memcpy(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)); 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(&(io[1].buf)[io[1].bufuse], io[0].buf, memcpy(
(n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse))); &(io[1].buf)[io[1].bufuse], io[0].buf,
(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); 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) {
continue; /* obuf not saturated - we could write more */ continue; /* obuf not saturated - we could write more */
} }
}
{ /* writes actually happen, or die */
size_t t;
t = io[1].bufuse; t = io[1].bufuse;
if(Io_write(&io[1])->bufuse == t && !noerror && io[1].error == 0) if (Io_write(&io[1])->bufuse == t
&& !noerror
&& io[1].error == 0) {
Io_write(&io[1]); /* second chance */ Io_write(&io[1]); /* second chance */
}
assert(io[1].error == 0 || io[1].bufuse == t);
/* if the Io_writes errored, bufuse wouldn't have changed, and
* the error will be reported at the end of the read/write
* loop */
assert(io[1].bufuse <= t); assert(io[1].bufuse <= t);
if (io[1].bufuse == t) { /* no more love */ if (io[1].bufuse == t) { /* no more love */
count = 1; count = 1;
break; 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); fprintf(stderr, "%s: Partial write:\n\t", program_name);
fprintio(stderr, fmt, io); fprintio(stderr, fmt, io);
if(!noerror)
count = 1; if(!noerror) { count = 1; }
}
}
} }
}while(io[0].bufuse > 0);
}while(count == 0 || --count > 0);
fprintio(stderr, fmt, io); 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) if (io[i].error) { return oserr(io[i].fn, io[i].error); }
return oserr(io[i].fn, io[i].error); }
return EX_OK; return EX_OK;
} }

View File

@@ -32,8 +32,8 @@ 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(); let mut d = '\u{1E}'.to_string(); /* ASCII record separator */
let mut index_arg = 0; let mut optind = 0;
let usage = format!( let usage = format!(
"Usage: {} [-d delimiter] index command [args...]", "Usage: {} [-d delimiter] index command [args...]",
@@ -43,10 +43,9 @@ fn main() {
while let Some(opt) = argv.getopt("d:") { while let Some(opt) = argv.getopt("d:") {
match opt.opt() { match opt.opt() {
Ok(_) => { Ok(_) => {
/* unwrap because Err(OptError::MissingArg) will be returned if /* delimiter */
* opt.arg() is None */
d = opt.arg().unwrap(); d = opt.arg().unwrap();
index_arg = opt.ind(); optind = opt.ind();
}, },
Err(_) => { Err(_) => {
eprintln!("{}", usage); eprintln!("{}", usage);
@@ -55,38 +54,46 @@ fn main() {
}; };
} }
let command_arg = index_arg as usize + 1; /* index of the argv[0] for the operator command */
let command_arg = optind as usize + 1;
argv.get(command_arg).unwrap_or_else(|| { /* argv[0] of the operator command */
let operator = argv.get(command_arg).unwrap_or_else(|| {
eprintln!("{}", usage); eprintln!("{}", usage);
exit(EX_USAGE); exit(EX_USAGE);
}); });
let index = argv[index_arg].parse::<usize>().unwrap_or_else(|e| { /* 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); eprintln!("{}: {}: {}", argv[0], argv[1], e);
exit(EX_DATAERR); exit(EX_DATAERR);
}); });
let mut buf = String::new(); let mut buf = String::new();
let _ = stdin().read_to_string(&mut buf); let _ = stdin().read_to_string(&mut buf);
/* 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 */
let opts = argv let opts = argv
.iter() .iter()
.clone() .clone()
.skip(command_arg + 1) .skip(command_arg + 1) /* skip the command name */
.collect::<Vec<&String>>(); .collect::<Vec<&String>>();
let mut spawned = Command::new(argv.get(command_arg).unwrap()) /* spawn the command to operate on the field */
.args(opts) let mut spawned = Command::new(operator)
.args(opts) /* spawn with the specified arguments */
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped()) /* piped stdout to handle output ourselves */
.spawn() .spawn()
.unwrap_or_else( |e| { .unwrap_or_else( |e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror()); eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror());
exit(EX_UNAVAILABLE); exit(EX_UNAVAILABLE);
}); });
/* get field we want to pipe into spawned program */
let field = fields.get(index).unwrap_or_else(|| { let field = fields.get(index).unwrap_or_else(|| {
eprintln!( eprintln!(
"{}: {}: No such index in input", "{}: {}: No such index in input",
@@ -96,9 +103,10 @@ fn main() {
exit(EX_DATAERR); exit(EX_DATAERR);
}); });
/* get the stdin of the newly spawned program and feed it the field val */
if let Some(mut child_stdin) = spawned.stdin.take() { if let Some(mut child_stdin) = spawned.stdin.take() {
let _ = child_stdin.write_all(field.as_bytes()); let _ = child_stdin.write_all(field.as_bytes());
drop(child_stdin); drop(child_stdin); /* stay safe! drop your children! */
} }
let output = spawned.wait_with_output().unwrap_or_else(|e| { let output = spawned.wait_with_output().unwrap_or_else(|e| {
@@ -106,17 +114,22 @@ fn main() {
exit(EX_IOERR); exit(EX_IOERR);
}); });
/* 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 */
if replace.pop() != Some(b'\n') { replace = output.stdout; } if replace.pop() != Some(b'\n') { replace = output.stdout; }
/* 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| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e); eprintln!("{}: {}: {}", argv[0], argv[command_arg], e);
exit(EX_IOERR); exit(EX_IOERR);
}); });
/* store the new field in the old fields vector */
fields[index] = &new_field; fields[index] = &new_field;
/* fop it */
stdout().write_all( stdout().write_all(
fields.join(&d.to_string()).as_bytes() fields.join(&d.to_string()).as_bytes()
).unwrap_or_else(|e| { ).unwrap_or_else(|e| {

View File

@@ -29,40 +29,45 @@ extern crate sysexits;
use strerror::StrError; use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE }; use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE };
/* list of SI prefixes */
const LIST: [(u32, &str); 10] = [ const LIST: [(u32, &str); 10] = [
(3, "k"), (3, "k"), /* kilo */
(6, "M"), (6, "M"), /* mega */
(9, "G"), (9, "G"), /* giga */
(12, "T"), (12, "T"), /* tera */
(15, "P"), (15, "P"), /* peta */
(18, "E"), (18, "E"), /* exa */
(21, "Z"), (21, "Z"), /* zetta */
(24, "Y"), (24, "Y"), /* yotta */
(27, "R"), (27, "R"), /* ronna */
(30, "Q") (30, "Q"), /* quetta */
]; ];
fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> { fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
/* preserve decimal places in output by casting to a float */
let mut out = (input as f64, (0_u32, "")); let mut out = (input as f64, (0_u32, ""));
if input < 1000 { return Ok(out); }
if input < 1000 { return Ok(out); } /* too low to convert */
for (n, p) in LIST { for (n, p) in LIST {
let c = match 10_u128.checked_pow(n) { let c = match 10_u128.checked_pow(n) {
Some(c) => c, Some(c) => c,
None => { None => { /* too big for the laws of computing :( */
return Err(format!("10^{}: Integer overflow", n.to_string())); return Err(format!("10^{}: Integer overflow", n.to_string()));
}, },
}; };
match c.cmp(&input) { match c.cmp(&input) {
Ordering::Less => { Ordering::Less => { /* c < input */
/* the program will keep assigning out every loop until either
* the list runs out of higher prefix bases or the input is
* greater than the prefix base */
out = (input as f64 / c as f64, (n, p)); out = (input as f64 / c as f64, (n, p));
}, },
Ordering::Equal => { Ordering::Equal => { /* c == input */
return Ok((input as f64 / c as f64, (n, p))); return Ok((input as f64 / c as f64, (n, p)));
}, },
Ordering::Greater => {}, Ordering::Greater => {}, /* c > input */
}; };
} }
@@ -72,6 +77,7 @@ fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
let mut buf = String::new(); let mut buf = String::new();
while let Ok(_) = stdin().read_line(&mut buf) { while let Ok(_) = stdin().read_line(&mut buf) {
if buf.is_empty() { return ExitCode::SUCCESS; } if buf.is_empty() { return ExitCode::SUCCESS; }
@@ -96,6 +102,7 @@ fn main() -> ExitCode {
let si_prefix = format!("{}B", prefix.1); let si_prefix = format!("{}B", prefix.1);
/* round output number to one decimal place */
let out = ((number * 10.0).round() / 10.0).to_string(); let out = ((number * 10.0).round() / 10.0).to_string();
stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes()) stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes())

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2023 DTB <trinity@trinity.moe> * Copyright (c) 2023 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
@@ -20,63 +21,67 @@
#include <stdio.h> /* fprintf(3), stderr */ #include <stdio.h> /* fprintf(3), stderr */
#include <stdlib.h> /* strtol(3), size_t, EXIT_FAILURE */ #include <stdlib.h> /* strtol(3), size_t, EXIT_FAILURE */
#include <unistd.h> /* getopt(3), optind */ #include <unistd.h> /* getopt(3), optind */
#include <sysexits.h> #include <sysexits.h> /* EX_OK, EX_USAGE */
/* 0b00? */ /* Equal | -e | 0b001 | 1 */ /* 0b00? */ /* Equal | -e | 0b001 | 1 */
#define EQUAL 0x01 /* Greater | -g | 0b010 | 2 */ #define EQUAL 0x01 /* Greater | -g | 0b010 | 2 */
/* 0b0?0 */ /* Greater or Equal | -ge | 0b011 | 3 */ /* 0b0?0 */ /* Greater or Equal | -ge | 0b011 | 3 */
#define GREATER 0x02 /* Less | -l | 0b100 | 4 */ #define GREATER 0x02 /* Lesser | -l | 0b100 | 4 */
/* 0b?00 */ /* Less or Equal | -le | 0b101 | 5 */ /* 0b?00 */ /* Lesser or Equal | -le | 0b101 | 5 */
#define LESS 0x04 /* Inequal (Greater or Less) | -gl | 0b110 | 6 */ #define LESSER 0x04 /* Inequal (Greater or Lesser) | -gl | 0b110 | 6 */
static char *program_name = "intcmp"; 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 main(int argc, char *argv[]) {
int c; int c;
size_t i; size_t i;
unsigned char mode; unsigned char mode;
int r; /* reference integer */ int r; /* reference integer */
char *s = (argv[0] == NULL ? program_name : argv[0]);
mode = 0; mode = 0;
if(argc < 3) if (argc == 0 | argc < 3) { return usage(s); }
goto usage;
while((c = getopt(argc, argv, "egl")) != -1) while ((c = getopt(argc, argv, "egl")) != -1) {
switch (c) { switch (c) {
case 'e': mode |= EQUAL; break; case 'e': mode |= EQUAL; break;
case 'g': mode |= GREATER; break; case 'g': mode |= GREATER; break;
case 'l': mode |= LESS; break; case 'l': mode |= LESSER; break;
default: goto usage; default: return usage(s);
}
} }
if(optind + 2 /* ref cmp */ > argc){ if (optind + 2 /* ref cmp */ > argc) { return usage(s); }
usage: fprintf(stderr,
"Usage: %s [-egl] integer integer...\n",
argv[0] == NULL ? program_name : argv[0]);
return EX_USAGE;
}
i = optind; i = optind;
do{ r = c; do {
r = c;
c = strtol(argv[i], &argv[i], 10); c = strtol(argv[i], &argv[i], 10);
if (*argv[i] != '\0' || errno != 0) { if (*argv[i] != '\0' || errno != 0) {
fprintf(stderr, "%s: argument #%d: Invalid integer\n", fprintf(
argv[0], (int)i); stderr, "%s: argument #%d: Invalid integer\n", argv[0], (int)i
);
return EX_USAGE; return EX_USAGE;
} }
if(i == optind) if (i == optind) { continue; }
continue;
/* rule enforcement; if a mode isn't permitted and the numbers /* rule enforcement; if a mode isn't permitted and the numbers
* correspond to it, return 1 */ * correspond to it, return 1 */
if ( (!(mode & EQUAL) && r == c) if ( (!(mode & EQUAL) && r == c)
|| (!(mode & GREATER) && r > c) || (!(mode & GREATER) && r > c)
|| (!(mode & LESS) && r < c)) || (!(mode & LESSER) && r < c)
return 1; ) { return 1; }
} while (++i < argc); } while (++i < argc);
return 0; return EX_OK;
} }

112
src/mm.c
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
@@ -23,10 +24,8 @@
#include <stdlib.h> /* free(3), realloc(3) */ #include <stdlib.h> /* free(3), realloc(3) */
#include <string.h> /* strcmp(3), strerror(3) */ #include <string.h> /* strcmp(3), strerror(3) */
#include <unistd.h> /* getopt(3) */ #include <unistd.h> /* getopt(3) */
#if !defined EX_IOERR || !defined EX_OK || !defined EX_OSERR \ #include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */
|| !defined EX_USAGE
# include <sysexits.h>
#endif
extern int errno; extern int errno;
/* This structure is how open files are tracked. */ /* This structure is how open files are tracked. */
@@ -96,11 +95,15 @@ oserr(char *s, char *r){
* from main. */ * from main. */
#define terminate \ #define terminate \
for (i = 0; i < 2; ++i) { \ for (i = 0; i < 2; ++i) { \
for(j = 0; j < files[i].s; ++j) \ for (j = 0; j < files[i].s; ++j) { \
if(files[i].files[j] != stdin \ if ( \
files[i].files[j] != stdin \
&& files[i].files[j] != stdout \ && files[i].files[j] != stdout \
&& files[i].files[j] != stderr) \ && files[i].files[j] != stderr \
) { \
fclose(files[i].files[j]); \ fclose(files[i].files[j]); \
} \
} \
free(files[i].files); \ free(files[i].files); \
free(files[i].names); \ free(files[i].names); \
} \ } \
@@ -134,53 +137,64 @@ int main(int argc, char *argv[]){
files[i].mode = fmode[i]; files[i].mode = fmode[i];
files[i].files = NULL; files[i].files = NULL;
files[i].names = NULL; files[i].names = NULL;
Files_append(&files[i], i == 0 ? stdin : stdout,
i == 0 ? stdin_name : stdout_name); Files_append(
&files[i],
i == 0 ? stdin : stdout,
i == 0 ? stdin_name : stdout_name
);
files[i].s = 0; files[i].s = 0;
} }
k = 0; k = 0;
if(argc > 0) if (argc > 0) { program_name = argv[0]; }
program_name = argv[0];
if(argc > 1) if (argc > 1) {
while((c = getopt(argc, argv, "aehi:no:u")) != -1) while ((c = getopt(argc, argv, "aehi:no:u")) != -1) {
switch (c){ switch (c){
case 'a': /* "rb+" -> "ab" */ case 'a': /* "rb+" -> "ab" */
files[1].mode[0] = 'a'; files[1].mode[0] = 'a';
files[1].mode[2] = '\0'; files[1].mode[2] = '\0';
break; break;
case 'e': case 'e':
if(Files_append(&files[1], stderr, stderr_name) != NULL) if (Files_append(&files[1], stderr, stderr_name) != NULL) {
break; break;
}
retval = oserr(argv[0], "-e"); retval = oserr(argv[0], "-e");
terminate; terminate;
case 'i': case 'i':
if((strcmp(optarg, "-") == 0 && Files_append(&files[0], if (
stdin, stdin_name) != NULL) (strcmp(optarg, "-") == 0
|| Files_open(&files[0], optarg) != NULL) && Files_append(&files[0], stdin, stdin_name) != NULL)
break; || Files_open(&files[0], optarg) != NULL
) { break; }
retval = oserr(argv[0], optarg); retval = oserr(argv[0], optarg);
terminate; terminate;
case 'o': case 'o':
if((strcmp(optarg, "-") == 0 && Files_append(&files[1], if (
stdout, stdout_name) != NULL) (strcmp(optarg, "-") == 0
|| Files_open(&files[1], optarg) != NULL) && Files_append(&files[1], stdout, stdout_name) != NULL)
break; || Files_open(&files[1], optarg) != NULL
) { break; }
/* does not exist, so try to create it */ /* does not exist, so try to create it */
if (errno == ENOENT) { if (errno == ENOENT) {
files[1].mode = wharsh; files[1].mode = wharsh;
if (Files_open(&files[1], optarg) != NULL) { if (Files_open(&files[1], optarg) != NULL) {
files[1].mode = fmode[1]; files[1].mode = fmode[1];
break; break;
} }
} }
retval = oserr(argv[0], optarg); retval = oserr(argv[0], optarg);
terminate; terminate;
case 'n': case 'n':
if(signal(SIGINT, SIG_IGN) != SIG_ERR) if (signal(SIGINT, SIG_IGN) != SIG_ERR) { break; }
break;
retval = oserr(argv[0], "-n"); retval = oserr(argv[0], "-n");
terminate; terminate;
case 'u': case 'u':
@@ -190,6 +204,8 @@ int main(int argc, char *argv[]){
retval = usage(argv[0]); retval = usage(argv[0]);
terminate; terminate;
} }
}
}
if (optind != argc) { if (optind != argc) {
retval = usage(argv[0]); retval = usage(argv[0]);
@@ -201,35 +217,51 @@ int main(int argc, char *argv[]){
/* Unbuffer files. */ /* Unbuffer files. */
if (k) { if (k) {
for(i = 0; for (
i < files[0].s; i = 0; i < files[0].s; setvbuf(files[0].files[i++], NULL, _IONBF, 0)
setvbuf(files[0].files[i++], NULL, _IONBF, 0)); );
for(i = 0; for (
i < files[1].s; i = 0; i < files[1].s; setvbuf(files[1].files[i++], NULL, _IONBF, 0)
setvbuf(files[1].files[i++], NULL, _IONBF, 0)); );
} }
retval = EX_OK; retval = EX_OK;
/* Actual program loop. */ /* Actual program loop. */
for(i = 0; i < files[0].s; ++i) /* iterate ins */ for (i = 0; i < files[0].s; ++i) { /* iterate ins */
while((c = getc(files[0].files[i])) != EOF) /* iterate chars */ while ((c = getc(files[0].files[i])) != EOF) { /* iterate chars */
for(j = 0; j < files[1].s; ++j) /* iterate outs */ for (j = 0; j < files[1].s; ++j) { /* iterate outs */
if (putc(c, files[1].files[j]) == EOF) { if (putc(c, files[1].files[j]) == EOF) {
/* notebook's full */ /* notebook's full */
retval = EX_IOERR; retval = EX_IOERR;
fprintf(stderr, "%s: %s: %s\n", fprintf(
program_name, files[1].names[j], strerror(errno)); stderr,
if(fclose(files[1].files[j]) == EOF) "%s: %s: %s\n",
fprintf(stderr, "%s: %s: %s\n", program_name,
program_name, files[1].names[j], strerror(errno)); 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 */ /* massage out the tense muscle */
for(k = j--; k < files[1].s - 1; ++k){ for(k = j--; k < files[1].s - 1; ++k){
files[1].files[k] = files[1].files[k+1]; files[1].files[k] = files[1].files[k+1];
files[1].names[k] = files[1].names[k+1]; files[1].names[k] = files[1].names[k+1];
} }
if(--files[1].s == 0)
terminate; if(--files[1].s == 0) { terminate; }
}
}
}
} }
terminate; terminate;

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2023 DTB <trinity@trinity.moe> * Copyright (c) 2023 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,46 +19,46 @@
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), putc(3), stdin, stdout, #include <stdio.h> /* fprintf(3), fputs(3), getc(3), putc(3), stdin, stdout,
* EOF */ * EOF */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */ #include <stdlib.h> /* EXIT_FAILURE */
#include <unistd.h> /* getopt(3) */ #include <unistd.h> /* getopt(3) */
#include <sysexits.h> #include <sysexits.h> /* EX_OK, EX_USAGE */
int main(int argc, char *argv[]){ int usage(char *s) {
int c; fprintf(stderr, "Usage: %s [-et]\n", s);
char showend;
char showtab;
showend = 0;
showtab = 0;
if(argc > 0)
while((c = getopt(argc, argv, "et")) != -1)
switch(c){
case 'e': showend = 1; break;
case 't': showtab = 1; break;
default: goto usage;
}
if(argc > optind){
usage: fprintf(stderr, "Usage: %s [-et]\n", argv[0]);
return EX_USAGE; return EX_USAGE;
} }
int main(int argc, char *argv[]) {
int c;
char showend = 0; /* print a dollar sign before each newline */
char showtab = 0; /* prints tab characters in caret notation */
if (argc > 0) {
while ((c = getopt(argc, argv, "et")) != -1) {
switch (c){
case 'e': showend = 1; break;
case 't': showtab = 1; break;
default: return usage(argv[0]);
}
}
}
if (argc > optind) { return usage(argv[0]); }
while ((c = getc(stdin)) != EOF) { while ((c = getc(stdin)) != EOF) {
if((c & 0x80) != 0) if ((c & 0x80) != 0) { fputs("M-", stdout); }
fputs("M-", stdout);
switch (c ^ 0x80 /* 0b 1000 0000 */) { switch (c ^ 0x80 /* 0b 1000 0000 */) {
case 0x7f: fputs("^?", stdout); case 0x7f: fputs("^?", stdout); break; /* delete character */
break; case '\n': if (showend) { putc('$', stdout); }
case '\n': if(showend)
putc('$', stdout);
default: default:
if(c >= ' ' || c == '\n' || (!showtab && c == '\t')) if (c >= ' ' || c == '\n' || (!showtab && c == '\t')) {
putc(c, stdout); putc(c, stdout);
else } else {
fprintf(stdout, "^%c", c + '@'); fprintf(stdout, "^%c", c + '@');
} }
} }
}
return EX_OK; return EX_OK;
} }

View File

@@ -57,7 +57,7 @@ extern crate sysexits;
use sysexits::EX_DATAERR; use sysexits::EX_DATAERR;
#[derive(Clone, PartialEq, PartialOrd, Debug)] #[derive(Clone, PartialEq, PartialOrd, Debug)]
// enum CalcType is a type containing operations used in the calculator /* enum CalcType is a type containing operations used in the calculator */
enum CalcType { enum CalcType {
Add, Add,
Subtract, Subtract,
@@ -117,8 +117,8 @@ struct EvaluationError {
code: i32, code: i32,
} }
// Im no math nerd but I want the highest possible approximation of 0.9 /* Im no math nerd but I want the highest possible approximation of 0.9
// repeating and it seems this can give it to me * repeating and it seems this can give it to me */
const PRECISION_MOD: f64 = 0.9 + f64::EPSILON * 100.0; const PRECISION_MOD: f64 = 0.9 + f64::EPSILON * 100.0;
fn eval( fn eval(
@@ -133,7 +133,7 @@ fn eval(
return Ok((stack, oper)); return Ok((stack, oper));
} }
// Split the input into tokens. /* Split the input into tokens. */
let mut toks: VecDeque<CalcType> = input let mut toks: VecDeque<CalcType> = input
.split_whitespace() .split_whitespace()
.rev() .rev()
@@ -183,7 +183,7 @@ fn eval(
Ok((stack, oper)) Ok((stack, oper))
} }
// Round a float to the given precision level /* Round a float to the given precision level */
fn round_precise(value: &f64, precision: usize) -> f64 { fn round_precise(value: &f64, precision: usize) -> f64 {
let multiplier = 10_f64.powi(precision as i32); let multiplier = 10_f64.powi(precision as i32);
(value * multiplier).round() / multiplier (value * multiplier).round() / multiplier
@@ -193,11 +193,11 @@ fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
let mut stack = VecDeque::new(); let mut stack = VecDeque::new();
let mut buf = String::new(); let mut buf = String::new();
// Set floating-point precision for correcting rounding errors based on /* Set floating-point precision for correcting rounding errors based on
// machine epsilon * machine epsilon */
let precision = (-f64::EPSILON.log10() * PRECISION_MOD).ceil() as usize; let precision = (-f64::EPSILON.log10() * PRECISION_MOD).ceil() as usize;
if argv.get(1).is_none() { if argv.get(1).is_none() { /* read from stdin */
while let Ok(_) = stdin().read_line(&mut buf) { while let Ok(_) = stdin().read_line(&mut buf) {
match eval(&buf.trim(), stack) { match eval(&buf.trim(), stack) {
Ok(s) => { Ok(s) => {
@@ -219,12 +219,13 @@ fn main() -> ExitCode {
}, },
}; };
} }
} else { } else { /* read from argv */
/* join argv into an owned String joined by spaces minus argv[0] */
let input = argv let input = argv
.iter() .iter()
.skip(1) .skip(1)
.map(|x| x.to_owned()) .map(|x| x.to_owned())
.collect::<Vec<String>>() .collect::<Vec<_>>()
.join(" "); .join(" ");
match eval(&input, stack) { match eval(&input, stack) {
@@ -233,7 +234,7 @@ fn main() -> ExitCode {
let val = match stack.iter().last() { let val = match stack.iter().last() {
Some(v) => v, Some(v) => v,
None => return ExitCode::from(0), None => return ExitCode::SUCCESS,
}; };
println!("{}", round_precise(val, precision).to_string()) println!("{}", round_precise(val, precision).to_string())
@@ -244,5 +245,5 @@ fn main() -> ExitCode {
}, },
}; };
} }
ExitCode::from(0) ExitCode::SUCCESS
} }

View File

@@ -20,88 +20,72 @@
#include <stdio.h> /* fprintf(3), stderr, NULL */ #include <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */ #include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <string.h> /* memset(3), strchr(3) */ #include <string.h> /* memset(3), strchr(3) */
#ifndef EX_USAGE #include <sysexits.h> /* EX_USAGE */
# include <sysexits.h>
#endif
#include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */ #include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */
#include <sys/stat.h> /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR, #include <sys/stat.h> /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR,
* 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)]; static char args[] = "bcdefgkprsuwxLS";
static char *program_name = "scrut";
int main(int argc, char *argv[]){ int usage(char *s) {
struct stat buf; fprintf(stderr, "Usage: %s [-%s] file...\n", s, args);
int c; return EX_USAGE;
size_t i;
char *p;
if(argc < 2)
goto usage;
memset(ops, '\0', sizeof ops);
while((c = getopt(argc, argv, args)) != -1)
if((p = strchr(args, c)) == NULL)
goto usage;
else
ops[p - args] = c;
/* straighten out ops */
for(i = 0, p = ops; i < (sizeof ops) / (sizeof *ops); ++i)
if(ops[i] != '\0'){
*p = ops[i];
if(&ops[i] != p++)
ops[i] = '\0';
} }
if(optind == argc) int main(int argc, char *argv[]) {
goto usage; char sel[(sizeof args) / (sizeof *args)];
argv += optind; if (argc < 2) { return usage(argv[0] == NULL ? program_name : argv[0]); }
do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1)
{ /* option parsing */
char *p;
memset(sel, '\0', sizeof sel);
for (int c; (c = getopt(argc, argv, args)) != -1;) {
if ((p = strchr(args, c)) == NULL) { return usage(argv[0]); }
else { sel[p - args] = c; }
}
/* straighten out selections; permute out nulls */
p = sel;
for (size_t i = 0; i < (sizeof sel) / (sizeof *sel); ++i) {
if (sel[i] != '\0') {
*p = sel[i];
if (&sel[i] != p++) { sel[i] = '\0'; }
}
}
}
if (optind == argc) { return usage(argv[0]); }
for (argv += optind ; *argv != NULL; ++argv) {
struct stat buf;
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))
usage: fprintf(stderr, "Usage: %s [-%s] file...\n", || (sel[i] == 'd' && !S_ISDIR(buf.st_mode))
argv[0] == NULL || (sel[i] == 'e' && 0)
? program_name || (sel[i] == 'f' && !S_ISREG(buf.st_mode))
: argv[0], || (sel[i] == 'g' && !(buf.st_mode & S_ISGID))
args); || (sel[i] == 'k' && !(buf.st_mode & S_ISVTX))
|| (sel[i] == 'p' && !S_ISFIFO(buf.st_mode))
return EX_USAGE; || (sel[i] == 'r' && access(*argv, R_OK) != 0)
}else if( || (sel[i] == 'u' && !(buf.st_mode & S_ISUID))
(ops[i] == 'b' || (sel[i] == 'w' && access(*argv, W_OK) != 0)
&& !S_ISBLK(buf.st_mode)) || (sel[i] == 'x' && access(*argv, X_OK) != 0)
|| (ops[i] == 'c' || (sel[i] == 'L' && !S_ISLNK(buf.st_mode))
&& !S_ISCHR(buf.st_mode)) || (sel[i] == 'S' && !S_ISSOCK(buf.st_mode))
|| (ops[i] == 'd' ) { return EXIT_FAILURE; }
&& !S_ISDIR(buf.st_mode)) }
|| (ops[i] == 'f' }
&& !S_ISREG(buf.st_mode))
|| (ops[i] == 'g'
&& !(buf.st_mode & S_ISGID))
|| (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;
}while(*++argv != NULL);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@@ -20,11 +20,11 @@
#include <ctype.h> #include <ctype.h>
#include <stddef.h> /* NULL */ #include <stddef.h> /* NULL */
#include <stdio.h> /* fprintf(3) */ #include <stdio.h> /* fprintf(3) */
#include <stdlib.h> /* EXIT_FAILURE */ #include <stdlib.h> /* size_t, EXIT_FAILURE */
#include <string.h> /* strcmp(3) */ #include <string.h> /* strcmp(3) */
#include <sysexits.h> #include <sysexits.h> /* EX_USAGE */
static char *program_name = "str"; char *program_name = "str";
static struct { static struct {
char *name; char *name;
@@ -41,35 +41,41 @@ static struct {
{ "isprint", isprint }, { "isprint", isprint },
{ "ispunct", ispunct }, { "ispunct", ispunct },
{ "isspace", isspace }, { "isspace", isspace },
{ "isupper", isupper } { "isupper", isupper },
{ NULL, NULL } /* marks end */
}; };
int main(int argc, char *argv[]){ int usage(char *s){
int ctype; fprintf(stderr, "Usage: %s type string...\n", s);
int i;
int r;
if(argc >= 3){
for(ctype = 0; ctype < (sizeof ctypes) / (sizeof *ctypes);
++ctype)
if(strcmp(argv[1], ctypes[ctype].name) == 0)
goto pass;
}
fprintf(stderr, "Usage: %s type string...\n",
argv[0] == NULL ? program_name : argv[0]);
return EX_USAGE; return EX_USAGE;
}
pass: for(argv += 2, r = 1; *argv != NULL; ++argv)
for(i = 0; argv[0][i] != '\0'; ++i) int main(int argc, char *argv[]){
/* First checks if argv[0][i] is valid ASCII; ctypes(3) size_t ctype; /* selected from ctypes.h; index of ctype */
* don't handle non-ASCII. int retval;
* This is bad. */
if((unsigned char)argv[0][i] < 0x80 && !ctypes[ctype].f(argv[0][i])) if (argc < 3) { return usage(argv[0] == NULL ? program_name : argv[0]); }
return 1;
else for ( /* iterate ctypes */
r = 0; ctype = 0;
ctypes[ctype].f != NULL /* break at the end of ctypes */
return r; && strcmp(argv[1], ctypes[ctype].name) != 0; /* break at match */
++ctype
);
if (ctypes[ctype].f == NULL) { return usage(argv[0]); }
/* iterate args */
for (argv += 2, retval = EXIT_FAILURE; *argv != NULL; ++argv) {
for (size_t i = 0; argv[0][i] != '\0'; ++i) { /* iterate arg bytes */
/* First checks if argv[0][i] is valid ASCII; ctypes(3) don't
* handle non-ASCII. This is bad. */
if(
(unsigned char)argv[0][i] < 0x80 && !ctypes[ctype].f(argv[0][i])
) { return EXIT_FAILURE; }
else { retval = EXIT_SUCCESS; }
}
}
return retval;
} }

View File

@@ -1,6 +1,24 @@
/*
* Copyright (c) 2023 DTB <trinity@trinity.moe>
* Copyright (c) 20232024 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 <stdio.h> /* fprintf(3), stderr */ #include <stdio.h> /* fprintf(3), stderr */
#include <stdlib.h> /* EXIT_FAILURE */ #include <sysexits.h> /* EX_OK, EX_USAGE */
#include <sysexits.h>
static char *program_name = "strcmp"; static char *program_name = "strcmp";
@@ -8,17 +26,26 @@ int main(int argc, char *argv[]){
int i; int i;
if (argc < 3) { if (argc < 3) {
fprintf(stderr, "Usage: %s string string...\n", fprintf(
argv[0] == NULL ? program_name : argv[0]); stderr,
"Usage: %s string string...\n",
argv[0] == NULL ? program_name : argv[0]
);
return EX_USAGE; return EX_USAGE;
} }
for(; *argv[1] != '\0'; ++argv[1]) for (; *argv[1] != '\0'; ++argv[1]) {
for(i = 2; i < argc; ++i) for (i = 2; i < argc; ++i) {
if(*argv[i-1] > *argv[i]) /* a former string has a greater byte value */
if (*argv[i-1] > *argv[i]) {
return 1; return 1;
else if(*argv[i-1] < *argv[i]++) /* a latter string has a greater byte value */
} else if (*argv[i-1] < *argv[i]++) {
return -1; /* actually 255 */ return -1; /* actually 255 */
}
return 0; }
}
return EX_OK;
} }