diff --git a/Makefile b/Makefile index 84c092f..265e554 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ include config.mk -LIB = util/afgets.o util/agetcwd.o util/enmasse.o util/eprintf.o util/recurse.o +LIB = util/afgets.o util/agetcwd.o util/concat.o util/enmasse.o util/eprintf.o \ + util/recurse.o SRC = basename.c cat.c chown.c date.c dirname.c echo.c false.c grep.c head.c \ - ln.c ls.c mkdir.c mkfifo.c pwd.c rm.c sleep.c tee.c touch.c true.c wc.c + ln.c ls.c mkdir.c mkfifo.c pwd.c rm.c sleep.c tail.c tee.c touch.c \ + true.c wc.c OBJ = $(SRC:.c=.o) $(LIB) BIN = $(SRC:.c=) MAN = $(SRC:.c=.1) diff --git a/basename.1 b/basename.1 index a2247ca..9deb351 100644 --- a/basename.1 +++ b/basename.1 @@ -13,4 +13,5 @@ with any leading path components, and the .IR suffix , removed. .SH SEE ALSO +.IR dirname (1), .IR basename (3) diff --git a/cat.c b/cat.c index 81a1eb4..99ae349 100644 --- a/cat.c +++ b/cat.c @@ -2,10 +2,9 @@ #include #include #include +#include "text.h" #include "util.h" -static void cat(FILE *, const char *); - int main(int argc, char *argv[]) { @@ -14,25 +13,12 @@ main(int argc, char *argv[]) if(getopt(argc, argv, "") != -1) exit(EXIT_FAILURE); if(optind == argc) - cat(stdin, ""); + concat(stdin, "", stdout, ""); else for(; optind < argc; optind++) { if(!(fp = fopen(argv[optind], "r"))) eprintf("fopen %s:", argv[optind]); - cat(fp, argv[optind]); + concat(fp, argv[optind], stdout, ""); 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(": write error:"); - if(ferror(fp)) - eprintf("%s: read error:", str); -} diff --git a/dirname.1 b/dirname.1 index ba25657..d750a33 100644 --- a/dirname.1 +++ b/dirname.1 @@ -10,4 +10,5 @@ prints the .I string with its final path component removed. .SH SEE ALSO +.IR basename (1), .IR dirname (3) diff --git a/head.1 b/head.1 index 4d9e588..6b26fc6 100644 --- a/head.1 +++ b/head.1 @@ -14,3 +14,5 @@ reads from stdin. .TP .BI \-n " lines" outputs the given number of lines. +.SH SEE ALSO +.IR tail (1) diff --git a/tail.1 b/tail.1 new file mode 100644 index 0000000..7d1ab61 --- /dev/null +++ b/tail.1 @@ -0,0 +1,20 @@ +.TH TAIL 1 sbase\-VERSION +.SH NAME +tail \- output last part of a file +.SH SYNOPSIS +.B tail +.RB [ \-n +.IR lines ] +.RI [ file ] +.SH DESCRIPTION +.B tail +writes the first 10 lines of the file to stdout. If no file is given, tail +reads from stdin. +.SH OPTIONS +.TP +.BI \-n " lines" +outputs the given number of lines. If +.I lines +begins with '+' it is used as an offset from the beginning of the file. +.SH SEE ALSO +.IR head (1) diff --git a/tail.c b/tail.c new file mode 100644 index 0000000..a53cbba --- /dev/null +++ b/tail.c @@ -0,0 +1,80 @@ +/* See LICENSE file for copyright and license detaketails. */ +#include +#include +#include +#include +#include "text.h" +#include "util.h" + +static void dropinit(FILE *, const char *, long); +static void taketail(FILE *, const char *, long); + +static void (*tail)(FILE *, const char *, long) = taketail; + +int +main(int argc, char *argv[]) +{ + char *end, c; + long n = 10; + FILE *fp; + + while((c = getopt(argc, argv, "n:")) != -1) + switch(c) { + case 'n': + n = abs(strtol(optarg, &end, 0)); + if(*end != '\0') + eprintf("%s: not a number\n", optarg); + if(optarg[0] == '+') + tail = dropinit; + break; + default: + exit(EXIT_FAILURE); + } + if(optind == argc) + tail(stdin, "", n); + else if(optind == argc-1) { + if(!(fp = fopen(argv[optind], "r"))) + eprintf("fopen %s:", argv[optind]); + tail(fp, argv[optind], n); + fclose(fp); + } + else + eprintf("usage: %s [-n lines] [file]\n", argv[0]); + + return EXIT_SUCCESS; +} + +void +dropinit(FILE *fp, const char *str, long n) +{ + char buf[BUFSIZ]; + long i = 0; + + while(i < n && fgets(buf, sizeof buf, fp)) + if(buf[strlen(buf)-1] == '\n') + i++; + concat(fp, str, stdout, ""); +} + +void +taketail(FILE *fp, const char *str, long n) +{ + char **ring; + long i, j; + size_t *size; + + if(!(ring = calloc(n, sizeof *ring)) || !(size = calloc(n, sizeof *size))) + eprintf("calloc:"); + for(i = j = 0; afgets(&ring[i], &size[i], fp); i = j = (i+1)%n) + ; + + do { + if(ring[j]) + fputs(ring[j], stdout); + } while((j = (j+1)%n) != i); + free(ring); + free(size); + + if(ferror(fp)) + eprintf("%s: read error:", str); +} diff --git a/text.h b/text.h index e5e2264..a686518 100644 --- a/text.h +++ b/text.h @@ -1,3 +1,4 @@ /* See LICENSE file for copyright and license details. */ -char *afgets(char **p, size_t *size, FILE *fp); +char *afgets(char **, size_t *, FILE *); +void concat(FILE *, const char *, FILE *, const char *); diff --git a/util/concat.c b/util/concat.c new file mode 100644 index 0000000..6dd923a --- /dev/null +++ b/util/concat.c @@ -0,0 +1,17 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include "../text.h" +#include "../util.h" + +void +concat(FILE *fp1, const char *s1, FILE *fp2, const char *s2) +{ + char buf[BUFSIZ]; + size_t n; + + while((n = fread(buf, 1, sizeof buf, fp1)) > 0) + if(fwrite(buf, 1, n, fp2) != n) + eprintf("%s: write error:", s2); + if(ferror(fp1)) + eprintf("%s: read error:", s1); +}