Compare commits

..

81 Commits

Author SHA1 Message Date
90de81b5bd
Merge branch 'quick-fixes' into trunk 2025-11-05 13:35:15 -07:00
1bd190ae99
mm.1: fixed attributions 2025-11-02 12:20:17 -07:00
791f44aa1e
dj.1, mm(1): updates dates for copyright and modification 2025-11-02 01:14:17 -06:00
feac3bf4f8
mm(1): fixes bug with -a; mm.1, tests/mm.mk: changes to test regression in -a and -t 2025-11-02 01:12:27 -06:00
544b544d84
mm.1: removed description of removed behavior 2025-11-02 00:30:27 -06:00
1750329c51
mm.1: removed erroneous copyrighy section 2025-10-27 01:53:50 -06:00
be6bd5386d
docs: POSIX → \*(Px 2025-10-26 20:10:24 -06:00
3b44ddeedd
scrut(1): remove erroneous utility from tree 2025-10-26 17:07:52 -06:00
8d8df711d9
all: formatting; mm(1): fixes speed, simplicity; docs/dj.1: grammar, typos, etc. 2025-10-26 16:58:13 -06:00
ac0f9e4019
README: add CONTRIBUTING mention 2025-04-03 16:46:50 -06:00
247e469f82
CONTRIBUTING, STYLE: merge into CONTRIBUTING 2025-04-03 16:45:22 -06:00
8eece4cf84
Makefile: removes deprecated variable 2025-03-28 19:38:51 -06:00
0e9127d417
tests: removed POSIX testing (fixes #163) 2025-02-24 23:28:52 -07:00
e6f747d635
Merge branch 'npc-broken-tests' 2025-02-24 23:19:47 -07:00
9b699d7298
Makefile, tests/bonsai/scrut.mk: cleanup 2025-02-24 23:17:10 -07:00
941f931f8b
Merge branch libfileis 2025-02-24 23:13:13 -07:00
f0eac562b3
tests: bonsai/npc.mk: fixed copyright header 2025-02-24 23:08:28 -07:00
138044e52f
Merge branch 'optimizations' 2025-02-24 23:04:10 -07:00
bd8bc0094d
rpn(1): fixes erroneous OpenBSD-gated import 2025-02-24 23:03:31 -07:00
99a99cdcb5
Merge branch 'style-c' 2025-02-24 22:59:50 -07:00
008c0624f6
tests: bonsai/npc.mk: removed test that is broken with GNU and BusyBox 2025-02-24 22:57:42 -07:00
982c67df13
intcmp(1), swab(1): consistent type annotations 2024-10-01 22:08:56 -06:00
c4cd2563f9
scrut(1): fixes failing write test 2024-09-11 03:26:00 -06:00
8bdb72ece8
false(1): fixes pledge(2) invocation 2024-09-11 03:19:56 -06:00
ba73f50527
true(1): removes NULL from commented imports 2024-09-11 03:19:23 -06:00
579bb98874
true(1): fixes pledge(2) invocation 2024-09-11 03:18:01 -06:00
8f1e570b50
strcmp(1): fixes pledge(2) invocation 2024-09-11 03:15:58 -06:00
a43daf2cf2
str(1): fixes pledge(2) invocation 2024-09-11 03:15:13 -06:00
f25a499ca0
scrut(1): fixes pledge(2) invocation 2024-09-11 03:14:36 -06:00
e0b5467fb6
npc(1): fixes pledge invocation 2024-09-11 03:13:38 -06:00
7498b283ce
mm(1): updates to use new unveil api 2024-09-11 02:32:32 -06:00
0b3ed37c38
libopenbsd.rs(3): makes using statically-allocated arrays possible 2024-09-11 02:32:03 -06:00
800a097903
swab(1): uses default for promises 2024-09-10 17:14:02 -06:00
1ccdc65d30
rpn(1): uses default for promises 2024-09-10 17:13:20 -06:00
df0a236f81
mm(1): uses default for Promises 2024-09-10 17:12:14 -06:00
e385d873ec
intcmp(1): uses default value for Promises 2024-09-10 17:11:22 -06:00
73a75a32df
hru(1): uses default Promises value for child processes 2024-09-10 17:10:26 -06:00
b8e5901d97
libopenbsd.rs(3): adds default value for Promises 2024-09-10 17:09:46 -06:00
f4bd4de2e4
fileis(1): various changes to make the code more efficient and idiomatic 2024-09-10 02:42:55 -06:00
f66fdef9c3
STYLE: avoid unbounded loops 2024-09-10 02:05:01 -06:00
b56a66f980
STYLE: fixes default case in example 2024-09-10 01:59:59 -06:00
f6559b464a
STYLE: fix minor issues 2024-09-07 12:51:18 -06:00
790b12fb40
STYLE: changes numbering scheme to transcend headers, fixes minor errors 2024-09-05 15:55:32 -06:00
4cb5ea78d7
STYLE: improves clarity, focus, distribution of items, adds more gripes, includes usage text style 2024-09-05 12:44:14 -06:00
62ce288524
rpn(1): makes rounding more efficient 2024-09-04 22:12:44 -06:00
731e62ee7e
rpn(1): made evaluation function more readable 2024-08-28 18:24:49 -06:00
f50bfeea92
fop(1): fixes panic on no arguments 2024-08-28 18:05:56 -06:00
8d00fa0afd
fop(1): minor formatting change 2024-08-28 00:46:32 -06:00
9f520df82b
strcmp(1): adds null unveil 2024-08-28 00:31:02 -06:00
41982c2f73
str(1): adds null unveil 2024-08-28 00:30:45 -06:00
6166a3ca36
npc(1): adds null unveil 2024-08-28 00:26:26 -06:00
efedcd3ae4
true(1), false(1): adds null unveils 2024-08-28 00:24:35 -06:00
8fd5bdf5a6
swab(1): adds null unveil 2024-08-28 00:15:15 -06:00
b946469da5
rpn(1): adds null unveil 2024-08-28 00:13:21 -06:00
06384c72fb
intcmp(1): adds null unveil 2024-08-28 00:09:40 -06:00
9aeadd8f8f
hru(1): adds null unveil 2024-08-27 23:57:12 -06:00
5b666d6858
fop(1): adds null unveil 2024-08-27 23:54:39 -06:00
b875aa1058
rpn(1): more improvements 2024-08-24 19:00:01 -06:00
cc53dab035
rpn(1): handles errors when writing to stdout 2024-08-24 18:18:27 -06:00
232629f4d3
hru(1): makes more efficient 2024-08-24 17:55:00 -06:00
ff4ff825bd
swab(1): changes disparate error handling functions to one function 2024-08-24 17:26:26 -06:00
5eb71e90a3
tests/bonsai: rpn.mk: tests the standard input 2024-08-24 17:22:57 -06:00
a0138be79e
rpn(1): refactor to make code more efficient, readable, and maintainable 2024-08-24 17:22:02 -06:00
150fa22f35
fop(1): changes casts to calls to .into() 2024-08-24 15:57:55 -06:00
f104598164
mm(1): updates error-handling functions and uncovers issue with error reporting 2024-08-24 15:53:58 -06:00
722679247a
hru(1): uses new sysexits 2024-08-23 14:31:02 -06:00
4db4160019
swab(1): uses new sysexits 2024-08-23 14:23:11 -06:00
928dce0234
fop(1): uses new sysexits 2024-08-23 14:22:11 -06:00
1fffd3df52
Makefile: sets bindgen to output exit codes as u8 2024-08-23 14:21:43 -06:00
a14b8fb9d7
fop(1): adds functions for error handling 2024-08-23 13:35:30 -06:00
dd39aeff02
mm(1): adds ioerr(), usage(), and oser() functions 2024-08-22 00:22:05 -06:00
DTB
8646d5c4ee
STYLE: make rules more granular and consistent, add examples 2024-08-08 02:31:54 -06:00
DTB
6d7173e438
libfileis(3): finish removal 2024-07-18 20:50:12 -06:00
DTB
958f08bd9e
fileis.1: rename from scrut.1 2024-07-18 20:45:14 -06:00
DTB
02b5edae05
fileis(1): feature parity with C scrut(1) 2024-07-18 20:43:39 -06:00
DTB
0819eeb75d
fileis(1): scrap libfileis(3), work on rewriting scrut(1) in Rust 2024-07-18 19:11:22 -06:00
DTB
0f12dcc552
scrut(1): fix ugly opt parsing 2024-07-15 14:28:00 -06:00
DTB
b9c4b49603
scrut(1): use libfileis 2024-07-15 14:13:53 -06:00
DTB
2c4349872c
scrut(1): banish gotos 2024-07-15 13:55:01 -06:00
DTB
59fa3ed3d2
scrut(1): get rid of -h 2024-07-15 13:47:55 -06:00
DTB
71655a8559
libfileis(3): a library for scrutinizing files 2024-07-15 13:47:04 -06:00
60 changed files with 1454 additions and 896 deletions

12
CONDUCT
View File

@ -36,9 +36,9 @@ issues, pull requests, and other endeavors in order to keep yourself and others
from being overwhelmed with responsibility, either from your zeal or your
negligence.
If you notice an issue, open an issue as soon as you can. If you see a neglected
branch, open a pull request or comment on an existing one, if applicable. Be
diligent in your commitment to making this project work.
If you notice an issue, open an issue as soon as you can. If you see a
neglected branch, open a pull request or comment on an existing one, if
applicable. Be diligent in your commitment to making this project work.
6. Patience (Khanti)
@ -81,3 +81,9 @@ more insight.
[0] <https://www.dhammatalks.org/books/#/books/TenPerfections/Section0001.html>
[1] <https://www.fsf.org/news/publication-of-the-fsf-funded-white-papers-on-questions-around-copilot>
--
Copyright © 20242025 Emma Tebibyte <emma@tebibyte.media>
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

@ -99,17 +99,247 @@ notice:
Style
=====
Make sure lines never exceed 80 columns in width when using four-character
indentation steps. This helps contributors with smaller screens, those using
side-by-side editor windows or panes, and those who have no text wrapping in
their editor or terminal.
“Everyone knows that debugging is twice as hard as writing a program in the
first place. So if youre as clever as you can be when you write it, how
will you ever debug it?”
Brian Kernighan, The Elements of Programming Style
For usage text and help messages, do not implement a -h option. Instead, print
usage information when any erroneous option is specified. Follow the NetBSD
style guide for the usage texts output format [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.
Use:
0. A single line for control flow statements short enough to be easily
understood at a glance:
if !(argc < 0) { usage(program_name); }
This applies to C switch statements and cases and Rust match statements, as
well:
switch (value) { /* aligning stuff to make it easier to read is fine */
case possibility: variable = foo; break;
default: variable = NULL; break;
}
1. Switch cases in C and match arms in Rust should start another level of
indentation:
switch (value) {
case possibility:
statement;
break;
default:
statement;
break;
}
match result {
Ok(n) => variable = n,
Err(e) => error = e,
}
2. Braces in control flow where their inclusion is left optional in C:
if (condition) { statement; }
3. Empty lines 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;
4. Compiler options that yield the most useful warnings, such as -Wpedantic in
a lot of C compilers. Fix the warnings, too [0].
5. One more level of indentation and one argument per line when a function
call or statement header is too long to fit on one line:
let usage = format!(
"Usage: {} [-d delimiter] index command [args...]",
argv[0],
);
6. One more level of indentation than the keyword that initiated a multi-line
block.
if (condition) {
statement;
statement;
}
7. The return value of all non-void functions, or explicitly ignore them (like
casting to void in C) [0]:
if ((a = malloc(sizeof char)) == NULL) { /* handle this error */
(void)fprintf(stderr, "oh noes!"); /* explicitly ignore this one */
return EX_OSERR; /* ...because the program is exiting anyway */
}
8. The smallest possible scope for data [0].
9. Comments noting all the symbols and macros used from a C header file, next
to its include macro:
#include <unistd.h> /* close(2), getopt(3), lseek(2), read(2), write(2),
(space-aligned) * optarg, optind, STDIN_FILENO, STDOUT_FILENO */
10. Spaces in control flow statements, after the keyword and before the
opening brace:
for (i = 2; i < argc; ++i) {
11. In Rust, a trailing comma on all arguments or fields that are on their own
lines:
return Err(EvaluationError {
message: format!("{}: Invalid token", i),
code: EX_DATAERR,
})
12. In Rust, place extern statements after use statements that include standard
library crates. Group like statements:
use std::fs::Path;
extern crate strerror;
extern crate sysexits;
use strerror::StrError;
use sysexits::{ EX_OSERR, EX_USAGE };
13. If text is on the same line as a brace, spaces after an opening brace and
before a closing one:
use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
14. Alphabetic sorting, where applicable:
use std::io::{ BufWriter, Read, Write, stderr, stdin, stdout }
15. In Rust, use the to_owned() method on string types (str, OsStr, CStr, etc.)
and the to_string() method on other types.
Avoid:
16. Unbounded loops [0].
17. Function pointers [0].
18. Heap memory allocation [0].
19. Using too much nested logic (within reason).
20. Too many levels of dereferences [0]:
/* do not do this */
for (size_t i = 0; i < sizeof a / sizeof *a; ++i) {
if (a[i].id == MATCH) { a[i].val = 0; }
}
/* do this */
for (struct MadeUp *s = &a[0]; *s != NULL; s = &s[1]) {
if (s->id == MATCH) { s->val = 0; }
}
21. Using C preprocessor macros; the fewer, the better [0].
22. The exit(3p) and std::process::exit() functions; returning from the main
function skips a system call.
Do not use:
23. More than the length of one printed page for a function [0].
24. Recursion, as its complex and can unexpectedly overflow the stack [0].
25. Any functionality not in the POSIX C specification and language features not
in C99.
26. Do-while loops, as theyre unique to C and confusing for casual programmers.
27. Labels and goto statements; use sensible flow control [0].
28. Pointer arithmetic, as it tends to be confusing and unnecessary; use
index-reference patterns like &p[1] instead of p + 1. &p[n] is the address at
p + sizeof p * n, not p + n, like pointer arithmetic suggests.
29. C struct bitfields in unions, to access certain bits of bigger data types,
as its poorly defined in the C standards; use bit arithmetic.
30. C trigraphs.
31. Inclusions in C header files, to prevent multiple file inclusions.
32. C preprocessor variables to prevent multiple inclusions of the same file,
such as:
#ifdef _FILE
#define _FILE
/* file body */
#endif /* ifdef _FILE */
Instead, take the time to ensure other files arent including any files twice.
33. The gets(3p) function from <stdio.h>, as its impossible to prevent buffer
overflows when it's used; use fgets(3p) from <stdio.h>.
34. The scanf(3p) function from <stdio.h> [1].
35. Any functionality not described in the latest POSIX make(1) specification.
36. Macros which panic on failure in Rust (such as the print!() and println!()
macros). Use a function and handle any errors. However, do use the eprintln!()
macro for error messages. Handling an error for writing an error message is
redundant.
37. A -h option for help text. Instead, print usage information when any
erroneous option is specified. See the Usage Text section below.
38. Lines which exceed 80 columns in width when using four-column indentation
steps. This helps contributors with smaller screens, those using side-by-side
editor windows or panes, and those who have no text wrapping in their editor or
terminal.
Usage Text
==========
This section is adapted from the NetBSD style guide [2].
When programs are invoked incorrectly and in the synopsis of manual pages, uasge
text should be provided to the user. The following is the format used by this
project for this purpose:
All optional arguments are to be placed in square brackets (U+005B, U+005D).
Mutually exclusive arguments can be separated by a vertical line (U+007C).
Groups of arguments should be specified in alphabetical order in most cases. The
order of arguments and an example of these rules follows:
0. Options with no option arguments.
1. Options with option arguments. Arguments should be specified inside the same
square brackets as the options.
3. Non-option arguments.
"usage: f [-aDde] [-b b_arg] [-m m_arg] req1 req2 [opt1 [opt2]]\n"
"usage: f [-a | -b] [-c [-de] [-n number]]\n"
If committing a new utility, please include tests and documentation (see
tests/ and docs/) for the new tool.
Committing
==========
@ -117,6 +347,10 @@ Committing
When contributing to Bonsai, please sign your commit with a PGP key and create
the commit with an identity which can be easily contacted.
If committing a new utility, please include tests and documentation (see
tests/ and docs/) for the new tool.
Format commit messages following these guidelines:
$ git commit -m 'tool(1): add feature x'
@ -147,9 +381,16 @@ Commit messages should be written in the present tense.
References
==========
[0] <http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/share/misc/style>
[0] <https://web.eecs.umich.edu/~imarkov/10rules.pdf>
[1] <http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html>
[2] <http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/share/misc/style>
--
This work © 20232024 by Emma Tebibyte is licensed under CC BY-SA 4.0. To view a
copy of this license, visit <http://creativecommons.org/licenses/by-sa/4.0/>
Copyright © 20232024 Emma Tebibyte <emma@tebibyte.media>
Copyright © 2024 DTB <trinity@trinity.moe>
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/>.

428
COPYING.DOC Normal file
View File

@ -0,0 +1,428 @@
Attribution-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.

View File

@ -1,12 +1,12 @@
# Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media>
# Copyright (c) 20232025 Emma Tebibyte <emma@tebibyte.media>
# Copyright (c) 20232024 DTB <trinity@trinity.moe>
# Copyright (c) 2023 Sasha Koshka <sashakoshka@tebibyte.media>
# Copyright (c) 2024 Aaditya Aryal <aryalaadi123@gmail.com>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
# The octal escape \043 is utilized twice in this file as make(1p) will
# interpret a hash in a rule as an inline comment.
@ -42,7 +42,7 @@ BIN = build/bin
default: all test
.PHONY: all
all: dj false fop hru intcmp mm npc peek rpn scrut str strcmp swab true
all: dj false fileis fop hru intcmp mm npc peek rpn str strcmp swab true
# keep build/include until bindgen(1) has stdin support
# https://github.com/rust-lang/rust-bindgen/issues/2703
@ -62,7 +62,12 @@ dist: all docs
install: dist
cp -r $(DESTDIR)/* /
include tests/tests.mk
TESTFILES != ls tests/*.mk
TESTS != printf '%s\n' "$(TESTFILES)" | xargs -n1 basename \
| sed 's/\.mk/_tests/g'
include $(TESTFILES)
.PHONY: test
test: all $(TESTS) /tmp/getopt
@ -95,7 +100,7 @@ build/o/libstrerror.rlib: build src/libstrerror.rs
src/libstrerror.rs
build/o/libsysexits.rlib: build/include/sysexits.h
bindgen --default-macro-constant-type signed --use-core --formatter=none \
bindgen --fit-macro-constant-types --default-macro-constant-type unsigned --use-core --formatter=none \
build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ -
# bandage solution until bindgen(1) gets stdin support
@ -112,6 +117,11 @@ false: build/bin/false
build/bin/false: src/false.c build
$(CC) $(CFLAGS) -o $@ src/false.c
.PHONY: fileis
fileis: build/bin/fileis
build/bin/fileis: src/fileis.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) -o $@ src/fileis.rs
.PHONY: fop
fop: build/bin/fop
build/bin/fop: src/fop.rs build rustlibs
@ -147,11 +157,6 @@ rpn: build/bin/rpn
build/bin/rpn: src/rpn.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) -o $@ src/rpn.rs
.PHONY: scrut
scrut: build/bin/scrut
build/bin/scrut: src/scrut.c build
$(CC) $(CFLAGS) -o $@ src/scrut.c
.PHONY: str
str: build/bin/str
build/bin/str: src/str.c build

9
README
View File

@ -14,11 +14,12 @@ utilities do, but to invent new utilities to perform the same tasks in more
intuitive ways. GNU and BSD extensions are convenient but often unhealthy,
forgetting the purposes of the tools they extend, or building into existing
utilities features that would be more useful as their own tools to be used
anywhere. Other utility sets aim to provide a number of fully-featured
programs to be used individually, Harakit utilities are meant to be easily
composable and work together in pipelines.
anywhere. Other utility sets aim to provide a number of fully-featured programs
to be used individually, Harakit utilities are meant to be easily composable
and work together in pipelines.
See docs/ for more on the specific utilities currently implemented.
See docs/ for more on the specific utilities currently implemented and see
CONTRIBUTING for guidelines for contributions.
Building

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

@ -1,10 +1,10 @@
.\" Copyright (c) 2024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
.\" Copyright (c) 20242025 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 DJ 1 2024-07-14 "Harakit X.X.X"
.TH DJ 1 2025-10-26 "Harakit X.X.X"
.SH NAME
dj \(en disk jockey
.\"
@ -28,45 +28,44 @@ dj
Perform precise read and write operations on files. This utility is useful for
reading and writing binary data to and from disks.
This manual page uses the terms \(lqskip\(rq and \(lqseek\(rq to refer to moving
to a specified byte by index in the input and output of the program
This manual page uses the terms \(lqskip\(rq and \(lqseek\(rq to refer to
moving to a specified byte by index in the input and output of the program
respectively. This language is inherited from the
.BR dd (1p)
utility and used here to decrease ambiguity.
specification in \*(Px and used here to decrease ambiguity.
The offset used when skipping or seeking refers to how many bytes are skipped
or sought. Running
.BR dj (1)
with a skip offset of 1 skips one byte into the input and reads from the second
byte onwards. A programmer may think of a file as a zero-indexed array of
bytes; in this analogy, the offset given is the index of the byte at which to
start reading or writing.
with a skip offset of 1 reads from the second byte onwards. A programmer may
think of a file as a zero-indexed array of bytes; in this analogy, the offset
given is the index of the byte at which to start reading or writing.
.\"
.SH OPTIONS
.IP \fB-i\fP\ \fIfile\fP
Takes a file path as an argument and opens it for use as an input.
.IP \fB-b\fP\ \fIblock_size\fP
Takes a numeric argument as the size in bytes of the input buffer, the default
being 1024.
.IP \fB-s\fP
Takes a numeric argument as the size in bytes of the input buffer. If this
option is not specified, the size is 1024 bytes.
.IP \fB-s\fP \fIoffset\fP
Takes a numeric argument as the index of the byte at which reading will
commence; \(lqskips\(rq that number of bytes. If the standard input is used,
bytes read to this point are discarded.
.IP \fB-o\fP
commence; the program \(lqskips\(rq that number of \fIbytes\fP. If the standard
input is used, bytes read to this point are discarded.
.IP \fB-o\fP \fIfile\fP
Takes a file path as an argument and opens it for use as an output.
.IP \fB-B\fP\ \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.
.IP \fB-S\fP
Takes a numeric argument as the size in bytes of the output buffer. The default
size is 1024. Note that this option only affects the size of output writes and
not the amount of output data itself. See the CAVEATS section.
.IP \fB-S\fP \fIoffset\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,
null characters are printed.
commence; the program \(lqseeks\(rq that number of bytes. If the standard
output is used, null characters are first printed this many times.
.IP \fB-a\fP
Accepts a single literal byte with which the input buffer is padded in the event
of an incomplete read from the input file. If the option argument is empty, the
null byte is used.
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.
@ -78,12 +77,12 @@ Retries failed reads once before exiting.
.\"
.SH STANDARD INPUT
The standard input shall be used as an input if no inputs are specified or if
The standard input shall be used as an input if none are specified or if the
input file is \(lq-\(rq.
.\"
.SH STANDARD OUTPUT
The standard output shall be used as an output if no inputs are specified or if
the output file is \(lq-\(rq.
The standard output shall be used as an output if none are specified or if the
output file is \(lq-\(rq.
.\"
.SH EXAMPLES
@ -135,13 +134,12 @@ invocation.
.\"
.SH DIAGNOSTICS
On a partial or empty read, a diagnostic message is printed. Then, the program
exits unless the
On a partial or empty read, a diagnostic message is printed. Then, unless the
.B -n
option is specified.
option is specified, the program exits.
By default, statistics are printed for input and output to the standard error in
the following format:
By default, statistics are printed for input and output to the standard error
in the following format:
.RS
{records read} {ASCII unit separator} {partial records read}
@ -172,8 +170,8 @@ If
.B -n
is specified along with the
.B -c
option and a count, actual byte output is the product of the count and the input
block size and therefore may be lower than expected. If the
option and a count, actual byte output is the product of the count and the
input block size and therefore may be lower than expected. If the
.B -a
option is specified, this could make written data nonsensical.
.\"
@ -202,12 +200,15 @@ The skipped or sought bytes while processing irregular files, such as streams,
are reported in the diagnostic output, because they were actually read or
written. This is as opposed to bytes skipped while processing regular files,
which are not reported.
Much of this program shares its functionality with
.BR mm (1).
.\"
.SH RATIONALE
This program was based on the
.BR dd (1p)
utility as specified in POSIX. While character conversion may have been the
utility as specified in \*(Px. While character conversion may have been the
original intent of
.BR dd (1p),
it is irrelevant to its modern use. Because of this, this program eschews
@ -215,12 +216,18 @@ character conversion and adds typical option formatting, allowing seeks to be
specified in bytes rather than in blocks, allowing arbitrary bytes as padding,
and printing in a format that\(cqs easy for machines to parse.
.\"
.SH AUTHOR
Written by DTB
.MT trinity@trinity.moe
.ME .
.\"
.SH COPYRIGHT
Copyright \(co 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/agpl.html>.
.\"
.SH SEE ALSO
.BR mm (1)
.BR dd (1p)
.BR lseek (3p)
.BR mm (1)

View File

@ -15,7 +15,7 @@ always be returned.
.\"
.SH RATIONALE
In POSIX.1-2017,
In \*(Px.1-2024,
.BR false (1p)
exists for the construction of control flow and loops based on a failure. This
implementation functions as described in that standard.

View File

@ -1,15 +1,15 @@
.\" Copyright (c) 2024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
.\" Copyright (c) 20242025 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 SCRUT 1 2024-06-06 "Harakit X.X.X"
.TH FILEIS 1 2025-02-24 "Harakit X.X.X"
.SH NAME
scrut \(en scrutinize file properties
fileis \(en scrutinize file properties
.SH SYNOPSIS
scrut
fileis
.RB [ -LSbcdefgkprsuwx ]
.B file...
.\"
@ -61,8 +61,8 @@ error code.
The
.BR test (1p)
utility contains functionality that was broken out into separate programs. Thus,
the scope of this program is narrower than it. Notably, the
utility contains functionality that was broken out into separate programs. \
Thus, the scope of this program is narrower than it. Notably, the
.B -h
option is now invalid and therefore shows usage information instead of being an
alias to the modern
@ -82,5 +82,6 @@ Copyright \(co 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
.\"
.SH SEE ALSO
.BR access (3p),
.BR chown (1p),
.BR lstat (3p),
.BR test (1p)

View File

@ -28,13 +28,14 @@ is an ASCII record separator.
.\"
.SH CAVEATS
Field indices are zero-indexed, which may be unexpected behavior for some users.
Field indices are zero-indexed, which may be unexpected behavior for some
users.
.\"
.SH RATIONALE
With the assumption that tools will output data separated with ASCII field
separators, there is a need for the ability to modify select fields in this data
easily and quickly.
separators, there is a need for the ability to modify select fields in this
data easily and quickly.
The idea for this utility originated in the fact that the GNU
.BR ls (1)

View File

@ -16,8 +16,8 @@ hru
Convert counts to higher units.
Byte counts will be read in the form of whole numbers from the standard input
and be written to the standard output the same number converted to a higher unit
of data as defined by the \fIInternational System of Units\fP.
and be written to the standard output the same number converted to a higher
unit of data as defined by the \fIInternational System of Units\fP.
The program will convert the byte count to the highest unit possible where the
value is greater than one.
@ -33,10 +33,11 @@ and print an error message.
The GNU project\(cqs
.BR ls (1)
implementation contains a human-readable option (\fB-h\fP) that, when specified,
makes the tool print size information in a format more immediately
readable. This functionality is useful not only in this context, so the decision
was made to split it into a new tool. The original functionality from GNU\(cqs
implementation contains a human-readable option (\fB-h\fP) that, when
specified, makes the tool print size information in a format more immediately
readable. This functionality is useful not only in this context, so the
decision was made to split it into a new tool. The original functionality from
GNU\(cqs
.BR ls (1)
can be emulated with
.BR fop (1)

View File

@ -31,9 +31,10 @@ It may help to think of the
.BR -g ,
and
.B -l
options as equivalent to the infix algebraic \(lq=\(rq, \(lq>\(rq, and \(lq<\(rq
operators respectively, with each option putting its symbol between every given
integer. The following example is equivalent to evaluating \(lq1 < 2 < 3\(rq:
options as equivalent to the infix algebraic \(lq=\(rq, \(lq>\(rq, and
\(lq<\(rq operators respectively, with each option putting its symbol between
every given integer. The following example is equivalent to evaluating
\(lq1 < 2 < 3\(rq:
\"
.RS
intcmp -l 1 2 3
@ -75,7 +76,7 @@ this is elegant but unintuitive.
.\"
.SH RATIONALE
The traditional tool for integer comparisons in POSIX and other Unix shells has
The traditional tool for integer comparisons in \*(Px and other Unix shells has
been
.BR test (1).
This tool also handles string comparisons and file scrutiny. These parts of its

View File

@ -1,16 +1,17 @@
.\" Copyright (c) 2024 DTB <trinity@trinity.moe>
.\" Copyright (c) 20242025 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 MM 1 2024-07-14 "Harakit X.X.X"
.TH MM 1 2025-11-01 "Harakit X.X.X"
.SH NAME
mm \(en middleman
.\"
.SH SYNOPSIS
mm
.RB [ -aetu ]
.RB [ -aet ]
.RB [ -i\ input ]
.RB [ -o\ output ]
.\"
@ -21,21 +22,21 @@ Catenate input files and write them to the start of each output file or stream.
.SH OPTIONS
.IP \fB-a\fP
Opens outputs for appending rather than updating.
Opens outputs for appending rather than updating. Implies the
.B -t
option.
.IP \fB-e\fP
Use the standard error as an output.
.IP \fB-t\fP
Causes outputs to be overwritten instead of being truncated.
.IP \fB-u\fP
Ensures neither input or output will be buffered.
.IP \fB-i\fP\ \fIinput\fP
Opens a path as an input. If one or more of the input files is \(lq-\(rq or if
no inputs are specified, the standard input shall be used. If specified as the
last option and if there are trailing arguments to the program, they shall be
appended to the list of files to use as inputs.
.IP \fB-o\fP\ \fIoutput\fP
Opens a path as an output. If one or more of the output files is \(lq-\(rq or if
no outputs are specified and the
Opens a path as an output. If one or more of the output files is \(lq-\(rq or
if no outputs are specified and the
.B -e
option is not specified, the standard output shall be used. If specified as the
last option and if there are trailing arguments to the program, they shall be
@ -43,9 +44,6 @@ appended to the list of files to use as outputs.
.\"
.SH DIAGNOSTICS
If an output cannot be written to, an error occurs; however, exiting will be
deferred until writing to any other specified outputs completes.
When an error is encountered, a diagnostic message is printed and the program
exits with the appropriate
.BR sysexits.h (3)
@ -57,13 +55,35 @@ The
.BR cat (1p)
and
.BR tee (1p)
programs specified in POSIX together provide similar functionality. The
programs specified in \*(Px together provide similar functionality. The
separation of the two sets of functionality into separate APIs seemed
unncessary.
.\"
.SH HISTORY
The \fB-t\fP option was originally the reverse functionality, as the default
behavior was to overwrite outputs. This was deemed counterintuitive and the
default behavior was changed.
This utility originally had a \fB-u\fP option for compatibility with cat(1p),
but this option is almost always default behavior in practice; therefore, it
was dropped both for simplicity and to expand the
.BR dj (1)
utility\(cqs niche.
.\"
.SH AUTHOR
Written by DTB
.MT trinity@trinity.moe
.ME , \
ported to Rust by Emma Tebibyte
.MT emma@tebibyte.media
.ME .
.\"
.SH COPYRIGHT
Copyright \(co 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
Copyright © 2024 DTB, 2024\(en2025 Emma Tebibyte. \
License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/agpl.html>.
.\"
.SH SEE ALSO

View File

@ -45,15 +45,15 @@ The program operates in single-byte chunks regardless of intended encoding.
.\"
.SH RATIONALE
POSIX currently lacks a way to display non-printing characters in the terminal
\*(Px currently lacks a way to display non-printing characters in the terminal
using a standard tool. A popular extension to
.BR cat (1p),
the
.B -v
option, is the bandage solution GNU and other software suites use.
This functionality is included in a separate tool because its usefulness extends
beyond that of
This functionality is included in a separate tool because its usefulness
extends beyond that of
.BR cat (1p).
.\"
.SH AUTHOR

View File

@ -47,9 +47,9 @@ 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.
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.
@ -80,10 +80,10 @@ $ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2
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
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:

View File

@ -50,17 +50,17 @@ with the
.I IEEE Standard for Floating Point Arithmetic
(\fIIEEE 754\fP), floating-point arithmetic has rounding errors. This is
somewhat curbed by using the machine epsilon as provided by the Rust standard
library to which numbers are rounded. Because of this, variation is expected in
the number of decimal places the program can handle based on the platform and
hardware of any given machine.
library, to which numbers are rounded. Because of this, variation is expected
in the number of decimal places the program can handle based on the platform
and hardware of any given machine.
.\"
.SH RATIONALE
An infix notation calculation utility,
.BR bc (1p),
is included in the POSIX standard, but does not accept expressions as arguments;
in scripts, any predefined, non-interactive input must be piped into the
program. A
is included in the \*(Px standard, but does not accept expressions as
arguments; in scripts, any predefined, non-interactive input must be piped into
the program. A
.BR dc (1)
pre-dates the standardized
.BR bc (1p),
@ -76,7 +76,7 @@ Written by Emma Tebibyte
.\"
.SH COPYRIGHT
Copyright (c) 2024 Emma Tebibyte. License AGPLv3+: GNU AGPL version 3 or later
Copyright \(co 2024 Emma Tebibyte. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/agpl.html>.
.\"
.SH SEE ALSO

View File

@ -17,14 +17,15 @@ str
Test the character types of string arguments.
The tests in this program are equivalent to the functions with the same names in
The tests in this program are equivalent to the functions with the same names
in
.BR ctype.h (0p)
and are the methods by which string arguments are tested.
.\"
.SH DIAGNOSTICS
If all tests pass, the program will exit successfully. If any of the tests fail,
the program will exit unsuccessfully with an error code of 1.
If all tests pass, the program will exit successfully. If any of the tests
fail, the program will exit unsuccessfully with an error code of 1.
When invoked incorrectly, a debug message will be printed and the program will
exit with the appropriate
@ -33,8 +34,8 @@ error code.
.\"
.SH CAVEATS
None of an empty string\(cqs contents pass any of the tests, so the program will
exit unsuccessfully if one is specified.
None of an empty string\(cqs contents pass any of the tests, so the program
will exit unsuccessfully if one is specified.
There\(cqs no way of knowing which argument failed the test without re-testing
arguments individually.

View File

@ -47,13 +47,13 @@ visual similarity and not byte similarity.
.\"
.SH RATIONALE
The traditional tool for string comparisons in POSIX and other Unix shells has
The traditional tool for string comparisons in \*(Px and other Unix shells has
been
.BR test (1).
This tool also handles integer comparisons and file scrutiny. These parts of its
functionality have been broken out into multiple utilities.
This tool also handles integer comparisons and file scrutiny. These parts of
its functionality have been broken out into multiple utilities.
This program\(cqs functionality may be performed on a POSIX-compliant system
This program\(cqs functionality may be performed on a \*(Px-compliant system
with
.BR test (1p).
.\"

View File

@ -15,7 +15,7 @@ always be returned.
.\"
.SH RATIONALE
In \fIPOSIX.1-2017\fP,
In \fI\*(Px.1-2024\fP,
.BR true (1p)
exists for the construction of control flow and loops based on a success. This
implementation functions as described in that standard.

View File

@ -2,5 +2,5 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
OSLIB = build/o/libopenbsd.rlib
RUSTFLAGS += --extern openbsd=$(OSLIB)

View File

@ -3,15 +3,15 @@
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* This program is 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.
* 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/.
@ -28,7 +28,8 @@
#include <unistd.h> /* close(2), getopt(3), lseek(2), read(2), write(2),
* pledge(2), unveil(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 */
char *program_name = "dj";
@ -284,7 +285,8 @@ int main(int argc, char *argv[]) {
}
/* 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;
}
}

View File

@ -13,7 +13,7 @@
int main(void) {
#ifdef __OpenBSD__
pledge(NULL, NULL);
(void)pledge("stdio", "");
#endif
return 1;
}

91
src/fileis.rs Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2024 DTB <trinity@trinity.moe>
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
* for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
use std::{
env::args,
fs::metadata,
os::unix::fs::{ FileTypeExt, MetadataExt },
process::ExitCode,
};
extern crate getopt;
extern crate strerror;
extern crate sysexits;
use getopt::GetOpt;
use strerror::StrError;
use sysexits::EX_USAGE;
const OPTS: &str = "bcdefgkprsuwxLS";
fn usage(argv0: &str) -> ExitCode {
eprintln!("Usage: {} [-{}] file...", argv0, OPTS);
ExitCode::from(EX_USAGE)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<_>>();
let mut sel = String::with_capacity(OPTS.len()); // selected options
let mut optind: usize = 1; // argv[0]
while let Some(opt) = argv.getopt(OPTS) {
if let Ok(optchr) = opt.opt() { sel.push_str(optchr); }
else { return usage(&argv[0]); }
optind = opt.ind();
}
if optind == argv.len() { return usage(&argv[0]); }
for arg in argv.iter().skip(optind) {
let fmeta = match metadata(arg) {
Ok(m) => m,
Err(e) => { // no perms or nonexistent
eprintln!("{}: {}: {}", argv[0], arg, e.strerror());
return ExitCode::FAILURE;
},
};
let fmode = fmeta.mode();
let ftype = fmeta.file_type();
for selection in sel.chars() { // run all selected tests
match selection {
'b' if ftype.is_block_device() => (),
'c' if ftype.is_char_device() => (),
'e' => (), // exists or metadata would have errored
'd' if fmeta.is_dir() => (),
'f' if fmeta.is_file() => (),
'g' if fmode & 0o2000 /* S_ISGID */ != 0 => (), // setgid
'k' if fmode & 0o1000 /* S_ISVTX */ != 0 => (), // setvtx
'p' if ftype.is_fifo() => (),
'r' if fmode & 0o0400 /* S_IRUSR */ != 0 => (), // read access
'u' if fmode & 0o4000 /* S_ISUID */ != 0 => (), // setuid
'w' if fmode & 0o0200 /* S_IWUSR */ != 0 => (), // write access
'x' if fmode & 0o0100 /* S_IXUSR */ != 0 => (), // exec access
'L' if fmeta.is_symlink() => (),
'S' if ftype.is_socket() => (),
_ => { return ExitCode::FAILURE; }
}
}
}
ExitCode::SUCCESS
}

