forked from bonsai/harakit
intcmp(1): new tool; npc(1): new tool; scrut(1): new tool; str(1): new tool; strcmp(1): new tool; tests: added POSIX compatibility test and C compiler compatibility test; Makefile: converted to GNUmakefile; README: added README; docs: added docs
This commit is contained in:
parent
b1f6caaf41
commit
ef1416cd42
73
GNUmakefile
Normal file
73
GNUmakefile
Normal file
@ -0,0 +1,73 @@
|
||||
# Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||
# Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
# 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.
|
||||
|
||||
# If we want to use POSIX make we can’t use ifeq
|
||||
# .POSIX:
|
||||
# .PRAGMA: posix_202x
|
||||
.PHONY: clean
|
||||
.PHONY: install
|
||||
.PHONY: test
|
||||
|
||||
PREFIX=/usr/local
|
||||
CC=cc
|
||||
CFLAGS=-O3 -Lbuild
|
||||
|
||||
ifeq ($(CC), gcc)
|
||||
CFLAGS=-O3 -s -Wl,-z,noseparate-code,-z,nosectionheader -flto -Lbuild
|
||||
endif
|
||||
|
||||
ifeq ($(CC), clang)
|
||||
CFLAGS=-O3 -Wall -Lbuild
|
||||
endif
|
||||
|
||||
ifeq ($(CC), tcc)
|
||||
CFLAGS=-O3 -s -Wl -flto -Lbuild
|
||||
endif
|
||||
|
||||
build: build_dir false intcmp scrut str strcmp true
|
||||
|
||||
build_dir:
|
||||
mkdir -p build
|
||||
|
||||
clean:
|
||||
rm -rf build/
|
||||
|
||||
install: build
|
||||
mkdir -p $(PREFIX)/bin $(PREFIX)/lib $(PREFIX)/man/man1 $(PREFIX)/man/man3
|
||||
cp -f build/*.o $(PREFIX)/lib/
|
||||
cp -f build/*.so $(PREFIX)/lib/
|
||||
cp -f build/* $(PREFIX)/bin/
|
||||
cp -f docs/*.1 $(PREFIX)/man/man1/
|
||||
cp -f docs/*.3 $(PREFIX)/man/man3/
|
||||
|
||||
test: build
|
||||
tests/cc-compat.sh
|
||||
tests/posix-compat.sh
|
||||
|
||||
false: src/false.c build_dir
|
||||
$(CC) $(CFLAGS) -o build/false src/false.c
|
||||
|
||||
intcmp: src/intcmp.c build_dir
|
||||
$(CC) $(CFLAGS) -o build/intcmp src/intcmp.c
|
||||
|
||||
scrut: src/scrut.c libfileis build_dir
|
||||
$(CC) $(CFLAGS) -lfileis -o build/scrut src/scrut.c
|
||||
|
||||
str: src/str.c build_dir
|
||||
$(CC) $(CFLAGS) -o build/str src/str.c
|
||||
|
||||
strcmp: src/strcmp.c build_dir
|
||||
$(CC) $(CFLAGS) -o build/strcmp src/strcmp.c
|
||||
|
||||
true: src/true.c build_dir
|
||||
$(CC) $(CFLAGS) -o build/true src/true.c
|
||||
|
||||
libfileis: src/libfileis.c src/libfileis.h build_dir
|
||||
$(CC) $(CFLAGS) -c -fPIC -o build/libfileis.o src/libfileis.c
|
||||
$(CC) -shared -o build/libfileis.so build/libfileis.o
|
||||
|
41
Makefile
41
Makefile
@ -1,41 +0,0 @@
|
||||
# Copyright (c) 2023 Emma Tebibyte
|
||||
# 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.
|
||||
|
||||
.POSIX:
|
||||
|
||||
PREFIX=/usr/local
|
||||
CFLAGS=-O3 -s -Wl,-z,noseparate-code,-z,nosectionheader -flto -Lbuild
|
||||
|
||||
build: build_dir cat false tail true
|
||||
|
||||
clean: build_dir
|
||||
rm -rf build/
|
||||
|
||||
cat: src/cat.c build_dir lib
|
||||
cc $(CFLAGS) -lyac -o build/cat src/cat.c
|
||||
|
||||
false: src/false.c build_dir
|
||||
cc $(CFLAGS) -o build/false src/false.c
|
||||
|
||||
tail: src/tail.c build_dir lib
|
||||
cc $(CFLAGS) -lyac -o build/tail src/tail.c
|
||||
|
||||
true: src/true.c build_dir
|
||||
cc $(CFLAGS) -o build/true src/true.c
|
||||
|
||||
lib: src/yac.c src/yac.h
|
||||
cc $(CFLAGS) -c -fPIC -o build/yac.o src/yac.c
|
||||
cc -shared -o build/libyac.so build/yac.o
|
||||
|
||||
build_dir:
|
||||
mkdir -p build
|
||||
|
||||
install: build
|
||||
rm build/*.o
|
||||
mkdir -p $(PREFIX)/lib $(PREFIX)/bin
|
||||
cp -f build/*.so $(PREFIX)/lib/
|
||||
cp -f build/* $(PREFIX)/bin/
|
32
README
Normal file
32
README
Normal file
@ -0,0 +1,32 @@
|
||||
“Seek not to walk the path of the masters; seek what they sought.”
|
||||
– Matsuo Basho
|
||||
|
||||
The Bonsai core utilities are the result of the careful examination of the
|
||||
current state of POSIX and Unix utilies. The Unix Philosophy, “do one thing and
|
||||
do it well” is its core but these tools do not cling to the names of the past.
|
||||
|
||||
The era of the original Unix tools has been long and fruitful, but they have
|
||||
their flaws. The new, non-POSIX era of this project started with frustration
|
||||
with the way certain tools work and how other projects that extend POSIX don’t
|
||||
make anything better.
|
||||
|
||||
This project will not follow in the footsteps of GNU; extensions of POSIX will
|
||||
not be found here. GNU extensions are a gateway to the misuse of the shell. The
|
||||
Bonsai core utilities will intentionally discourage use of the shell for
|
||||
purposes beyond its scope.
|
||||
|
||||
See docs/ for more on the specific utilities currently implemented.
|
||||
|
||||
Read More
|
||||
|
||||
An Introduction to the Unix Shell
|
||||
<https://porkmail.org/era/unix/shell>
|
||||
|
||||
Master Foo and the Ten Thousand Lines
|
||||
<http://www.catb.org/~esr/writings/unix-koans/ten-thousand.html>
|
||||
|
||||
Master Foo Discourses on the Unix-Nature
|
||||
<http://www.catb.org/~esr/writings/unix-koans/unix-nature.html>
|
||||
|
||||
Shell Programming!
|
||||
<https://tldp.org/LDP/abs/html/why-shell.html>
|
35
docs/false.1
Normal file
35
docs/false.1
Normal file
@ -0,0 +1,35 @@
|
||||
.\" Copyright (c) 2022 DTB <trinity@trinity.moe>
|
||||
.\" Copyright (c) 2023 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 FALSE 1
|
||||
|
||||
.SH NAME
|
||||
|
||||
false \(en do nothing, unsuccessfully
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
False does nothing regardless of operands or standard input.
|
||||
False will always return an exit code of 1.
|
||||
|
||||
.SH RATIONALE
|
||||
|
||||
False exists for the construction of control flow and loops based on a failure.
|
||||
|
||||
False functions as described in POSIX.1-2017.
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
Written by Emma Tebibyte <emma@tebibyte.media>.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
This work is marked with CC0 1.0. To see a copy of this license, visit
|
||||
<http://creativecommons.org/publicdomain/zero/1.0>.
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
true(1)
|
78
docs/intcmp.1
Normal file
78
docs/intcmp.1
Normal file
@ -0,0 +1,78 @@
|
||||
.\" Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
.\" Copyright (c) 2023 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 intcmp 1
|
||||
|
||||
.SH NAME
|
||||
|
||||
intcmp \(en compare integers
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
intcmp
|
||||
.RB ( -eghl )
|
||||
.RB [ integer ]
|
||||
.RB [ integer... ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
Intcmp compares integers.
|
||||
|
||||
.SH USAGE
|
||||
|
||||
The -e option permits given integers to be equal to each other. If combined
|
||||
with -g or -l, only adjacent integers in the argument sequence can be equal.
|
||||
.PP
|
||||
The -g option permits a given integer to be greater than the following integer.
|
||||
.PP
|
||||
The -l option permits a given integer to be less than the following integer.
|
||||
.PP
|
||||
It may help to think of the -e, -g, and -l options as equivalent to the
|
||||
infix algebraic “=”, “>”, and “<” operators respectively, with each option
|
||||
putting its symbol between every given integer. For example,
|
||||
.R intcmp -l 1 2 3
|
||||
is equivalent to evaluating "1 < 2 < 3".
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
|
||||
Intcmp exits 0 for a valid expression and 1 for an invalid expression.
|
||||
.PP
|
||||
Intcmp prints a debug message and exits with the appropriate sysexits(3) error
|
||||
code in the event of an error.
|
||||
|
||||
.SH BUGS
|
||||
|
||||
There are multiple ways to express compound comparisons; “less than or equal
|
||||
to” can be -le or -el, for example.
|
||||
.PP
|
||||
The inequality comparison is -gl or -lg for “less than or greater than”; this
|
||||
is elegant but unintuitive.
|
||||
.PP
|
||||
-egl, "equal to or less than or greater than", exits 0 no matter what for valid
|
||||
program usage and may be abused to function as an integer validator.
|
||||
Use str(1) instead.
|
||||
|
||||
.SH RATIONALE
|
||||
|
||||
The traditional tool for integer comparisons in POSIX and other Unix shells has
|
||||
been test(1). This tool also handles string comparisons and file scrutiny.
|
||||
These parts of its functionality have been broken out into multiple utilities.
|
||||
|
||||
Strcmp’s functionality may be performed on a POSIX-compliant system with
|
||||
test(1p).
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
Written by DTB <trinity@trinity.moe>.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Copyright © 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
|
||||
<https://gnu.org/licenses/gpl.html>.
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
strcmp(1), scrut(1), str(1), test(1)
|
68
docs/npc.1
Normal file
68
docs/npc.1
Normal file
@ -0,0 +1,68 @@
|
||||
.\" Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
.\" Copyright (c) 2023 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 npc 1
|
||||
|
||||
.SH NAME
|
||||
|
||||
npc \(en show non-printing characters
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
npc
|
||||
.RB ( -eht )
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
Npc reads from standard input and writes to standard output, replacing non-
|
||||
printing characters with printable equivalents. Control characters print as a
|
||||
carat ('^') followed by the character '@' through '_' corresponding to the
|
||||
character replaced (e.g. control-X becomes "^X"). The delete character (0x7F)
|
||||
becomes "^?". Characters with the high bit set (>127) are printed as "M-"
|
||||
followed by the graphical representation for the same character without the
|
||||
high bit set.
|
||||
.PP
|
||||
The
|
||||
.B -e
|
||||
option prints a currency sign ('$') before each line ending.
|
||||
.PP
|
||||
The
|
||||
.B -t
|
||||
option prints tab characters as "^I" rather than a literal horizontal tab.
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
|
||||
Npc prints a debug message and exits with the appropriate sysexits(3) error
|
||||
code in the event of an error, otherwise it exits successfully.
|
||||
|
||||
.SH BUGS
|
||||
|
||||
Npc operates in single-byte chunks regardless of intended encoding.
|
||||
|
||||
.SH RATIONALE
|
||||
|
||||
POSIX currently lacks a way to display non-printing characters in the terminal
|
||||
using a standard tool. A popular extension to cat(1p), the -v option, is the
|
||||
bandage solution GNU and other software suites use.
|
||||
|
||||
This functionality should be a separate tool because its usefulness extends
|
||||
beyond that of cat(1p).
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
Written by DTB <trinity@trinity.moe>.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Copyright © 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
|
||||
<https://gnu.org/licenses/agpl.html>.
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
cat(1), cat-v(1)
|
||||
|
||||
.I UNIX Style, or cat -v Considered Harmful
|
||||
by Rob Pike
|
58
docs/str.1
Normal file
58
docs/str.1
Normal file
@ -0,0 +1,58 @@
|
||||
.\" Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
.\" Copyright (c) 2023 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 STR 1
|
||||
|
||||
.SH NAME
|
||||
|
||||
str \(en test the character types of string arguments
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
str
|
||||
.RB [ type ]
|
||||
.RB [ string... ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
Str tests each character in an arbitrary quantity of string arguments against
|
||||
the function of the same name within ctype(3).
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
|
||||
Str exits successfully if all tests pass and unsuccessfully if a test failed.
|
||||
.PP
|
||||
Str will exit unsuccessfully if a string is empty, as none of its contents
|
||||
passed the test.
|
||||
.PP
|
||||
Str will print a message to standard error and exit unsuccessfully if used
|
||||
improperly.
|
||||
|
||||
.SH DEPRECATED FEATURES
|
||||
|
||||
Str used to have an "isvalue" type as an extension to ctype(3). This was
|
||||
removed in favor of using strcmp(1) to compare strings against the empty string
|
||||
('').
|
||||
|
||||
.SH BUGS
|
||||
|
||||
There's no way of knowing which argument failed the test without re-testing
|
||||
arguments individually.
|
||||
.PP
|
||||
If a character in a string isn't valid ASCII str will exit unsuccessfully.
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
Written by DTB <trinity@trinity.moe>.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Copyright © 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
|
||||
<https://gnu.org/licenses/gpl.html>.
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
ctype(3), strcmp(1), ascii(7)
|
63
docs/strcmp.1
Normal file
63
docs/strcmp.1
Normal file
@ -0,0 +1,63 @@
|
||||
.\" Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
.\" Copyright (c) 2023 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 STRCMP 1
|
||||
|
||||
.SH NAME
|
||||
|
||||
strcmp \(en compare strings
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
strcmp
|
||||
.RM [ string ]
|
||||
.RB [ strings... ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
Strcmp checks whether the given strings are the same.
|
||||
Strcmp exits successfully if the strings are identical. Otherwise, strcmp exits
|
||||
with the value 1 if an earlier string has a greater byte value than a later
|
||||
string (e.g.
|
||||
.R strcmp b a
|
||||
)
|
||||
and 255 if an earlier string has a lesser byte value (e.g.
|
||||
.R strcmp a b
|
||||
).
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
|
||||
Strcmp will print an error message and exit unsuccessfully with a status
|
||||
described in sysexits(3) if used incorrectly (given less than two operands).
|
||||
|
||||
.SH UNICODE
|
||||
|
||||
Strcmp will exit unsuccessfully if the given strings are not identical;
|
||||
Unicode strings may need to be normalized if the intent is to check visual
|
||||
similarity and not byte similarity.
|
||||
|
||||
.SH RATIONALE
|
||||
|
||||
The traditional tool for string comparisons in POSIX and other Unix shells has
|
||||
been test(1). This tool also handles integer comparisons and file scrutiny.
|
||||
These parts of its functionality have been broken out into multiple utilities.
|
||||
|
||||
Strcmp’s functionality may be performed on a POSIX-compliant system with
|
||||
test(1p).
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
Written by DTB <trinity@trinity.moe>.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Copyright © 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
|
||||
<https://gnu.org/licenses/gpl.html>.
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
strcmp(3), intcmp(1), scrut(1), test(1)
|
35
docs/true.1
Normal file
35
docs/true.1
Normal file
@ -0,0 +1,35 @@
|
||||
.\" Copyright (c) 2022 DTB <trinity@trinity.moe>
|
||||
.\" Copyright (c) 2023 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 TRUE 1
|
||||
|
||||
.SH NAME
|
||||
|
||||
true \(en do nothing, successfully
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
True does nothing regardless of operands or standard input.
|
||||
True will always return an exit code of 0.
|
||||
|
||||
.SH RATIONALE
|
||||
|
||||
True exists for the construction of control flow and loops based on a success.
|
||||
|
||||
True functions as described in POSIX.1-2017.
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
Written by Emma Tebibyte <emma@tebibyte.media>.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
This work is marked with CC0 1.0. To see a copy of this license, visit
|
||||
<http://creativecommons.org/publicdomain/zero/1.0>.
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
false(1)
|
104
src/cat.c
104
src/cat.c
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This file is part of YAC coreutils.
|
||||
*
|
||||
* YAC coreutils 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.
|
||||
*
|
||||
* YAC coreutils is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "yac.h"
|
||||
|
||||
void cat(FILE *file, bool u) {
|
||||
int byte = 0; /* variable for storing bytes as they are read */
|
||||
int p = 0; /* index counter for bytes in buffered reading */
|
||||
char buf[4096]; /* buffer for buffered reading */
|
||||
|
||||
if (u) {
|
||||
while ((byte = fgetc(file)) != EOF) { putchar(byte); }
|
||||
} else {
|
||||
while ((byte = fgetc(file)) != EOF) {
|
||||
if (p > sizeof(buf) - 1) {
|
||||
fputs(buf, stdout);
|
||||
p = 0;
|
||||
} else {
|
||||
buf[p] = byte;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
fwrite(buf, 1, p, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bool u = false;
|
||||
int opt;
|
||||
int i;
|
||||
|
||||
extern int optind;
|
||||
while ((opt = getopt(argc, argv, "u")) != -1) {
|
||||
switch (opt) {
|
||||
/*
|
||||
* From cat(1p):
|
||||
*
|
||||
* -u Write bytes from the input file to the standard output
|
||||
* without delay as each is read.
|
||||
*/
|
||||
case 'u':
|
||||
u = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usage: %s (-u) file...\n", argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* From cat(1p):
|
||||
*
|
||||
* file A pathname of an input file. If no file operands are
|
||||
* specified, the standard input shall be used. If a file is
|
||||
* '-', the cat utility shall read from the standard input at
|
||||
* that point in the sequence. The cat utility shall not close
|
||||
* and reopen standard input when it is referenced in this way,
|
||||
* but shall accept multiple occurrences of '-' as a file
|
||||
* operand.
|
||||
*/
|
||||
|
||||
if (optind == argc) {
|
||||
cat(stdin, u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *file;
|
||||
|
||||
for (i = optind; i < argc; i++) {
|
||||
file = rpath(argv[0], argv[i]);
|
||||
if (file != NULL) {
|
||||
cat(file, u);
|
||||
if (file != stdin) { fclose(file); }
|
||||
} else { continue; }
|
||||
}
|
||||
|
||||
return EX_OK;
|
||||
}
|
18
src/false.c
18
src/false.c
@ -1,21 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
* SPDX-License-Identifier: CC0
|
||||
*
|
||||
* This file is part of YAC coreutils.
|
||||
*
|
||||
* YAC coreutils 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.
|
||||
*
|
||||
* YAC coreutils 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/.
|
||||
* This work is marked with CC0 1.0. To view a copy of this license, visit
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0>.
|
||||
*/
|
||||
|
||||
int main() { return 1; }
|
||||
|
84
src/intcmp.c
Normal file
84
src/intcmp.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <errno.h> /* errno */
|
||||
#include <stdio.h> /* fprintf(3), stderr */
|
||||
#include <stdlib.h> /* strtol(3), size_t */
|
||||
#ifndef EX_USAGE
|
||||
# include <sysexits.h> /* EX_USAGE */
|
||||
#endif
|
||||
#include <unistd.h> /* getopt(3), optind */
|
||||
|
||||
/* 0b00? */ /* Equal | -e | 0b001 | 1 */
|
||||
#define EQUAL 0x01 /* Greater | -g | 0b010 | 2 */
|
||||
/* 0b0?0 */ /* Greater or Equal | -ge | 0b011 | 3 */
|
||||
#define GREATER 0x02 /* Less | -l | 0b100 | 4 */
|
||||
/* 0b?00 */ /* Less or Equal | -le | 0b101 | 5 */
|
||||
#define LESS 0x04 /* Inequal (Greater or Less) | -gl | 0b110 | 6 */
|
||||
|
||||
static char *program_name = "intcmp";
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int c;
|
||||
size_t i;
|
||||
unsigned char mode;
|
||||
int r; /* reference integer */
|
||||
|
||||
mode = 0;
|
||||
|
||||
if(argc < 3)
|
||||
goto usage;
|
||||
|
||||
while((c = getopt(argc, argv, "egl")) != -1)
|
||||
switch(c){
|
||||
case 'e': mode |= EQUAL; break;
|
||||
case 'g': mode |= GREATER; break;
|
||||
case 'l': mode |= LESS; break;
|
||||
default: goto usage;
|
||||
}
|
||||
|
||||
if(optind + 2 /* ref cmp */ > argc){
|
||||
usage: fprintf(stderr,
|
||||
"Usage: %s (-eghl) [integer] [integer...]\n",
|
||||
argv[0] == NULL ? program_name : argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
i = optind;
|
||||
|
||||
do{ r = c;
|
||||
c = strtol(argv[i], &argv[i], 10);
|
||||
if(*argv[i] != '\0' || errno != 0){
|
||||
fprintf(stderr, "%s: argument #%d: Invalid integer\n",
|
||||
argv[0], (int)i);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
if(i == optind)
|
||||
continue;
|
||||
|
||||
/* rule enforcement; if a mode isn't permitted and the numbers
|
||||
* correspond to it, return 1 */
|
||||
if( (!(mode & EQUAL) && r == c)
|
||||
|| (!(mode & GREATER) && r > c)
|
||||
|| (!(mode & LESS) && r < c))
|
||||
return 1;
|
||||
}while(++i < argc);
|
||||
|
||||
return 0;
|
||||
}
|
63
src/libfileis.c
Normal file
63
src/libfileis.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include "libfileis.h"
|
||||
|
||||
#include <unistd.h> /* access(3), F_OK, R_OK, W_OK, X_OK */
|
||||
|
||||
int f_executable(char *path){ return access(path, X_OK) == 0; } /* test -x */
|
||||
int f_exists(char *path){ return access(path, F_OK) == 0; } /* test -e */
|
||||
int f_readable(char *path){ return access(path, R_OK) == 0; } /* test -r */
|
||||
int f_writeable(char *path){ return access(path, W_OK) == 0; } /* test -w */
|
||||
|
||||
#include <sys/stat.h> /* lstat(3), struct stat, S_ISBLK, S_ISCHR, S_ISDIR,
|
||||
* S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
|
||||
* S_ISUID, S_ISVTX */
|
||||
|
||||
static struct stat buf;
|
||||
|
||||
int f_blockspecial(char *path){ /* test -b */
|
||||
return lstat(path, &buf) != -1 && S_ISBLK(buf.st_mode);
|
||||
}
|
||||
int f_charspecial(char *path){ /* test -c */
|
||||
return lstat(path, &buf) != -1 && S_ISCHR(buf.st_mode);
|
||||
}
|
||||
int f_directory(char *path){ /* test -d */
|
||||
return lstat(path, &buf) != -1 && S_ISDIR(buf.st_mode);
|
||||
}
|
||||
int f_fifospecial(char *path){ /* test -p */
|
||||
return lstat(path, &buf) != -1 && S_ISFIFO(buf.st_mode);
|
||||
}
|
||||
int f_gid(char *path){ /* test -g */
|
||||
return lstat(path, &buf) != -1 && (buf.st_mode & S_ISGID);
|
||||
}
|
||||
int f_regular(char *path){ /* test -f */
|
||||
return lstat(path, &buf) != -1 && S_ISREG(buf.st_mode);
|
||||
}
|
||||
int f_socket(char *path){ /* test -S */
|
||||
return lstat(path, &buf) != -1 && S_ISSOCK(buf.st_mode);
|
||||
}
|
||||
int f_sticky(char *path){ /* test -k */
|
||||
return lstat(path, &buf) != -1 && (buf.st_mode & S_ISVTX);
|
||||
}
|
||||
int f_symlink(char *path){ /* test -h; test -L */
|
||||
return lstat(path, &buf) != -1 && S_ISLNK(buf.st_mode);
|
||||
}
|
||||
int f_uid(char *path){ /* test -u */
|
||||
return lstat(path, &buf) != -1 && (buf.st_mode & S_ISUID);
|
||||
}
|
62
src/libfileis.h
Normal file
62
src/libfileis.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
/* libfileis functions return true if the condition is true and false if the
|
||||
* condition is false. Returned values are 0 or 1 only. */
|
||||
|
||||
/* True if file exists and is a block special file. */
|
||||
int f_blockspecial(char *path);
|
||||
|
||||
/* True if file exists and is a character special file. */
|
||||
int f_charspecial(char *path);
|
||||
|
||||
/* True if file exists and is a directory. */
|
||||
int f_directory(char *path);
|
||||
|
||||
/* True if file exists and is executable. */
|
||||
int f_executable(char *path);
|
||||
|
||||
/* True if file exists (regardless of type). */
|
||||
int f_exists(char *path);
|
||||
|
||||
/* True if file exists and is a named pipe (FIFO). */
|
||||
int f_fifospecial(char *path);
|
||||
|
||||
/* True if file exists and its set group ID flag is set. */
|
||||
int f_gid(char *path);
|
||||
|
||||
/* True if file exists and is readable. */
|
||||
int f_readable(char *path);
|
||||
|
||||
/* True if file exists and is a regular file. */
|
||||
int f_regular(char *path);
|
||||
|
||||
/* True if file exists and is a socket. */
|
||||
int f_socket(char *path);
|
||||
|
||||
/* True if file exists and its sticky bit is set. */
|
||||
int f_sticky(char *path);
|
||||
|
||||
/* True if file exists and is a symbolic link. */
|
||||
int f_symlink(char *path);
|
||||
|
||||
/* True if file exists and its set user ID flag is set. */
|
||||
int f_uid(char *path);
|
||||
|
||||
/* True if file exists and is writeable. */
|
||||
int f_writeable(char *path);
|
64
src/npc.c
Normal file
64
src/npc.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), putc(3), stdin, stdout,
|
||||
* EOF */
|
||||
#include <unistd.h> /* getopt(3) */
|
||||
#if !defined EX_USAGE || !defined EX_OK
|
||||
# include <sysexits.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int c;
|
||||
char showend;
|
||||
char showtab;
|
||||
|
||||
showend = 0;
|
||||
showtab = 0;
|
||||
|
||||
if(argc > 0)
|
||||
while((c = getopt(argc, argv, "et")) != -1)
|
||||
switch(c){
|
||||
case 'e': showend = 1; break;
|
||||
case 't': showtab = 1; break;
|
||||
default: goto usage;
|
||||
}
|
||||
|
||||
if(argc > optind){
|
||||
usage: fprintf(stderr, "Usage: %s (-eht)\n", argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
while((c = getc(stdin)) != EOF){
|
||||
if((c & 0x80) != 0)
|
||||
fputs("M-", stdout);
|
||||
switch(c ^ 0x80 /* 0b 1000 0000 */){
|
||||
case 0x7f: fputs("^?", stdout);
|
||||
break;
|
||||
case '\n': if(showend)
|
||||
putc('$', stdout);
|
||||
default:
|
||||
if(c >= ' ' || c == '\n' || (!showtab && c == '\t'))
|
||||
putc(c, stdout);
|
||||
else
|
||||
fprintf(stdout, "^%c", c + '@');
|
||||
}
|
||||
}
|
||||
|
||||
return EX_OK;
|
||||
}
|
98
src/scrut.c
Normal file
98
src/scrut.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* fprintf(3), stderr, NULL */
|
||||
#include <string.h> /* strchr(3) */
|
||||
#ifndef EX_USAGE
|
||||
# include <sysexits.h>
|
||||
#endif
|
||||
#include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */
|
||||
#include <sys/stat.h> /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR,
|
||||
* S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
|
||||
* S_ISUID, S_ISVTX */
|
||||
|
||||
static char args[] = "bcdefghkprsuwxLS";
|
||||
static char ops[(sizeof args) / (sizeof *args)];
|
||||
static char *program_name = "scrut";
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
struct stat buf;
|
||||
int c;
|
||||
size_t i;
|
||||
|
||||
if(argc < 2)
|
||||
goto usage;
|
||||
|
||||
i = 0;
|
||||
while((c = getopt(argc, argv, args)) != -1)
|
||||
if(strchr(args, c) == NULL)
|
||||
goto usage;
|
||||
else
|
||||
ops[i++] = c;
|
||||
ops[i] = '\0';
|
||||
|
||||
if(optind == argc)
|
||||
goto usage;
|
||||
|
||||
argv += optind;
|
||||
do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1)
|
||||
return 1; /* doesn't exist or isn't stattable */
|
||||
|
||||
for(i = 0; ops[i] != '\0'; ++i)
|
||||
if(ops[i] == 'e')
|
||||
continue;
|
||||
else if(ops[i] == 'h'){
|
||||
usage: fprintf(stderr, "Usage: %s (-%s) [file...]\n",
|
||||
argv[0] == NULL
|
||||
? program_name
|
||||
: argv[0],
|
||||
args);
|
||||
|
||||
return EX_USAGE;
|
||||
}else if(
|
||||
(ops[i] == 'b'
|
||||
&& !S_ISBLK(buf.st_mode))
|
||||
|| (ops[i] == 'c'
|
||||
&& !S_ISCHR(buf.st_mode))
|
||||
|| (ops[i] == 'd'
|
||||
&& !S_ISDIR(buf.st_mode))
|
||||
|| (ops[i] == 'f'
|
||||
&& !S_ISREG(buf.st_mode))
|
||||
|| (ops[i] == 'g'
|
||||
&& !(buf.st_mode & S_ISGID))
|
||||
|| (ops[i] == 'k'
|
||||
&& !(buf.st_mode & S_ISVTX))
|
||||
|| (ops[i] == 'p'
|
||||
&& !S_ISFIFO(buf.st_mode))
|
||||
|| (ops[i] == 'r'
|
||||
&& access(*argv, R_OK) != 0)
|
||||
|| (ops[i] == 'u'
|
||||
&& !(buf.st_mode & S_ISUID))
|
||||
|| (ops[i] == 'w'
|
||||
&& access(*argv, W_OK) != 0)
|
||||
|| (ops[i] == 'x'
|
||||
&& access(*argv, X_OK) != 0)
|
||||
|| (ops[i] == 'L'
|
||||
&& !S_ISLNK(buf.st_mode))
|
||||
|| (ops[i] == 'S'
|
||||
&& !S_ISSOCK(buf.st_mode)))
|
||||
return 1;
|
||||
}while(*++argv != NULL);
|
||||
|
||||
return 0;
|
||||
}
|
75
src/str.c
Normal file
75
src/str.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h> /* NULL */
|
||||
#include <stdio.h> /* fprintf(3) */
|
||||
#include <string.h> /* strcmp(3) */
|
||||
#if !defined EX_USAGE
|
||||
# include <sysexits.h>
|
||||
#endif
|
||||
|
||||
static char *program_name = "str";
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
int (*f)(int);
|
||||
}ctypes[] = {
|
||||
{ "isalnum", isalnum },
|
||||
{ "isalpha", isalpha },
|
||||
{ "isblank", isblank },
|
||||
{ "iscntrl", iscntrl },
|
||||
{ "isdigit", isdigit },
|
||||
{ "isxdigit", isxdigit },
|
||||
{ "isgraph", isgraph },
|
||||
{ "islower", islower },
|
||||
{ "isprint", isprint },
|
||||
{ "ispunct", ispunct },
|
||||
{ "isspace", isspace },
|
||||
{ "isupper", isupper }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int ctype;
|
||||
int i;
|
||||
int r;
|
||||
|
||||
if(argc >= 3){
|
||||
for(ctype = 0; ctype < (sizeof ctypes) / (sizeof *ctypes);
|
||||
++ctype)
|
||||
if(strcmp(argv[1], ctypes[ctype].name) == 0)
|
||||
goto pass;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Usage: %s [type] [string...]\n",
|
||||
argv[0] == NULL ? program_name : argv[0]);
|
||||
|
||||
return EX_USAGE;
|
||||
|
||||
pass: for(argv += 2, r = 1; *argv != NULL; ++argv)
|
||||
for(i = 0; argv[0][i] != '\0'; ++i)
|
||||
/* First checks if argv[0][i] is valid ASCII; ctypes(3)
|
||||
* don't handle non-ASCII.
|
||||
* This is bad. */
|
||||
if(argv[0][i] < 0x80 && !ctypes[ctype].f(argv[0][i]))
|
||||
return 1;
|
||||
else
|
||||
r = 0;
|
||||
|
||||
return r;
|
||||
}
|
25
src/strcmp.c
Normal file
25
src/strcmp.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include <stdio.h> /* fprintf(3), stderr */
|
||||
#ifndef EX_USAGE
|
||||
# include <sysexits.h> /* EX_USAGE */
|
||||
#endif
|
||||
|
||||
static char *program_name = "strcmp";
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int i;
|
||||
|
||||
if(argc < 3){
|
||||
fprintf(stderr, "Usage: %s [string] [string...]\n",
|
||||
argv[0] == NULL ? program_name : argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
for(; *argv[1] != '\0'; ++argv[1])
|
||||
for(i = 2; i < argc; ++i)
|
||||
if(*argv[i-1] > *argv[i])
|
||||
return 1;
|
||||
else if(*argv[i-1] < *argv[i]++)
|
||||
return -1; /* actually 255 */
|
||||
|
||||
return 0;
|
||||
}
|
209
src/tail.c
209
src/tail.c
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||
* Copyright (c) 2023 Marceline Cramer <mars@tebibyte.media>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This file is part of YAC coreutils.
|
||||
*
|
||||
* YAC coreutils 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.
|
||||
*
|
||||
* YAC coreutils is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sysexits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "yac.h"
|
||||
|
||||
typedef struct string_t {
|
||||
char *mem;
|
||||
int len;
|
||||
int capacity;
|
||||
} string_t;
|
||||
|
||||
string_t *string_new() {
|
||||
string_t *string = calloc(1, sizeof(string_t));
|
||||
string->capacity = 1024;
|
||||
string->len = 0;
|
||||
string->mem = calloc(string->capacity, sizeof(char));
|
||||
return string;
|
||||
}
|
||||
|
||||
void string_putc(string_t *string, char c) {
|
||||
string->mem[string->len] = c;
|
||||
string->len++;
|
||||
if (string->len >= string->capacity) {
|
||||
string->capacity *= 2;
|
||||
string->mem = realloc(string->mem, string->capacity * sizeof(char));
|
||||
}
|
||||
}
|
||||
|
||||
void tailc(FILE *f, long num) {}
|
||||
|
||||
void tailn(FILE *f, long num) {
|
||||
string_t *lines[num];
|
||||
int cursor = 0;
|
||||
int looped = 0;
|
||||
|
||||
lines[cursor] = string_new();
|
||||
|
||||
int c = fgetc(f);
|
||||
for (;;) {
|
||||
if (c == EOF) {
|
||||
string_putc(lines[cursor], '\0');
|
||||
break;
|
||||
}
|
||||
|
||||
string_putc(lines[cursor], c);
|
||||
|
||||
if (c == '\n') {
|
||||
string_putc(lines[cursor], '\0');
|
||||
|
||||
if ((c = fgetc(f)) == EOF) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (++cursor >= num) {
|
||||
looped = 1;
|
||||
cursor = 0;
|
||||
}
|
||||
|
||||
lines[cursor] = string_new();
|
||||
} else {
|
||||
c = fgetc(f);
|
||||
}
|
||||
}
|
||||
|
||||
int read = looped ? cursor + 1 : 0;
|
||||
do {
|
||||
if (read >= num) {
|
||||
read = 0;
|
||||
}
|
||||
|
||||
fputs(lines[read]->mem, stdout);
|
||||
} while (read++ != cursor);
|
||||
}
|
||||
|
||||
void tailf(FILE *file) {
|
||||
int byte;
|
||||
|
||||
while(true) {
|
||||
if ((byte = fgetc(file)) != EOF) { putchar(byte); }
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bool c = false;
|
||||
bool f = false;
|
||||
bool n = false;
|
||||
int i;
|
||||
int opt;
|
||||
long num;
|
||||
void (*fn)(FILE *, long) = tailn;
|
||||
|
||||
extern int optind;
|
||||
while ((opt = getopt(argc, argv, "c:fn:")) != -1) {
|
||||
switch (opt) {
|
||||
/*
|
||||
* From tail(1p):
|
||||
*
|
||||
* -c number The application shall ensure that the number option-argument
|
||||
* is a decimal integer, optionally including a sign. The sign
|
||||
* shall affect the location in the file, measured in bytes, to
|
||||
* begin the copying:
|
||||
*
|
||||
* ┌─────┬────────────────────────────────────────┐
|
||||
* │Sign │ Copying Starts │
|
||||
* ├─────┼────────────────────────────────────────┤
|
||||
* │ + │ Relative to the beginning of the file. │
|
||||
* │ - │ Relative to the end of the file. │
|
||||
* │none │ Relative to the end of the file. │
|
||||
* └─────┴────────────────────────────────────────┘
|
||||
* The application shall ensure that if the sign of the number
|
||||
* option-argument is '+', the number option-argument is a non-
|
||||
* zero decimal integer.
|
||||
*
|
||||
* The origin for counting shall be 1; that is, -c +1 represents
|
||||
* the first byte of the file, -c -1 the last.
|
||||
*
|
||||
* -f If the input file is a regular file or if the file operand
|
||||
* specifies a FIFO, do not terminate after the last line of the
|
||||
* input file has been copied, but read and copy further bytes
|
||||
* from the input file when they become available. If no file
|
||||
* operand is specified and standard input is a pipe or FIFO,
|
||||
* the -f option shall be ignored. If the input file is not a
|
||||
* FIFO, pipe, or regular file, it is unspecified whether or not
|
||||
* the -f option shall be ignored.
|
||||
*
|
||||
* -n number This option shall be equivalent to -c number, except the
|
||||
* starting location in the file shall be measured in lines
|
||||
* instead of bytes. The origin for counting shall be 1; that
|
||||
* is, -n +1 represents the first line of the file, -n -1 the
|
||||
* last.
|
||||
*
|
||||
* If neither -c nor -n is specified, -n 10 shall be assumed.
|
||||
*/
|
||||
case 'c':
|
||||
c = true;
|
||||
fn = tailc;
|
||||
num = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'f':
|
||||
f = true;
|
||||
case 'n':
|
||||
n = true;
|
||||
num = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
default:
|
||||
fprintf(
|
||||
stderr,
|
||||
"Usage: %s (-f) [-c characters] [-n lines] file...\n",
|
||||
argv[0]
|
||||
);
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!n && !c) {
|
||||
num = 10;
|
||||
} else if (n && c) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Usage: %s (-f) [-c characters] [-n lines] file...\n",
|
||||
argv[0]
|
||||
);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
FILE *file;
|
||||
|
||||
if (optind == argc) {
|
||||
fn(stdin, num);
|
||||
|
||||
if (f) { tailf(stdin); }
|
||||
} else {
|
||||
for (i = optind; i < argc; i++) {
|
||||
if ((file = rpath(argv[0], argv[i])) != NULL) {
|
||||
fn(file, num);
|
||||
}
|
||||
|
||||
if (f) { tailf(file); }
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
return EX_OK;
|
||||
}
|
18
src/true.c
18
src/true.c
@ -1,21 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
* SPDX-License-Identifier: CC0
|
||||
*
|
||||
* This file is part of YAC coreutils.
|
||||
*
|
||||
* YAC coreutils 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.
|
||||
*
|
||||
* YAC coreutils 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/.
|
||||
* This work is marked with CC0 1.0. To view a copy of this license, visit
|
||||
* <http://creativecommons.org/publicdomain/zero/1.0>.
|
||||
*/
|
||||
|
||||
int main() {}
|
||||
|
77
src/yac.c
77
src/yac.c
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This file is part of YAC coreutils.
|
||||
*
|
||||
* YAC coreutils 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.
|
||||
*
|
||||
* YAC coreutils is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sysexits.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* Resolve a file from a path */
|
||||
FILE *rpath(char *argv0, char *path) {
|
||||
struct stat stats;
|
||||
FILE *file;
|
||||
|
||||
if (path[0] == '-') {
|
||||
switch (path[1]) {
|
||||
case '\0':
|
||||
file = stdin;
|
||||
break;
|
||||
default:
|
||||
file = NULL;
|
||||
break;
|
||||
}
|
||||
} else if (stat(path, &stats) == 0 && S_ISDIR(stats.st_mode)) {
|
||||
fprintf(stderr, "%s: %s: Is a directory.\n", argv0, path);
|
||||
exit(EX_NOINPUT);
|
||||
} else if ((file = fopen(path, "r")) == NULL) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
fprintf(stderr, "%s: %s: Permission denied.\n", argv0, path);
|
||||
exit(EX_NOINPUT);
|
||||
case EISDIR:
|
||||
fprintf(stderr, "%s: %s: Is a directory.\n", argv0, path);
|
||||
exit(EX_NOINPUT);
|
||||
case ELOOP:
|
||||
fprintf(
|
||||
stderr,
|
||||
"%s: %s: Is a symbolic link loop.\n",
|
||||
argv0,
|
||||
path
|
||||
);
|
||||
exit(EX_UNAVAILABLE);
|
||||
case EMFILE:
|
||||
fprintf(stderr, "%s: Internal error.\n", argv0);
|
||||
exit(EX_SOFTWARE);
|
||||
case ENOENT: case ENOTDIR: case ENXIO:
|
||||
fprintf(
|
||||
stderr,
|
||||
"%s: %s: No such path or directory.\n",
|
||||
argv0,
|
||||
path
|
||||
);
|
||||
exit(EX_NOINPUT);
|
||||
default:
|
||||
fprintf(stderr, "%s: Unknown error.\n", argv0);
|
||||
exit(EX_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
26
src/yac.h
26
src/yac.h
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* This file is part of YAC coreutils.
|
||||
*
|
||||
* YAC coreutils 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.
|
||||
*
|
||||
* YAC coreutils 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/.
|
||||
*/
|
||||
|
||||
#ifndef YAC_H
|
||||
#define YAC_H
|
||||
|
||||
FILE *rpath(char *argv0, char *path);
|
||||
|
||||
#endif
|
35
tests/cc-compat.sh
Executable file
35
tests/cc-compat.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2023 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.
|
||||
|
||||
set -e
|
||||
|
||||
if ! ls GNUmakefile >/dev/null 2>&1
|
||||
then
|
||||
printf '%s: Run this script in the root of the project.\n' "$0" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
make clean
|
||||
|
||||
for CC in cc\
|
||||
clang \
|
||||
gcc \
|
||||
tcc \
|
||||
'zig cc'
|
||||
do
|
||||
export CC
|
||||
printf '%s: %s: Testing build.\n' "$0" "$CC"
|
||||
|
||||
make CC="$CC" && printf '%s: Build successful.\n' "$0"
|
||||
|
||||
ls -lA build/
|
||||
|
||||
make clean
|
||||
printf '\n'
|
||||
done
|
24
tests/posix-compat.sh
Executable file
24
tests/posix-compat.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2023 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.
|
||||
|
||||
set -e
|
||||
|
||||
if ! ls GNUmakefile >/dev/null 2>&1
|
||||
then
|
||||
printf '%s: Run this script in the root of the project.\n' "$0" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "Starting POSIX compatibility testing.\n"
|
||||
|
||||
for utility in tests/posix/*; do
|
||||
printf '%s: %s: Testing utility.\n' "$0" "$utility"
|
||||
"$utility"
|
||||
printf '\n'
|
||||
done
|
19
tests/posix/test
Executable file
19
tests/posix/test
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2023 DTB <trinity@trinity.moe>
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
i=1
|
||||
while test -e "$i".test; do
|
||||
printf '%s: %s: Running test...\n' "$0" "$i".test
|
||||
./"$i".test >"$i".result
|
||||
diff 1.expected 1.result
|
||||
printf '%s: %s: Test passed.\n' "$0" "$i".test
|
||||
i="$(printf '%s + %s\n' 1 "$i" | bc)"
|
||||
done
|
Loading…
Reference in New Issue
Block a user