#include /* errno, EBADF, EFAULT, EINVAL, ELOOP, ENAMETOOLONG, * ENOENT, ENOMEM, ENOTDIR, EOVERFLOW */ #include /* fprintf(3), getc(3), putc(3), setvbuf(3), stderr, stdin, * stdout */ #include /* strerror(3) */ #include /* EX_DATAERR, EX_NOINPUT, EX_OK, EX_OSERR, * EX_UNAVAILABLE, EX_USAGE */ #include /* getopt(3) */ #include /* stat(2), struct stat, S_ISDIR */ static char *program_name = "cat"; /* NetBSD's default size according to an strace */ static unsigned char buf[4096]; int main(int argc, char *argv[]){ char *argv0; int c; struct stat fi; /* info */ char *fn; /* name */ FILE *fo; /* object */ setvbuf(stdout, (char *)buf, _IOFBF, (sizeof buf)/(sizeof *buf)); if(argc < 2){ argv0 = argc > 0 ? argv[0] : program_name; argv -= argv[0] == NULL; goto simple; } while((c = getopt(argc, argv, "hu")) != -1) switch(c){ case 'u': setvbuf(stdout, NULL, _IONBF, 0); continue; default: fprintf(stderr, "Usage: %s (-h) (file...)\n", argv[0]); return EX_USAGE; } argv0 = argv[0]; argv += optind; while(*argv != NULL){ if(argv[0][0] == '-' && argv[0][1] == '\0') /* "-" */ simple: fo = stdin; else{ fn = argv[0]; if(stat(fn, &fi) == -1){ fprintf(stderr, "%s: %s: %s\n", argv0, fn, strerror(errno)); switch(errno){ case EFAULT: case ENOENT: case ENOTDIR: return EX_NOINPUT; case EBADF: case EINVAL: case ENOMEM: case EOVERFLOW: return EX_OSERR; case ELOOP: case ENAMETOOLONG: return EX_DATAERR; } } if(S_ISDIR(fi.st_mode)){ fprintf(stderr, "%s: %s: Is a directory\n", argv[0], fn); return EX_DATAERR; } if((fo = fopen(fn, "r")) == NULL){ fprintf(stderr, "%s: %s: %s\n", argv0, fn, strerror(errno)); return EX_OSERR; } } while((c = getc(fo)) != EOF) if(putc(c, stdout) == EOF){ fprintf(stderr, "%s: Exiting due to error writing to" " output...\n", argv0); fclose(fo); return EX_UNAVAILABLE; } if(fo != stdin) fclose(fo); ++argv; } return EX_OK; }