Skip to content

Instantly share code, notes, and snippets.

@sinclairtarget
Last active September 27, 2024 05:42
Show Gist options
  • Save sinclairtarget/47143ba52b9d9e360d8db3762ee0cbf5 to your computer and use it in GitHub Desktop.
Save sinclairtarget/47143ba52b9d9e360d8db3762ee0cbf5 to your computer and use it in GitHub Desktop.
cat through the ages
" cat
lac 017777 i " Load accumulator (AC) with argument count
sad d4 " Skip next if we have more than 4 words of args
jmp nofiles " Otherwise, jump to nofiles
lac 017777 " Load AC with address of args
tad d1 " Increment AC by 1, past argument count
tad d4 " Increment AC by 4, now AC points to first real arg
dac name " Save arg pointer from AC into 'name'
loop:
sys open; name: 0; 0 " Open filename in 'name' in read mode
spa " Skip next if AC is greater than 0
jmp badfile " File descriptor is not positive, jump to badfile
dac fi " Save AC, now the file descriptor, in 'fi'
1:
jms getc " Call subroutine getc
sad o4 " Skip next if getc did not read EOF
jmp 1f " Read EOF, jump forward to 1
jms putc " Call subroutine putc
jmp 1b " Jump back to 1
1:
lac fi " Load AC with file descriptor
sys close " Close file
loop1: " Below we basically subtract 4 from our arg count
-4 " Maybe this should be 'lac -4'? Not sure
tad 017777 i " Increment AC by count of args left
dac 017777 i " Save AC to count of args left
sad d4 " Skip next if AC is not 4
jmp done " No args left, jump to done
lac name " Load AC from name
tad d4 " Increment by 4 words
dac name " Save AC to name, name is now next arg
jmp loop " Jump back to loop
badfile:
lac name " Load AC with bad filename
dac 1f " Save filename to label 1 below
lac d8 " Load 8 into AC (point write to stderr?)
sys write; 1:0; 4 " Write four words of filename
lac d8 " Load 8 into AC again
sys write; 1f; 2 " Write ' ?\n'
jmp loop1 " Jump back to loop1
1: 040;077012 " String literal ' ?\n'
nofiles:
lac d8 " Load 8 into AC (point write to stderr?)
sys write; 1f; 5 " Write 'no files\n'
sys exit
1: <no>; 040; <fi>;<le>;<s 012 " String literal 'no files\n'
done: " Flush the output buffer if necessary before exit
lac noc " Load AC with num chars left
sns " Skip next if num chars left is not zero (sna?)
sys exit " No chars left, exit
and d1 " binary AND with 1, check num chars left is odd?
sna cla " skip next if AC is not zero (num chars left is odd)
jmp 1f " num chars left is even, jump forward to 1
jms putc " call putc
jmp done " go back up to 1
1:
lac noc " Load AC with num chars left
rcr " Divide by two?
dac 1f " Store AC in label 1 below
lac fo " Load AC with file out descriptor
sys write; iopt+1; 1:.. " Write remaining chars
sys exit
getc: 0
lac ipt " Load pointer to next word in buffer
sad eipt " Is pointer at end of input buffer?
jmp 1f " Yes, jump forward to 1, we need to read more
dac 2f " Not
add o400000 " so
dac ipt " sure
ral " about
lac 2f i " all
szl " this
lrss 9
and o177
sna
jmp getc+1
jmp getc i
1:
lac fi " Buffer empty, load file descriptor into AC
sys read; iipt+1; 64 " Read into input buffer
sna " Skip next if AC is not zero
jmp 1f " We read zero characters, jump forward to 1
tad iipt " Add chars read to input buffer base
dac eipt " Save result in end buffer pointer
lac iipt " Load base input buffer pointer
dac ipt " Store in input pointer (so reset ipt to iipt)
jmp getc+1 " Jump back to beginning of subroutine
1:
lac o4 " No chars left, return 4 (EOF?)
jmp getc i " Return
putc: 0 " Also not sure I understand what is going on here
and o177
dac 2f+1
lac opt
dac 2f
add o400000
dac opt
spa
jmp 1f
lac 2f i
xor 2f+1
jmp 3f
1:
lac 2f+1
alss 9
3:
dac 2f i
isz noc
lac noc
sad d128
skp
jmp putc i
lac fo
sys write; iopt+1; 64
lac iopt
dac opt
dzm noc
jmp putc i
2: 0;0
ipt: 0 " Input pointer (points to next word)
eipt: 0 " Input buffer end pointer
iipt: .+1; .=.+64 " Input buffer base pointer and input buffer
fi: 0
opt: .+2
iopt: .+1; .=.+64 " Output buffer base pointer and output buffer
noc: 0 " num chars queued to write
fo: 1
d1: 1 " Octal and decimal constants?
o4:d4: 4
d8: 8
o400000: 0400000
o177: 0177
d128: 128
/ cat -- concatinate files
mov (sp)+,r5 / Pop argument count off stack and save in r5
tst (sp)+ / Pop first argument and discard it
mov $obuf,r2 / Store address of output buffer in r2
cmp r5,$1 / Is argument count 1?
beq 3f / If argument count was 1, jump to 3 below
loop:
dec r5 / Decrement argument count
ble done / If argument count is <= 0, jump to done
mov (sp)+,r0 / Pop next argument and save in r0
cmpb (r0),$'- / Is next argument "-"?
bne 2f / If next argument was not "-", jump down to 2
clr fin / Clear file descriptor
br 3f / Jump down to 3
2:
mov r0,0f / Store file name in r0 at label 0 right below
sys open; 0:..; 0 / Open file in read mode (r0 now file descriptor)
bes loop / If error opening file, jump to loop
mov r0,fin / Save file descriptor to fin
3:
mov fin,r0 / Copy file descriptor to r0
sys read; ibuf; 512. / Read from file descriptor in r0 into input buffer
bes 3f / Jump forward to 3 on error
mov r0,r4 / r0 now contains bytes read, save in r4
beq 3f / If bytes read was zero, jump down to 3
mov $ibuf,r3 / Copy address of input buffer to r3
4:
movb (r3)+,r0 / Copy byte from input buffer to r0, auto-increment
jsr pc,putc / Call putc subroutine
dec r4 / Decrement r4 (bytes read/left to write)
bne 4b / If there are bytes left, jump back to 4
br 3b / Otherwise, jump back to 3
3:
mov fin,r0 / Copy file descriptor to r0
beq loop / If file descriptor was 0 (stdin), jump to loop
sys close / Otherwise close file descriptor
br loop / Jump to loop
done:
sub $obuf,r2 / Do we still have anything in the output buffer?
beq 1f / If no, jump down to 1
mov r2,0f / Copy bytes left to flush to label 0 below
mov $1,r0 / Set up to write to stdout
sys write; obuf; 0:.. / Write remaining bytes to stdout
1:
sys exit
putc:
movb r0,(r2)+ / Copy byte from input to output buffer
cmp r2,$obuf+512. / Check output buffer size
blo 1f / If output buffer is not full, jump down to 1
mov $1,r0 / Set up to write to stdout
sys write; obuf; 512. / Write output buffer
mov $obuf,r2 / Reset output buffer index
1:
rts pc / Return from subroutine
.bss
ibuf: .=.+512. / Input buffer
obuf: .=.+512. / Output buffer
fin: .=.+2 / Input file descriptor
.text
/ Not sure why this is here, seems to just repeat code from above?
/ Next line doesn't look like a complete statement
,$'-
bne 2f
clr fin
br 3f
2:
mov r0,0f
sys open; 0:..; 0
bes loop
mov r0,fin
3:
mov fin,r0
sys read; ibuf; 512.
bes 3f
mov r0,r4
beq 3f
mov $ibuf,r3
4:
movb (r3)+,r0
jsr pc,putc
dec r4
bne 4b
br 3b
3:
mov fin,r0
beq loop
sys close
br loop
done:
sub $obuf,r2
beq 1f
mov r2,0f
mov $1,r0
sys write; obuf; 0:..
1:
sys exit
putc:
movb r0,(r2)+
cmp r2
/*
* Concatenate files.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
/* All comments below this one not in the original file. */
char stdbuf[BUFSIZ]; /* BUFSIZ is typically 512, according to man page. */
/* Old style function declaration. Return type defaults to int. Param types
* default to int. You can specify param types (when not int) after the
* parentheses like below.
*/
main(argc, argv)
char **argv;
{
int fflg = 0; /* Flag set to 1 if reading from stdin */
register FILE *fi; /* File handle */
register c; /* Current char */
int dev, ino = -1; /* Keep track of file inodes */
struct stat statb; /* File status struct */
/* Handle -u flag */
setbuf(stdout, stdbuf);
for( ; argc>1 && argv[1][0]=='-'; argc--,argv++) {
switch(argv[1][1]) {
case 0:
break;
case 'u': /* -u flag means don't buffer */
setbuf(stdout, (char *)NULL);
continue;
}
break;
}
fstat(fileno(stdout), &statb); /* Get file status. */
statb.st_mode &= S_IFMT; /* Use mask to get file type. */
/* If file is not "character special" or "block special"... */
if (statb.st_mode!=S_IFCHR && statb.st_mode!=S_IFBLK) {
/* Save file inode data in local vars */
dev = statb.st_dev;
ino = statb.st_ino;
}
if (argc < 2) {
argc = 2;
fflg++; /* We're reading from stdin */
}
while (--argc > 0) {
if (fflg || (*++argv)[0]=='-' && (*argv)[1]=='\0')
fi = stdin;
else {
if ((fi = fopen(*argv, "r")) == NULL) {
fprintf(stderr, "cat: can't open %s\n", *argv);
continue;
}
}
fstat(fileno(fi), &statb);
/* Check if we're reading from and writing to same file */
if (statb.st_dev==dev && statb.st_ino==ino) {
fprintf(stderr, "cat: input %s is output\n",
fflg?"-": *argv);
fclose(fi);
continue;
}
while ((c = getc(fi)) != EOF)
putchar(c);
if (fi!=stdin)
fclose(fi);
}
return(0);
}
/*
* Concatenate files.
*/
static char *Sccsid = "@(#)cat.c 4.2 (Berkeley) 10/9/80";
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
/* All comments below this one not in the original file. */
char stdbuf[BUFSIZ];
int bflg, eflg, nflg, sflg, tflg, vflg;
int spaced, col, lno, inline;
main(argc, argv)
char **argv;
{
int fflg = 0;
register FILE *fi;
register c;
int dev, ino = -1;
struct stat statb;
lno = 1;
setbuf(stdout, stdbuf);
for( ; argc>1 && argv[1][0]=='-'; argc--,argv++) {
switch(argv[1][1]) {
case 0:
break;
case 'u': /* Don't buffer */
setbuf(stdout, (char *)NULL);
continue;
case 'n': /* Print with line numbers */
nflg++;
continue;
case 'b': /* Omit line numbers from blank lines */
bflg++;
nflg++;
continue;
case 'v': /* Print control characters */
vflg++;
continue;
case 's': /* Collapse multiple adjacent blank lines */
sflg++;
continue;
case 'e': /* Print '$' after lines */
eflg++;
vflg++;
continue;
case 't': /* Print tabs as '^I' */
tflg++;
vflg++;
continue;
}
break;
}
fstat(fileno(stdout), &statb);
statb.st_mode &= S_IFMT;
if (statb.st_mode!=S_IFCHR && statb.st_mode!=S_IFBLK) {
dev = statb.st_dev;
ino = statb.st_ino;
}
if (argc < 2) {
argc = 2;
fflg++;
}
while (--argc > 0) {
if (fflg || (*++argv)[0]=='-' && (*argv)[1]=='\0')
fi = stdin;
else {
if ((fi = fopen(*argv, "r")) == NULL) {
fprintf(stderr, "cat: can't open %s\n", *argv);
continue;
}
}
fstat(fileno(fi), &statb);
if (statb.st_dev==dev && statb.st_ino==ino) {
fprintf(stderr, "cat: input %s is output\n",
fflg?"-": *argv);
fclose(fi);
continue;
}
if (nflg||sflg||vflg)
copyopt(fi);
else {
while ((c = getc(fi)) != EOF)
putchar(c);
}
if (fi!=stdin)
fclose(fi);
}
if (ferror(stdout))
fprintf(stderr, "cat: output write error\n");
return(0);
}
copyopt(f)
register FILE *f;
{
register int c;
top:
c = getc(f);
if (c == EOF)
return;
if (c == '\n') {
if (inline == 0) {
if (sflg && spaced)
goto top;
spaced = 1;
}
if (nflg && bflg==0 && inline == 0)
printf("%6d\t", lno++);
if (eflg)
putchar('$');
putchar('\n');
inline = 0;
goto top;
}
if (nflg && inline == 0)
printf("%6d\t", lno++);
inline = 1;
if (vflg) {
if (tflg==0 && c == '\t')
putchar(c);
else {
if (c > 0177) {
printf("M-");
c &= 0177;
}
if (c < ' ')
printf("^%c", c+'@');
else if (c == 0177)
printf("^?");
else
putchar(c);
}
} else
putchar(c);
spaced = 0;
goto top;
}
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Kevin Fall.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)cat.c 5.15 (Berkeley) 5/23/91";
#endif /* not lint */
#include <sys/param.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* Most comments after this one not in the original */
int bflag, eflag, nflag, sflag, tflag, vflag;
int rval; /* Program exit code */
char *filename;
void cook_args(), cook_buf(), raw_args(), raw_cat();
void err __P((int, const char *, ...));
main(argc, argv)
int argc;
char **argv;
{
extern int optind; /* Set by getopt() to number of options passed */
int ch; /* Command-line flag character */
while ((ch = getopt(argc, argv, "benstuv")) != EOF)
switch (ch) {
case 'b':
bflag = nflag = 1; /* -b implies -n */
break;
case 'e':
eflag = vflag = 1; /* -e implies -v */
break;
case 'n':
nflag = 1;
break;
case 's':
sflag = 1;
break;
case 't':
tflag = vflag = 1; /* -t implies -v */
break;
case 'u':
setbuf(stdout, (char *)NULL);
break;
case 'v':
vflag = 1;
break;
case '?':
(void)fprintf(stderr,
"usage: cat [-benstuv] [-] [file ...]\n");
exit(1);
}
argv += optind; /* Increment past all options */
if (bflag || eflag || nflag || sflag || tflag || vflag)
cook_args(argv);
else
raw_args(argv);
if (fclose(stdout))
err(1, "stdout: %s", strerror(errno));
exit(rval);
}
/* Process file arguments and feed to "cooked" mode of cat */
void
cook_args(argv)
char **argv;
{
register FILE *fp;
fp = stdin;
filename = "stdin";
do {
if (*argv) {
if (!strcmp(*argv, "-"))
fp = stdin;
else if (!(fp = fopen(*argv, "r"))) {
err(0, "%s: %s", *argv, strerror(errno));
++argv;
continue;
}
filename = *argv++;
}
cook_buf(fp);
if (fp != stdin)
(void)fclose(fp);
} while (*argv);
}
/* "Cooked" mode of cat, i.e. prepared and seasoned. Have to handle all the
* different combinations of command-line flags */
void
cook_buf(fp)
register FILE *fp;
{
register int ch, gobble, line, prev;
line = gobble = 0;
for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
if (prev == '\n') {
if (ch == '\n') {
if (sflag) {
if (!gobble && putchar(ch) == EOF)
break;
gobble = 1;
continue;
}
if (nflag && !bflag) {
(void)fprintf(stdout, "%6d\t", ++line);
if (ferror(stdout))
break;
}
} else if (nflag) {
(void)fprintf(stdout, "%6d\t", ++line);
if (ferror(stdout))
break;
}
}
gobble = 0;
if (ch == '\n') {
if (eflag)
if (putchar('$') == EOF)
break;
} else if (ch == '\t') {
if (tflag) {
if (putchar('^') == EOF || putchar('I') == EOF)
break;
continue;
}
} else if (vflag) {
if (!isascii(ch)) {
if (putchar('M') == EOF || putchar('-') == EOF)
break;
ch = toascii(ch);
}
if (iscntrl(ch)) {
if (putchar('^') == EOF ||
putchar(ch == '\177' ? '?' :
ch | 0100) == EOF)
break;
continue;
}
}
if (putchar(ch) == EOF)
break;
}
if (ferror(fp)) {
err(0, "%s: %s", strerror(errno));
clearerr(fp);
}
if (ferror(stdout))
err(1, "stdout: %s", strerror(errno));
}
/* Process file arguments and feed to "raw" mode of cat */
void
raw_args(argv)
char **argv;
{
register int fd;
fd = fileno(stdin);
filename = "stdin";
do {
if (*argv) {
if (!strcmp(*argv, "-"))
fd = fileno(stdin);
else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
err(0, "%s: %s", *argv, strerror(errno));
++argv;
continue;
}
filename = *argv++;
}
raw_cat(fd);
if (fd != fileno(stdin))
(void)close(fd);
} while (*argv);
}
/* "Raw" mode cat -- no special handling, just print char for char */
void
raw_cat(rfd)
register int rfd;
{
register int nr, nw, off, wfd;
static int bsize;
static char *buf;
struct stat sbuf;
wfd = fileno(stdout);
if (!buf) {
if (fstat(wfd, &sbuf))
err(1, "%s: %s", filename, strerror(errno));
bsize = MAX(sbuf.st_blksize, 1024);
if (!(buf = malloc((u_int)bsize)))
err(1, "%s", strerror(errno));
}
while ((nr = read(rfd, buf, bsize)) > 0)
for (off = 0; off < nr; nr -= nw, off += nw)
if ((nw = write(wfd, buf + off, nr)) < 0)
err(1, "stdout");
if (nr < 0)
err(0, "%s: %s", filename, strerror(errno));
}
/* Error handling function err(), with some conditional compilation based on
* available libraries, it looks like */
#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
void
#if __STDC__
err(int ex, const char *fmt, ...)
#else
err(ex, fmt, va_alist)
int ex;
char *fmt;
va_dcl
#endif
{
va_list ap;
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
(void)fprintf(stderr, "cat: ");
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
if (ex)
exit(1);
rval = 1;
}
/* $NetBSD: cat.c,v 1.18 1998/07/28 05:31:22 mycroft Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Kevin Fall.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT(
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n");
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95";
#else
__RCSID("$NetBSD: cat.c,v 1.18 1998/07/28 05:31:22 mycroft Exp $");
#endif
#endif /* not lint */
#include <sys/param.h>
#include <sys/stat.h>
#include <locale.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int bflag, eflag, nflag, sflag, tflag, vflag;
int rval;
char *filename;
int main __P((int, char *[]));
void cook_args __P((char *argv[]));
void cook_buf __P((FILE *));
void raw_args __P((char *argv[]));
void raw_cat __P((int));
int
main(argc, argv)
int argc;
char *argv[];
{
extern int optind;
int ch;
(void)setlocale(LC_ALL, "");
while ((ch = getopt(argc, argv, "benstuv")) != -1)
switch (ch) {
case 'b':
bflag = nflag = 1; /* -b implies -n */
break;
case 'e':
eflag = vflag = 1; /* -e implies -v */
break;
case 'n':
nflag = 1;
break;
case 's':
sflag = 1;
break;
case 't':
tflag = vflag = 1; /* -t implies -v */
break;
case 'u':
setbuf(stdout, (char *)NULL);
break;
case 'v':
vflag = 1;
break;
default:
case '?':
(void)fprintf(stderr,
"usage: cat [-benstuv] [-] [file ...]\n");
exit(1);
/* NOTREACHED */
}
argv += optind;
if (bflag || eflag || nflag || sflag || tflag || vflag)
cook_args(argv);
else
raw_args(argv);
if (fclose(stdout))
err(1, "stdout");
exit(rval);
/* NOTREACHED */
}
void
cook_args(argv)
char **argv;
{
FILE *fp;
fp = stdin;
filename = "stdin";
do {
if (*argv) {
if (!strcmp(*argv, "-"))
fp = stdin;
else if ((fp = fopen(*argv, "r")) == NULL) {
warn("%s", *argv);
rval = 1;
++argv;
continue;
}
filename = *argv++;
}
cook_buf(fp);
if (fp != stdin)
(void)fclose(fp);
} while (*argv);
}
void
cook_buf(fp)
FILE *fp;
{
int ch, gobble, line, prev;
line = gobble = 0;
for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
if (prev == '\n') {
if (ch == '\n') {
if (sflag) {
if (!gobble && putchar(ch) == EOF)
break;
gobble = 1;
continue;
}
if (nflag) {
if (!bflag) {
(void)fprintf(stdout,
"%6d\t", ++line);
if (ferror(stdout))
break;
} else if (eflag) {
(void)fprintf(stdout,
"%6s\t", "");
if (ferror(stdout))
break;
}
}
} else if (nflag) {
(void)fprintf(stdout, "%6d\t", ++line);
if (ferror(stdout))
break;
}
}
gobble = 0;
if (ch == '\n') {
if (eflag)
if (putchar('$') == EOF)
break;
} else if (ch == '\t') {
if (tflag) {
if (putchar('^') == EOF || putchar('I') == EOF)
break;
continue;
}
} else if (vflag) {
if (!isascii(ch)) {
if (putchar('M') == EOF || putchar('-') == EOF)
break;
ch = toascii(ch);
}
if (iscntrl(ch)) {
if (putchar('^') == EOF ||
putchar(ch == '\177' ? '?' :
ch | 0100) == EOF)
break;
continue;
}
}
if (putchar(ch) == EOF)
break;
}
if (ferror(fp)) {
warn("%s", filename);
rval = 1;
clearerr(fp);
}
if (ferror(stdout))
err(1, "stdout");
}
void
raw_args(argv)
char **argv;
{
int fd;
fd = fileno(stdin);
filename = "stdin";
do {
if (*argv) {
if (!strcmp(*argv, "-"))
fd = fileno(stdin);
else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
warn("%s", *argv);
rval = 1;
++argv;
continue;
}
filename = *argv++;
}
raw_cat(fd);
if (fd != fileno(stdin))
(void)close(fd);
} while (*argv);
}
void
raw_cat(rfd)
int rfd;
{
int nr, nw, off, wfd;
static int bsize;
static char *buf;
struct stat sbuf;
wfd = fileno(stdout);
if (buf == NULL) {
if (fstat(wfd, &sbuf))
err(1, "%s", filename);
bsize = MAX(sbuf.st_blksize, 1024);
if ((buf = malloc((u_int)bsize)) == NULL)
err(1, "cannot allocate buffer");
}
while ((nr = read(rfd, buf, (u_int)bsize)) > 0)
for (off = 0; nr; nr -= nw, off += nw)
if ((nw = write(wfd, buf + off, (u_int)nr)) < 0)
err(1, "stdout");
if (nr < 0) {
warn("%s", filename);
rval = 1;
}
}
/*-
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Kevin Fall.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if 0
#ifndef lint
static char const copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#endif
#ifndef lint
#if 0
static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95";
#endif
#endif /* not lint */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/bin/cat/cat.c,v 1.32 2005/01/10 08:39:20 imp Exp $");
#include <sys/param.h>
#include <sys/stat.h>
#ifndef NO_UDOM_SUPPORT
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#endif
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
int bflag, eflag, nflag, sflag, tflag, vflag;
int rval;
const char *filename;
static void usage(void);
static void scanfiles(char *argv[], int cooked);
static void cook_cat(FILE *);
static void raw_cat(int);
#ifndef NO_UDOM_SUPPORT
static int udom_open(const char *path, int flags);
#endif
int
main(int argc, char *argv[])
{
int ch;
setlocale(LC_CTYPE, "");
while ((ch = getopt(argc, argv, "benstuv")) != -1)
switch (ch) {
case 'b':
bflag = nflag = 1; /* -b implies -n */
break;
case 'e':
eflag = vflag = 1; /* -e implies -v */
break;
case 'n':
nflag = 1;
break;
case 's':
sflag = 1;
break;
case 't':
tflag = vflag = 1; /* -t implies -v */
break;
case 'u':
setbuf(stdout, NULL);
break;
case 'v':
vflag = 1;
break;
default:
usage();
}
argv += optind;
if (bflag || eflag || nflag || sflag || tflag || vflag)
scanfiles(argv, 1);
else
scanfiles(argv, 0);
if (fclose(stdout))
err(1, "stdout");
exit(rval);
/* NOTREACHED */
}
static void
usage(void)
{
fprintf(stderr, "usage: cat [-benstuv] [file ...]\n");
exit(1);
/* NOTREACHED */
}
static void
scanfiles(char *argv[], int cooked)
{
int i = 0;
char *path;
FILE *fp;
while ((path = argv[i]) != NULL || i == 0) {
int fd;
if (path == NULL || strcmp(path, "-") == 0) {
filename = "stdin";
fd = STDIN_FILENO;
} else {
filename = path;
fd = open(path, O_RDONLY);
#ifndef NO_UDOM_SUPPORT
if (fd < 0 && errno == EOPNOTSUPP)
fd = udom_open(path, O_RDONLY);
#endif
}
if (fd < 0) {
warn("%s", path);
rval = 1;
} else if (cooked) {
if (fd == STDIN_FILENO)
cook_cat(stdin);
else {
fp = fdopen(fd, "r");
cook_cat(fp);
fclose(fp);
}
} else {
raw_cat(fd);
if (fd != STDIN_FILENO)
close(fd);
}
if (path == NULL)
break;
++i;
}
}
static void
cook_cat(FILE *fp)
{
int ch, gobble, line, prev;
/* Reset EOF condition on stdin. */
if (fp == stdin && feof(stdin))
clearerr(stdin);
line = gobble = 0;
for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
if (prev == '\n') {
if (sflag) {
if (ch == '\n') {
if (gobble)
continue;
gobble = 1;
} else
gobble = 0;
}
if (nflag && (!bflag || ch != '\n')) {
(void)fprintf(stdout, "%6d\t", ++line);
if (ferror(stdout))
break;
}
}
if (ch == '\n') {
if (eflag && putchar('$') == EOF)
break;
} else if (ch == '\t') {
if (tflag) {
if (putchar('^') == EOF || putchar('I') == EOF)
break;
continue;
}
} else if (vflag) {
if (!isascii(ch) && !isprint(ch)) {
if (putchar('M') == EOF || putchar('-') == EOF)
break;
ch = toascii(ch);
}
if (iscntrl(ch)) {
if (putchar('^') == EOF ||
putchar(ch == '\177' ? '?' :
ch | 0100) == EOF)
break;
continue;
}
}
if (putchar(ch) == EOF)
break;
}
if (ferror(fp)) {
warn("%s", filename);
rval = 1;
clearerr(fp);
}
if (ferror(stdout))
err(1, "stdout");
}
static void
raw_cat(int rfd)
{
int off, wfd;
ssize_t nr, nw;
static size_t bsize;
static char *buf = NULL;
struct stat sbuf;
wfd = fileno(stdout);
if (buf == NULL) {
if (fstat(wfd, &sbuf))
err(1, "%s", filename);
bsize = MAX(sbuf.st_blksize, 1024);
if ((buf = malloc(bsize)) == NULL)
err(1, "buffer");
}
while ((nr = read(rfd, buf, bsize)) > 0)
for (off = 0; nr; nr -= nw, off += nw)
if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
err(1, "stdout");
if (nr < 0) {
warn("%s", filename);
rval = 1;
}
}
#ifndef NO_UDOM_SUPPORT
static int
udom_open(const char *path, int flags)
{
struct sockaddr_un sou;
int fd;
unsigned int len;
bzero(&sou, sizeof(sou));
/*
* Construct the unix domain socket address and attempt to connect
*/
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd >= 0) {
sou.sun_family = AF_UNIX;
if ((len = strlcpy(sou.sun_path, path,
sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) {
errno = ENAMETOOLONG;
return (-1);
}
len = offsetof(struct sockaddr_un, sun_path[len+1]);
if (connect(fd, (void *)&sou, len) < 0) {
close(fd);
fd = -1;
}
}
/*
* handle the open flags by shutting down appropriate directions
*/
if (fd >= 0) {
switch(flags & O_ACCMODE) {
case O_RDONLY:
if (shutdown(fd, SHUT_WR) == -1)
warn(NULL);
break;
case O_WRONLY:
if (shutdown(fd, SHUT_RD) == -1)
warn(NULL);
break;
default:
break;
}
}
return(fd);
}
#endif
@priyadarshan
Copy link

Very instructive, thank you.

You might wish to add illumos (Solaris) version.

@nilname
Copy link

nilname commented Jan 3, 2019

nice

@zhoumengkang
Copy link

zhoumengkang commented Sep 20, 2019

666 ,thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment