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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user