1 /* $OpenBSD: wc.c,v 1.9 2003/06/03 02:56:22 millert Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1987, 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #ifndef lint 33 static char copyright[] = 34 "@(#) Copyright (c) 1980, 1987, 1991, 1993\n\ 35 The Regents of the University of California. All rights reserved.\n"; 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)wc.c 8.2 (Berkeley) 5/2/95"; 41 #else 42 static char rcsid[] = "$OpenBSD: wc.c,v 1.9 2003/06/03 02:56:22 millert Exp $"; 43 #endif 44 #endif /* not lint */ 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <locale.h> 50 #include <ctype.h> 51 #include <err.h> 52 #include <sys/param.h> 53 #include <sys/stat.h> 54 #include <sys/file.h> 55 #include <unistd.h> 56 57 int64_t tlinect, twordct, tcharct; 58 int doline, doword, dochar; 59 int rval; 60 extern char *__progname; 61 62 void print_counts(int64_t, int64_t, int64_t, char *); 63 void cnt(char *); 64 65 int 66 main(int argc, char *argv[]) 67 { 68 int ch; 69 70 setlocale(LC_ALL, ""); 71 72 while ((ch = getopt(argc, argv, "lwcm")) != -1) 73 switch((char)ch) { 74 case 'l': 75 doline = 1; 76 break; 77 case 'w': 78 doword = 1; 79 break; 80 case 'c': 81 case 'm': 82 dochar = 1; 83 break; 84 case '?': 85 default: 86 (void)fprintf(stderr, 87 "usage: %s [-c | -m] [-lw] [file ...]\n", 88 __progname); 89 exit(1); 90 } 91 argv += optind; 92 argc -= optind; 93 94 /* 95 * wc is unusual in that its flags are on by default, so, 96 * if you don't get any arguments, you have to turn them 97 * all on. 98 */ 99 if (!doline && !doword && !dochar) 100 doline = doword = dochar = 1; 101 102 if (!*argv) { 103 cnt((char *)NULL); 104 } else { 105 int dototal = (argc > 1); 106 107 do { 108 cnt(*argv); 109 } while(*++argv); 110 111 if (dototal) 112 print_counts(tlinect, twordct, tcharct, "total"); 113 } 114 115 exit(rval); 116 } 117 118 void 119 cnt(char *file) 120 { 121 u_char *C; 122 short gotsp; 123 int len; 124 int64_t linect, wordct, charct; 125 struct stat sbuf; 126 int fd; 127 u_char buf[MAXBSIZE]; 128 129 linect = wordct = charct = 0; 130 if (file) { 131 if ((fd = open(file, O_RDONLY, 0)) < 0) { 132 warn("%s", file); 133 rval = 1; 134 return; 135 } 136 } else { 137 fd = STDIN_FILENO; 138 } 139 140 if (!doword) { 141 /* 142 * Line counting is split out because it's a lot 143 * faster to get lines than to get words, since 144 * the word count requires some logic. 145 */ 146 if (doline) { 147 while ((len = read(fd, buf, MAXBSIZE)) > 0) { 148 charct += len; 149 for (C = buf; len--; ++C) 150 if (*C == '\n') 151 ++linect; 152 } 153 if (len == -1) { 154 warn("%s", file); 155 rval = 1; 156 } 157 } 158 /* 159 * If all we need is the number of characters and 160 * it's a directory or a regular or linked file, just 161 * stat the puppy. We avoid testing for it not being 162 * a special device in case someone adds a new type 163 * of inode. 164 */ 165 else if (dochar) { 166 mode_t ifmt; 167 168 if (fstat(fd, &sbuf)) { 169 warn("%s", file); 170 rval = 1; 171 } else { 172 ifmt = sbuf.st_mode & S_IFMT; 173 if (ifmt == S_IFREG || ifmt == S_IFLNK 174 || ifmt == S_IFDIR) { 175 charct = sbuf.st_size; 176 } else { 177 while ((len = read(fd, buf, MAXBSIZE)) > 0) 178 charct += len; 179 if (len == -1) { 180 warn("%s", file); 181 rval = 1; 182 } 183 } 184 } 185 } 186 } else { 187 /* Do it the hard way... */ 188 gotsp = 1; 189 while ((len = read(fd, buf, MAXBSIZE)) > 0) { 190 /* 191 * This loses in the presence of multi-byte characters. 192 * To do it right would require a function to return a 193 * character while knowing how many bytes it consumed. 194 */ 195 charct += len; 196 for (C = buf; len--; ++C) { 197 if (isspace (*C)) { 198 gotsp = 1; 199 if (*C == '\n') 200 ++linect; 201 } else { 202 /* 203 * This line implements the POSIX 204 * spec, i.e. a word is a "maximal 205 * string of characters delimited by 206 * whitespace." Notice nothing was 207 * said about a character being 208 * printing or non-printing. 209 */ 210 if (gotsp) { 211 gotsp = 0; 212 ++wordct; 213 } 214 } 215 } 216 } 217 if (len == -1) { 218 warn("%s", file); 219 rval = 1; 220 } 221 } 222 223 print_counts(linect, wordct, charct, file ? file : ""); 224 225 /* 226 * Don't bother checking doline, doword, or dochar -- speeds 227 * up the common case 228 */ 229 tlinect += linect; 230 twordct += wordct; 231 tcharct += charct; 232 233 if (close(fd) != 0) { 234 warn("%s", file); 235 rval = 1; 236 } 237 } 238 239 void 240 print_counts(int64_t lines, int64_t words, int64_t chars, char *name) 241 { 242 243 if (doline) 244 (void)printf(" %7lld", (long long)lines); 245 if (doword) 246 (void)printf(" %7lld", (long long)words); 247 if (dochar) 248 (void)printf(" %7lld", (long long)chars); 249 250 (void)printf(" %s\n", name); 251 } 252