View File

@ -2,15 +2,15 @@
* 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 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.
* 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/.
@ -18,8 +18,8 @@
use std::{
env::args,
io::{ Read, stdin, stdout, Write },
process::{ Command, exit, Stdio },
io::{ Error, Read, Write, stdin, stdout },
process::{ Command, ExitCode, Stdio, exit },
};
extern crate getopt;
@ -32,25 +32,36 @@ use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
fn main() {
fn err(argv0: &String, e: Error) {
eprintln!("{}: {}", argv0, e.strerror());
}
fn usage(argv0: &String) -> u8 {
eprintln!("Usage: {} [-d delimiter] index command [args...]", argv0);
EX_USAGE
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
let mut d = '\u{1E}'.to_string(); /* ASCII record separator */
let mut optind = 1;
#[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio proc exec");
let promises = Promises::new("exec proc stdio unveil");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_OSERR);
err(&argv[0], e);
return ExitCode::from(EX_OSERR);
}
if let Err(e) = unveil(None, None) {
err(&argv[0], e);
return ExitCode::from(EX_OSERR);
}
}
let usage = format!(
"Usage: {} [-d delimiter] index command [args...]",
argv[0],
);
if argv.len() == 1 { return ExitCode::from(usage(&argv[0])); }
while let Some(opt) = argv.getopt("d:") {
match opt.opt() {
@ -60,8 +71,7 @@ fn main() {
optind = opt.ind();
},
_ => {
eprintln!("{}", usage);
exit(EX_USAGE);
return ExitCode::from(usage(&argv[0]));
}
};
}
@ -69,7 +79,7 @@ fn main() {
/* 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);
exit(EX_DATAERR.into());
});
/* index of the argv[0] for the operator command */
@ -77,15 +87,14 @@ fn main() {
/* argv[0] of the operator command */
let operator = argv.get(command_arg).unwrap_or_else(|| {
eprintln!("{}", usage);
exit(EX_USAGE);
exit(usage(&argv[0]).into());
});
/* 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);
err(&argv[0], e);
exit(EX_IOERR.into());
};
/* split the buffer by the delimiter (by default, '\u{1E}') */
@ -105,18 +114,14 @@ fn main() {
.stdout(Stdio::piped()) /* piped stdout to handle output ourselves */
.spawn()
.unwrap_or_else( |e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror());
exit(EX_UNAVAILABLE);
err(&argv[0], e);
exit(EX_UNAVAILABLE.into());
});
/* get field we want to pipe into spawned program */
let field = fields.get(index).unwrap_or_else(|| {
eprintln!(
"{}: {}: No such index in input",
argv[0],
index.to_string(),
);
exit(EX_DATAERR);
eprintln!("{}: {}: no such index in input", argv[0], index);
exit(EX_DATAERR.into());
});
/* get the stdin of the newly spawned program and feed it the field val */
@ -126,8 +131,8 @@ fn main() {
}
let output = spawned.wait_with_output().unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror());
exit(EX_IOERR);
err(&argv[0], e);
exit(EX_IOERR.into());
});
/* get the output with which the original field will be replaced */
@ -136,15 +141,15 @@ fn main() {
/* 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 */
/* restore replacement to original command output if popped char was
* not a newline */
replace = output.stdout;
}
/* convert the output of the program to UTF-8 */
let new_field = String::from_utf8(replace).unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e);
exit(EX_IOERR);
eprintln!("{}: {}", argv[0], e);
exit(EX_IOERR.into());
});
/* store the new field in the old fields vector */
@ -154,7 +159,9 @@ fn main() {
stdout().write_all(
fields.join(&d.to_string()).as_bytes()
).unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
err(&argv[0], e);
exit(EX_IOERR.into());
});
ExitCode::SUCCESS
}

