summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cat
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/cat
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/cat')
-rw-r--r--usr/src/cmd/cat/Makefile47
-rw-r--r--usr/src/cmd/cat/cat.c627
2 files changed, 674 insertions, 0 deletions
diff --git a/usr/src/cmd/cat/Makefile b/usr/src/cmd/cat/Makefile
new file mode 100644
index 0000000000..c9e892a543
--- /dev/null
+++ b/usr/src/cmd/cat/Makefile
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= cat
+
+include ../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+
+CPPFLAGS += -DEUC -D_FILE_OFFSET_BITS=64
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/cat/cat.c b/usr/src/cmd/cat/cat.c
new file mode 100644
index 0000000000..15945776c3
--- /dev/null
+++ b/usr/src/cmd/cat/cat.c
@@ -0,0 +1,627 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Concatenate files.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <widec.h>
+#include <wctype.h>
+#include <limits.h>
+#include <libintl.h>
+#define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
+
+#define MAXMAPSIZE (8*1024*1024) /* map at most 8MB */
+#define SMALLFILESIZE (32*1024) /* don't use mmap on little files */
+
+static int vncat(FILE *);
+static int cat(FILE *, struct stat *, struct stat *, char *);
+
+static int silent = 0; /* s flag */
+static int visi_mode = 0; /* v flag */
+static int visi_tab = 0; /* t flag */
+static int visi_newline = 0; /* e flag */
+static int bflg = 0; /* b flag */
+static int nflg = 0; /* n flag */
+static long ibsize;
+static long obsize;
+static unsigned char buf[SMALLFILESIZE];
+
+
+int
+main(int argc, char **argv)
+{
+ FILE *fi;
+ int c;
+ extern int optind;
+ int errflg = 0;
+ int stdinflg = 0;
+ int status = 0;
+ int estatus = 0;
+ struct stat source, target;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+#ifdef STANDALONE
+ /*
+ * If the first argument is NULL,
+ * discard arguments until we find cat.
+ */
+ if (argv[0][0] == '\0')
+ argc = getargv("cat", &argv, 0);
+#endif
+
+ /*
+ * Process the options for cat.
+ */
+
+ while ((c = getopt(argc, argv, "usvtebn")) != EOF) {
+ switch (c) {
+
+ case 'u':
+
+ /*
+ * If not standalone, set stdout to
+ * completely unbuffered I/O when
+ * the 'u' option is used.
+ */
+
+#ifndef STANDALONE
+ setbuf(stdout, (char *)NULL);
+#endif
+ continue;
+
+ case 's':
+
+ /*
+ * The 's' option requests silent mode
+ * where no messages are written.
+ */
+
+ silent++;
+ continue;
+
+ case 'v':
+
+ /*
+ * The 'v' option requests that non-printing
+ * characters (with the exception of newlines,
+ * form-feeds, and tabs) be displayed visibly.
+ *
+ * Control characters are printed as "^x".
+ * DEL characters are printed as "^?".
+ * Non-printable and non-contrlol characters with the
+ * 8th bit set are printed as "M-x".
+ */
+
+ visi_mode++;
+ continue;
+
+ case 't':
+
+ /*
+ * When in visi_mode, this option causes tabs
+ * to be displayed as "^I".
+ */
+
+ visi_tab++;
+ continue;
+
+ case 'e':
+
+ /*
+ * When in visi_mode, this option causes newlines
+ * and form-feeds to be displayed as "$" at the end
+ * of the line prior to the newline.
+ */
+
+ visi_newline++;
+ continue;
+
+ case 'b':
+
+ /*
+ * Precede each line output with its line number,
+ * but omit the line numbers from blank lines.
+ */
+
+ bflg++;
+ nflg++;
+ continue;
+
+ case 'n':
+
+ /*
+ * Precede each line output with its line number.
+ */
+
+ nflg++;
+ continue;
+
+ case '?':
+ errflg++;
+ break;
+ }
+ break;
+ }
+
+ if (errflg) {
+ if (!silent)
+ (void) fprintf(stderr,
+ gettext("usage: cat [ -usvtebn ] [-|file] ...\n"));
+ exit(2);
+ }
+
+ /*
+ * Stat stdout to be sure it is defined.
+ */
+
+ if (fstat(fileno(stdout), &target) < 0) {
+ if (!silent)
+ (void) fprintf(stderr,
+ gettext("cat: Cannot stat stdout\n"));
+ exit(2);
+ }
+ obsize = target.st_blksize;
+
+ /*
+ * If no arguments given, then use stdin for input.
+ */
+
+ if (optind == argc) {
+ argc++;
+ stdinflg++;
+ }
+
+ /*
+ * Process each remaining argument,
+ * unless there is an error with stdout.
+ */
+
+
+ for (argv = &argv[optind];
+ optind < argc && !ferror(stdout); optind++, argv++) {
+
+ /*
+ * If the argument was '-' or there were no files
+ * specified, take the input from stdin.
+ */
+
+ if (stdinflg ||
+ ((*argv)[0] == '-' && (*argv)[1] == '\0'))
+ fi = stdin;
+ else {
+ /*
+ * Attempt to open each specified file.
+ */
+
+ if ((fi = fopen(*argv, "r")) == NULL) {
+ if (!silent)
+ (void) fprintf(stderr,
+ gettext("cat: cannot open %s\n"), *argv);
+ status = 2;
+ continue;
+ }
+ }
+
+ /*
+ * Stat source to make sure it is defined.
+ */
+
+ if (fstat(fileno(fi), &source) < 0) {
+ if (!silent)
+ (void) fprintf(stderr,
+ gettext("cat: cannot stat %s\n"),
+ (stdinflg) ? "-" : *argv);
+ status = 2;
+ continue;
+ }
+
+
+ /*
+ * If the source is not a character special file, socket or a
+ * block special file, make sure it is not identical
+ * to the target.
+ */
+
+ if (!S_ISCHR(target.st_mode) &&
+ !S_ISBLK(target.st_mode) &&
+ !S_ISSOCK(target.st_mode) &&
+ IDENTICAL(target, source)) {
+ if (!silent)
+ (void) fprintf(stderr,
+ gettext("cat: input/output files '%s' identical\n"),
+ stdinflg?"-": *argv);
+ if (fclose(fi) != 0)
+ (void) fprintf(stderr,
+ gettext("cat: close error\n"));
+ status = 2;
+ continue;
+ }
+ ibsize = source.st_blksize;
+
+ /*
+ * If in visible mode and/or nflg, use vncat;
+ * otherwise, use cat.
+ */
+
+ if (visi_mode || nflg)
+ estatus = vncat(fi);
+ else
+ estatus = cat(fi, &source, &target,
+ fi != stdin ? *argv : "standard input");
+
+ if (estatus)
+ status = estatus;
+
+ /*
+ * If the input is not stdin, close the source file.
+ */
+
+ if (fi != stdin) {
+ if (fclose(fi) != 0)
+ if (!silent)
+ (void) fprintf(stderr,
+ gettext("cat: close error\n"));
+ }
+ }
+
+ /*
+ * Display any error with stdout operations.
+ */
+
+ if (fclose(stdout) != 0) {
+ if (!silent)
+ perror(gettext("cat: close error"));
+ status = 2;
+ }
+ return (status);
+}
+
+
+
+static int
+cat(FILE *fi, struct stat *statp, struct stat *outp, char *filenm)
+{
+ int nitems;
+ int nwritten;
+ int offset;
+ int fi_desc;
+ long buffsize;
+ char *bufferp;
+ off_t mapsize, munmapsize;
+ off_t filesize;
+ off_t mapoffset;
+
+ fi_desc = fileno(fi);
+ if (S_ISREG(statp->st_mode) && (lseek(fi_desc, (off_t)0, SEEK_CUR)
+ == 0) && (statp->st_size > SMALLFILESIZE)) {
+ mapsize = (off_t)MAXMAPSIZE;
+ if (statp->st_size < mapsize)
+ mapsize = statp->st_size;
+ munmapsize = mapsize;
+
+ /*
+ * Mmap time!
+ */
+ bufferp = mmap((caddr_t)NULL, (size_t)mapsize, PROT_READ,
+ MAP_SHARED, fi_desc, (off_t)0);
+ if (bufferp == (caddr_t)-1)
+ mapsize = 0; /* I guess we can't mmap today */
+ } else
+ mapsize = 0; /* can't mmap non-regular files */
+
+ if (mapsize != 0) {
+ int read_error = 0;
+ char x;
+
+ /*
+ * NFS V2 will let root open a file it does not have permission
+ * to read. This read() is here to make sure that the access
+ * time on the input file will be updated. The VSC tests for
+ * cat do this:
+ * cat file > /dev/null
+ * In this case the write()/mmap() pair will not read the file
+ * and the access time will not be updated.
+ */
+
+ if (read(fi_desc, &x, 1) == -1)
+ read_error = 1;
+ mapoffset = 0;
+ filesize = statp->st_size;
+ for (;;) {
+ /*
+ * Note that on some systems (V7), very large writes to
+ * a pipe return less than the requested size of the
+ * write. In this case, multiple writes are required.
+ */
+ offset = 0;
+ nitems = (int)mapsize;
+ do {
+ if ((nwritten = write(fileno(stdout),
+ &bufferp[offset], (size_t)nitems)) < 0) {
+ if (!silent) {
+ if (read_error == 1)
+ (void) fprintf(
+ stderr, gettext(
+ "cat: cannot read "
+ "%s: "), filenm);
+ else
+ (void) fprintf(
+ stderr, gettext(
+ "cat: write error: "));
+ perror("");
+ }
+ (void) munmap(bufferp,
+ (size_t)munmapsize);
+ (void) lseek(fi_desc, (off_t)mapoffset,
+ SEEK_SET);
+ return (2);
+ }
+ offset += nwritten;
+ } while ((nitems -= nwritten) > 0);
+
+ filesize -= mapsize;
+ mapoffset += mapsize;
+ if (filesize == 0)
+ break;
+ if (filesize < mapsize)
+ mapsize = filesize;
+ if (mmap(bufferp, (size_t)mapsize, PROT_READ,
+ MAP_SHARED|MAP_FIXED, fi_desc,
+ mapoffset) == (caddr_t)-1) {
+ if (!silent)
+ perror(gettext("cat: mmap error"));
+ (void) munmap(bufferp, (size_t)munmapsize);
+ (void) lseek(fi_desc, (off_t)mapoffset,
+ SEEK_SET);
+ return (1);
+ }
+ }
+ /*
+ * Move the file pointer past what we read. Shell scripts
+ * rely on cat to do this, so that successive commands in
+ * the script won't re-read the same data.
+ */
+ (void) lseek(fi_desc, (off_t)mapoffset, SEEK_SET);
+ (void) munmap(bufferp, (size_t)munmapsize);
+ } else {
+ if (((statp->st_mode & S_IFREG) == S_IFREG) &&
+ ((outp->st_mode & S_IFREG) == S_IFREG)) {
+ bufferp = (char *)buf;
+ buffsize = SMALLFILESIZE;
+ } else {
+ if (obsize)
+ /*
+ * common case, use output blksize
+ */
+ buffsize = obsize;
+ else if (ibsize)
+ buffsize = ibsize;
+ else
+ buffsize = (long)BUFSIZ;
+
+ if (buffsize <= SMALLFILESIZE) {
+ bufferp = (char *)buf;
+ } else if ((bufferp =
+ malloc((size_t)buffsize)) == NULL) {
+ perror(gettext("cat: no memory"));
+ return (1);
+ }
+ }
+
+ /*
+ * While not end of file, copy blocks to stdout.
+ */
+ while ((nitems = read(fi_desc, bufferp, (size_t)buffsize)) >
+ 0) {
+ offset = 0;
+ /*
+ * Note that on some systems (V7), very large writes
+ * to a pipe return less than the requested size of
+ * the write. In this case, multiple writes are
+ * required.
+ */
+ do {
+ nwritten = write(1, bufferp+offset,
+ (size_t)nitems);
+ if (nwritten < 0) {
+ if (!silent) {
+ if (nwritten == -1)
+ nwritten = 0l;
+ (void) fprintf(stderr, gettext(\
+"cat: output error (%d/%d characters written)\n"), nwritten, nitems);
+ perror("");
+ }
+ if (bufferp != (char *)buf)
+ free(bufferp);
+ return (2);
+ }
+ offset += nwritten;
+ } while ((nitems -= nwritten) > 0);
+ }
+ if (bufferp != (char *)buf)
+ free(bufferp);
+ if (nitems < 0) {
+ (void) fprintf(stderr,
+ gettext("cat: input error on %s: "), filenm);
+ perror("");
+ }
+ }
+
+ return (0);
+}
+
+static int
+vncat(fi)
+ FILE *fi;
+{
+ int c;
+ int lno;
+ int boln; /* = 1 if at beginning of line */
+ /* = 0 otherwise */
+ wchar_t wc;
+ int len, n;
+ unsigned char *p1, *p2;
+
+ lno = 1;
+ boln = 1;
+ p1 = p2 = buf;
+ for (;;) {
+ if (p1 >= p2) {
+ p1 = buf;
+ if ((len = fread(p1, 1, BUFSIZ, fi)) <= 0)
+ break;
+ p2 = p1 + len;
+ }
+ c = *p1++;
+
+ /*
+ * Display newlines as "$<newline>"
+ * if visi_newline set
+ */
+ if (c == '\n') {
+ if (nflg && boln && !bflg)
+ (void) printf("%6d\t", lno++);
+ boln = 1;
+
+ if (visi_mode && visi_newline)
+ (void) putchar('$');
+ (void) putchar(c);
+ continue;
+ }
+
+ if (nflg && boln)
+ (void) printf("%6d\t", lno++);
+ boln = 0;
+
+ /*
+ * For non-printable and non-cntrl chars,
+ * use the "M-x" notation.
+ */
+
+ if (isascii(c)) {
+ if (isprint(c) || visi_mode == 0) {
+ (void) putchar(c);
+ continue;
+ }
+
+ /*
+ * For non-printable ascii characters.
+ */
+
+ if (iscntrl(c)) {
+ /* For cntrl characters. */
+ if ((c == '\t') || (c == '\f')) {
+ /*
+ * Display tab as "^I" if visi_tab set
+ */
+ if (visi_mode && visi_tab) {
+ (void) putchar('^');
+ (void) putchar(c^0100);
+ } else
+ (void) putchar(c);
+ continue;
+ }
+ (void) putchar('^');
+ (void) putchar(c^0100);
+ continue;
+ }
+ continue;
+ }
+
+ /*
+ * For non-ascii characters.
+ */
+ p1--;
+ if ((len = (p2 - p1)) < MB_LEN_MAX) {
+ for (n = 0; n < len; n++)
+ buf[n] = *p1++;
+ p1 = buf;
+ p2 = p1 + n;
+ if ((len = fread(p2, 1, BUFSIZ - n, fi)) > 0)
+ p2 += len;
+ }
+
+ if ((len = (p2 - p1)) > MB_LEN_MAX)
+ len = MB_LEN_MAX;
+
+ if ((len = mbtowc(&wc, (char *)p1, len)) > 0) {
+ if (iswprint(wc) || visi_mode == 0) {
+ (void) putwchar(wc);
+ p1 += len;
+ continue;
+ }
+ }
+
+ (void) putchar('M');
+ (void) putchar('-');
+ c -= 0200;
+
+ if (isprint(c)) {
+ (void) putchar(c);
+ }
+
+ /* For non-printable characters. */
+ if (iscntrl(c)) {
+ /* For cntrl characters. */
+ if ((c == '\t') || (c == '\f')) {
+ /*
+ * Display tab as "^I" if visi_tab set
+ */
+ if (visi_mode && visi_tab) {
+ (void) putchar('^');
+ (void) putchar(c^0100);
+ } else
+ (void) putchar(c);
+ } else {
+ (void) putchar('^');
+ (void) putchar(c^0100);
+ }
+ }
+ p1++;
+ }
+ return (0);
+}