From 4daa873acbfc240e9ecc350e74c14bbfd58fabc9 Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Sat, 19 Oct 2013 13:51:28 +0000 Subject: [PATCH] Added implementations from pdcore Found implementations of several utilities at https://github.com/mooseman/pdcore --- utils/cat/cat.1 | 36 ++++ utils/cat/cat.c | 100 +++++++++ utils/cksum/cksum.1 | 27 +++ utils/cksum/cksum.c | 115 +++++++++++ utils/cmp/cmp.1 | 37 ++++ utils/cmp/cmp.c | 163 +++++++++++++++ utils/comm/comm.1 | 37 ++++ utils/comm/comm.c | 252 +++++++++++++++++++++++ utils/dirname/dirname.1 | 26 +++ utils/dirname/dirname.c | 52 +++++ utils/false/false.1 | 24 +++ utils/false/false.c | 21 ++ utils/head/head.1 | 33 +++ utils/head/head.c | 147 +++++++++++++ utils/tail/tail.1 | 54 +++++ utils/tail/tail.c | 446 ++++++++++++++++++++++++++++++++++++++++ utils/tee/tee.1 | 30 +++ utils/tee/tee.c | 102 +++++++++ utils/true/true.1 | 24 +++ utils/true/true.c | 21 ++ 20 files changed, 1747 insertions(+) create mode 100644 utils/cat/cat.1 create mode 100644 utils/cat/cat.c create mode 100644 utils/cksum/cksum.1 create mode 100644 utils/cksum/cksum.c create mode 100644 utils/cmp/cmp.1 create mode 100644 utils/cmp/cmp.c create mode 100644 utils/comm/comm.1 create mode 100644 utils/comm/comm.c create mode 100644 utils/dirname/dirname.1 create mode 100644 utils/dirname/dirname.c create mode 100644 utils/false/false.1 create mode 100644 utils/false/false.c create mode 100644 utils/head/head.1 create mode 100644 utils/head/head.c create mode 100644 utils/tail/tail.1 create mode 100644 utils/tail/tail.c create mode 100644 utils/tee/tee.1 create mode 100644 utils/tee/tee.c create mode 100644 utils/true/true.1 create mode 100644 utils/true/true.c diff --git a/utils/cat/cat.1 b/utils/cat/cat.1 new file mode 100644 index 0000000..d8b6bc8 --- /dev/null +++ b/utils/cat/cat.1 @@ -0,0 +1,36 @@ +.TH CAT 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +cat \- concatenate file(s) to standard output +.SH SYNOPSIS +.B cat +[-u] [file ...] +.SH DESCRIPTION +Sequentially read input files and write their contents to standard output. +.SH OPTIONS +.TP +-u +Fully unbuffered read and write. +.SH OPERANDS +.TP +file +Input file(s). If none provided, or the filename '-' is passed to cat, +standard input will be used. +.SH NOTES +As files are processed sequentially, beware of shell commands like +.P +.RS +cat x y > x +.RE +.P +which destroy input files before reading them. +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/cat/cat.c b/utils/cat/cat.c new file mode 100644 index 0000000..c109550 --- /dev/null +++ b/utils/cat/cat.c @@ -0,0 +1,100 @@ +/* + * cat.c - concatenate file(s) to standard output + * + * Version: 2008-1.01 + * Build: c89 -o cat cat.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "usage: cat [-u] [file ...]\n" +#define BUFSIZE 4096 + +static void catfile(int fd, char *fn); +static void error(char *s); + +static int exitstatus; +static int optu; + + +int main(int argc, char **argv) +{ + extern int opterr, optind; + int c, fd; + char *fn; + + setlocale(LC_ALL, ""); + opterr = 0; + + while ((c = getopt(argc, argv, "u")) != -1) + switch (c) + { + case 'u': + optu = 1; + break; + default: + fprintf(stderr, USAGE); + return(1); + } + + if (optind >= argc) + catfile(STDIN_FILENO, "stdin"); + else + while (optind < argc) + { + fn = argv[optind++]; + + if (strcmp(fn, "-") == 0) + catfile(STDIN_FILENO, "stdin"); + else + if ((fd = open(fn, O_RDONLY)) == -1) + error(fn); + else + { + catfile(fd, fn); + if (close(fd) == -1) + error(fn); + } + } + + return(exitstatus); +} + + +void catfile(int fd, char *fn) +{ + unsigned char buf[BUFSIZE]; + ssize_t n; + + while ((n = read(fd, buf, (optu ? 1 : BUFSIZE))) > 0) + if (write(STDOUT_FILENO, buf, (size_t)n) != n) + { + error("stdout"); + break; + } + + if (n < 0) + error(fn); +} + + +void error(char *s) +{ + fprintf(stderr, "cat: %s: %s\n", s, strerror(errno)); + exitstatus = 1; +} diff --git a/utils/cksum/cksum.1 b/utils/cksum/cksum.1 new file mode 100644 index 0000000..cd44936 --- /dev/null +++ b/utils/cksum/cksum.1 @@ -0,0 +1,27 @@ +.TH CKSUM 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +cksum \- report checksum and octet count of file(s) +.SH SYNOPSIS +.B cksum +[file ...] +.SH DESCRIPTION +Display CRC check value and octet count of given file(s). +.SH OPTIONS +.TP +None +.SH OPERANDS +.TP +file +Input file. If none provided, or the filename '-' is passed to cksum, +standard input will be used. +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/cksum/cksum.c b/utils/cksum/cksum.c new file mode 100644 index 0000000..daba540 --- /dev/null +++ b/utils/cksum/cksum.c @@ -0,0 +1,115 @@ +/* + * cksum - report checksum and octet count of file(s) + * + * Version: 2008-1.01 + * Build: c89 -o cksum cksum.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 4096 + +static void cksumfile(int fd, char *fn); +static void error(char *s); + +static int exitstatus; + + +int main(int argc, char **argv) +{ + int i, fd, hasrun = 0; + char *fn; + + setlocale(LC_ALL, ""); + + for (i = 1; i < argc; i++) + { + fn = argv[i]; + + if (strcmp(fn, "--") == 0) + continue; + else + { + hasrun = 1; + + if (strcmp(fn, "-") == 0) + cksumfile(STDIN_FILENO, "stdin"); + else + if ((fd = open(fn, O_RDONLY)) == -1) + error(fn); + else + { + cksumfile(fd, fn); + if (close(fd) == -1) + error(fn); + } + } + } + + if (! hasrun) + cksumfile(STDIN_FILENO, "stdin"); + + return(exitstatus); +} + + +void cksumfile(int fd, char *fn) +{ + unsigned char buf[BUFSIZE]; + ssize_t cnt, n; + uint32_t crctab[256], crc, i; + int j; + + for (i = 0; i < 256; ++i) + { + for (crc = i << 24, j = 0; j < 8; j++) + crc = (crc << 1) ^ (crc & 0x80000000 ? 0x04c11db7 : 0); + crctab[i] = crc; + } + + cnt = crc = 0; + + while ((n = read(fd, buf, BUFSIZE)) > 0) + { + cnt += n; + + for (i = 0; i < (size_t)n; i++) + crc = crctab[((crc >> 24) ^ buf[i]) & 0xFF] ^ (crc << 8); + } + + if (n < 0) + error(fn); + else + { + for (i = cnt; i != 0; i >>= 8) + crc = crctab[((crc >> 24) ^ i) & 0xFF] ^ (crc << 8); + + printf("%lu %lu", (unsigned long)~crc, (unsigned long)cnt); + if (fd != STDIN_FILENO) + printf(" %s", fn); + printf("\n"); + } +} + + +void error(char *s) +{ + fprintf(stderr, "cksum: %s: %s\n", s, strerror(errno)); + exitstatus = 1; +} diff --git a/utils/cmp/cmp.1 b/utils/cmp/cmp.1 new file mode 100644 index 0000000..b7e9081 --- /dev/null +++ b/utils/cmp/cmp.1 @@ -0,0 +1,37 @@ +.TH CMP 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +cmp \- compare two files +.SH SYNOPSIS +.B cmp +[-l|-s] file1 file2 +.SH DESCRIPTION +Compare two files. If the contents are the same, produce no output and return +an exit status of 0. If they differ, report the line and byte position of the +first difference, and return an exit status of 1. An exit status of 2 will be +returned in the event of any error. +.SH OPTIONS +.TP +-l +List all differences, not just the first. +.TP +-s +Silent. No output under any circumstances; returns exit status only. +.P +Note that options -l and -s are mutually exclusive. +.SH OPERANDS +.TP +file1, file2 +Input files. If none provided, or the filename '-' is passed to cmp, standard +input will be used. Note that standard input may only be used for one of the +file inputs. +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/cmp/cmp.c b/utils/cmp/cmp.c new file mode 100644 index 0000000..a1c0572 --- /dev/null +++ b/utils/cmp/cmp.c @@ -0,0 +1,163 @@ +/* + * cmp.c - compare two files + * + * Version: 2008-1.01 + * Build: c89 -o cmp cmp.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "usage: cmp [-l|-s] file1 file2\n" +#define ELSMUTEXCL "Options -l and -s are mutually exclusive" +#define EBOTHSTDIN "Input files must be different" +#define BUFSIZE 4096 +#define SYSERR 1 +#define APPERR 0 + +static int cmpfiles(int fd1, char *fn1, int fd2, char *fn2); +static void fatal(int errtype, char *s); + +static int optl, opts; + + +int main(int argc, char **argv) +{ + extern int opterr, optind; + int c, exitstatus = 0; + int fd1, fd2; + char *fn1, *fn2; + + setlocale(LC_ALL, ""); + opterr = 0; + + while ((c = getopt(argc, argv, "ls")) != -1) + switch (c) + { + case 'l': + optl = 1; + break; + case 's': + opts = 1; + break; + default: + fprintf(stderr, USAGE); + exit(2); + } + + argc -= optind; + argv += optind; + + if (optl && opts) + fatal(APPERR, ELSMUTEXCL); + + if (argc != 2) + fatal(APPERR, USAGE); + + fn1 = argv[0]; + if (strcmp(fn1, "-") == 0) + fd1 = STDIN_FILENO; + else + if ((fd1 = open(fn1, O_RDONLY)) == -1) + fatal(SYSERR, fn1); + + fn2 = argv[1]; + if (strcmp(fn2, "-") == 0) + fd2 = STDIN_FILENO; + else + if ((fd2 = open(fn2, O_RDONLY)) == -1) + fatal(SYSERR, fn2); + + if (fd1 == STDIN_FILENO && fd2 == STDIN_FILENO) + fatal(APPERR, EBOTHSTDIN); + + exitstatus = cmpfiles(fd1, fn1, fd2, fn2); + + if (fd1 != STDIN_FILENO && (close(fd1) == -1)) + fatal(SYSERR, fn1); + + if (fd2 != STDIN_FILENO && (close(fd2) == -1)) + fatal(SYSERR, fn2); + + return(exitstatus); +} + + +int cmpfiles(int fd1, char *fn1, int fd2, char *fn2) +{ + unsigned char buf1[BUFSIZE], buf2[BUFSIZE]; + ssize_t n1, n2, byte = 1, line = 1; + int cnt, i, eof = 0, differ = 0, shorter = 0; + + while (! eof) + { + if ((n1 = read(fd1, buf1, BUFSIZE)) == -1) + fatal(SYSERR, fn1); + + if ((n2 = read(fd2, buf2, BUFSIZE)) == -1) + fatal(SYSERR, fn2); + + if (n1 != BUFSIZE || n2 != BUFSIZE) + { + eof = 1; + if (n1 != n2) + shorter = (n1 < n2) ? 1 : 2; + } + + cnt = (n1 < n2) ? n1 : n2; + for (i = 0; i < cnt; i++) + { + if (buf1[i] != buf2[i]) + { + if (! differ && ! optl && ! opts) + { + printf("%s %s differ: char %d, line %d\n", fn1, fn2, byte, line); + return(1); /* only need to report first mismatch */ + } + + differ = 1; + + if (optl) + printf("%d %o %o\n", byte, buf1[i], buf2[i]); + } + + byte++; + if (buf1[i] == '\n') + line++; + } + } + + if (shorter && (optl || (! opts && ! differ))) + { + differ = 1; + fprintf(stderr, "cmp: EOF on %s\n", (shorter == 1 ? fn1 : fn2)); + } + + return(differ || shorter); +} + + +void fatal(int errtype, char *s) +{ + if (errtype == SYSERR) + fprintf(stderr, "cmp: %s: %s\n", s, strerror(errno)); + else + fprintf(stderr, "cmp: %s\n", s); + + exit(2); +} diff --git a/utils/comm/comm.1 b/utils/comm/comm.1 new file mode 100644 index 0000000..db37d1d --- /dev/null +++ b/utils/comm/comm.1 @@ -0,0 +1,37 @@ +.TH COMM 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +comm \- select or reject lines common to two files +.SH SYNOPSIS +.B comm +[-123] file1 file2 +.SH DESCRIPTION +Read 'file1' and 'file2', both of which should be ordered in the current +collating sequence, and produce three text columns as output: lines unique +to 'file1', lines unique to 'file2', and lines common to both files. +.SH OPTIONS +.TP +-1 +Suppress output of column one (lines unique to 'file1'). +.TP +-2 +Suppress output of column two (lines unique to 'file2'). +.TP +-3 +Suppress output of column three (lines common to both files). +.P +.SH OPERANDS +.TP +file1, file2 +Input files. If none provided, or the filename '-' is passed to comm, +standard input will be used. +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/comm/comm.c b/utils/comm/comm.c new file mode 100644 index 0000000..72aba31 --- /dev/null +++ b/utils/comm/comm.c @@ -0,0 +1,252 @@ +/* + * comm.c - select or reject lines common to two files + * + * Version: 2008-1.01 + * Build: c89 -o comm comm.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "usage: comm [-123] file1 file2\n" +#define ENOMALLOC "Unable to allocate memory for read buffer" +#define BUFSIZE 4096 +#define LINESIZE 64 +#define SYSERR 1 +#define APPERR 0 + +typedef struct { int fd; + char *fn; + int eof; + char buf[BUFSIZE]; + size_t bufpos; + size_t eobuf; + char *line; + size_t linesz; + } FINFO; + +static void initfile(FINFO *file, char *fn); +static void cleanup(FINFO *file); +static void commfiles(FINFO *file1, FINFO *file2); +static int getline(FINFO *file); +static void fillbuf(FINFO *file); +static void fatal(int errtype, char *s); + +static int opt1, opt2, opt3; + + +int main(int argc, char **argv) +{ + extern int optind, opterr; + int c; + FINFO file1, file2; + + setlocale(LC_ALL, ""); + opterr = 0; + + while ((c = getopt(argc, argv, "123")) != -1) + switch (c) + { + case '1': + opt1 = 1; + break; + case '2': + opt2 = 1; + break; + case '3': + opt3 = 1; + break; + default: + fprintf(stderr, USAGE); + exit(1); + } + + argc -= optind; + argv += optind; + + if (argc != 2) + fatal(APPERR, USAGE); + + initfile(&file1, argv[0]); + initfile(&file2, argv[1]); + + commfiles(&file1, &file2); + + cleanup(&file1); + cleanup(&file2); + + return(0); +} + + +void initfile(FINFO *file, char *fn) +{ + int fd; + + if (strcmp(fn, "-") == 0) + fd = STDIN_FILENO; + else + if ((fd = open(fn, O_RDONLY)) == -1) + fatal(SYSERR, fn); + + file->fd = fd; + file->fn = fn; + file->eof = 0; + file->bufpos = 0; + file->eobuf = 0; + file->linesz = LINESIZE; + + if ((file->line = malloc(file->linesz)) == NULL) + fatal(APPERR, ENOMALLOC); +} + + +void cleanup(FINFO *file) +{ + free(file->line); + + if (strcmp(file->fn, "-") != 0 && close(file->fd) == -1) + fatal(SYSERR, file->fn); +} + + +void commfiles(FINFO *file1, FINFO *file2) +{ + int cmp; + + if (getline(file1) && getline(file2)) + while(1) + { + cmp = strcoll(file1->line, file2->line); + + if (cmp == 0) + { + if (! opt3) + printf("%s%s%s\n", (opt1 ? "" : "\t"), (opt2 ? "" : "\t"), file1->line); + + if ((getline(file1) + getline(file2)) < 2) + break; + } + + else if (cmp > 0) + { + if (! opt2) + printf("%s%s\n", (opt1 ? "" : "\t"), file2->line); + + if (! getline(file2)) + break; + } + + else /* cmp < 0 */ + { + if (! opt1) + printf("%s\n", file1->line); + + if (! getline(file1)) + break; + } + } + + while(! file1->eof && ! opt1) + { + printf("%s\n", file1->line); + getline(file1); + } + + while(! file2->eof && ! opt2) + { + printf("%s%s\n", (opt1 ? "" : "\t"), file2->line); + getline(file2); + } +} + + +int getline(FINFO *file) +{ + int success = 0; + size_t i = 0; + + if (! file->eof) + { + while(1) + { + if (i == file->linesz) + { + file->linesz = file->linesz + LINESIZE; + if ((file->line = realloc(file->line, file->linesz)) == NULL) + fatal(APPERR, ENOMALLOC); + } + + if (file->bufpos >= file->eobuf) + { + fillbuf(file); + if (file->eof) + { + if (i > 0) + { + file->line[i] = '\0'; + success = 1; + } + break; + } + } + + if (file->buf[file->bufpos] == '\n') + { + file->bufpos++; + file->line[i] = '\0'; + success = 1; + break; + } + else + file->line[i++] = file->buf[file->bufpos++]; + } + } + + return(success); +} + + +void fillbuf(FINFO *file) +{ + ssize_t n; + + if (! file->eof) + { + if ((n = read(file->fd, file->buf, BUFSIZE)) < 0) + fatal(SYSERR, file->fn); + + if (n == 0) + file->eof = 1; + else + { + file->bufpos = 0; + file->eobuf = n; + } + } +} + + +void fatal(int errtype, char *s) +{ + if (errtype == SYSERR) + fprintf(stderr, "comm: %s: %s\n", s, strerror(errno)); + else + fprintf(stderr, "comm: %s\n", s); + + exit(1); +} diff --git a/utils/dirname/dirname.1 b/utils/dirname/dirname.1 new file mode 100644 index 0000000..f7230e4 --- /dev/null +++ b/utils/dirname/dirname.1 @@ -0,0 +1,26 @@ +.TH DIRNAME 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +dirname \- return the directory portion of a pathname +.SH SYNOPSIS +.B dirname +string +.SH DESCRIPTION +Return the directory portion of a path/filename string. +.SH OPTIONS +.TP +None +.SH OPERANDS +.TP +string +Input pathname +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/dirname/dirname.c b/utils/dirname/dirname.c new file mode 100644 index 0000000..c490a56 --- /dev/null +++ b/utils/dirname/dirname.c @@ -0,0 +1,52 @@ +/* + * dirname.c - return the directory portion of a pathname + * + * Version: 2008-1.01 + * Build: c89 -o dirname dirname.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include + +#define USAGE "usage: dirname string\n" + + +int main(int argc, char **argv) +{ + char *head, *tail; + + setlocale (LC_ALL, ""); + + if (argc != 2) + { + fprintf(stderr, USAGE); + return(1); + } + + head = tail = argv[1]; + while (*tail) + tail++; + + while (tail > head && tail[-1] == '/') + tail--; + while (tail > head && tail[-1] != '/') + tail--; + while (tail > head && tail[-1] == '/') + tail--; + + if (head == tail) + printf(*head == '/' ? "/\n" : ".\n"); + else + printf("%.*s\n", (tail - head), head); + + return(0); +} diff --git a/utils/false/false.1 b/utils/false/false.1 new file mode 100644 index 0000000..439608f --- /dev/null +++ b/utils/false/false.1 @@ -0,0 +1,24 @@ +.TH FALSE 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +false \- return false value +.SH SYNOPSIS +.B true +.SH DESCRIPTION +Return with a non-zero exit status. +.SH OPTIONS +.TP +None +.SH OPERANDS +.TP +None +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/false/false.c b/utils/false/false.c new file mode 100644 index 0000000..49ed57c --- /dev/null +++ b/utils/false/false.c @@ -0,0 +1,21 @@ +/* + * false.c - return false value + * + * Version: 2008-1.01 + * Build: c89 -o false false.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + + +int main(void) +{ + return(1); +} diff --git a/utils/head/head.1 b/utils/head/head.1 new file mode 100644 index 0000000..9869db0 --- /dev/null +++ b/utils/head/head.1 @@ -0,0 +1,33 @@ +.TH HEAD 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +head \- copy the first part of files +.SH SYNOPSIS +.B head +[-n number] [file ...] +.SH DESCRIPTION +Copy the first 'number' lines of the input files to standard output. +.SH OPTIONS +.TP +-n number +Where 'number' is a positive integer indicating the number of lines to copy. +If omitted, head will default to copying the first 10 lines of input. +.SH OPERANDS +.TP +file +Input file(s). If none provided, or the filename '-' is passed to head, +standard input will be used. +.SH NOTES +Prior versions of head allowed '-x' as an option, with 'x' representing +the number of lines to be copied. The equivalent acceptable syntax is +now '-n x'. +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/head/head.c b/utils/head/head.c new file mode 100644 index 0000000..bf83ab3 --- /dev/null +++ b/utils/head/head.c @@ -0,0 +1,147 @@ +/* + * head.c - copy the first part of files + * + * Version: 2008-1.01 + * Build: c89 -o head head.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "usage: head [-n number] [file ...]\n" +#define BUFSIZE 4096 + +static long ctol(char *s); +static void headfile(int fd, char *fn, int lines); +static void error(char *s); + +static int exitstatus; + + +int main(int argc, char **argv) +{ + extern int opterr, optind; + extern char *optarg; + int c, fd, many, first; + char *fn; + long lines = 10; + + setlocale(LC_ALL, ""); + opterr = 0; + + while ((c = getopt(argc, argv, "n:")) != -1) + switch (c) + { + case 'n': + if (*optarg && (lines = ctol(optarg)) > 0) + break; + /* else fall through */ + default: + fprintf(stderr, USAGE); + exit(1); + } + + if (optind >= argc) + headfile(STDIN_FILENO, "stdin", lines); + else + { + many = (optind + 1 < argc) ? 1 : 0; + first = 1; + + while (optind < argc) + { + fn = argv[optind++]; + + if (many) + { + if (first) + first = 0; + else + printf("\n"); + printf("==> %s <==\n", fn); + } + + if (strcmp(fn, "-") == 0) + headfile(STDIN_FILENO, "stdin", lines); + else + if ((fd = open(fn, O_RDONLY)) == -1) + error(fn); + else + { + headfile(fd, fn, lines); + if (close(fd) == -1) + error(fn); + } + } + } + + return(exitstatus); +} + + +long ctol(char *s) +{ + int badch = 0; + char *c = s; + + /* only copes with non-zero, optionally signed, */ + /* decimal integers; that's all we need */ + if (! (isdigit(*c) || *c == '+' || *c == '-')) + badch = 1; + else + for (c++; *c; c++) + if (! isdigit(*c)) + badch = 1; + + return (badch ? 0 : atol(s)); +} + + +void headfile(int fd, char *fn, int lines) +{ + unsigned char buf[BUFSIZE], *c; + ssize_t n, o; + + while (lines) + { + if ((n = read(fd, buf, BUFSIZE)) <= 0) + break; + + for (c = buf; lines && c < (buf + n); c++) + if (*c == '\n') + lines--; + + o = lines ? n : c - buf; + if (write(STDOUT_FILENO, buf, (size_t)o) != o) + { + error("stdout"); + break; + } + } + + if (n < 0) + error(fn); +} + + +void error(char *s) +{ + fprintf(stderr, "head: %s: %s\n", s, strerror(errno)); + exitstatus = 1; +} diff --git a/utils/tail/tail.1 b/utils/tail/tail.1 new file mode 100644 index 0000000..1299399 --- /dev/null +++ b/utils/tail/tail.1 @@ -0,0 +1,54 @@ +.TH TAIL 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +tail \- copy the last part of a file +.SH SYNOPSIS +.B tail +[-f] [-c number | -n number] [file] +.SH DESCRIPTION +Copy the input file to standard output, starting from a specified location. +.SH OPTIONS +.TP +-f +Follow the file; keep updating tail's output as the input file grows. Note that +this option is only available for regular files and FIFOs that are named as +the 'file' operand, and will be ignored for other inputs (such as unnamed +pipes). +.TP +-c number +Byte offset of the input file at which output will commence. +.TP +-n number +Line offset of the input file at which output will commence. +.P +Byte and line offsets commence at the value 1. If 'number' includes a +leading '+', the offset is from the start of the input file. If 'number' +includes a leading '-' or has no prefix, it indicates that the desired +offset is backwards from the end of the input file. +.P +For example, '-n +3' will output the contents of the input file commencing from +the third line; '-c 20' or '-c -20' starts output twenty bytes prior to the end +of the input file. +.P +For byte offsets, a 'number' value of zero is not permitted. For line offsets, +the POSIX standard states that the option '-n 0' is valid, but outputs nothing, +whilst '-n +0' is invalid. +.P +Options '-c' and '-n' are mutually exclusive. If neither is specified, +the default behaviour of tail is to output the last ten lines of the input +file, as if it had been invoked with the option '-n 10'. +.SH OPERANDS +.TP +file +Input file. If none provided, or the filename '-' is passed to tail, +standard input will be used. +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/tail/tail.c b/utils/tail/tail.c new file mode 100644 index 0000000..5fa8849 --- /dev/null +++ b/utils/tail/tail.c @@ -0,0 +1,446 @@ +/* + * tail.c - copy the last part of a file + * + * Version: 2008-1.01 + * Build: c89 -o tail tail.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "usage: tail [-f] [-c number] [-n number] [file]\n" +#define NOMALLOC "Unable to allocate memory for read buffer" +#define NOFOLLOW "tail: Option [-f] ignored; can only follow regular files and FIFOs\n" +#define BADBYTES "Option [-c] requires non-zero byte offset" +#define BADLINES "Option [-n] requires integer line offset" +#define CNMUTEXCL "Options [-c] and [-n] are mutually exclusive" +#define FVANISH "Followed file has vanished" +#define FSHRUNK "Followed file has shrunk" +#define SYSERR 1 +#define APPERR 0 + +static int isdecint(char *s); +static void tailfile(int fd, char *fn); +static void tailbyteoffset(int fd, char *fn, int isregfile, ssize_t len); +static void taillineoffset(int fd, char *fn, int isregfile, ssize_t len); +static void copytoeof(int fd, char *fn); +static void fatal(int errtype, char *s); + +static unsigned char* buf; +static ssize_t bufsize, offset; +static int optf, optc, optn; + + +int main(int argc, char **argv) +{ + extern int opterr, optind; + extern char *optarg; + int c, fd; + char *fn; + + setlocale(LC_ALL, ""); + opterr = 0; + + /* + * try to get a buffer _twice_ the size mandated by the standard as in + * later processing we may be paging through the file in _half_ buffer + * increments; this way we guarentee POSIX buffer size conformance + */ + bufsize = sysconf(_SC_LINE_MAX) * 20; /* POSIX says [{LINE_MAX)*10] */ + bufsize = bufsize < 4096 ? 4096 : bufsize; + + if ((buf = malloc(bufsize)) == NULL) + fatal(APPERR, NOMALLOC); + + while ((c = getopt(argc, argv, "fc:n:")) != -1) + switch (c) + { + case 'f': /* follow file */ + optf = 1; + break; + + case 'c': /* offset in bytes */ + optc = 1; + offset = 0; + if (*optarg && isdecint(optarg)) + { + offset = atol(optarg); + offset = isdigit(*optarg) ? -offset : offset; + } + + if (offset == 0) + fatal(APPERR, BADBYTES); + else + break; + + case 'n': /* offset in lines */ + optn = 1; + if (*optarg && isdecint(optarg)) + { + offset = atol(optarg); + offset = isdigit(*optarg) ? -offset : offset; + + /* the standard says [-n 0] is OK, but [-n +0] isn't &_& */ + if (offset != 0 || (offset == 0 && *optarg != '+')) + break; + } + fatal(APPERR, BADLINES); + + default: + fprintf(stderr, USAGE); + exit(1); + } + + if (! optc && ! optn) /* if none, set the default options */ + { + optn = 1; + offset = -10; + } + + if (optc && optn) + fatal(APPERR, CNMUTEXCL); + + if (optind >= argc) + tailfile(STDIN_FILENO, "stdin"); + else + { + fn = argv[optind++]; + + if (strcmp(fn, "-") == 0) + tailfile(STDIN_FILENO, "stdin"); + else + if ((fd = open(fn, O_RDONLY)) == -1) + fatal(SYSERR, fn); + else + { + tailfile(fd, fn); + if (close(fd) == -1) + fatal(SYSERR, fn); + } + } + + return(0); +} + + +int isdecint(char *s) +{ + int badch = 0; + char *c = s; + + if (! (isdigit(*c) || *c == '+' || *c == '-')) + badch = 1; + else + for (c++; *c; c++) + if (! isdigit(*c)) + badch = 1; + + return (! badch); +} + + +void tailfile(int fd, char *fn) +{ + struct stat sbuf; + int isregfile; + ssize_t n, len; + + if (fstat(fd, &sbuf) == -1) + fatal(SYSERR, fn); + + if (S_ISREG(sbuf.st_mode)) + { + isregfile = 1; + len = sbuf.st_size; + } + else + isregfile = len = 0; + + if (offset != 0) /* [-n 0] is permitted; but no point processing that */ + { + if (optc) + tailbyteoffset(fd, fn, isregfile, len); + else + taillineoffset(fd, fn, isregfile, len); + } + + if (optf) + { + if (fd == STDIN_FILENO) /* can't follow; warn then exit no error */ + { + fprintf(stderr, NOFOLLOW); + exit(0); + } + + while(1) /* follow forever */ + { + sleep(1); + + if (isregfile) /* if we can, be polite to the user */ + { + if (stat(fn, &sbuf) == -1) + fatal(APPERR, FVANISH); + else if (sbuf.st_size < len) + fatal(APPERR, FSHRUNK); + + len = sbuf.st_size; + } + + while ((n = read(fd, buf, bufsize)) > 0) + if (write(STDOUT_FILENO, buf, n) != n) + fatal(SYSERR, "stdout"); + + if (n < 0) + fatal(SYSERR, fn); + } + } +} + + +void tailbyteoffset(int fd, char *fn, int isregfile, ssize_t len) +{ + ssize_t n, halfbuf; + + if (isregfile) /* should be seekable, so we'll do so; it's fastest */ + { + if (offset > 0) + offset = (offset - 1) > len ? len : offset - 1; + else + offset = (len + offset) > 0 ? len + offset : 0; + + if (lseek(fd, (off_t)offset, SEEK_SET) == (off_t)-1) + fatal(SYSERR, fn); + + copytoeof(fd, fn); + } + + else /* possibly non-seekable */ + { + if (offset > 0) /* forwards through file */ + { + offset--; + + while(1) + { + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n == 0) + offset = 0; + + if (offset <= n) + break; + else + offset -= n; + } + + if (write(STDOUT_FILENO, buf + offset, n - offset) != n - offset) + fatal(SYSERR, "stdout"); + + copytoeof(fd, fn); + } + + else /* backwards through file; remember that offset is negative */ + { + halfbuf = bufsize / 2; + + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n < bufsize) /* we've got the whole file */ + { + offset = (n + offset) < 0 ? 0 : n + offset; + len = n - offset; + } + + else /* we haven't got the whole file */ + { + while(1) /* page through the file, half a buffer at a time */ + { + memcpy(buf, buf + halfbuf, halfbuf); + + if ((n = read(fd, buf + halfbuf, halfbuf)) < 0) + fatal(SYSERR, fn); + else if (n < halfbuf) + break; + } + + offset = (halfbuf + n + offset) < 0 ? 0 : halfbuf + n + offset; + len = halfbuf + n - offset; + } + + if (write(STDOUT_FILENO, buf + offset, len) != len) + fatal(SYSERR, "stdout"); + } + } +} + + +void taillineoffset(int fd, char *fn, int isregfile, ssize_t len) +{ + ssize_t n, i, halfbuf; + + if (offset > 0) /* forwards through file */ + { + offset--; + + while(1) + { + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n == 0) + { + offset = 0; + break; + } + + for (i = 0; i < n && offset > 0; i++) + if (buf[i] == '\n') + offset--; + + if (offset == 0) + { + offset = i; + break; + } + } + + if (write(STDOUT_FILENO, buf + offset, n - offset) != n - offset) + fatal(SYSERR, "stdout"); + + copytoeof(fd, fn); + } + + else /* backwards through file; remember that offset is negative */ + { + if (isregfile && len > 0) /* should be seekable, so we'll do so */ + { + n = (len - bufsize) < 0 ? 0 : len - bufsize; + + if (lseek(fd, (off_t)n, SEEK_SET) == (off_t)-1) + fatal(SYSERR, fn); + + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (buf[n-1] == '\n') + offset--; + + for (i = n - 1; i >= 0 && offset < 0; i--) + if (buf[i] == '\n') + offset++; + + if (offset == 0) + offset = i + 2; + else + offset = 0; + + len = n - offset; + + if (write(STDOUT_FILENO, buf + offset, len) != len) + fatal(SYSERR, "stdout"); + } + + else + { + halfbuf = bufsize / 2; + + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n < bufsize) /* we've got the whole file */ + { + if (n == 0) + offset = 0; + else + { + if (buf[n-1] == '\n') + offset--; + + for (i = n - 1; i >= 0 && offset < 0; i--) + if (buf[i] == '\n') + offset++; + + if (offset == 0) + offset = i + 2; + else + offset = 0; + } + + len = n - offset; + } + + else /* we haven't got the whole file */ + { + while(1) /* page through the file, half a buffer at a time */ + { + memcpy(buf, buf + halfbuf, halfbuf); + + if ((n = read(fd, buf + halfbuf, halfbuf)) < 0) + fatal(SYSERR, fn); + else if (n < halfbuf) + break; + } + + if (buf[halfbuf+n-1] == '\n') + offset--; + + for (i = halfbuf + n - 1; i >= 0 && offset < 0; i--) + if (buf[i] == '\n') + offset++; + + if (offset == 0) + offset = i + 2; + else + offset = 0; + + len = halfbuf + n - offset; + } + + if (write(STDOUT_FILENO, buf + offset, len) != len) + fatal(SYSERR, "stdout"); + } + } +} + + +void copytoeof(int fd, char *fn) +{ + ssize_t n; + + while ((n = read(fd, buf, bufsize)) > 0) + if (write(STDOUT_FILENO, buf, n) != n) + fatal(SYSERR, "stdout"); + + if (n < 0) + fatal(SYSERR, fn); +} + + +void fatal(int errtype, char *s) +{ + if (errtype == SYSERR) + fprintf(stderr, "tail: %s: %s\n", s, strerror(errno)); + else + fprintf(stderr, "tail: %s\n", s); + + exit(1); +} diff --git a/utils/tee/tee.1 b/utils/tee/tee.1 new file mode 100644 index 0000000..2c13d79 --- /dev/null +++ b/utils/tee/tee.1 @@ -0,0 +1,30 @@ +.TH TEE 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +tee \- duplicate standard input +.SH SYNOPSIS +.B tee +[-ai] [file ...] +.SH DESCRIPTION +Copy standard input to standard output and any other named files. +.SH OPTIONS +.TP +-a +Append output to existing files rather than overwriting them. +.TP +-i +Ignore the SIGINT signal. +.SH OPERANDS +.TP +file +File(s) to duplicate standard input into. +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/tee/tee.c b/utils/tee/tee.c new file mode 100644 index 0000000..9b6d4cb --- /dev/null +++ b/utils/tee/tee.c @@ -0,0 +1,102 @@ +/* + * tee.c - duplicate standard input + * + * Version: 2008-1.01 + * Build: c89 -o tee tee.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "usage: tee [-ai] [file ...]\n" +#define AMBIGOPT "tee: Ambiguous [-]; operand or bad option?\n" +#define FLIMIT "tee: Maximum of %d output files exceeded\n" +#define BUFSIZE 4096 +#define MAXFILES 13 + +static void error(char *s); + +static int exitstatus; + + +int main(int argc, char **argv) +{ + extern int opterr, optind; + int c, i; + unsigned char buf[BUFSIZE]; + ssize_t n; + int slot, fdfn[MAXFILES + 1][2], mode = O_WRONLY | O_CREAT | O_TRUNC; + + setlocale(LC_ALL, ""); + opterr = 0; + + while ((c = getopt(argc, argv, "ai")) != -1) + switch (c) + { + case 'a': /* append to rather than overwrite file(s) */ + mode = O_WRONLY | O_CREAT | O_APPEND; + break; + + case 'i': /* ignore the SIGINT signal */ + signal(SIGINT, SIG_IGN); + break; + + default: + fprintf(stderr, USAGE); + return(1); + } + + i = optind; + fdfn[0][0] = STDOUT_FILENO; + + for (slot = 1; i < argc ; i++) + { + if (slot > MAXFILES) + fprintf(stderr, FLIMIT, MAXFILES); + else + { + if ((fdfn[slot][0] = open(argv[i], mode, 0666)) == -1) + error(argv[i]); + else + fdfn[slot++][1] = i; + } + } + + while ((n = read(STDIN_FILENO, buf, BUFSIZE)) > 0) + { + for (i = 0; i < slot; i++) + if (write(fdfn[i][0], buf, (size_t)n) != n) + error(argv[fdfn[i][1]]); + } + + if (n < 0) + error("stdin"); + + for (i = 1; i < slot; i++) + if (close(fdfn[i][0]) == -1) + error(argv[fdfn[i][1]]); + + return(exitstatus); +} + + +void error(char *s) +{ + fprintf(stderr, "tee: %s: %s\n", s, strerror(errno)); + exitstatus = 1; +} diff --git a/utils/true/true.1 b/utils/true/true.1 new file mode 100644 index 0000000..570117c --- /dev/null +++ b/utils/true/true.1 @@ -0,0 +1,24 @@ +.TH TRUE 1 "2008-1.01" "pdcore utilities" "User Commands" +.SH NAME +true \- return true value +.SH SYNOPSIS +.B true +.SH DESCRIPTION +Return with an exit status of zero. +.SH OPTIONS +.TP +None +.SH OPERANDS +.TP +None +.SH "AUTHORITATIVE DOCUMENTATION" + +.SH VERSION AND COMPLIANCE +Utility version 1.01 +.P +Functional compliance with POSIX:2008 (IEEE Std 1003.1-2008; +Shell and Utilities, Base Specifications Issue 7). +.SH UNLICENSE +This is free and unencumbered software released into the public domain, +provided "as is", without warranty of any kind, express or implied. See the +file UNLICENSE and the website for further details. diff --git a/utils/true/true.c b/utils/true/true.c new file mode 100644 index 0000000..23afd93 --- /dev/null +++ b/utils/true/true.c @@ -0,0 +1,21 @@ +/* + * true.c - return true value + * + * Version: 2008-1.01 + * Build: c89 -o true true.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + + +int main(void) +{ + return(0); +} -- 2.40.0