Unix Tools

Implementations of common Unix command-line utilities written in C.


unix-tools / wc.c

wc.c raw | download
1#define _POSIX_C_SOURCE 200809L
2#include <fcntl.h>
3#include <unistd.h>
4#include <errno.h>
5#include <ctype.h>
6#include <inttypes.h>
7#include <stdio.h>
8#include <string.h>
9
10static int pline, pword, pchar;
11static uintmax_t nline, nword, nchar;
12
13static void
14report(uintmax_t lines, uintmax_t words, uintmax_t bytes, const char *name)
15{
16 if (pline) printf("%7ju ", lines);
17 if (pword) printf("%7ju ", words);
18 if (pchar) printf("%7ju", bytes);
19 if (name) printf(" %s", name);
20 putchar('\n');
21}
22
23static int
24count(int fd, const char *name)
25{
26 unsigned char buf[8192];
27 int inword = 0;
28 nline = nword = nchar = 0;
29 for (;;) {
30 ssize_t n = read(fd, buf, sizeof(buf));
31 size_t i;
32 if (n < 0 && errno == EINTR) continue;
33 if (n < 0) { perror(name ? name : "wc: stdin"); return 1; }
34 if (n == 0) break;
35 nchar += (uintmax_t)n;
36 for (i = 0; i < (size_t)n; ++i) {
37 if (buf[i] == '\n') ++nline;
38 if (isspace(buf[i])) inword = 0;
39 else if (!inword) { ++nword; inword = 1; }
40 }
41 }
42 report(nline, nword, nchar, name);
43 return 0;
44}
45
46int
47main(int argc, char **argv)
48{
49 uintmax_t tnline = 0, tnword = 0, tnchar = 0;
50 int option, status = 0, files;
51 while ((option = getopt(argc, argv, "lwc")) != -1) {
52 if (option == 'l') pline = 1;
53 else if (option == 'w') pword = 1;
54 else if (option == 'c') pchar = 1;
55 else { fprintf(stderr, "usage: %s [-lwc] [file...]\n", argv[0]); return 1; }
56 }
57 if (!pline && !pword && !pchar) pline = pword = pchar = 1;
58 files = argc - optind;
59 if (!files) status = count(STDIN_FILENO, NULL);
60 for (; optind < argc; ++optind) {
61 int fd = strcmp(argv[optind], "-") == 0 ? STDIN_FILENO : open(argv[optind], O_RDONLY);
62 if (fd < 0) { perror(argv[optind]); status = 1; continue; }
63 status |= count(fd, argv[optind]);
64 tnline += nline; tnword += nword; tnchar += nchar;
65 if (fd != STDIN_FILENO && close(fd) < 0) { perror(argv[optind]); status = 1; }
66 }
67 if (files > 1) report(tnline, tnword, tnchar, "total");
68 if (fflush(stdout) == EOF || ferror(stdout)) { perror("wc: stdout"); status = 1; }
69 return status;
70}