commit 8e26716a5a33fc4a7c6f5f0a72356523a8ca9119
Author: Connor Lane Smith <cls@lubutu.com>
Date: Mon, 23 May 2011 02:36:34 +0100
initial commit
Diffstat:
A | LICENSE | | | 21 | +++++++++++++++++++++ |
A | Makefile | | | 31 | +++++++++++++++++++++++++++++++ |
A | basename.1 | | | 14 | ++++++++++++++ |
A | basename.c | | | 30 | ++++++++++++++++++++++++++++++ |
A | cat.1 | | | 10 | ++++++++++ |
A | cat.c | | | 36 | ++++++++++++++++++++++++++++++++++++ |
A | config.mk | | | 9 | +++++++++ |
A | echo.1 | | | 14 | ++++++++++++++ |
A | echo.c | | | 23 | +++++++++++++++++++++++ |
A | false.1 | | | 8 | ++++++++ |
A | false.c | | | 8 | ++++++++ |
A | grep.1 | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
A | grep.c | | | 97 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tee.1 | | | 14 | ++++++++++++++ |
A | tee.c | | | 36 | ++++++++++++++++++++++++++++++++++++ |
A | touch.1 | | | 22 | ++++++++++++++++++++++ |
A | touch.c | | | 58 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | true.1 | | | 8 | ++++++++ |
A | true.c | | | 8 | ++++++++ |
A | util.c | | | 22 | ++++++++++++++++++++++ |
A | util.h | | | 3 | +++ |
A | wc.1 | | | 28 | ++++++++++++++++++++++++++++ |
A | wc.c | | | 89 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
23 files changed, 632 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,21 @@
+MIT/X Consortium License
+
+© 2011 Connor Lane Smith <cls@lubutu.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,31 @@
+include config.mk
+
+SRC = basename.c cat.c echo.c false.c grep.c tee.c touch.c true.c wc.c
+OBJ = $(SRC:.c=.o) util.o
+BIN = $(SRC:.c=)
+MAN = $(SRC:.c=.1)
+
+all: $(BIN)
+
+$(OBJ): util.h
+$(BIN): util.o
+
+.o:
+ @echo CC -o $@
+ @$(CC) -o $@ $< util.o $(LDFLAGS)
+
+.c.o:
+ @echo CC -c $<
+ @$(CC) -c $< $(CFLAGS)
+
+dist: clean
+ @echo creating dist tarball
+ @mkdir -p sbase-$(VERSION)
+ @cp LICENSE Makefile config.mk $(SRC) $(MAN) util.c util.h sbase-$(VERSION)
+ @tar -cf sbase-$(VERSION).tar sbase-$(VERSION)
+ @gzip sbase-$(VERSION).tar
+ @rm -rf sbase-$(VERSION)
+
+clean:
+ @echo cleaning
+ @rm -f $(BIN) $(OBJ)
diff --git a/basename.1 b/basename.1
@@ -0,0 +1,14 @@
+.TH BASENAME 1 sbase\-VERSION
+.SH NAME
+basename \- strip directory from filename
+.SH SYNOPSIS
+.B basename
+.I string
+.RI [ suffix ]
+.SH DESCRIPTION
+.B basename
+prints to stdout the
+.I string
+with any leading directory components, and the
+.IR suffix ,
+removed.
diff --git a/basename.c b/basename.c
@@ -0,0 +1,30 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+
+int
+main(int argc, char *argv[])
+{
+ char *str = argv[1];
+ size_t n, i = 0;
+
+ if(argc < 2)
+ eprintf("usage: %s string [suffix]\n", argv[0]);
+ if(str[0] != '\0')
+ for(i = strlen(str)-1; i > 0 && str[i] == '/'; i--)
+ str[i] = '\0';
+ if(i == 0 || !(str = strrchr(argv[1], '/')))
+ str = argv[1];
+ else
+ str++;
+
+ if(argc > 2 && strlen(str) > strlen(argv[2])) {
+ n = strlen(str) - strlen(argv[2]);
+ if(!strcmp(&str[n], argv[2]))
+ str[n] = '\0';
+ }
+ puts(str);
+ return EXIT_SUCCESS;
+}
diff --git a/cat.1 b/cat.1
@@ -0,0 +1,10 @@
+.TH CAT 1 sbase\-VERSION
+.SH NAME
+cat \- concatenate files
+.SH SYNOPSIS
+.B cat
+.RI [ files ...]
+.SH DESCRIPTION
+.B cat
+reads each file in sequence and writes it to stdout. If no file is given, cat
+reads from stdin.
diff --git a/cat.c b/cat.c
@@ -0,0 +1,36 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include "util.h"
+
+static void cat(FILE *, const char *);
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ FILE *fp;
+
+ if(argc == 1)
+ cat(stdin, "<stdin>");
+ else for(i = 1; i < argc; i++) {
+ if(!(fp = fopen(argv[i], "r")))
+ eprintf("fopen %s:", argv[i]);
+ cat(fp, argv[i]);
+ fclose(fp);
+ }
+ return EXIT_SUCCESS;
+}
+
+void
+cat(FILE *fp, const char *str)
+{
+ char buf[BUFSIZ];
+ size_t n;
+
+ while((n = fread(buf, 1, sizeof buf, fp)) > 0)
+ if(fwrite(buf, 1, n, stdout) != n)
+ eprintf("<stdout>: write error:");
+ if(ferror(fp))
+ eprintf("%s: read error:", str);
+}
diff --git a/config.mk b/config.mk
@@ -0,0 +1,9 @@
+# sbase version
+VERSION = 0.0
+
+CC = cc
+#CC = musl-gcc
+
+CPPFLAGS = -D_BSD_SOURCE
+CFLAGS = -Os -ansi -Wall -pedantic $(CPPFLAGS)
+LDFLAGS = -s -static
diff --git a/echo.1 b/echo.1
@@ -0,0 +1,14 @@
+.TH ECHO 1 sbase\-VERSION
+.SH NAME
+echo \- print arguments
+.SH SYNOPSIS
+.B echo
+.RB [ \-n ]
+.RI [ string ...]
+.SH DESCRIPTION
+.B echo
+prints its arguments to stdout, separated by spaces and terminated by a newline.
+.SH OPTIONS
+.TP
+.B \-n
+Do not print terminating newline.
diff --git a/echo.c b/echo.c
@@ -0,0 +1,23 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main(int argc, char *argv[])
+{
+ bool nflag = false;
+ int i;
+
+ if(argc > 1 && !strcmp(argv[1], "-n"))
+ nflag = true;
+ for(i = nflag ? 2 : 1; i < argc; i++) {
+ fputs(argv[i], stdout);
+ if(i+1 < argc)
+ fputc(' ', stdout);
+ }
+ if(!nflag)
+ fputc('\n', stdout);
+ return EXIT_SUCCESS;
+}
diff --git a/false.1 b/false.1
@@ -0,0 +1,8 @@
+.TH FALSE 1 sbase\-VERSION
+.SH NAME
+false \- return failure
+.SH SYNOPSIS
+.B false
+.SH DESCRIPTION
+.B false
+returns with a status code indicating failure.
diff --git a/false.c b/false.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdlib.h>
+
+int
+main(void)
+{
+ return EXIT_FAILURE;
+}
diff --git a/grep.1 b/grep.1
@@ -0,0 +1,43 @@
+.TH GREP 1 sbase\-VERSION
+.SH NAME
+grep \- search files for a pattern
+.SH SYNOPSIS
+.B grep
+.RB [ \-c ]
+.RB [ \-i ]
+.RB [ \-l ]
+.RB [ \-n ]
+.RB [ \-q ]
+.RB [ \-v ]
+.I pattern
+.RI [ file ...]
+.SH DESCRIPTION
+.B grep
+searches the input files for lines that match the pattern, a regular expression as defined in
+.BR regex (7).
+By default each matching line is printed to stdout. If no file is given, grep
+reads from stdin.
+.P
+The status code is 0 if any lines match, and 1 if not. If an error occurred the
+status code is 2.
+.SH OPTIONS
+.TP
+.B \-c
+prints only a count of matching lines.
+.TP
+.B \-i
+matches lines case insensitively.
+.TP
+.B \-l
+prints only the names of files with matching lines.
+.TP
+.B \-n
+prefixes each matching line with its line number in the input.
+.TP
+.B \-q
+prints nothing, only returns status.
+.TP
+.B \-v
+selects lines which do
+.B not
+match the pattern.
diff --git a/grep.c b/grep.c
@@ -0,0 +1,97 @@
+/* See LICENSE file for copyright and license details. */
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void grep(FILE *, const char *, regex_t *);
+
+static bool iflag = false;
+static bool vflag = false;
+static bool many;
+static bool match = false;
+static char mode = 0;
+
+int
+main(int argc, char *argv[])
+{
+ int i, flags = 0;
+ regex_t preg;
+ FILE *fp;
+
+ for(i = 1; i < argc; i++)
+ if(!strcmp(argv[i], "-c"))
+ mode = 'c';
+ else if(!strcmp(argv[i], "-i"))
+ iflag = true;
+ else if(!strcmp(argv[i], "-l"))
+ mode = 'l';
+ else if(!strcmp(argv[i], "-n"))
+ mode = 'n';
+ else if(!strcmp(argv[i], "-q"))
+ mode = 'q';
+ else if(!strcmp(argv[i], "-v"))
+ vflag = true;
+ else
+ break;
+
+ if(i == argc) {
+ fprintf(stderr, "usage: %s [-c] [-i] [-l] [-n] [-v] pattern [files...]\n", argv[0]);
+ exit(2);
+ }
+ if(mode == 'c')
+ flags |= REG_NOSUB;
+ if(iflag)
+ flags |= REG_ICASE;
+ regcomp(&preg, argv[i++], flags);
+
+ many = (argc > i+1);
+ if(i == argc)
+ grep(stdin, "<stdin>", &preg);
+ else for(; i < argc; i++) {
+ if(!(fp = fopen(argv[i], "r"))) {
+ fprintf(stderr, "fopen %s: ", argv[i]);
+ perror(NULL);
+ exit(2);
+ }
+ grep(fp, argv[i], &preg);
+ fclose(fp);
+ }
+ return match ? 0 : 1;
+}
+
+void
+grep(FILE *fp, const char *str, regex_t *preg)
+{
+ char buf[BUFSIZ];
+ int n, c = 0;
+
+ for(n = 1; fgets(buf, sizeof buf, fp); n++) {
+ if(regexec(preg, buf, 0, NULL, 0) ^ vflag)
+ continue;
+ if(mode == 'c')
+ c++;
+ else if(mode == 'l') {
+ puts(str);
+ break;
+ }
+ else if(mode == 'q')
+ exit(0);
+ else {
+ if(many)
+ printf("%s:", str);
+ if(mode == 'n')
+ printf("%d:", n);
+ fputs(buf, stdout);
+ }
+ match = true;
+ }
+ if(mode == 'c')
+ printf("%d\n", c);
+ if(ferror(fp)) {
+ fprintf(stderr, "%s: read error: ", str);
+ perror(NULL);
+ exit(2);
+ }
+}
diff --git a/tee.1 b/tee.1
@@ -0,0 +1,14 @@
+.TH TEE 1 sbase\-VERSION
+.SH NAME
+tee \- duplicate stdin
+.SH SYNOPSIS
+.B tee
+.RB [ \-a ]
+.RI [ file ...]
+.SH DESCRIPTION
+.B tee
+writes from stdin to stdout, making copies in each file.
+.SH OPTIONS
+.TP
+.B \-a
+append to each file rather than overwriting.
diff --git a/tee.c b/tee.c
@@ -0,0 +1,36 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+
+int
+main(int argc, char *argv[])
+{
+ bool aflag = false;
+ char buf[BUFSIZ];
+ int i, nfps = 1;
+ size_t n;
+ FILE **fps;
+
+ if(argc > 1 && !strcmp(argv[1], "-a"))
+ aflag = true;
+ if(!(fps = malloc(sizeof *fps)))
+ eprintf("malloc:");
+ fps[nfps-1] = stdout;
+
+ for(i = aflag ? 2 : 1; i < argc; i++) {
+ if(!(fps = realloc(fps, ++nfps * sizeof *fps)))
+ eprintf("realloc:");
+ if(!(fps[nfps-1] = fopen(argv[i], aflag ? "a" : "w")))
+ eprintf("fopen %s:", argv[i]);
+ }
+ while((n = fread(buf, 1, sizeof buf, stdin)) > 0)
+ for(i = 0; i < nfps; i++)
+ if(fwrite(buf, 1, n, fps[i]) != n)
+ eprintf("%s: write error:", buf);
+ if(ferror(stdin))
+ eprintf("<stdin>: read error:");
+ return EXIT_SUCCESS;
+}
diff --git a/touch.1 b/touch.1
@@ -0,0 +1,22 @@
+.TH TOUCH 1 sbase\-VERSION
+.SH NAME
+touch \- set files' modification time
+.SH SYNOPSIS
+.B touch
+.RB [ \-c ]
+.RB [ \-t
+.IR time ]
+.RI [ file ...]
+.SH DESCRIPTION
+.B touch
+sets the files' modification time to the current time. If a file does not exist
+it is created.
+.SH OPTIONS
+.TP
+.B \-c
+do not create files if they do not exist.
+.TP
+.BI \-t " time"
+sets the files' modification time to
+.IR time ,
+given as the number of seconds since the Unix epoch.
diff --git a/touch.c b/touch.c
@@ -0,0 +1,58 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include "util.h"
+
+static void touch(const char *);
+
+static bool cflag = false;
+static time_t t;
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ t = time(NULL);
+
+ for(i = 1; i < argc; i++)
+ if(!strcmp(argv[i], "-c"))
+ cflag = true;
+ else if(!strcmp(argv[i], "-t") && i+1 < argc)
+ t = strtol(argv[++i], NULL, 0);
+ else
+ break;
+
+ for(; i < argc; i++)
+ touch(argv[i]);
+ return EXIT_SUCCESS;
+}
+
+void
+touch(const char *str)
+{
+ int fd;
+ struct stat st;
+ struct utimbuf ut;
+
+ if(stat(str, &st) < 0) {
+ if(errno != ENOENT)
+ eprintf("stat %s:", str);
+ if(cflag)
+ return;
+ if((fd = creat(str, O_RDONLY|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
+ eprintf("creat %s:", str);
+ close(fd);
+ }
+ ut.actime = st.st_atime;
+ ut.modtime = t;
+ if(utime(str, &ut) < 0)
+ eprintf("utime %s:", str);
+}
diff --git a/true.1 b/true.1
@@ -0,0 +1,8 @@
+.TH TRUE 1 sbase\-VERSION
+.SH NAME
+true \- return success
+.SH SYNOPSIS
+.B true
+.SH DESCRIPTION
+.B true
+returns with a status code indicating success.
diff --git a/true.c b/true.c
@@ -0,0 +1,8 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdlib.h>
+
+int
+main(void)
+{
+ return EXIT_SUCCESS;
+}
diff --git a/util.c b/util.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+
+void
+eprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if(fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ }
+ exit(EXIT_FAILURE);
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,3 @@
+/* See LICENSE file for copyright and license details. */
+
+void eprintf(const char *, ...);
diff --git a/wc.1 b/wc.1
@@ -0,0 +1,28 @@
+.TH WC 1 sbase\-VERSION
+.SH NAME
+wc \- word count
+.SH SYNOPSIS
+.B wc
+.RB [ \-c ]
+.RB [ \-l ]
+.RB [ \-m ]
+.RB [ \-w ]
+.RI [ file ...]
+.SH DESCRIPTION
+.B wc
+prints the number of lines, words, and bytes in each file. If any flags are
+given, wc will print only the requested information. If no files are given, wc
+reads stdin.
+.SH OPTIONS
+.TP
+.B \-c
+print the number of bytes.
+.TP
+.B \-l
+print the number of lines.
+.TP
+.B \-m
+print the number of characters, not bytes.
+.TP
+.B \-w
+print the number of words.
diff --git a/wc.c b/wc.c
@@ -0,0 +1,89 @@
+/* See LICENSE file for copyright and license details. */
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+
+static void output(const char *, long, long, long);
+static void wc(FILE *, const char *);
+
+static bool lflag = false;
+static bool wflag = false;
+static char cmode = 0;
+static long tc = 0, tl = 0, tw = 0;
+
+int
+main(int argc, char *argv[])
+{
+ bool many;
+ int i;
+ FILE *fp;
+
+ for(i = 1; i < argc; i++)
+ if(!strcmp(argv[i], "-c"))
+ cmode = 'c';
+ else if(!strcmp(argv[i], "-l"))
+ lflag = true;
+ else if(!strcmp(argv[i], "-m"))
+ cmode = 'm';
+ else if(!strcmp(argv[i], "-w"))
+ wflag = true;
+ else
+ break;
+ many = (argc > i+1);
+
+ if(i == argc)
+ wc(stdin, NULL);
+ else for(; i < argc; i++) {
+ if(!(fp = fopen(argv[i], "r")))
+ eprintf("fopen %s:", argv[i]);
+ wc(fp, argv[i]);
+ fclose(fp);
+ }
+ if(many)
+ output("total", tc, tl, tw);
+ return EXIT_SUCCESS;
+}
+
+void
+output(const char *str, long nc, long nl, long nw)
+{
+ bool noflags = !cmode && !lflag && !wflag;
+
+ if(lflag || noflags)
+ printf(" %5ld", nl);
+ if(wflag || noflags)
+ printf(" %5ld", nw);
+ if(cmode || noflags)
+ printf(" %5ld", nc);
+ if(str)
+ printf(" %s", str);
+ fputc('\n', stdout);
+}
+
+void
+wc(FILE *fp, const char *str)
+{
+ bool word = false;
+ char c;
+ long nc = 0, nl = 0, nw = 0;
+
+ while((c = fgetc(fp)) != EOF) {
+ if(cmode != 'm' || (c & 0xc0) != 0x80)
+ nc++;
+ if(c == '\n')
+ nl++;
+ if(!isspace(c))
+ word = true;
+ else if(word) {
+ word = false;
+ nw++;
+ }
+ }
+ tc += nc;
+ tl += nl;
+ tw += nw;
+ output(str, nc, nl, nw);
+}