View File

@ -2,15 +2,15 @@
* 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 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.
* 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/.
@ -19,7 +19,7 @@
use std::{
cmp::Ordering,
env::args,
io::{ stdin, stdout, Write },
io::{ Write, stdin, stdout },
process::{ ExitCode, exit },
};
@ -31,7 +31,7 @@ use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
/* list of SI prefixes */
const LIST: [(u32, &str); 10] = [
@ -47,6 +47,16 @@ const LIST: [(u32, &str); 10] = [
(30, "Q"), /* quetta */
];
fn err(argv0: &String, message: String, code: Option<u8>) -> ExitCode {
eprintln!("{}: {}", argv0, message);
ExitCode::from(code.unwrap_or(1 /* unknown error */))
}
fn usage(argv0: &String) -> ExitCode {
eprintln!("Usage: {}", argv0);
ExitCode::from(EX_USAGE)
}
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, ""));
@ -81,53 +91,48 @@ fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
if let Some(_) = argv.get(1) {
eprintln!("Usage: {}", argv[0]);
return ExitCode::from(EX_USAGE as u8);
}
if let Some(_) = argv.get(1) { return usage(&argv[0]); }
#[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
return err(&argv[0], e.strerror(), Some(EX_OSERR));
}
if let Err(e) = unveil(None, None) {
return err(&argv[0], e.strerror(), Some(EX_OSERR));
}
}
let mut buf = String::new();
while let Ok(_) = stdin().read_line(&mut buf) {
if buf.is_empty() { return ExitCode::SUCCESS; }
if let Err(e) = stdin().read_line(&mut buf) {
return err(&argv[0], e.strerror(), Some(EX_IOERR));
}
let n: u128 = match buf.trim().parse() {
Ok(f) => {
buf.clear();
f
},
Err(err) => {
eprintln!("{}: {}", argv[0], err);
return ExitCode::from(EX_DATAERR as u8);
},
};
if buf.is_empty() { return ExitCode::SUCCESS; }
let (number, prefix) = match convert(n) {
Ok(x) => x,
Err(err) => {
eprintln!("{}: {}", argv[0], err);
return ExitCode::from(EX_SOFTWARE as u8);
},
};
let n: u128 = match buf.trim().parse() {
Ok(f) => {
buf.clear();
f
},
Err(e) => return err(&argv[0], e.to_string(), Some(EX_DATAERR)),
};
let si_prefix = format!("{}B", prefix.1);
let (number, prefix) = convert(n).unwrap_or_else(|e| {
let _ = err(&argv[0], e.to_string(), None);
exit(EX_SOFTWARE.into());
});
/* round output number to one decimal place */
let out = ((number * 10.0).round() / 10.0).to_string();
let si_prefix = prefix.1.to_owned() + "B";
stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes())
.unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
});
/* round output number to one decimal place */
let rounded = (number * 10.0).round() / 10.0;
let out = rounded.to_string() + " " + &si_prefix + &'\n'.to_string();
if let Err(e) = stdout().write_all(out.as_bytes()) {
return err(&argv[0], e.strerror(), Some(EX_IOERR));
}
ExitCode::SUCCESS

