8888 loader: rename STAND to _STANDALONE
[unleashed.git] / bin / cat / cat.c
blob40e9ead3ad7dc24d6064e32179dddd21bc627552
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
32 * Concatenate files.
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <locale.h>
41 #include <unistd.h>
42 #include <sys/mman.h>
43 #include <errno.h>
44 #include <string.h>
46 #include <widec.h>
47 #include <wctype.h>
48 #include <limits.h>
49 #include <libintl.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 */
64 static long ibsize;
65 static long obsize;
66 static unsigned char buf[SMALLFILESIZE];
69 int
70 main(int argc, char **argv)
72 FILE *fi;
73 int c;
74 extern int optind;
75 int errflg = 0;
76 int stdinflg = 0;
77 int status = 0;
78 int estatus = 0;
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 */
84 #endif
85 (void) textdomain(TEXT_DOMAIN);
87 #ifdef STANDALONE
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);
94 #endif
97 * Process the options for cat.
100 while ((c = getopt(argc, argv, "usvtebn")) != EOF) {
101 switch (c) {
103 case 'u':
106 * If not standalone, set stdout to
107 * completely unbuffered I/O when
108 * the 'u' option is used.
111 #ifndef STANDALONE
112 setbuf(stdout, NULL);
113 #endif
114 continue;
116 case 's':
119 * The 's' option requests silent mode
120 * where no messages are written.
123 silent++;
124 continue;
126 case 'v':
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".
139 visi_mode++;
140 continue;
142 case 't':
145 * When in visi_mode, this option causes tabs
146 * to be displayed as "^I".
149 visi_tab++;
150 continue;
152 case 'e':
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.
160 visi_newline++;
161 continue;
163 case 'b':
166 * Precede each line output with its line number,
167 * but omit the line numbers from blank lines.
170 bflg++;
171 nflg++;
172 continue;
174 case 'n':
177 * Precede each line output with its line number.
180 nflg++;
181 continue;
183 case '?':
184 errflg++;
185 break;
187 break;
190 if (errflg) {
191 if (!silent)
192 (void) fprintf(stderr,
193 gettext("usage: cat [ -usvtebn ] [-|file] ...\n"));
194 exit(2);
198 * Stat stdout to be sure it is defined.
201 if (fstat(fileno(stdout), &target) < 0) {
202 if (!silent)
203 (void) fprintf(stderr,
204 gettext("cat: Cannot stat stdout\n"));
205 exit(2);
207 obsize = target.st_blksize;
210 * If no arguments given, then use stdin for input.
213 if (optind == argc) {
214 argc++;
215 stdinflg++;
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.
232 if (stdinflg ||
233 ((*argv)[0] == '-' && (*argv)[1] == '\0'))
234 fi = stdin;
235 else {
237 * Attempt to open each specified file.
240 if ((fi = fopen(*argv, "r")) == NULL) {
241 if (!silent)
242 (void) fprintf(stderr, gettext(
243 "cat: cannot open %s: %s\n"),
244 *argv, strerror(errno));
245 status = 2;
246 continue;
251 * Stat source to make sure it is defined.
254 if (fstat(fileno(fi), &source) < 0) {
255 if (!silent)
256 (void) fprintf(stderr,
257 gettext("cat: cannot stat %s: %s\n"),
258 (stdinflg) ? "-" : *argv, strerror(errno));
259 status = 2;
260 continue;
265 * If the source is not a character special file, socket or a
266 * block special file, make sure it is not identical
267 * to the target.
270 if (!S_ISCHR(target.st_mode) &&
271 !S_ISBLK(target.st_mode) &&
272 !S_ISSOCK(target.st_mode) &&
273 IDENTICAL(target, source)) {
274 if (!silent)
275 (void) fprintf(stderr,
276 gettext("cat: input/output files '%s' identical\n"),
277 stdinflg?"-": *argv);
278 if (fclose(fi) != 0)
279 (void) fprintf(stderr,
280 gettext("cat: close error: %s\n"),
281 strerror(errno));
282 status = 2;
283 continue;
285 ibsize = source.st_blksize;
288 * If in visible mode and/or nflg, use vncat;
289 * otherwise, use cat.
292 if (visi_mode || nflg)
293 estatus = vncat(fi);
294 else
295 estatus = cat(fi, &source, &target,
296 fi != stdin ? *argv : "standard input");
298 if (estatus)
299 status = estatus;
302 * If the input is not stdin, close the source file.
305 if (fi != stdin) {
306 if (fclose(fi) != 0)
307 if (!silent)
308 (void) fprintf(stderr,
309 gettext("cat: close error: %s\n"),
310 strerror(errno));
315 * Display any error with stdout operations.
318 if (fclose(stdout) != 0) {
319 if (!silent)
320 perror(gettext("cat: close error"));
321 status = 2;
323 return (status);
328 static int
329 cat(FILE *fi, struct stat *statp, struct stat *outp, char *filenm)
331 int nitems;
332 int nwritten;
333 int offset;
334 int fi_desc;
335 long buffsize;
336 char *bufferp;
337 off_t mapsize, munmapsize;
338 off_t filesize;
339 off_t mapoffset;
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;
350 * Mmap time!
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 */
356 } else
357 mapsize = 0; /* can't mmap non-regular files */
359 if (mapsize != 0) {
360 int read_error = 0;
361 char x;
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
367 * cat do this:
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)
374 read_error = 1;
375 mapoffset = 0;
376 filesize = statp->st_size;
377 for (;;) {
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.
383 offset = 0;
384 nitems = (int)mapsize;
385 do {
386 if ((nwritten = write(fileno(stdout),
387 &bufferp[offset], (size_t)nitems)) < 0) {
388 if (!silent) {
389 if (read_error == 1)
390 (void) fprintf(
391 stderr, gettext(
392 "cat: cannot read "
393 "%s: "), filenm);
394 else
395 (void) fprintf(stderr,
396 gettext(
397 "cat: write "
398 "error: "));
399 perror("");
401 (void) munmap(bufferp,
402 (size_t)munmapsize);
403 (void) lseek(fi_desc, (off_t)mapoffset,
404 SEEK_SET);
405 return (2);
407 offset += nwritten;
408 } while ((nitems -= nwritten) > 0);
410 filesize -= mapsize;
411 mapoffset += mapsize;
412 if (filesize == 0)
413 break;
414 if (filesize < mapsize)
415 mapsize = filesize;
416 if (mmap(bufferp, (size_t)mapsize, PROT_READ,
417 MAP_SHARED|MAP_FIXED, fi_desc,
418 mapoffset) == MAP_FAILED) {
419 if (!silent)
420 perror(gettext("cat: mmap error"));
421 (void) munmap(bufferp, (size_t)munmapsize);
422 (void) lseek(fi_desc, (off_t)mapoffset,
423 SEEK_SET);
424 return (1);
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);
434 } else {
435 if (S_ISREG(statp->st_mode) && S_ISREG(outp->st_mode)) {
436 bufferp = (char *)buf;
437 buffsize = SMALLFILESIZE;
438 } else {
439 if (obsize)
441 * common case, use output blksize
443 buffsize = obsize;
444 else if (ibsize)
445 buffsize = ibsize;
446 else
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"));
454 return (1);
459 * While not end of file, copy blocks to stdout.
461 while ((nitems = read(fi_desc, bufferp, (size_t)buffsize)) >
462 0) {
463 offset = 0;
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
468 * required.
470 do {
471 nwritten = write(1, bufferp+offset,
472 (size_t)nitems);
473 if (nwritten < 0) {
474 if (!silent) {
475 if (nwritten == -1)
476 nwritten = 0l;
477 (void) fprintf(stderr, gettext(\
478 "cat: output error (%d/%d characters written)\n"), nwritten, nitems);
479 perror("");
481 if (bufferp != (char *)buf)
482 free(bufferp);
483 return (2);
485 offset += nwritten;
486 } while ((nitems -= nwritten) > 0);
488 if (bufferp != (char *)buf)
489 free(bufferp);
490 if (nitems < 0) {
491 (void) fprintf(stderr,
492 gettext("cat: input error on %s: "), filenm);
493 perror("");
494 return (1);
498 return (0);
501 static int
502 vncat(fi)
503 FILE *fi;
505 int c;
506 int lno;
507 int boln; /* = 1 if at beginning of line */
508 /* = 0 otherwise */
509 wchar_t wc;
510 int len, n;
511 unsigned char *p1, *p2;
513 lno = 1;
514 boln = 1;
515 p1 = p2 = buf;
516 for (;;) {
517 if (p1 >= p2) {
518 p1 = buf;
519 if ((len = fread(p1, 1, BUFSIZ, fi)) <= 0)
520 break;
521 p2 = p1 + len;
523 c = *p1++;
526 * Display newlines as "$<newline>"
527 * if visi_newline set
529 if (c == '\n') {
530 if (nflg && boln && !bflg)
531 (void) printf("%6d\t", lno++);
532 boln = 1;
534 if (visi_mode && visi_newline)
535 (void) putchar('$');
536 (void) putchar(c);
537 continue;
540 if (nflg && boln)
541 (void) printf("%6d\t", lno++);
542 boln = 0;
545 * For non-printable and non-cntrl chars,
546 * use the "M-x" notation.
549 if (isascii(c)) {
550 if (isprint(c) || visi_mode == 0) {
551 (void) putchar(c);
552 continue;
556 * For non-printable ascii characters.
559 if (iscntrl(c)) {
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) {
566 (void) putchar('^');
567 (void) putchar(c^0100);
568 } else
569 (void) putchar(c);
570 continue;
572 (void) putchar('^');
573 (void) putchar(c^0100);
574 continue;
576 continue;
580 * For non-ascii characters.
582 p1--;
583 if ((len = (p2 - p1)) < MB_LEN_MAX) {
584 for (n = 0; n < len; n++)
585 buf[n] = *p1++;
586 p1 = buf;
587 p2 = p1 + n;
588 if ((len = fread(p2, 1, BUFSIZ - n, fi)) > 0)
589 p2 += len;
592 if ((len = (p2 - p1)) > MB_LEN_MAX)
593 len = MB_LEN_MAX;
595 if ((len = mbtowc(&wc, (char *)p1, len)) > 0) {
596 if (iswprint(wc) || visi_mode == 0) {
597 (void) putwchar(wc);
598 p1 += len;
599 continue;
603 (void) putchar('M');
604 (void) putchar('-');
605 c -= 0200;
607 if (isprint(c)) {
608 (void) putchar(c);
611 /* For non-printable characters. */
612 if (iscntrl(c)) {
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) {
619 (void) putchar('^');
620 (void) putchar(c^0100);
621 } else
622 (void) putchar(c);
623 } else {
624 (void) putchar('^');
625 (void) putchar(c^0100);
628 p1++;
630 return (0);