21 Commits

Author SHA1 Message Date
DTB
47ecf87e16 peek(1): use stdbool.h 2024-08-28 18:34:33 -06:00
DTB
8374b34834 peek(1): better error on piping 2024-08-28 18:31:25 -06:00
DTB
21ee79a929 peek(1): warn on pledge(2) or unveil(2) errors 2024-08-28 18:17:58 -06:00
DTB
4dc1900339 peek(1): pledge(2) and unveil(2) on OpenBSD 2024-08-27 21:16:12 -06:00
DTB
15c4f0cb8a peek.1: correct references to use the 1p section, add reference to stty(1p) 2024-08-14 11:06:31 -06:00
DTB
b68be7ff62 peek(1): fix ioerr typos 2024-07-27 18:51:09 -06:00
DTB
a217c4f631 Merge branch 'main' into peek 2024-07-27 18:46:48 -06:00
DTB
3f23e437e8 peek(1): more style fixes 2024-07-26 07:58:34 -06:00
DTB
a927c39c06 peek(1): update style 2024-07-26 07:42:05 -06:00
DTB
26558d6ffe Merge branch 'main' into peek 2024-06-28 08:40:17 -06:00
DTB
c7a4cd1d19 peek.1: s/inputted/input/ 2024-06-28 08:37:49 -06:00
DTB
e807a49612 peek.1: fix version string 2024-06-28 08:36:47 -06:00
DTB
70ee886b83 peek.1: fix wording on -i 2024-06-26 09:55:32 -06:00
DTB
ff993eebdc Makefile: all: add peek 2024-06-26 09:46:32 -06:00
DTB
262b3c18fb Merge branch 'main' into peek 2024-06-26 09:40:59 -06:00
3db83fd79c peek.1: updates to be more consistent with current documentation 2024-06-17 22:28:41 -06:00
DTB
6d3f084d9c peek(1): remove unused variables, clean up some typos 2024-04-18 10:14:52 -06:00
DTB
c295f531cb peek(1): correct grammar 2024-04-18 10:04:26 -06:00
DTB
c7739612a7 peek(1): strip down to bare essentials, rewrite man page to match 2024-04-18 10:03:02 -06:00
DTB
46b607384a peek(1): remove -p 2024-04-18 09:08:08 -06:00
DTB
4e5fc680e9 Import peek(1) from trinity/src 2024-04-17 16:37:54 -06:00
14 changed files with 629 additions and 630 deletions

View File

@@ -32,7 +32,7 @@ RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \
CFLAGS += -I$(SYSEXITS)
.PHONY: all
all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true
all: docs dj false fop hru intcmp mm npc peek rpn scrut str strcmp swab true
# keep build/include until bindgen(1) has stdin support
# https://github.com/rust-lang/rust-bindgen/issues/2703
@@ -120,7 +120,12 @@ build/bin/mm: src/mm.rs build rustlibs
.PHONY: npc
npc: build/bin/npc
build/bin/npc: src/npc.c build
$(CC) $(CFLAGAS) -o $@ src/npc.c
$(CC) $(CFLAGS) -o $@ src/npc.c
.PHONY: peek
peek: build/bin/peek
build/bin/peek: src/peek.c build
$(CC) $(CFLAGS) -o $@ src/peek.c
.PHONY: rpn
rpn: build/bin/rpn

124
STYLE
View File

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

111
docs/peek.1 Normal file
View File

