an open file will never be a symbolic link (from pendry)
[unix-history] / usr / src / usr.bin / wc / wc.c
index d07167f..fe4f1c2 100644 (file)
 /*
 /*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1980, 1987, 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * %sccs.include.redist.c%
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1980 Regents of the University of California.\n\
- All rights reserved.\n";
-#endif not lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1991, 1993\n\
      The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
 
 #ifndef lint
 
 #ifndef lint
-static char sccsid[] = "@(#)wc.c       5.1 (Berkeley) %G%";
-#endif not lint
-
-/* wc line and word count */
+static char sccsid[] = "@(#)wc.c       8.2 (Berkeley) %G%";
+#endif /* not lint */
 
 
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdio.h>
-long   linect, wordct, charct, pagect;
-long   tlinect, twordct, tcharct, tpagect;
-char   *wd = "lwc";
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+u_long tlinect, twordct, tcharct;
+int doline, doword, dochar;
 
 
+void cnt __P((char *));
+void err __P((const char *, ...));
+void usage __P((void));
+
+int
 main(argc, argv)
 main(argc, argv)
-char **argv;
+       int argc;
+       char *argv[];
 {
 {
-       int i, token;
-       register FILE *fp;
-       register int c;
-       char *p;
-
-       while (argc > 1 && *argv[1] == '-') {
-               switch (argv[1][1]) {
-               case 'l': case 'w': case 'c': 
-                       wd = argv[1]+1;
+       register int ch;
+       int total;
+
+       while ((ch = getopt(argc, argv, "lwc")) != EOF)
+               switch((char)ch) {
+               case 'l':
+                       doline = 1;
                        break;
                        break;
+               case 'w':
+                       doword = 1;
+                       break;
+               case 'c':
+                       dochar = 1;
+                       break;
+               case '?':
                default:
                default:
-               usage:
-                       fprintf(stderr, "Usage: wc [-lwc] [files]\n");
-                       exit(1);
+                       usage();
                }
                }
-               argc--;
-               argv++;
+       argv += optind;
+       argc -= optind;
+
+       /* Wc's flags are on by default. */
+       if (doline + doword + dochar == 0)
+               doline = doword = dochar = 1;
+
+       total = 0;
+       if (!*argv) {
+               cnt(NULL);
+               (void)printf("\n");
        }
        }
+       else do {
+               cnt(*argv);
+               (void)printf(" %s\n", *argv);
+               ++total;
+       } while(*++argv);
 
 
-       i = 1;
-       fp = stdin;
-       do {
-               if(argc>1 && (fp=fopen(argv[i], "r")) == NULL) {
-                       perror(argv[i]);
-                       continue;
+       if (total > 1) {
+               if (doline)
+                       (void)printf(" %7ld", tlinect);
+               if (doword)
+                       (void)printf(" %7ld", twordct);
+               if (dochar)
+                       (void)printf(" %7ld", tcharct);
+               (void)printf(" total\n");
+       }
+       exit(0);
+}
+
+void
+cnt(file)
+       char *file;
+{
+       register u_char *p;
+       register short gotsp;
+       register int ch, len;
+       register u_long linect, wordct, charct;
+       struct stat sb;
+       int fd;
+       u_char buf[MAXBSIZE];
+
+       fd = STDIN_FILENO;
+       linect = wordct = charct = 0;
+       if (file) {
+               if ((fd = open(file, O_RDONLY, 0)) < 0)
+                       err("%s: %s", file, strerror(errno));
+               if (doword)
+                       goto word;
+               /*
+                * Line counting is split out because it's a lot faster to get
+                * lines than to get words, since the word count requires some
+                * logic.
+                */
+               if (doline) {
+                       while (len = read(fd, buf, MAXBSIZE)) {
+                               if (len == -1)
+                                       err("%s: %s", file, strerror(errno));
+                               charct += len;
+                               for (p = buf; len--; ++p)
+                                       if (*p == '\n')
+                                               ++linect;
+                       }
+                       tlinect += linect;
+                       (void)printf(" %7lu", linect);
+                       if (dochar) {
+                               tcharct += charct;
+                               (void)printf(" %7lu", charct);
+                       }
+                       (void)close(fd);
+                       return;
                }
                }
-               linect = 0;
-               wordct = 0;
-               charct = 0;
-               token = 0;
-               for(;;) {
-                       c = getc(fp);
-                       if (c == EOF)
-                               break;
-                       charct++;
-                       if(' '<c&&c<0177) {
-                               if(!token) {
-                                       wordct++;
-                                       token++;
-                               }
-                               continue;
+               /*
+                * If all we need is the number of characters and it's a
+                * regular or linked file, just stat the puppy.
+                */
+               if (dochar) {
+                       if (fstat(fd, &sb))
+                               err("%s: %s", file, strerror(errno));
+                       if (S_ISREG(sb.st_mode)) {
+                               (void)printf(" %7qu", sb.st_size);
+                               tcharct += sb.st_size;
+                               (void)close(fd);
+                               return;
                        }
                        }
-                       if(c=='\n') {
-                               linect++;
+               }
+       }
+
+       /* Do it the hard way... */
+word:  for (gotsp = 1; len = read(fd, buf, MAXBSIZE);) {
+               if (len == -1)
+                       err("%s: %s", file, strerror(errno));
+               /*
+                * This loses in the presence of multi-byte characters.
+                * To do it right would require a function to return a
+                * character while knowing how many bytes it consumed.
+                */
+               charct += len;
+               for (p = buf; len--;) {
+                       ch = *p++;
+                       if (ch == '\n')
+                               ++linect;
+                       if (isspace(ch))
+                               gotsp = 1;
+                       else if (gotsp) {
+                               gotsp = 0;
+                               ++wordct;
                        }
                        }
-                       else if(c!=' '&&c!='\t')
-                               continue;
-                       token = 0;
                }
                }
-               /* print lines, words, chars */
-               wcp(wd, charct, wordct, linect);
-               if(argc>1) {
-                       printf(" %s\n", argv[i]);
-               } else
-                       printf("\n");
-               fclose(fp);
+       }
+       if (doline) {
                tlinect += linect;
                tlinect += linect;
+               (void)printf(" %7lu", linect);
+       }
+       if (doword) {
                twordct += wordct;
                twordct += wordct;
+               (void)printf(" %7lu", wordct);
+       }
+       if (dochar) {
                tcharct += charct;
                tcharct += charct;
-       } while(++i<argc);
-       if(argc > 2) {
-               wcp(wd, tcharct, twordct, tlinect);
-               printf(" total\n");
+               (void)printf(" %7lu", charct);
        }
        }
-       exit(0);
+       (void)close(fd);
 }
 
 }
 
-wcp(wd, charct, wordct, linect)
-register char *wd;
-long charct; long wordct; long linect;
+void
+usage()
 {
 {
-       while (*wd) switch (*wd++) {
-       case 'l':
-               ipr(linect);
-               break;
-
-       case 'w':
-               ipr(wordct);
-               break;
-
-       case 'c':
-               ipr(charct);
-               break;
-
-       }
+       (void)fprintf(stderr, "usage: wc [-clw] [files]\n");
+       exit(1);
 }
 
 }
 
-ipr(num)
-long num;
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+       char *fmt;
+        va_dcl
+#endif
 {
 {
-       printf(" %7ld", num);
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)fprintf(stderr, "wc: ");
+       (void)vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       (void)fprintf(stderr, "\n");
+       exit(1);
+       /* NOTREACHED */
 }
 }
-