View File

@ -3,15 +3,15 @@
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* This program is 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.
* 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/.
@ -26,27 +26,35 @@ extern crate getopt;
extern crate sysexits;
use getopt::GetOpt;
use sysexits::EX_USAGE;
use sysexits::{ EX_DATAERR, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] extern crate strerror;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
#[cfg(target_os="openbsd")] use strerror::StrError;
fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} [-egl] integer integer...", s);
ExitCode::from(EX_USAGE as u8)
fn err(argv0: &String, e: String, code: u8) -> ExitCode {
eprintln!("{}: {}", argv0, e);
ExitCode::from(code)
}
fn usage(argv0: &String) -> ExitCode {
eprintln!("Usage: {} [-egl] integer integer...", argv0);
ExitCode::from(EX_USAGE)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
#[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
return err(&argv[0], e.strerror(), EX_OSERR);
}
if let Err(e) = unveil(None, None) {
return err(&argv[0], e.strerror(), EX_OSERR);
}
}
@ -78,8 +86,8 @@ fn main() -> ExitCode {
match arg.parse::<usize>() { /* parse current operand */
Ok(n) => currn = n,
Err(e) => {
eprintln!("{}: {}: {}", &argv[0], arg, e);
return ExitCode::from(EX_USAGE as u8);
let error = arg.to_owned() + ": " + &e.to_string();
return err(&argv[0], error, EX_DATAERR);
}
}

View File

@ -2,15 +2,15 @@
* 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 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.
* 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/.
@ -107,8 +107,8 @@ impl GetOpt for Vec<String> {
*
* A <question-mark> ('?') shall be returned if getopt()
* encounters an option character not in optstring or detects a
* missing argument and the first character of optstring was not
* a <colon> (':').
* missing argument and the first character of optstring was
* not a <colon> (':').
*
* Otherwise, getopt() shall return -1 when all command line
* options are parsed. */

View File

@ -2,15 +2,15 @@
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* This program is 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.
* 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/.
@ -43,6 +43,10 @@ impl Promises {
}
}
impl Default for Promises {
fn default() -> Self { Promises::new("") }
}
pub fn pledge(
promises: Option<Promises>, execpromises: Option<Promises>
) -> Result<(), Error> {
@ -65,14 +69,12 @@ pub fn pledge(
pub struct UnveilPerms(CString);
impl UnveilPerms {
pub fn new(permissions: Vec<char>) -> Self {
if permissions.is_empty() {
return UnveilPerms(CString::new("").unwrap());
}
UnveilPerms(
CString::new(permissions.iter().collect::<String>()).unwrap()
)
pub fn new<T: IntoIterator<Item = char>>(permissions: T) -> Self {
let perms = CString::new(
permissions.into_iter().collect::<String>()
).unwrap();
UnveilPerms(perms)
}
}
@ -83,9 +85,9 @@ pub fn unveil(
let path_c = path.map(CString::new).map(Result::unwrap);
let arg1 = path_c.map(|p| p.into_raw() as *const c_char).unwrap_or(null());
let arg2 = permissions
.map(|p| p.0.into_raw() as *const c_char)
.unwrap_or(null());
let arg2 = permissions.map(|p| {
p.0.into_raw() as *const c_char
}).unwrap_or(null());
unsafe {
match openbsd::unveil(arg1, arg2) {

View File

@ -4,7 +4,8 @@
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any warranty.
* this notice are preserved. This file is offered as-is, without any
* warranty.
*/
use std::ffi::{ c_int, c_char, CStr };

114
src/mm.rs
View File

@ -1,17 +1,17 @@
/*
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* Copyright (c) 20242025 Emma Tebibyte <emma@tebibyte.media>
* Copyright (c) 2024 DTB <trinity@trinity.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* This program is 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.
* 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/.
@ -20,9 +20,8 @@
use std::{
env::args,
fs::File,
io::{ stdin, stdout, stderr, BufWriter, Read, Write },
os::fd::{ AsRawFd, FromRawFd },
process::{ exit, ExitCode },
io::{ Error, Read, Write, stderr, stdin, stdout },
process::{ ExitCode, exit},
};
extern crate getopt;
@ -47,24 +46,31 @@ use ArgMode::*;
enum ArgMode { In, Out }
fn err(argv0: &String, e: Error, code: Option<u8>) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(code.unwrap_or(1 /* unknown error */))
}
fn usage(argv0: &String) -> ExitCode {
eprintln!("Usage: {} [-aet] [-i input] [-o output]", argv0);
ExitCode::from(EX_USAGE)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<_>>();
let usage = format!("Usage: {} [-aetu] [-i input] [-o output]", argv[0]);
#[cfg(target_os="openbsd")] {
let promises = Promises::new("cpath rpath stdio unveil wpath");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
return err(&argv[0], e, Some(EX_OSERR));
}
}
let mut a = false; /* append to the file */
let mut e = false; /* use stderr as an output */
let mut t = true; /* do not truncate the file before writing */
let mut u = false; /* unbuffer i/o */
let mut ins = Vec::new(); /* initial input file path vector */
let mut outs = Vec::new(); /* initial output file path vector */
let mut ins = Vec::new(); /* initial inputs */
let mut outs = Vec::new(); /* initial outputs */
let mut mode: Option<ArgMode> = None; /* mode set by last-used option */
let mut optind = 0;
@ -72,7 +78,6 @@ fn main() -> ExitCode {
match opt.opt() {
Ok("a") => a = true,
Ok("e") => e = true,
Ok("u") => u = true,
Ok("t") => t = false,
Ok("i") => { /* add inputs */
let input = opt.arg().unwrap();
@ -85,8 +90,7 @@ fn main() -> ExitCode {
mode = Some(Out); /* latest argument == -o */
},
Err(_) | Ok(_) => {
eprintln!("{}", usage);
return ExitCode::from(EX_USAGE as u8);
return usage(&argv[0]);
},
};
@ -108,32 +112,28 @@ fn main() -> ExitCode {
#[cfg(target_os="openbsd")] {
for input in &ins {
let perms = UnveilPerms::new(vec!['r']);
let perms = UnveilPerms::new(['r']);
if let Err(e) = unveil(Some(&input), Some(perms)) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
return err(&argv[0], e, Some(EX_OSERR));
}
}
for output in &outs {
let perms = UnveilPerms::new(vec!['c', 'w']);
let perms = UnveilPerms::new(['c', 'w']);
if let Err(e) = unveil(Some(&output), Some(perms)) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
return err(&argv[0], e, Some(EX_OSERR));
}
}
if let Err(e) = unveil(None, None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
return err(&argv[0], e, Some(EX_OSERR));
}
}
if ins.is_empty() && outs.is_empty() && argv.len() > optind {
eprintln!("Usage: {}", usage);
return ExitCode::from(EX_USAGE as u8);
if ins.is_empty() && outs.is_empty() && (argv.len() - 1) > optind {
return usage(&argv[0]);
}
/* use stdin if no inputs are specified */
@ -147,29 +147,28 @@ fn main() -> ExitCode {
/* if a file is “-”, it is stdin */
if *file == "-" {
/* portable way to access stdin as a file */
return unsafe { File::from_raw_fd(stdin().as_raw_fd()) };
return Box::new(stdin().lock()) as Box<dyn Read>;
}
match File::open(file) {
Ok(f) => f,
Ok(f) => Box::new(f) as Box<dyn Read>,
Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, e.strerror());
exit(EX_IOERR);
let _ = err(&(argv[0].clone() + ": " + file), e, None);
exit(EX_IOERR.into());
},
}
}).collect::<Vec<_>>();
}).collect::<Vec<Box<dyn Read>>>();
/* map all path strings to files */
let mut outputs = outs.iter().map(|file| {
/* of a file is “-”, it is stdout */
if *file == "-" {
/* portable way to access stdout as a file */
return unsafe { File::from_raw_fd(stdout().as_raw_fd()) };
return Box::new(stdout().lock()) as Box<dyn Write>;
}
let options = File::options()
/* dont truncate if -t is specified, append if -a is specified */
.truncate(t)
.truncate(!a && t)
.append(a)
/* enable the ability to create and write to files */
.create(true)
@ -178,49 +177,30 @@ fn main() -> ExitCode {
.open(file);
match options {
Ok(f) => return f,
Ok(f) => return Box::new(f) as Box<dyn Write>,
Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, e.strerror());
exit(EX_IOERR);
let _ = err(&(argv[0].clone() + ": " + file), e, None);
exit(EX_IOERR.into());
},
};
}).collect::<Vec<_>>();
}).collect::<Vec<Box<dyn Write>>>();
/* if -e is specified, use stderr */
if e {
/* portable way to access stderr as a file */
outputs.push(unsafe { File::from_raw_fd(stderr().as_raw_fd()) });
outputs.push(Box::new(stderr().lock()) as Box<dyn Write>);
}
let mut outputs = outputs.iter().map(|o| {
if u {
/* unbuffered writing through a buffer of capacity 0 */
BufWriter::with_capacity(0, o)
} else {
/* theoretically buffered writing */
BufWriter::new(o)
}
}).collect::<Vec<_>>();
for file in inputs {
for byte in file.bytes().map(|b| {
b.unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
let _ = err(&argv[0], e, None);
exit(EX_IOERR.into());
})
}) {
for out in &mut outputs {
if let Err(e) = out.write(&[byte]) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_IOERR as u8);
}
if u {
/* immediately flush the output for -u */
if let Err(e) = out.flush() {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_IOERR as u8);
}
return err(&argv[0], e, Some(EX_IOERR));
}
}
}

