xref: /original-bsd/usr.bin/wc/wc.c (revision 2c12987e)
1 /*
2  * Copyright (c) 1980, 1987 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1980, 1987 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)wc.c	5.6 (Berkeley) 06/01/90";
16 #endif /* not lint */
17 
18 /* wc line, word and char count */
19 
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <sys/file.h>
23 #include <stdio.h>
24 
25 #define DEL	0177			/* del char */
26 #define NL	012			/* newline char */
27 #define SPACE	040			/* space char */
28 #define TAB	011			/* tab char */
29 
30 static long	tlinect, twordct, tcharct;
31 static int	doline, doword, dochar;
32 
33 main(argc, argv)
34 	int argc;
35 	char **argv;
36 {
37 	extern int optind;
38 	register int ch;
39 	int total;
40 
41 	/*
42 	 * wc is unusual in that its flags are on by default, so,
43 	 * if you don't get any arguments, you have to turn them
44 	 * all on.
45 	 */
46 	if (argc > 1 && argv[1][0] == '-' && argv[1][1]) {
47 		while ((ch = getopt(argc, argv, "lwc")) != EOF)
48 			switch((char)ch) {
49 			case 'l':
50 				doline = 1;
51 				break;
52 			case 'w':
53 				doword = 1;
54 				break;
55 			case 'c':
56 				dochar = 1;
57 				break;
58 			case '?':
59 			default:
60 				fputs("usage: wc [-lwc] [files]\n", stderr);
61 				exit(1);
62 			}
63 		argv += optind;
64 		argc -= optind;
65 	}
66 	else {
67 		++argv;
68 		--argc;
69 		doline = doword = dochar = 1;
70 	}
71 
72 	total = 0;
73 	if (!*argv) {
74 		cnt((char *)NULL);
75 		putchar('\n');
76 	}
77 	else do {
78 		cnt(*argv);
79 		printf(" %s\n", *argv);
80 		++total;
81 	} while(*++argv);
82 
83 	if (total > 1) {
84 		if (doline)
85 			printf(" %7ld", tlinect);
86 		if (doword)
87 			printf(" %7ld", twordct);
88 		if (dochar)
89 			printf(" %7ld", tcharct);
90 		puts(" total");
91 	}
92 	exit(0);
93 }
94 
95 static
96 cnt(file)
97 	char *file;
98 {
99 	register u_char *C;
100 	register short gotsp;
101 	register int len;
102 	register long linect, wordct, charct;
103 	struct stat sbuf;
104 	int fd;
105 	u_char buf[MAXBSIZE];
106 
107 	linect = wordct = charct = 0;
108 	if (file) {
109 		if ((fd = open(file, O_RDONLY, 0)) < 0) {
110 			perror(file);
111 			exit(1);
112 		}
113 		if (!doword) {
114 			/*
115 			 * line counting is split out because it's a lot
116 			 * faster to get lines than to get words, since
117 			 * the word count requires some logic.
118 			 */
119 			if (doline) {
120 				while(len = read(fd, buf, MAXBSIZE)) {
121 					if (len == -1) {
122 						perror(file);
123 						exit(1);
124 					}
125 					charct += len;
126 					for (C = buf; len--; ++C)
127 						if (*C == '\n')
128 							++linect;
129 				}
130 				tlinect += linect;
131 				printf(" %7ld", linect);
132 				if (dochar) {
133 					tcharct += charct;
134 					printf(" %7ld", charct);
135 				}
136 				close(fd);
137 				return;
138 			}
139 			/*
140 			 * if all we need is the number of characters and
141 			 * it's a directory or a regular or linked file, just
142 			 * stat the puppy.  We avoid testing for it not being
143 			 * a special device in case someone adds a new type
144 			 * of inode.
145 			 */
146 			if (dochar) {
147 				if (fstat(fd, &sbuf)) {
148 					perror(file);
149 					exit(1);
150 				}
151 				if (sbuf.st_mode & (S_IFREG | S_IFLNK | S_IFDIR)) {
152 					printf(" %7ld", sbuf.st_size);
153 					tcharct += sbuf.st_size;
154 					close(fd);
155 					return;
156 				}
157 			}
158 		}
159 	}
160 	else
161 		fd = 0;
162 	/* do it the hard way... */
163 	for (gotsp = 1; len = read(fd, buf, MAXBSIZE);) {
164 		if (len == -1) {
165 			perror(file);
166 			exit(1);
167 		}
168 		charct += len;
169 		for (C = buf; len--; ++C)
170 			switch(*C) {
171 				case NL:
172 					++linect;
173 				case TAB:
174 				case SPACE:
175 					gotsp = 1;
176 					continue;
177 				default:
178 #ifdef notdef
179 					/*
180 					 * This line of code implements the
181 					 * original V7 wc algorithm, i.e.
182 					 * a non-printing character doesn't
183 					 * toggle the "word" count, so that
184 					 * "  ^D^F  " counts as 6 spaces,
185 					 * while "foo^D^Fbar" counts as 8
186 					 * characters.
187 					 *
188 					 * test order is important -- gotsp
189 					 * will normally be NO, so test it
190 					 * first
191 					 */
192 					if (gotsp && *C > SPACE && *C < DEL) {
193 #endif
194 					/*
195 					 * This line implements the manual
196 					 * page, i.e. a word is a "maximal
197 					 * string of characters delimited by
198 					 * spaces, tabs or newlines."  Notice
199 					 * nothing was said about a character
200 					 * being printing or non-printing.
201 					 */
202 					if (gotsp) {
203 						gotsp = 0;
204 						++wordct;
205 					}
206 			}
207 	}
208 	if (doline) {
209 		tlinect += linect;
210 		printf(" %7ld", linect);
211 	}
212 	if (doword) {
213 		twordct += wordct;
214 		printf(" %7ld", wordct);
215 	}
216 	if (dochar) {
217 		tcharct += charct;
218 		printf(" %7ld", charct);
219 	}
220 	close(fd);
221 }
222