Implementations of common Unix command-line utilities written in C.
unix-tools / wc.c
| 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 | |
| 10 | static int pline, pword, pchar; |
| 11 | static uintmax_t nline, nword, nchar; |
| 12 | |
| 13 | static void |
| 14 | report(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 | |
| 23 | static int |
| 24 | count(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 | |
| 46 | int |
| 47 | main(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 | } |