View File

@ -3,15 +3,15 @@
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* This program is 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.
* 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/.
@ -20,7 +20,7 @@
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), perror(3), putc(3), stdin,
* stdout, EOF */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */
#include <unistd.h> /* pledge(2), getopt(3) */
#include <unistd.h> /* NULL, getopt(3), pledge(2), unveil(2) */
char *program_name = "npc";
@ -44,7 +44,7 @@ int main(int argc, char *argv[]) {
char showtab = 0; /* prints tab characters in caret notation */
#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) {
if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL)) {
perror(argv[0] == NULL ? program_name : argv[0]);
return EX_OSERR;
}

View File

@ -2,15 +2,15 @@
* 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 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.
* 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/.

View File

@ -1,22 +1,22 @@
/*
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* Copyright (c) 20242025 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 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.
* 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/.
*
* This file incorporates work covered by the following copyright and permission
* notice:
* This file incorporates work covered by the following copyright and
* permission notice:
*
* MIT License
*
@ -35,32 +35,32 @@
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
use std::{
collections::VecDeque,
env::args,
fmt::{ self, Display, Formatter },
io::stdin,
io::{ Error, Write, stdin, stdout },
process::ExitCode,
};
use CalcType::*;
extern crate strerror;
extern crate sysexits;
use sysexits::EX_DATAERR;
use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate strerror;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use strerror::StrError;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
#[derive(Clone, PartialEq, PartialOrd, Debug)]
/* enum CalcType is a type containing operations used in the calculator */
@ -120,12 +120,46 @@ impl Display for CalcType {
#[derive(Debug, Clone)]
struct EvaluationError {
message: String,
code: i32,
code: u8,
}
/* 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;
impl StrError for EvaluationError {
fn strerror(&self) -> String {
self.message.clone()
}
}
fn err<T: StrError>(argv0: &String, e: &T, code: Option<u8>) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(code.unwrap_or(1 /* unknown error */))
}
fn operate(
mut stack: VecDeque<f64>,
op: CalcType,
) -> Result<VecDeque<f64>, EvaluationError> {
let vals = (stack.pop_back(), stack.pop_back());
if let (Some(x), Some(y)) = vals {
match op {
Add => stack.push_back(y + x),
Subtract => stack.push_back(y - x),
Multiply => stack.push_back(y * x),
Divide => stack.push_back(y / x),
Power => stack.push_back(y.powf(x)),
Floor => stack.push_back((y / x).floor()),
Modulo => stack.push_back(y % x),
_ => {},
};
} else {
return Err(EvaluationError {
message: format!("{}: unexpected operation", op),
code: EX_DATAERR,
})
}
Ok(stack)
}
fn eval(
input: &str,
@ -145,6 +179,7 @@ fn eval(
.rev()
.map(|t| CalcType::from(t))
.collect();
let mut ops: VecDeque<CalcType> = VecDeque::new();
while let Some(n) = toks.pop_back() {
@ -152,36 +187,15 @@ fn eval(
Val(v) => stack.push_back(v),
Invalid(i) => {
return Err(EvaluationError {
message: format!("{}: Invalid token", i),
message: format!("{}: invalid token", i),
code: EX_DATAERR,
})
},
op => {
ops.push_back(op.clone());
oper = true;
let vals = (
stack.pop_back(),
stack.pop_back(),
);
if let (Some(x), Some(y)) = vals {
match op {
Add => stack.push_back(y + x),
Subtract => stack.push_back(y - x),
Multiply => stack.push_back(y * x),
Divide => stack.push_back(y / x),
Power => stack.push_back(y.powf(x)),
Floor => stack.push_back((y / x).floor()),
Modulo => stack.push_back(y % x),
_ => {},
};
} else {
return Err(EvaluationError {
message: format!("{}: Unexpected operation", op),
code: EX_DATAERR,
})
}
oper = true; /* this is an operation */
return operate(stack, op).map(|s| (s, oper));
},
};
}
@ -190,51 +204,46 @@ fn eval(
}
/* Round a float to the given precision level */
fn round_precise(value: &f64, precision: usize) -> f64 {
let multiplier = 10_f64.powi(precision as i32);
fn round_precise(value: &f64) -> f64 {
/* Set floating-point precision for correcting rounding errors based on
* machine epsilon */
let precision = (-f64::EPSILON.log10()).floor() as i32;
let multiplier = 10_f64.powi(precision);
(value * multiplier).round() / multiplier
}
/* print the stack and let the caller know if evaluation should continue */
fn unstack(stack: VecDeque<f64>, op: bool) -> Result<bool, Error> {
if let Some(val) = stack.iter().last() {
if !op { return Ok(true); }
let out = round_precise(val).to_string() + &'\n'.to_string();
return stdout().write_all(out.as_bytes()).map(|_| true);
} else {
return Ok(false);
}
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
#[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
return err(&argv[0], &e, Some(EX_OSERR));
}
if let Err(e) = unveil(None, None) {
return err(&argv[0], &e, Some(EX_OSERR));
}
}
let mut stack = VecDeque::new();
let mut buf = String::new();
/* 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 */
while let Ok(_) = stdin().read_line(&mut buf) {
match eval(&buf.trim(), stack) {
Ok(s) => {
buf.clear();
stack = s.0.clone();
let val = match stack.iter().last() {
Some(v) => v,
None => break,
};
if s.1 == false { continue; }
println!("{}", round_precise(val, precision).to_string());
},
Err(err) => {
eprintln!("{}: {}", argv[0], err.message);
return ExitCode::from(err.code as u8);
},
};
}
} else { /* read from argv */
if argv.get(1).is_some() { /* read expressions from argv */
/* join argv into an owned String joined by spaces minus argv[0] */
let input = argv
.iter()
@ -245,20 +254,44 @@ fn main() -> ExitCode {
match eval(&input, stack) {
Ok(s) => {
stack = s.0.clone();
/* we can ignore the return value of unstack() because we are
* not continually evaluating from stdin */
if let Err(e) = unstack(s.0.clone(), s.1.clone()) {
return err(&argv[0], &e, Some(EX_IOERR));
}
let val = match stack.iter().last() {
Some(v) => v,
None => return ExitCode::SUCCESS,
};
println!("{}", round_precise(val, precision).to_string())
return ExitCode::SUCCESS;
},
Err(err) => {
eprintln!("{}: {}", argv[0], err.message);
return ExitCode::from(err.code as u8);
Err(e) => {
return err(&argv[0], &e, Some(e.code));
},
};
}
/* else, read from stdin */
loop { /* take input until EOF */
if let Err(e) = stdin().read_line(&mut buf) {
return err(&argv[0], &e, Some(EX_IOERR));
}
match eval(&buf.trim(), stack) {
Ok(s) => {
buf.clear();
stack = s.0.clone(); /* out with the old, in with the new */
match unstack(s.0, s.1) {
Ok(b) if b => continue,
Ok(_) => break,
Err(e) => {
return err(&argv[0], &e, Some(EX_IOERR))
},
};
},
Err(e) => {
return err(&argv[0], &e, Some(e.code));
},
};
}
ExitCode::SUCCESS
}

View File

@ -1,116 +0,0 @@
/*
* Copyright (c) 20232024 DTB <trinity@trinity.moe>
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <assert.h> /* assert(3) */
#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_OSERR, EX_USAGE */
#include <unistd.h> /* access(3), getopt(3), pledge(2), unveil(2), 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"
/* this is an array so main:sel's size can be known at compile time */
static char opts[] = OPTS;
static int
usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s [-" OPTS "] file...\n", argv0);
return EX_USAGE;
}
int main(int argc, char *argv[]) {
char sel[(sizeof opts) / (sizeof *opts)];
program_name = argv[0] == NULL ? program_name : argv[0];
#ifdef __OpenBSD__
if (pledge("rpath stdio unveil", NULL) == -1) {
perror(program_name);
return EX_OSERR;
}
#endif
if (argc < 2) { return usage(program_name); }
{ /* 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 {
assert(p - opts < sizeof sel / sizeof *sel); /* bounds check */
sel[p - opts] = 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 = &argv[1]) {
struct stat buf;
#ifdef __OpenBSD__
if (unveil(*argv, "r") == -1) {
perror(program_name);
return EX_OSERR;
}
#endif
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; }
}
}
return EXIT_SUCCESS;
}