@@ -0,0 +1,111 @@
.\" Copyright (c) 2023-2024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
.\"
.\" 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/>.
.\"
.TH PEEK 1 2024-08-14 "Harakit X.X.X"
.SH NAME
peek \(en read from the standard input, furtively
.\"
.SH SYNOPSIS
peek
.RB ( -i )
.\"
.SH DESCRIPTION
Read input from the standard input with terminal echo disabled.
.\"
.SH OPTIONS
.IP \fB-i\fP
Allows input to come from sources other than terminals (pipes).
.\"
.SH DIAGNOSTICS
In the event of an error, a debug message will be printed and the program will
exit with the appropriate
.BR sysexits.h (3)
error code.
In order to ensure the user\(cqs terminal is still usable after premature
termination, the program attempts to handle the SIGINT signal; if it cannot,
an error message is printed and execution continues. If the program is
interrupted, it exits unsuccessfully without an error message.
.\"
.SH RATIONALE
This tool was originally written to accept passwords in shell scripts as an
extremely simple alternative to the GNU Privacy Guard project\(cqs
.BR pinentry (1)
utility.
Accepting input without showing what is being typed is useful when keying in
secrets in public settings or in places where surveillance cameras are
installed.
.\"
.SH CAVEATS
This program does nothing to prevent others from seeing the key presses input to
a keyboard. It also does not protect against the sound of typing being analyzed
to determine what was input without needing to see screen or keyboard.
Accepting secrets in shell scripts is probably not advisable.
On systems that support it, the
.BR ioctl (2)
command TIOCSTI can be used to insert characters into the standard input. This
doesn't allow snooping but can be used for general mischief.
.\"
.SH EXAMPLES
This is an
.BR sh (1p)
command line that hashes a given password. It uses
.BR head (1p)
to only accept one line of input,
.BR xargs (1p)
and
.BR printf (1p)
to strip the trailing newline,
.BR htpasswd (1)
from Apache\(cqs utilities to hash the input with the bcrypt algorithm, and
.BR cut (1p)
to print only the resulting hash:
.RS
$ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2
.RE
This is an
.BR sh (1p)
command line that allows a user to write blindly into a text file but displaying
only written lines. Some writers have the habit of prematurely revising their
work and use tools with functionality similar to this to prevent it.
It uses
.BR mm (1)
to pipe the output of the program to both the standard error and the regular
file writing.txt:
.RS
$ echo Input ^D to quit. && peek | mm -eo - >writing.txt
.RE
.\"
.SH AUTHOR
Written by DTB
.MT trinity@trinity.moe
.ME .
.\"
.SH COPYRIGHT
Copyright \(co 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.\"
.SH SEE ALSO
.BR ioctl (2),
.BR ioctl_tty (2),
.BR read (1p),
.BR sh (1p),
.BR stty (1p)

368
src/dj.c
View File

@@ -1,6 +1,5 @@
/*
* 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
@@ -20,23 +19,26 @@
#include <assert.h> /* assert(3) */
#include <errno.h> /* errno */
#include <fcntl.h> /* open(2) */
#include <stdbool.h> /* bool */
#include <stdio.h> /* fprintf(3), stderr */
#include <stdlib.h> /* malloc(3), strtol(3), size_t */
#include <string.h> /* memcpy(3), memmove(3), memset(3) */
#include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */
#if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE
# include <sysexits.h>
#endif
#include <unistd.h> /* close(2), getopt(3), lseek(2), read(2), write(2),
* 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";
/* dj uses two structures that respectively correspond to the reading and
* writing ends of its jockeyed "pipe". User-configurable members are noted
* with their relevant options. */
struct Io {
char *buf; /* buffer */
char *fn; /* file name (-io) */
struct Io{
char *buf; /* buffer */
char *fn; /* file name (-io) */
size_t bs; /* buffer size (-bB) */
size_t bufuse; /* buffer usage */
size_t bytes; /* bytes processed */
@@ -44,10 +46,14 @@ struct Io {
size_t rec; /* records processed */
long seek; /* remaining bytes to seek/skip (-sS) */
int error; /* errno */
int fd; /* file descriptor */
int fl; /* file opening flags */
int fd; /* file descriptor */
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 *stdout_name = "<stdout>";
@@ -61,15 +67,14 @@ static int write_flags = O_WRONLY | O_CREAT;
/* Macro to check if fd is stdin or stdout */
#define fdisstd(fd) ((fd) == STDIN_FILENO || (fd) == STDOUT_FILENO)
/* Completes one Io block read */
static struct Io *
Io_read(struct Io *io) {
Io_read(struct Io *io){
int t;
assert(io->bs > 0);
assert(io->bufuse < io->bs);
if ((t = read(io->fd, &(io->buf)[io->bufuse], io->bs - io->bufuse)) < 0) {
if((t = read(io->fd, &(io->buf)[io->bufuse], io->bs - io->bufuse)) < 0){
io->error = errno;
t = 0;
}
@@ -84,20 +89,18 @@ Io_read(struct Io *io) {
return io;
}
/* Completes one Io block write */
static struct Io *
Io_write(struct Io *io) {
Io_write(struct Io *io){
int t;
assert(io->bufuse > 0);
assert(io->bufuse <= io->bs);
if ((t = write(io->fd, io->buf, io->bufuse)) < 0) {
if((t = write(io->fd, io->buf, io->bufuse)) < 0){
io->error = errno;
t = 0;
} else if (t > 0) {
(void)memmove(io->buf, &(io->buf)[t], (io->bufuse -= t));
}
}else if(t > 0)
memmove(io->buf, &(io->buf)[t], (io->bufuse -= t));
io->bytes += t;
io->prec += (t > 0 && io->bufuse > 0);
@@ -107,79 +110,62 @@ Io_write(struct Io *io) {
}
static int
oserr(char *e, int n) {
(void)fprintf(stderr, "%s: %s: %s\n", program_name, e, strerror(n));
oserr(char *e, int n){
fprintf(stderr, "%s: %s: %s\n", program_name, e, strerror(n));
return EX_OSERR;
}
/* Prints statistics regarding the use of dj, particularly partially and
* completely read and written records. */
static int
fprintio(FILE *stream, char *fmt, struct Io io[2]) {
return fprintf(
stream,
fmt,
io[0].rec,
io[0].prec,
io[1].rec,
io[1].prec,
io[0].bytes,
io[1].bytes
);
}
static void
fprintio(FILE *stream, char *fmt, struct Io io[2]){
/* 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";
fprintf(stream, fmt,
io[0].rec, io[0].prec, io[1].rec, io[1].prec,
io[0].bytes, io[1].bytes);
return;
}
/* 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
* (e.g. -B [int]) in dj and no negative integer would be valid anyway. */
static long
parse(char *s) {
parse(char *s){
long r;
errno = 0;
r = strtol(s, &s, 0);
return (*s == '\0' /* no chars left unparsed */ && errno == 0) ? r : -1;
return (*s == '\0' /* no chars left unparsed */ && errno == 0)
? r
: -1;
}
static int
usage(char *argv0) {
(void)fprintf(
stderr,
"Usage: %s [-Hn] [-a byte] [-c count]\n"
usage(char *s){
fprintf(stderr, "Usage: %s [-Hn] [-a byte] [-c count]\n"
"\t[-i file] [-b block_size] [-s offset]\n"
"\t[-o file] [-B block_size] [-S offset]\n",
argv0
);
program_name);
return EX_USAGE;
}
int main(int argc, char *argv[]) {
int align; /* low 8b used, negative if no alignment is being done */
int count; /* -1 if dj(1) runs until no more reads are possible */
char *fmt; /* set to fmt_asv (default) or fmt_human (-H) */
size_t i; /* side of io (in or out) being modified */
bool retry; /* false if exits on partial reads or writes */
int main(int argc, char *argv[]){
int align; /* low 8b used, negative if no alignment is being done */
int count; /* 0 if dj(1) runs until no more reads are possible */
char *fmt; /* == fmt_asv (default) or fmt_human (-H) */
size_t i; /* side of io being modified */
char noerror; /* 0=exits (default) 1=retries on partial reads or writes */
struct Io io[2 /* { in, out } */];
/* Set defaults. */
align = -1;
count = -1;
count = 0;
fmt = fmt_asv;
retry = 0;
for (i = 0; i < (sizeof io) / (sizeof *io); ++i) {
noerror = 0;
for(i = 0; i < (sizeof io) / (sizeof *io); ++i){
io[i].bs = 1024 /* 1 KiB */; /* GNU dd(1) default; POSIX says 512B */
io[i].bufuse = 0;
io[i].bytes = 0;
@@ -192,113 +178,88 @@ int main(int argc, char *argv[]) {
io[i].seek = 0;
}
if (argc > 0) {
if(argc > 0){
int c;
program_name = argv[0];
while ((c = getopt(argc, argv, "a:b:B:c:i:hHns:S:o:")) != -1) {
switch (c) {
case 'i': case 'o': /* input, output */
i = (c == 'o');
while((c = getopt(argc, argv, ":a:b:B:c:i:hHns:S:o:")) != -1)
switch(c){
case 'i': case 'o': i = (c == 'o');
if(optarg[0] == '-' && optarg[1] == '\0'){ /* optarg == "-" */
io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO;
io[i].fn = i == 0 ? stdin_name : stdout_name;
break;
}else{
int fd;
/* optarg == "-" (stdin/stdout) */
if (optarg[0] == '-' && optarg[1] == '\0') {
io[i].fd = i == 0 ? STDIN_FILENO : STDOUT_FILENO;
io[i].fn = i == 0 ? stdin_name : stdout_name;
break;
} else {
int fd;
if (
(fd = open(optarg, io[i].fl, creat_mode)) != -1
&& (fdisstd(io[i].fd) || close(io[i].fd) == 0)
) {
io[i].fd = fd;
io[i].fn = optarg;
break;
}
}
return oserr(optarg, errno);
/* UNREACHABLE */
case 'n': retry = 1; break; /* retry failed reads once */
case 'H': fmt = fmt_human; break; /* human-readable output */
case 'a': /* input buffer padding */
if (optarg[0] == '\0' || optarg[1] == '\0') {
align = optarg[0];
if((fd = open(optarg, io[i].fl, creat_mode)) != -1
&& (fdisstd(io[i].fd) || close(io[i].fd) == 0)){
io[i].fd = fd;
io[i].fn = optarg;
break;
}
/* FALLTHROUGH */
case 'c': /* number of reads */
case 'b': case 'B': /* input/output block size */
case 's': case 'S': /* (s)kip/(S)eek in input/output */
if (c == 'c' && (count = parse(optarg)) >= 0) { break; }
i = (c >= 'A' && c <= 'Z');
c |= 0x20; /* 0b 0010 0000 (ASCII) make lowercase */
if ( /* if -b or -s is parsed out correctly */
(c == 'b' && (io[i].bs = parse(optarg)) > 0)
|| (c == 's' && (io[i].seek = parse(optarg)) >= 0)
) { break; } /* don't error */
/* FALLTHROUGH */
default:
return usage(program_name);
}
return oserr(optarg, errno);
case 'n': noerror = 1; break;
case 'H': fmt = fmt_human; break;
case 'a':
if(optarg[0] == '\0' || optarg[1] == '\0'){
align = optarg[0];
break;
}
/* FALLTHROUGH */
case 'c': case 'b': case 's': case 'B': case 'S': /* numbers */
if(c == 'c' && (count = parse(optarg)) >= 0)
break;
i = (c >= 'A' && c <= 'Z');
c |= 0x20 /* 0b 0010 0000 */; /* (ASCII) make lowercase */
if((c == 'b' && (io[i].bs = parse(optarg)) > 0)
|| (c == 's' && (io[i].seek = parse(optarg)) >= 0))
break;
/* FALLTHROUGH */
default:
return usage(program_name);
}
}
}
assert(io->fd != STDIN_FILENO || io->fl == read_flags);
assert(io->fd != STDOUT_FILENO || io->fl == write_flags);
if (argc > optind) { return usage(program_name); }
if(argc > optind)
return usage(program_name);
for (i = 0; i < (sizeof io) / (sizeof *io); ++i) {
for(i = 0; i < (sizeof io) / (sizeof *io); ++i){
/* buffer allocation */
if ((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL) {
(void)fprintf(
stderr, "%s: Failed to allocate %zd bytes\n",
program_name, io[i].bs
);
if((io[i].buf = malloc(io[i].bs * (sizeof *(io[i].buf)))) == NULL){
fprintf(stderr, "%s: Failed to allocate %zd bytes\n",
program_name, io[i].bs);
return EX_OSERR;
}
/* 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;
}
}
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)) {
(void)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 && !retry && io[1].error == 0) {
(void)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! */
/* 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;
}
io[1].bufuse = 0; /* reset after hard seek */
if (io[1].seek > 0) { /* hard seeking failed */
(void)fprintio(stderr, fmt, io);
if(io[1].seek > 0){
fprintio(stderr, fmt, io);
return oserr(io[1].fn, errno);
}
for ( ;
count == -1 || count > 0;
count -= (count != -1) /* decrement if counting */
) {
do{
assert(io[0].bufuse == 0);
{ /* read */
@@ -306,114 +267,87 @@ int main(int argc, char *argv[]) {
size_t t;
/* hack to intentionally get a partial read from Io_read */
if ((skipping = MIN(io[0].seek, io[0].bs)) > 0) {
if((skipping = MIN(io[0].seek, io[0].bs)) > 0)
io[0].bufuse = io[0].bs - (size_t)skipping;
}
t = io[0].bufuse;
if (Io_read(&io[0])->bufuse == t && !retry && io[0].error == 0) {
(void)Io_read(&io[0]); /* second chance */
}
if(Io_read(&io[0])->bufuse == t && !noerror && io[0].error == 0)
Io_read(&io[0]); /* second chance */
assert(io[0].bufuse >= t);
if(io[0].bufuse == t) /* that's all she wrote */
break;
if (io[0].bufuse == t) { break; } /* that's all she wrote */
if (/* t < io[0].bufuse && */ io[0].bufuse < io[0].bs) {
(void)fprintf(stderr, "%s: Partial read:\n\t", program_name);
(void)fprintio(stderr, fmt, io);
if (!retry) { count = 1; }
if (align >= 0) {
if(/* t < io[0].bufuse && */ io[0].bufuse < io[0].bs){
fprintf(stderr, "%s: Partial read:\n\t", program_name);
fprintio(stderr, fmt, io);
if(!noerror)
count = 1;
if(align >= 0){
/* fill the rest of the ibuf with padding */
(void)memset(
&(io[0].buf)[io[0].bufuse],
align,
io[0].bs - io[0].bufuse
);
memset(&(io[0].buf)[io[0].bufuse], align,
io[0].bs - io[0].bufuse);
io->bufuse = io->bs;
}
}
if (skipping > 0) {
if(skipping > 0){
io[0].seek -= skipping;
io[0].bufuse = 0;
count += (count != -1); /* increment if counting */
count += (count != 0);
continue;
}
}
assert(io[0].bufuse > 0);
/* write */
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;
(void)memcpy( /* saturate obuf */
io[1].buf, io[0].buf,
(io[1].bufuse = (n = MIN(io[0].bufuse, io[1].bs)))
);
/* saturate obuf */
memcpy(io[1].buf, io[0].buf,
(io[1].bufuse = (n = MIN(io[0].bufuse, io[1].bs))));
/* permute the copied units out of ibuf */
(void)memmove(io[0].buf, &(io[0].buf)[n], (io[0].bufuse -= n));
} else /* if(io[0].bs > io[1].bs) */ {
memmove(io[0].buf, &(io[0].buf)[n], (io[0].bufuse -= n));
}else /* if(io[0].bs < io[1].bs) */ {
int n;
/* drain what we can from ibuf */
(void)memcpy(
&(io[1].buf)[io[1].bufuse], io[0].buf,
(n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse))
);
memcpy(&(io[1].buf)[io[1].bufuse], io[0].buf,
(n = MIN(io[0].bufuse, io[1].bs - io[1].bufuse)));
io[1].bufuse += n;
/* permute out the copied units */
(void)memmove(io[0].buf, &(io[0].buf)[n], io[0].bs - n);
memmove(io[0].buf, &(io[0].buf)[n], io[0].bs - n);
io[0].bufuse -= n;
if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1) {
if(io[0].bs + io[1].bufuse <= io[1].bs && count != 1)
continue; /* obuf not saturated - we could write more */
}
}
{ /* writes actually happen, or die */
size_t t;
t = io[1].bufuse;
if(Io_write(&io[1])->bufuse == t && !noerror && io[1].error == 0)
Io_write(&io[1]); /* second chance */
assert(io[1].bufuse <= t);
if(io[1].bufuse == t){ /* no more love */
count = 1;
break;
}
t = io[1].bufuse;
if (Io_write(&io[1])->bufuse == t
&& !retry
&& io[1].error == 0) {
(void)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);
if (io[1].bufuse == t) { /* no more love */
if(0 < io[1].bufuse /* && io[1].bufuse < t */){
fprintf(stderr, "%s: Partial write:\n\t", program_name);
fprintio(stderr, fmt, io);
if(!noerror)
count = 1;
break;
}
}
}while(io[0].bufuse > 0);
}while(count == 0 || --count > 0);
if (0 < io[1].bufuse /* && io[1].bufuse < t */) {
(void)fprintf(stderr, "%s: Partial write:\n\t", program_name);
(void)fprintio(stderr, fmt, io);
fprintio(stderr, fmt, io);
if(!retry) { count = 1; }
}
}
}
(void)fprintio(stderr, fmt, io);
for (i = 0; i < (sizeof io) / (sizeof *io); ++i) {
if (io[i].error) { return oserr(io[i].fn, io[i].error); }
}
for(i = 0; i < (sizeof io) / (sizeof *io); ++i)
if(io[i].error)
return oserr(io[i].fn, io[i].error);
return EX_OK;
}

View File

@@ -32,8 +32,8 @@ use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
fn main() {
let argv = args().collect::<Vec<String>>();
let mut d = '\u{1E}'.to_string(); /* ASCII record separator */
let mut optind = 1;
let mut d = '\u{1E}'.to_string();
let mut index_arg = 0;
let usage = format!(
"Usage: {} [-d delimiter] index command [args...]",
@@ -42,62 +42,51 @@ fn main() {
while let Some(opt) = argv.getopt("d:") {
match opt.opt() {
Ok("d") => {
/* delimiter */
Ok(_) => {
/* unwrap because Err(OptError::MissingArg) will be returned if
* opt.arg() is None */
d = opt.arg().unwrap();
optind = opt.ind();
index_arg = opt.ind();
},
_ => {
Err(_) => {
eprintln!("{}", 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);
});
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[0] of the operator command */
let operator = argv.get(command_arg).unwrap_or_else(|| {
argv.get(command_arg).unwrap_or_else(|| {
eprintln!("{}", usage);
exit(EX_USAGE);
});
/* read entire standard input into memory */
let mut buf = String::new();
if let Err(e) = stdin().read_to_string(&mut buf) {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
};
let index = argv[index_arg].parse::<usize>().unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[1], e);
exit(EX_DATAERR);
});
/* split the buffer by the delimiter (by default, '\u{1E}') */
let mut buf = String::new();
let _ = stdin().read_to_string(&mut buf);
let mut fields = buf.split(&d).collect::<Vec<&str>>();
/* collect arguments for the operator command */
let command_args = argv
let opts = argv
.iter()
.clone()
.skip(command_arg + 1) /* skip the command name */
.skip(command_arg + 1)
.collect::<Vec<&String>>();
/* spawn the command to operate on the field */
let mut spawned = Command::new(operator)
.args(command_args) /* spawn with the specified arguments */
let mut spawned = Command::new(argv.get(command_arg).unwrap())
.args(opts)
.stdin(Stdio::piped())
.stdout(Stdio::piped()) /* piped stdout to handle output ourselves */
.stdout(Stdio::piped())
.spawn()
.unwrap_or_else( |e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror());
exit(EX_UNAVAILABLE);
});
/* get field we want to pipe into spawned program */
let field = fields.get(index).unwrap_or_else(|| {
eprintln!(
"{}: {}: No such index in input",
@@ -107,10 +96,9 @@ fn main() {
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() {
let _ = child_stdin.write_all(field.as_bytes());
drop(child_stdin); /* stay safe! drop your children! */
drop(child_stdin);
}
let output = spawned.wait_with_output().unwrap_or_else(|e| {
@@ -118,27 +106,17 @@ fn main() {
exit(EX_IOERR);
});
/* get the output with which the original field will be replaced */
let mut replace = output.stdout.clone();
/* pop trailing newline out if the input did not contain it */
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;
}
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| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e);
exit(EX_IOERR);
});
/* store the new field in the old fields vector */
fields[index] = &new_field;
/* fop it */
stdout().write_all(
fields.join(&d.to_string()).as_bytes()
).unwrap_or_else(|e| {

View File

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

View File

@@ -1,6 +1,5 @@
/*
* 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
@@ -17,65 +16,46 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), perror(3), putc(3), stdin,
* stdout, EOF */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_USAGE */
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), putc(3), stdin, stdout,
* EOF */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* getopt(3) */
#include <sysexits.h>
char *program_name = "npc";
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;
}
int main(int argc, char *argv[]) {
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 */
char showend;
char showtab;
if (argc > 0) {
program_name = argv[0];
showend = 0;
showtab = 0;
while ((c = getopt(argc, argv, "et")) != -1) {
switch (c){
case 'e': showend = 1; break;
case 't': showtab = 1; break;
default: return usage(program_name);
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;
}
if (argc > optind) { return usage(program_name); }
while ((c = getc(stdin)) != EOF) {
if ((c & 0x80) != 0 && fputs("M-", stdout) == EOF) {
return ioerr(argv[0]);
}
switch (c ^ 0x80 /* 0b 1000 0000 */) {
case 0x7f: /* ASCII DEL (127d) */
if(fputs("^?", stdout) == EOF) { return ioerr(argv[0]); }
while((c = getc(stdin)) != EOF){
if((c & 0x80) != 0)
fputs("M-", stdout);
switch(c ^ 0x80 /* 0b 1000 0000 */){
case 0x7f: fputs("^?", stdout);
break;
case '\n':
if (showend && fputc('$', stdout) == EOF) {
return ioerr(argv[0]);
}
default:
if (c >= ' ' || c == '\n' || (!showtab && c == '\t')) {
if (fputc(c, stdout) == EOF) { return ioerr(argv[0]); }
} else if (fprintf(stdout, "^%c", c + '@') < 0) {
return ioerr(argv[0]);
}
case '\n': if(showend)
putc('$', stdout);
default:
if(c >= ' ' || c == '\n' || (!showtab && c == '\t'))
putc(c, stdout);
else
fprintf(stdout, "^%c", c + '@');
}
}

139
src/peek.c Normal file
View File

@@ -0,0 +1,139 @@
/*
* 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/.
*/
#include <signal.h> /* sigaction(2), signal(2), struct sigaction, SIGINT */
#include <stdbool.h> /* bool */
#include <stdio.h> /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin,
* stdout, EOF, NULL */
#include <stdlib.h> /* exit(3), EXIT_FAILURE */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_USAGE */
#include <termios.h> /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */
#include <unistd.h> /* getopt(3), isatty(3), pledge(2), unveil(2),
* STDIN_FILENO */
char *program_name = "peek";
/* Restores terminal echo; otherwise when a user ^Cs the terminal would
* continue to not display typed text. If sig isn't zero, this will terminate
* the program. */
static void
restore_echo(int sig) {
static struct termios t;
/* Failure isn't reported because this is the termination routine anyway;
* errors will be obvious. */
if (tcgetattr(STDIN_FILENO, &t) == 0) {
t.c_lflag |= ECHO;
(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &t);
}
if (sig != 0) { exit(EXIT_FAILURE); } /* Terminated by signal. */
return;
}
static int
ioerr(char *argv0) {
perror(argv0);
restore_echo(0);
return EX_IOERR;
}
static int
usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s [-i]\n", argv0);
return EX_USAGE;
}
int main(int argc, char *argv[]){
#ifdef __OpenBSD__
if (pledge("stdio tty unveil", "") != 0 || unveil(NULL, NULL) != 0) {
/* This isn't fatal; these return values could be cast to void just as
* easily. */
(void)perror(argv[0] == NULL ? argv[0] : program_name);
}
#endif
if (argc > 0) { /* option parsing */
bool allow_nonterminals;
int c;
program_name = argv[0];
allow_nonterminals = 0;
while ((c = getopt(argc, argv, "i")) != -1) {
switch (c) {
case 'i': allow_nonterminals = 1; break;
default: return usage(argv[0]);
}
}
if (argc > optind) { return usage(argv[0]); }
if (!allow_nonterminals && isatty(STDIN_FILENO) != 1) {
(void)fprintf(
stderr,
"%s: Must be run in a terminal (specify -i to skip this"
" check)\n",
argv[0]
);
return EX_USAGE;
}
}
{ /* Install signal handler */
/* There isn't a difference in functionality between the signal(2) and
* sigaction(2) methods. sigaction(2) is vastly preferred for
* portability but some configurations can only use signal(2). */
/* Errors aren't terminating because the worst that happens is some
* terminal phooeyness if things go awry. */
#if defined _POSIX_C_SOURCE
struct sigaction act = { 0 };
act.sa_handler = restore_echo;
if (sigaction(SIGINT, &act, NULL) != 0) { perror(program_name); }
#else
if (signal(SIGINT, restore_echo) == SIG_ERR) { perror(program_name); }
#endif
}
/* Banish terminal echo; this terminates when it fails, because it's the
* whole point of the program. */
{
struct termios t;
if (tcgetattr(STDIN_FILENO, &t) != 0) { return ioerr(program_name); }
t.c_lflag ^= ECHO;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) != 0) {
return ioerr(program_name);
}
}
{ /* Input loop */
int c;
while ((c = fgetc(stdin)) != EOF) {
if (fputc(c, stdout) == EOF) { return ioerr(program_name); }
}
}
restore_echo(0);
return EX_OK;
}

View File

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

View File

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

View File

@@ -20,16 +20,16 @@
#include <ctype.h>
#include <stddef.h> /* NULL */
#include <stdio.h> /* fprintf(3) */
#include <stdlib.h> /* size_t, EXIT_FAILURE */
#include <stdlib.h> /* EXIT_FAILURE */
#include <string.h> /* strcmp(3) */
#include <sysexits.h> /* EX_USAGE */
#include <sysexits.h>
char *program_name = "str";
static char *program_name = "str";
static struct {
char *name;
int (*f)(int);
} ctypes[] = {
}ctypes[] = {
{ "isalnum", isalnum },
{ "isalpha", isalpha },
{ "isblank", isblank },
@@ -41,44 +41,35 @@ static struct {
{ "isprint", isprint },
{ "ispunct", ispunct },
{ "isspace", isspace },
{ "isupper", isupper },
{ NULL, NULL } /* marks end */
{ "isupper", isupper }
};
static int
usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s type string...\n", argv0);
int main(int argc, char *argv[]){
int ctype;
int i;
int r;
return EX_USAGE;
}
int main(int argc, char *argv[]) {
size_t ctype; // selected from ctypes.h; index of ctype
int retval; // initially fail but becomes success on the first valid char
if (argc < 3) { return usage(argv[0] == NULL ? program_name : argv[0]); }
for ( /* iterate ctypes */
ctype = 0;
ctypes[ctype].f != NULL /* break at the end of ctypes */
&& 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 // argv[0][i] is ASCII,
&& !ctypes[ctype].f(argv[0][i]) // so use ctypes(3)
) { return EXIT_FAILURE; }
else { retval = EXIT_SUCCESS; }
}
if(argc >= 3){
for(ctype = 0; ctype < (sizeof ctypes) / (sizeof *ctypes);
++ctype)
if(strcmp(argv[1], ctypes[ctype].name) == 0)
goto pass;
}
return retval;
fprintf(stderr, "Usage: %s type string...\n",
argv[0] == NULL ? program_name : argv[0]);
return EX_USAGE;
pass: for(argv += 2, r = 1; *argv != NULL; ++argv)
for(i = 0; argv[0][i] != '\0'; ++i)
/* 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 1;
else
r = 0;
return r;
}

View File

@@ -1,6 +1,5 @@
/*
* Copyright (c) 2023 DTB <trinity@trinity.moe>
* Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media>
* Copyright (c) 20222024 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
@@ -16,35 +15,29 @@
* 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 <sysexits.h> /* EX_OK, EX_USAGE */
#include <stdlib.h> /* size_t */
#include <sysexits.h> /* EX_USAGE */
char *program_name = "strcmp";
int main(int argc, char *argv[]) {
int i;
int main(int argc, char *argv[]){
if (argc < 3) {
(void)fprintf(
stderr,
"Usage: %s string string...\n",
fprintf(stderr, "Usage: %s string string...\n",
argv[0] == NULL ? program_name : argv[0]
);
return EX_USAGE;
}
for (; *argv[1] != '\0'; ++argv[1]) {
for (i = 2; i < argc; ++i) {
/* a former string has a greater byte value */
if (*argv[i-1] > *argv[i]) {
return 1;
/* a latter string has a greater byte value */
} else if (*argv[i-1] < *argv[i]++) {
return -1; /* actually 255 */
}
/* This compares the Nth character of arg[2] onward with argv[1]'s Nth
* character, rather than comparing each arg with argv[1] sequentially. */
for (; *argv[1] != '\0'; ++argv[1]) { /* iterate chars in argv[1] */
for (size_t i = 2; i < argc; ++argv[i], ++i) { /* iterate &argv[2] */
/* this never overruns because of nul termination */
if (*argv[i-1] != *argv[i]) { return *argv[i-1] - *argv[i]; }
}
}
return EX_OK;
return 0;
}

View File

@@ -25,23 +25,19 @@ use std::{
};
extern crate getopt;
extern crate sysexits;
extern crate strerror;
use getopt::GetOpt;
use sysexits::{ EX_IOERR, EX_OK, EX_OSERR, EX_USAGE };
extern crate sysexits;
use sysexits::{ EX_OK, EX_OSERR, EX_USAGE };
extern crate strerror;
use strerror::StrError;
fn oserr(argv0: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
fn oserr(s: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", s, e.strerror());
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 {
eprintln!("Usage: {} [-w word_size]", s);
ExitCode::from(EX_USAGE as u8)
@@ -49,11 +45,12 @@ fn usage(s: &str) -> ExitCode {
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
let mut buf: Vec<u8> = Vec::new(); // holds the sequence getting swabbed
let mut buf: Vec<u8> = Vec::new();
let mut input = stdin();
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 wordsize: usize = 2; // Equivalent to dd(1p).
while let Some(opt) = argv.getopt("w:") {
match opt.opt() {
@@ -76,19 +73,17 @@ fn main() -> ExitCode {
loop {
match input.read(&mut buf) {
Ok(0) => break ExitCode::from(EX_OK as u8), // read nothing; bye
Ok(v) if v == wordsize => { // read full block; swab
Ok(0) => break ExitCode::from(EX_OK as u8),
Ok(v) if v == wordsize => {
let (left, right) = buf.split_at(v/2);
if let Err(e) = output.write(&right)
.and_then(|_| output.write(&left)) {
break ioerr(&argv[0], e);
break oserr(&argv[0], e)
}
},
Ok(v) => { // partial read; partially write
Ok(v) => {
if let Err(e) = output.write(&buf[..v]) {
break ioerr(&argv[0], e);
break oserr(&argv[0], e)
}
},
Err(e) => break oserr(&argv[0], e)