xref: /original-bsd/usr.bin/wc/wc.c (revision 95a66346)
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.7 (Berkeley) 03/02/91";
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 cnt(file)
96 	char *file;
97 {
98 	register u_char *C;
99 	register short gotsp;
100 	register int len;
101 	register long linect, wordct, charct;
102 	struct stat sbuf;
103 	int fd;
104 	u_char buf[MAXBSIZE];
105 
106 	linect = wordct = charct = 0;
107 	if (file) {
108 		if ((fd = open(file, O_RDONLY, 0)) < 0) {
109 			perror(file);
110 			exit(1);
111 		}
112 		if (!doword) {
113 			/*
114 			 * line counting is split out because it's a lot
115 			 * faster to get lines than to get words, since
116 			 * the word count requires some logic.
117 			 */
118 			if (doline) {
119 				while(len = read(fd, buf, MAXBSIZE)) {
120 					if (len == -1) {
121 						perror(file);
122 						exit(1);
123 					}
124 					charct += len;
125 					for (C = buf; len--; ++C)
126 						if (*C == '\n')
127 							++linect;
128 				}
129 				tlinect += linect;
130 				printf(" %7ld", linect);
131 				if (dochar) {
132 					tcharct += charct;
133 					printf(" %7ld", charct);
134 				}
135 				close(fd);
136 				return;
137 			}
138 			/*
139 			 * if all we need is the number of characters and
140 			 * it's a directory or a regular or linked file, just
141 			 * stat the puppy.  We avoid testing for it not being
142 			 * a special device in case someone adds a new type
143 			 * of inode.
144 			 */
145 			if (dochar) {
146 				if (fstat(fd, &sbuf)) {
147 					perror(file);
148 					exit(1);
149 				}
150 				if (sbuf.st_mode & (S_IFREG | S_IFLNK | S_IFDIR)) {
151 					printf(" %7ld", sbuf.st_size);
152 					tcharct += sbuf.st_size;
153 					close(fd);
154 					return;
155 				}
156 			}
157 		}
158 	}
159 	else
160 		fd = 0;
161 	/* do it the hard way... */
162 	for (gotsp = 1; len = read(fd, buf, MAXBSIZE);) {
163 		if (len == -1) {
164 			perror(file);
165 			exit(1);
166 		}
167 		charct += len;
168 		for (C = buf; len--; ++C)
169 			switch(*C) {
170 				case NL:
171 					++linect;
172 				case TAB:
173 				case SPACE:
174 					gotsp = 1;
175 					continue;
176 				default:
177 #ifdef notdef
178 					/*
179 					 * This line of code implements the
180 					 * original V7 wc algorithm, i.e.
181 					 * a non-printing character doesn't
182 					 * toggle the "word" count, so that
183 					 * "  ^D^F  " counts as 6 spaces,
184 					 * while "foo^D^Fbar" counts as 8
185 					 * characters.
186 					 *
187 					 * test order is important -- gotsp
188 					 * will normally be NO, so test it
189 					 * first
190 					 */
191 					if (gotsp && *C > SPACE && *C < DEL) {
192 #endif
193 					/*
194 					 * This line implements the manual
195 					 * page, i.e. a word is a "maximal
196 					 * string of characters delimited by
197 					 * spaces, tabs or newlines."  Notice
198 					 * nothing was said about a character
199 					 * being printing or non-printing.
200 					 */
201 					if (gotsp) {
202 						gotsp = 0;
203 						++wordct;
204 					}
205 			}
206 	}
207 	if (doline) {
208 		tlinect += linect;
209 		printf(" %7ld", linect);
210 	}
211 	if (doword) {
212 		twordct += wordct;
213 		printf(" %7ld", wordct);
214 	}
215 	if (dochar) {
216 		tcharct += charct;
217 		printf(" %7ld", charct);
218 	}
219 	close(fd);
220 }
221