View File

@ -3,15 +3,15 @@
* Copyright (c) 2023 Marceline Cramer <mars@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 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.
* 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/.
@ -25,7 +25,7 @@
#include <sysexits.h> /* EX_OSERR, EX_USAGE */
#ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */
# include <unistd.h> /* pledge(2), unveil(2) */
#endif
char *program_name = "str";
@ -62,7 +62,7 @@ int main(int argc, char *argv[]) {
program_name = argv[0] == NULL ? program_name : argv[0];
#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) {
if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL) == -1) {
perror(program_name);
return EX_OSERR;
}

View File

@ -3,24 +3,25 @@
* 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 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.
* 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), perror(3), stderr */
#include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */
#ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */
# include <unistd.h> /* pledge(2), unveil(2) */
#endif
char *program_name = "strcmp";
@ -29,7 +30,7 @@ int main(int argc, char *argv[]) {
unsigned int i;
#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) {
if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL) == -1) {
perror(argv[0] == NULL ? program_name : argv[0]);
return EX_OSERR;

View File

@ -3,15 +3,15 @@
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* This program is 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.
* 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/.
@ -33,31 +33,29 @@ use sysexits::{ EX_IOERR, EX_OK, EX_OSERR, EX_USAGE };
use strerror::StrError;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
fn oserr(argv0: &str, e: Error) -> ExitCode {
fn err(argv0: &String, e: Error, code: u8) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_OSERR as u8)
ExitCode::from(code)
}
fn ioerr(argv0: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_IOERR as u8)
}
fn usage(s: &str) -> ExitCode {
fn usage(s: &String) -> ExitCode {
eprintln!("Usage: {} [-w word_size]", s);
ExitCode::from(EX_USAGE as u8)
ExitCode::from(EX_USAGE)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
#[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
return oserr(&argv[0], e);
let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
return err(&argv[0], e, EX_OSERR);
}
if let Err(e) = unveil(None, None) {
return err(&argv[0], e, EX_OSERR);
}
}
@ -88,22 +86,22 @@ fn main() -> ExitCode {
loop {
match input.read(&mut buf) {
Ok(0) => break ExitCode::from(EX_OK as u8), // read nothing; bye
Ok(0) => break ExitCode::from(EX_OK), // read nothing; bye
Ok(v) if v == wordsize => { // read full block; swab
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 err(&argv[0], e, EX_IOERR);
}
},
Ok(v) => { // partial read; partially write
if let Err(e) = output.write(&buf[..v]) {
break ioerr(&argv[0], e);
break err(&argv[0], e, EX_IOERR);
}
},
Err(e) => break oserr(&argv[0], e)
Err(e) => break err(&argv[0], e, EX_OSERR)
}
}
}

