diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b620128 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +# 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/bin +CFLAGS=-O3 -s -Wl,-z,noseparate-code,-z,nosectionheader -flto + +build: build_dir cat false true + +clean: build_dir + rm -rf build/ + +cat: build_dir + cc $(CFLAGS) -o build/cat src/cat.c + +false: build_dir + cc $(CFLAGS) -o build/false src/false.c + +true: build_dir + cc $(CFLAGS) -o build/true src/true.c + +build_dir: + mkdir -p build + +install: build + mkdir -p $(PREFIX) + cp -f build/* $(PREFIX)/ diff --git a/src/cat.c b/src/cat.c new file mode 100644 index 0000000..6fd9671 --- /dev/null +++ b/src/cat.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023 Emma Tebibyte + * 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 +#include +#include +#include +#include +#include + +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 != EOF) { + byte = fgetc(file); + putchar(byte); + } + } else { + while (byte != EOF) { + byte = fgetc(file); + if (p > sizeof(buf)) { + fputs(buf, stdout); + p = 0; + } else { + buf[p] = byte; + p += 1; + } + } + + 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; + struct stat stats; + + for (i = optind; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case '\0': + file = stdin; + break; + default: + continue; + } + } else if (stat(argv[i], &stats) == 0 && S_ISDIR(stats.st_mode)) { + fprintf(stderr, "%s: %s: Is a directory.\n", argv[0], argv[i]); + return EX_NOINPUT; + } else if ((file = fopen(argv[i], "r")) == NULL) { + switch (errno) { + case EACCES: + fprintf(stderr, "%s: %s: Permission denied.\n", argv[0], argv[i]); + return EX_NOINPUT; + case EISDIR: + fprintf(stderr, "%s: %s: Is a directory.\n", argv[0], argv[i]); + return EX_NOINPUT; + case ELOOP: + fprintf( + stderr, + "%s: %s: Is a symbolic link loop.\n", + argv[0], + argv[i] + ); + return EX_UNAVAILABLE; + case EMFILE: + fprintf(stderr, "%s: Internal error.\n", argv[0]); + return EX_SOFTWARE; + case ENOENT: case ENOTDIR: case ENXIO: + fprintf( + stderr, + "%s: %s: No such file or directory.\n", + argv[0], + argv[i] + ); + return EX_NOINPUT; + default: + fprintf(stderr, "%s: Unknown error.\n", argv[0]); + return EX_UNAVAILABLE; + } + } + + cat(file, u); + if (file != stdin) { fclose(file); } + } + + return EX_OK; +} diff --git a/src/false.c b/src/false.c new file mode 100644 index 0000000..e6f64b3 --- /dev/null +++ b/src/false.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Emma Tebibyte + * 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/. + */ + +int main() { return 1; } diff --git a/src/true.c b/src/true.c new file mode 100644 index 0000000..9d1f79f --- /dev/null +++ b/src/true.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Emma Tebibyte + * 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/. + */ + +int main() {}