#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 <errno.h>
#include <string.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)
#define SMALLFILESIZE (32*1024)
static int vncat(FILE *);
static int cat(FILE *, struct stat *, struct stat *, char *);
static int silent = 0;
static int visi_mode = 0;
static int visi_tab = 0;
static int visi_newline = 0;
static int bflg = 0;
static int nflg = 0;
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)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
#ifdef STANDALONE
if (argv[0][0] == '\0')
argc = getargv("cat", &argv, 0);
#endif
while ((c = getopt(argc, argv, "usvtebn")) != EOF) {
switch (c) {
case 'u':
#ifndef STANDALONE
setbuf(stdout, (char *)NULL);
#endif
continue;
case 's':
silent++;
continue;
case 'v':
visi_mode++;
continue;
case 't':
visi_tab++;
continue;
case 'e':
visi_newline++;
continue;
case 'b':
bflg++;
nflg++;
continue;
case 'n':
nflg++;
continue;
case '?':
errflg++;
break;
}
break;
}
if (errflg) {
if (!silent)
(void) fprintf(stderr,
gettext("usage: cat [ -usvtebn ] [-|file] ...\n"));
exit(2);
}
if (fstat(fileno(stdout), &target) < 0) {
if (!silent)
(void) fprintf(stderr,
gettext("cat: Cannot stat stdout\n"));
exit(2);
}
obsize = target.st_blksize;
if (optind == argc) {
argc++;
stdinflg++;
}
for (argv = &argv[optind];
optind < argc && !ferror(stdout); optind++, argv++) {
if (stdinflg ||
((*argv)[0] == '-' && (*argv)[1] == '\0'))
fi = stdin;
else {
if ((fi = fopen(*argv, "r")) == NULL) {
if (!silent)
(void) fprintf(stderr, gettext(
"cat: cannot open %s: %s\n"),
*argv, strerror(errno));
status = 2;
continue;
}
}
if (fstat(fileno(fi), &source) < 0) {
if (!silent)
(void) fprintf(stderr,
gettext("cat: cannot stat %s: %s\n"),
(stdinflg) ? "-" : *argv, strerror(errno));
status = 2;
continue;
}
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: %s\n"),
strerror(errno));
status = 2;
continue;
}
ibsize = source.st_blksize;
if (visi_mode || nflg)
estatus = vncat(fi);
else
estatus = cat(fi, &source, &target,
fi != stdin ? *argv : "standard input");
if (estatus)
status = estatus;
if (fi != stdin) {
if (fclose(fi) != 0)
if (!silent)
(void) fprintf(stderr,
gettext("cat: close error: %s\n"),
strerror(errno));
}
}
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;
bufferp = mmap((caddr_t)NULL, (size_t)mapsize, PROT_READ,
MAP_SHARED, fi_desc, (off_t)0);
if (bufferp == (caddr_t)-1)
mapsize = 0;
} else
mapsize = 0;
if (mapsize != 0) {
int read_error = 0;
char x;
if (read(fi_desc, &x, 1) == -1)
read_error = 1;
mapoffset = 0;
filesize = statp->st_size;
for (;;) {
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);
}
}
(void) lseek(fi_desc, (off_t)mapoffset, SEEK_SET);
(void) munmap(bufferp, (size_t)munmapsize);
} else {
if (S_ISREG(statp->st_mode) && S_ISREG(outp->st_mode)) {
bufferp = (char *)buf;
buffsize = SMALLFILESIZE;
} else {
if (obsize)
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 ((nitems = read(fi_desc, bufferp, (size_t)buffsize)) >
0) {
offset = 0;
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 (1);
}
}
return (0);
}
static int
vncat(fi)
FILE *fi;
{
int c;
int lno;
int boln;
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++;
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;
if (isascii(c)) {
if (isprint(c) || visi_mode == 0) {
(void) putchar(c);
continue;
}
if (iscntrl(c)) {
if ((c == '\t') || (c == '\f')) {
if (visi_mode && visi_tab) {
(void) putchar('^');
(void) putchar(c^0100);
} else
(void) putchar(c);
continue;
}
(void) putchar('^');
(void) putchar(c^0100);
continue;
}
continue;
}
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);
}
if (iscntrl(c)) {
if ((c == '\t') || (c == '\f')) {
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);
}