diff --git a/Makefile b/Makefile index 9e6bf07..cb3e93e 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ include config.mk SUBDIRS = lib9 yacc awk basename bc dc cat cleanname date echo grep mk \ - rc sed seq sleep sort tee test touch tr uniq + rc read sed seq sleep sort tee test touch tr uniq all: @echo 9base build options: diff --git a/read/Makefile b/read/Makefile new file mode 100644 index 0000000..7853500 --- /dev/null +++ b/read/Makefile @@ -0,0 +1,6 @@ +# read - read unix port from plan9 +# Depends on ../lib9 + +TARG = read + +include ../std.mk diff --git a/read/read.1 b/read/read.1 new file mode 100644 index 0000000..0738206 --- /dev/null +++ b/read/read.1 @@ -0,0 +1,108 @@ +.TH CAT 1 +.SH NAME +cat, read, nobs \- catenate files +.SH SYNOPSIS +.B cat +[ +.I file ... +] +.br +.B read +[ +.B -m +] [ +.B -n +.I nline +] [ +.I file ... +] +.br +.B nobs +[ +.I file ... +] +.SH DESCRIPTION +.I Cat +reads each +.I file +in sequence and writes it on the standard output. +Thus +.IP +.L +cat file +.LP +prints a file and +.IP +.L +cat file1 file2 >file3 +.LP +concatenates the first two files and places the result +on the third. +.PP +If no +.I file +is given, +.I cat +reads from the standard input. +Output is buffered in blocks matching the input. +.PP +.I Read +copies to standard output exactly one line from the named +.IR file , +default standard input. +It is useful in interactive +.IR rc (1) +scripts. +.PP +The +.B -m +flag causes it to continue reading and writing multiple lines until end of file; +.B -n +causes it to read no more than +.I nline +lines. +.PP +.I Read +always executes a single +.B write +for each line of input, which can be helpful when +preparing input to programs that expect line-at-a-time data. +It never reads any more data from the input than it prints to the output. +.PP +.I Nobs +copies the named files to +standard output except that it removes all backspace +characters and the characters that precede them. +It is useful to use as +.B $PAGER +with the Unix version of +.IR man (1) +when run inside a +.I win +(see +.IR acme (1)) +window. +.SH SOURCE +.B \*9/src/cmd/cat.c +.br +.B \*9/src/cmd/read.c +.br +.B \*9/bin/nobs +.SH SEE ALSO +.IR cp (1) +.SH DIAGNOSTICS +.I Read +exits with status +.B eof +on end of file or, in the +.B -n +case, if it doesn't read +.I nlines +lines. +.SH BUGS +Beware of +.L "cat a b >a" +and +.LR "cat a b >b" , +which +destroy input files before reading them. diff --git a/read/read.c b/read/read.c new file mode 100644 index 0000000..56d8013 --- /dev/null +++ b/read/read.c @@ -0,0 +1,91 @@ +#include +#include + +int multi; +int nlines; +char *status = nil; + +int +line(int fd, char *file) +{ + char c; + int m, n, nalloc; + char *buf; + + nalloc = 0; + buf = nil; + for(m=0; ; ){ + n = read(fd, &c, 1); + if(n < 0){ + fprint(2, "read: error reading %s: %r\n", file); + exits("read error"); + } + if(n == 0){ + if(m == 0) + status = "eof"; + break; + } + if(m == nalloc){ + nalloc += 1024; + buf = realloc(buf, nalloc); + if(buf == nil){ + fprint(2, "read: malloc error: %r\n"); + exits("malloc"); + } + } + buf[m++] = c; + if(c == '\n') + break; + } + if(m > 0) + write(1, buf, m); + free(buf); + return m; +} + +void +lines(int fd, char *file) +{ + do{ + if(line(fd, file) == 0) + break; + }while(multi || --nlines>0); +} + +void +main(int argc, char *argv[]) +{ + int i, fd; + char *s; + + ARGBEGIN{ + case 'm': + multi = 1; + break; + case 'n': + s = ARGF(); + if(s){ + nlines = atoi(s); + break; + } + /* fall through */ + default: + fprint(2, "usage: read [-m] [-n nlines] [files...]\n"); + exits("usage"); + }ARGEND + + if(argc == 0) + lines(0, ""); + else + for(i=0; i