diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/cat | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/cat')
| -rw-r--r-- | usr/src/cmd/cat/Makefile | 47 | ||||
| -rw-r--r-- | usr/src/cmd/cat/cat.c | 627 |
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); +} |