View File

@ -13,6 +13,6 @@
int main(void) {
#ifdef __OpenBSD__
pledge(NULL, NULL);
(void)pledge("stdio", "");
#endif
}

View File

@ -1,24 +1,13 @@
The testing suite contains two trees: the Bonsai tree and the POSIX tree:
Tests
=====
.
├── README
├── bonsai/
│   ├── dj.mk
│   ├── false.mk
│   ├── fop.mk
│   └── ...
├── posix/
└── tests.mk
The Harakit testing suite verifies the functionality of Harakit utilities
and checks for regressions and other issues relating to compliance to our
standards of practice.
The Bonsai tree tests the functionality of Harakit utilities for regressions and
other issues relating to compliance to our standards of practice.
The POSIX tests are currently a work-in-progress. Their status in this
repository is uncertain.
Both sets of tests also inherit the environment set by the top-level Makefile,
which sets the BIN variable to the build/bin directory at the root of the
project; therefore, each binary is located at $(BIN)/tool for idiomatic access.
Tests inherit the environment set by the top-level Makefile, which sets the BIN
variable to the build/bin directory at the root of the project; therefore, each
binary is located at $(BIN)/tool for idiomatic access.
Each test contains a set of PHONY targets which are prefixed with the name of
the tool being tested and an underscore. The first target is tests, which
@ -27,10 +16,10 @@ included in the top Makefile, so they can be called from the root of the
repository. This also means that BIN can be set manually so that tests can be
run using make(1) inside of the tests directory:
$ make -f tests.mk BIN=../build/bin dj_tests
$ make -f dj.mk BIN=../build/bin dj_tests
--
Copyright © 2024 Emma Tebibyte <emma@tebibyte.media>
Copyright © 20242025 Emma Tebibyte <emma@tebibyte.media>
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

@ -1,34 +0,0 @@
# Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
.PHONY: mm_tests
mm_tests: mm_args mm_help mm_stderr mm_remaining
.PHONY: mm_none
mm_none: $(BIN)/mm
test "$$(printf 'meow\n' | $(BIN)/mm)" = meow
.PHONY: mm_args
# mm(1) will error if positional arguments are given without -i or -o
mm_args: $(BIN)/mm
! $(BIN)/mm argument
.PHONY: mm_help
mm_help: $(BIN)/mm
! $(BIN)/mm -h
.PHONY: mm_stderr
# check if stderr is empty upon specifying -e
mm_stderr: $(BIN)/mm
test "$$(printf 'test\n' | $(BIN)/mm -e 2>&1 >/dev/null )" = "test"
.PHONY: mm_remaining
# check to make sure remaining arguments are used
mm_remaining: $(BIN)/mm
test "$$($(BIN)/mm -i README COPYING)" = "$$(cat README COPYING)"
$(BIN)/mm -i README -o /tmp/mm_test0 /tmp/mm_test1
diff /tmp/mm_test0 /tmp/mm_test1

