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:
Emma Tebibyte 2023-12-24 17:13:17 -07:00
parent b1f6caaf41
commit ef1416cd42
Signed by untrusted user: emma
GPG Key ID: 06FA419A1698C270
25 changed files with 997 additions and 487 deletions

73
GNUmakefile Normal file
View 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 cant 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

View File

@ -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
View 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 dont
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
View 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
View 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.
Strcmps 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
View 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
View 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
View 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.
Strcmps 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
View 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
View File

@ -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;
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View File

@ -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;
}

View File

@ -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() {}

View File

@ -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;
}

View File

@ -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
View 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
View 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
View 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