4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
26 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
38 #include <sys/types.h>
50 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
52 #define MAXMAPSIZE (8*1024*1024) /* map at most 8MB */
53 #define SMALLFILESIZE (32*1024) /* don't use mmap on little files */
55 static int vncat(FILE *);
56 static int cat(FILE *, struct stat
*, struct stat
*, char *);
58 static int silent
= 0; /* s flag */
59 static int visi_mode
= 0; /* v flag */
60 static int visi_tab
= 0; /* t flag */
61 static int visi_newline
= 0; /* e flag */
62 static int bflg
= 0; /* b flag */
63 static int nflg
= 0; /* n flag */
66 static unsigned char buf
[SMALLFILESIZE
];
70 main(int argc
, char **argv
)
79 struct stat source
, target
;
81 (void) setlocale(LC_ALL
, "");
82 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
83 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
85 (void) textdomain(TEXT_DOMAIN
);
89 * If the first argument is NULL,
90 * discard arguments until we find cat.
92 if (argv
[0][0] == '\0')
93 argc
= getargv("cat", &argv
, 0);
97 * Process the options for cat.
100 while ((c
= getopt(argc
, argv
, "usvtebn")) != EOF
) {
106 * If not standalone, set stdout to
107 * completely unbuffered I/O when
108 * the 'u' option is used.
112 setbuf(stdout
, NULL
);
119 * The 's' option requests silent mode
120 * where no messages are written.
129 * The 'v' option requests that non-printing
130 * characters (with the exception of newlines,
131 * form-feeds, and tabs) be displayed visibly.
133 * Control characters are printed as "^x".
134 * DEL characters are printed as "^?".
135 * Non-printable and non-contrlol characters with the
136 * 8th bit set are printed as "M-x".
145 * When in visi_mode, this option causes tabs
146 * to be displayed as "^I".
155 * When in visi_mode, this option causes newlines
156 * and form-feeds to be displayed as "$" at the end
157 * of the line prior to the newline.
166 * Precede each line output with its line number,
167 * but omit the line numbers from blank lines.
177 * Precede each line output with its line number.
192 (void) fprintf(stderr
,
193 gettext("usage: cat [ -usvtebn ] [-|file] ...\n"));
198 * Stat stdout to be sure it is defined.
201 if (fstat(fileno(stdout
), &target
) < 0) {
203 (void) fprintf(stderr
,
204 gettext("cat: Cannot stat stdout\n"));
207 obsize
= target
.st_blksize
;
210 * If no arguments given, then use stdin for input.
213 if (optind
== argc
) {
219 * Process each remaining argument,
220 * unless there is an error with stdout.
224 for (argv
= &argv
[optind
];
225 optind
< argc
&& !ferror(stdout
); optind
++, argv
++) {
228 * If the argument was '-' or there were no files
229 * specified, take the input from stdin.
233 ((*argv
)[0] == '-' && (*argv
)[1] == '\0'))
237 * Attempt to open each specified file.
240 if ((fi
= fopen(*argv
, "r")) == NULL
) {
242 (void) fprintf(stderr
, gettext(
243 "cat: cannot open %s: %s\n"),
244 *argv
, strerror(errno
));
251 * Stat source to make sure it is defined.
254 if (fstat(fileno(fi
), &source
) < 0) {
256 (void) fprintf(stderr
,
257 gettext("cat: cannot stat %s: %s\n"),
258 (stdinflg
) ? "-" : *argv
, strerror(errno
));
265 * If the source is not a character special file, socket or a
266 * block special file, make sure it is not identical
270 if (!S_ISCHR(target
.st_mode
) &&
271 !S_ISBLK(target
.st_mode
) &&
272 !S_ISSOCK(target
.st_mode
) &&
273 IDENTICAL(target
, source
)) {
275 (void) fprintf(stderr
,
276 gettext("cat: input/output files '%s' identical\n"),
277 stdinflg
?"-": *argv
);
279 (void) fprintf(stderr
,
280 gettext("cat: close error: %s\n"),
285 ibsize
= source
.st_blksize
;
288 * If in visible mode and/or nflg, use vncat;
289 * otherwise, use cat.
292 if (visi_mode
|| nflg
)
295 estatus
= cat(fi
, &source
, &target
,
296 fi
!= stdin
? *argv
: "standard input");
302 * If the input is not stdin, close the source file.
308 (void) fprintf(stderr
,
309 gettext("cat: close error: %s\n"),
315 * Display any error with stdout operations.
318 if (fclose(stdout
) != 0) {
320 perror(gettext("cat: close error"));
329 cat(FILE *fi
, struct stat
*statp
, struct stat
*outp
, char *filenm
)
337 off_t mapsize
, munmapsize
;
341 fi_desc
= fileno(fi
);
342 if (S_ISREG(statp
->st_mode
) && (lseek(fi_desc
, (off_t
)0, SEEK_CUR
)
343 == 0) && (statp
->st_size
> SMALLFILESIZE
)) {
344 mapsize
= (off_t
)MAXMAPSIZE
;
345 if (statp
->st_size
< mapsize
)
346 mapsize
= statp
->st_size
;
347 munmapsize
= mapsize
;
352 bufferp
= mmap(NULL
, (size_t)mapsize
, PROT_READ
,
353 MAP_SHARED
, fi_desc
, (off_t
)0);
354 if (bufferp
== MAP_FAILED
)
355 mapsize
= 0; /* I guess we can't mmap today */
357 mapsize
= 0; /* can't mmap non-regular files */
364 * NFS V2 will let root open a file it does not have permission
365 * to read. This read() is here to make sure that the access
366 * time on the input file will be updated. The VSC tests for
368 * cat file > /dev/null
369 * In this case the write()/mmap() pair will not read the file
370 * and the access time will not be updated.
373 if (read(fi_desc
, &x
, 1) == -1)
376 filesize
= statp
->st_size
;
379 * Note that on some systems (V7), very large writes to
380 * a pipe return less than the requested size of the
381 * write. In this case, multiple writes are required.
384 nitems
= (int)mapsize
;
386 if ((nwritten
= write(fileno(stdout
),
387 &bufferp
[offset
], (size_t)nitems
)) < 0) {
395 (void) fprintf(stderr
,
401 (void) munmap(bufferp
,
403 (void) lseek(fi_desc
, (off_t
)mapoffset
,
408 } while ((nitems
-= nwritten
) > 0);
411 mapoffset
+= mapsize
;
414 if (filesize
< mapsize
)
416 if (mmap(bufferp
, (size_t)mapsize
, PROT_READ
,
417 MAP_SHARED
|MAP_FIXED
, fi_desc
,
418 mapoffset
) == MAP_FAILED
) {
420 perror(gettext("cat: mmap error"));
421 (void) munmap(bufferp
, (size_t)munmapsize
);
422 (void) lseek(fi_desc
, (off_t
)mapoffset
,
428 * Move the file pointer past what we read. Shell scripts
429 * rely on cat to do this, so that successive commands in
430 * the script won't re-read the same data.
432 (void) lseek(fi_desc
, (off_t
)mapoffset
, SEEK_SET
);
433 (void) munmap(bufferp
, (size_t)munmapsize
);
435 if (S_ISREG(statp
->st_mode
) && S_ISREG(outp
->st_mode
)) {
436 bufferp
= (char *)buf
;
437 buffsize
= SMALLFILESIZE
;
441 * common case, use output blksize
447 buffsize
= (long)BUFSIZ
;
449 if (buffsize
<= SMALLFILESIZE
) {
450 bufferp
= (char *)buf
;
451 } else if ((bufferp
=
452 malloc((size_t)buffsize
)) == NULL
) {
453 perror(gettext("cat: no memory"));
459 * While not end of file, copy blocks to stdout.
461 while ((nitems
= read(fi_desc
, bufferp
, (size_t)buffsize
)) >
465 * Note that on some systems (V7), very large writes
466 * to a pipe return less than the requested size of
467 * the write. In this case, multiple writes are
471 nwritten
= write(1, bufferp
+offset
,
477 (void) fprintf(stderr
, gettext(\
478 "cat: output error (%d/%d characters written)\n"), nwritten
, nitems
);
481 if (bufferp
!= (char *)buf
)
486 } while ((nitems
-= nwritten
) > 0);
488 if (bufferp
!= (char *)buf
)
491 (void) fprintf(stderr
,
492 gettext("cat: input error on %s: "), filenm
);
507 int boln
; /* = 1 if at beginning of line */
511 unsigned char *p1
, *p2
;
519 if ((len
= fread(p1
, 1, BUFSIZ
, fi
)) <= 0)
526 * Display newlines as "$<newline>"
527 * if visi_newline set
530 if (nflg
&& boln
&& !bflg
)
531 (void) printf("%6d\t", lno
++);
534 if (visi_mode
&& visi_newline
)
541 (void) printf("%6d\t", lno
++);
545 * For non-printable and non-cntrl chars,
546 * use the "M-x" notation.
550 if (isprint(c
) || visi_mode
== 0) {
556 * For non-printable ascii characters.
560 /* For cntrl characters. */
561 if ((c
== '\t') || (c
== '\f')) {
563 * Display tab as "^I" if visi_tab set
565 if (visi_mode
&& visi_tab
) {
567 (void) putchar(c
^0100);
573 (void) putchar(c
^0100);
580 * For non-ascii characters.
583 if ((len
= (p2
- p1
)) < MB_LEN_MAX
) {
584 for (n
= 0; n
< len
; n
++)
588 if ((len
= fread(p2
, 1, BUFSIZ
- n
, fi
)) > 0)
592 if ((len
= (p2
- p1
)) > MB_LEN_MAX
)
595 if ((len
= mbtowc(&wc
, (char *)p1
, len
)) > 0) {
596 if (iswprint(wc
) || visi_mode
== 0) {
611 /* For non-printable characters. */
613 /* For cntrl characters. */
614 if ((c
== '\t') || (c
== '\f')) {
616 * Display tab as "^I" if visi_tab set
618 if (visi_mode
&& visi_tab
) {
620 (void) putchar(c
^0100);
625 (void) putchar(c
^0100);