View File

@ -3,8 +3,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PRAGMA: command_comment

View File

@ -3,8 +3,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PHONY: false_tests
false_tests: false_test false_help

View File

@ -3,25 +3,25 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PRAGMA: command_comment
.PHONY: scrut_tests
scrut_tests: scrut_help scrut_options
.PHONY: fileis_tests
fileis_tests: fileis_help fileis_options
.PHONY: scrut_help
scrut_help: $(BIN)/scrut
! $(BIN)/scrut -h
.PHONY: fileis_help
fileis_help: $(BIN)/fileis
! $(BIN)/fileis -h
.PHONY: scrut_options
# scrut tests file attributes, but files of a certain attribute aren't
.PHONY: fileis_options
# fileis tests file attributes, but files of a certain attribute aren't
# guaranteed to be present on a system. This test checks all of the files in
# harakit and, if test(1p) says a file matches a certain attribute, then checks
# scrut.
# fileis.
# opts are space-delimited (for command splitting), sel is not
scrut_options: $(BIN)/scrut
fileis_options: $(BIN)/fileis
set -e; \
opts="b c d e f g k p r s u w x L S"; \
sel=; \
@ -30,12 +30,12 @@ scrut_options: $(BIN)/scrut
for opt in $$opts; \
do if ! printf "%s\n" $$sel | grep $$opt >/dev/null; then \
if test -$$opt "$$f"; then \
if ! $(BIN)/scrut -$$opt "$$f"; \
then printf "[!!] scrut -%s failed on %s.\n" \
if ! $(BIN)/fileis -$$opt "$$f"; \
then printf "[!!] fileis -%s failed on %s.\n" \
$$opt "$$f"; \
fi; \
sel="$$sel$$opt"; \
printf "[OK] Tested scrut -%s using %s\n" \
printf "[OK] Tested fileis -%s using %s\n" \
$$opt "$$f"; \
fi; \
fi; \

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PHONY: fop_tests
fop_tests: fop_functionality fop_delimiter fop_help fop_fail
@ -24,6 +24,7 @@ fop_fail: $(BIN)/fop
! printf 'test\n' | $(BIN)/fop 1 cat
! printf 'test\n' | $(BIN)/fop 'test' cat
! printf 'test\n' | $(BIN)/fop -d'test' cat
! $(BIN)/fop
.PHONY: fop_functionality
fop_functionality: $(BIN)/fop

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PHONY: hru_tests
hru_tests: hru_help hru_functionality hru_negative hru_regressions

View File

@ -3,8 +3,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PHONY: intcmp_tests
intcmp_tests: intcmp_help intcmp_none intcmp_e intcmp_g intcmp_l intcmp_combined

57
tests/mm.mk Executable file
View File

@ -0,0 +1,57 @@
# Copyright (c) 20242025 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
MM_FILE!=mktemp /tmp/mm.XXXXXX
.PHONY: mm_tests
mm_tests: mm_args mm_help mm_stderr mm_remaining mm_remaining_options
.PHONY: mm_none
mm_none: $(BIN)/mm
test "$$(printf 'meow\n' | $(BIN)/mm)" = meow
.PHONY: mm_args
# mm(1) will error if positional arguments are given without -i or -o
mm_args: $(BIN)/mm
! $(BIN)/mm argument
.PHONY: mm_help
mm_help: $(BIN)/mm
! $(BIN)/mm -h
.PHONY: mm_stderr
# check if stderr is empty upon specifying -e
mm_stderr: $(BIN)/mm
test "$$(printf 'test\n' | $(BIN)/mm -e 2>&1 >/dev/null )" = "test"
.PHONY: mm_remaining
# check to make sure remaining arguments are used
mm_remaining: $(BIN)/mm
test "$$($(BIN)/mm -i README COPYING)" = "$$(cat README COPYING)"
$(BIN)/mm -i README -o /tmp/mm_test0 /tmp/mm_test1
diff /tmp/mm_test0 /tmp/mm_test1
.PHONY: mm_remaining_options
# check to make sure mm -i with trailing arguments interprets -o as one
mm_remaining_options:
! $(BIN)/mm -i README COPYING -o - 2>&1 | cut -d: -f2 \
| xargs test " -o" =
.PHONY: mm_append
mm_append:
$(BIN)/mm -i /dev/null -o $(MM_FILE)
printf 'test\nstring\nmulti-line\n' | $(BIN)/mm -o $(MM_FILE)
printf 'new line\n' | $(BIN)/mm -a -o $(MM_FILE)
$(BIN)/mm -i $(MM_FILE) | wc -l | xargs test 4 -eq
.PHONY: mm_no_truncate
mm_no_truncate:
$(BIN)/mm -i /dev/null -o $(MM_FILE)
printf 'test\nstring\nmulti-line\n' | $(BIN)/mm -o $(MM_FILE)
printf 'new line\n' | $(BIN)/mm -t -o $(MM_FILE)
$(BIN)/mm -i $(MM_FILE) | wc -l | xargs test 3 -eq

View File

@ -1,11 +1,11 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
# Copyright (c) 20242025 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PRAGMA: command_comment
@ -26,7 +26,7 @@ npc_args:
.PHONY: npc_ascii
# Test 0x00 to 0x7f in input; in other words, the full 7b ASCII range.
npc_ascii: npc_ascii_controls npc_ascii_symbols npc_ascii_uppers # \
npc_ascii: npc_ascii_controls npc_ascii_uppers # npc_ascii_symbols \
# npc_ascii_lowers
.PHONY: npc_ascii_controls
@ -63,14 +63,18 @@ npc_ascii_controls:
| tail -n 1 \
| xargs -I out test "^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_"
.PHONY: npc_ascii_symbols
# ASCII 0x1f to 0x3f (^_ and symbols)
npc_ascii_symbols:
# shell quoting olympics
awk 'BEGIN{ for (i = 31; i < 64; ++i) printf("%c", i); print }' \
| $(BIN)/npc \
| sed -e s"/\'/\\\'/g" -e 's/"/\\"/g' \
| xargs -I out test "^_ !\"#$$%&'()*+,-./0123456789:;<=>?" = out
# This test is broken on Linux and will need closer inspection along with GNU
# xargs(1).
# .PHONY: npc_ascii_symbols
# # ASCII 0x1f to 0x3f (^_ and symbols)
# npc_ascii_symbols:
# # shell quoting olympics
# c="$(awk 'BEGIN{ for (i = 31; i < 64; ++i) printf("%c", i); print }')"
#
# printf '%s\n' "$c" | $(BIN)/npc \
# | sed -e s"/\'/\\\'/g" -e 's/"/\\"/g' \
# | tr -d '\n' \
# | xargs -I out test "^_ !\"#$$%&\'()*+,-./0123456789:;<=>?" = out
.PHONY: npc_ascii_uppers
# ASCII 0x40 to 0x5f (uppercases)

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
# Testing peek is hard as it requires visual confirmation that text isn't being
# echoed. These tests don't go that far but are a start, and have already

View File

@ -1,22 +0,0 @@
#!/bin/sh
# Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# Strictly POSIX-compliant cat(1) implementation. See cat(1p)
for arg in "$@"; do
case "$arg" in
-u) args="$(printf '%s %s\n' "$args" "$arg")" ;;
*) args="$(printf -- '%s -i %s\n' "$args" "$arg")" ;;
esac
done
# See IEEE Std 1003.1-2017 3.282
# https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282
IFS=' '
mm $args

View File

@ -1,12 +0,0 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# Strictly POSIX-compliant false(1) implementation. See false(1p)
false "$@"

View File

@ -1,11 +0,0 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# Strictly POSIX-compliant true(1) implementation. See true(1p)
true "$@"

View File

@ -1,5 +0,0 @@
#!/bin/sh
set -ex
PATH="$PWD/bin:$PATH"

View File

@ -2,11 +2,11 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PHONY: rpn_tests
rpn_tests: rpn_help rpn_add rpn_sub rpn_mul rpn_div rpn_mod rpn_flr
rpn_tests: rpn_help rpn_add rpn_sub rpn_mul rpn_div rpn_mod rpn_flr rpn_stdin
.PHONY: rpn_help
rpn_help: $(BIN)/rpn
@ -41,3 +41,8 @@ rpn_mod: $(BIN)/rpn
rpn_flr: $(BIN)/rpn
test "$$($(BIN)/rpn 12 5 //)" -eq 2
test "$$($(BIN)/rpn 9 4 //)" -eq 2
# done last because all operations have been tested
.PHONY: rpn_stdin
rpn_stdin: $(BIN)/rpn
test "$$(printf '1\n2\n+\n3\n-\n' | $(BIN)/rpn | tail -n1)" -eq 0

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PRAGMA: command_comment

View File

@ -3,8 +3,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
NAME = strcmp
TARGET = $(NAME)_tests

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PRAGMA: command_comment

View File

@ -1,15 +0,0 @@
# Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
#TESTFILES != for file in tests/bonsai/*.mk tests/posix/*.mk; do printf '%s ' "$$file"; done;
TESTFILES != for file in tests/bonsai/*.mk; do printf '%s ' "$$file"; done;
TESTS != printf '%s\n' "$(TESTFILES)" | xargs -n1 basename \
| sed 's/\.mk/_tests/g'
include $(TESTFILES)

View File

@ -4,8 +4,8 @@
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# permitted in any medium without royalty provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
.PHONY: true_tests
true_tests: true_test