/*
 *  wc [ -lwcp ] [ -spagesize ] [ -v ] [ files... ]
 *
 *  Count lines, words, characters, and pages.
 *
 *  Runs faster by doing less work if it doesn't have to count
 *  all quantities.
 *
 *  Use this program as you wish, but please leave this header intact.
 *
 *  Steve Summit 12/4/84
 */

#include <stdio.h>

#ifdef unix
extern char *rindex();
#else
#define rindex strrchr
extern char *strrchr();
#endif
extern char *strcat();
extern char *strcpy();

#define TRUE 1
#define FALSE 0

long int totchars = 0;
long int totwords = 0;
long int totlines = 0;
long int totpages = 0;

#define LINES	04
#define WORDS	02
#define CHARS	01

int count = LINES | WORDS | CHARS;
char want[10] = "lwc";

int verbose = FALSE;

int pagelen = 66;

int errs = 0;

int deflt = TRUE;

#define Isdigit(c) ((c) >= '0' && (c) <= '9')
#define Ctod(c) ((c) - '0')

#ifdef unix

#define Append(mask, letter)	if(deflt)				\
					{				\
					count = mask;			\
					(void)strcpy(want, letter);	\
					deflt = FALSE;			\
					}				\
				else	{				\
					count |= mask;			\
					(void)strcat(want, letter);	\
					}

#define Append2(letter)		if(deflt)				\
					{				\
					(void)strcpy(want, letter);	\
					deflt = FALSE;			\
					}				\
				else	(void)strcat(want, letter)

#else

Append(mask, letter)
{
	if(deflt)
	{
		count = mask;
		(void)strcpy(want, letter);
		deflt = FALSE;
	}
	else
	{
		count |= mask;
		(void)strcat(want, letter);
	}
}

Append2(letter)
{
	if(deflt)
	{
		(void)strcpy(want, letter);
		deflt = FALSE;
	}
	else
	{
		(void)strcat(want, letter);
	}
}

#endif

char *progname = "wc";

main(argc, argv)
int argc;
char *argv[];
{
int fd;
int argi;
char *p;
int totals;

if(argc > 0)
	{
	p = rindex(argv[0], '/');
	if(p != NULL)
		progname = p + 1;
	else	progname = argv[0];
	}

for(argi = 1; argi < argc && argv[argi][0] == '-'; argi++)
	{
	for(p = &argv[argi][1]; *p != '\0'; p++)
		{
		switch(*p)
			{
			case 'l':
				Append(LINES, "l");
				break;

			case 'w':
				Append(WORDS, "w");
				break;

			case 'c':
				Append(CHARS, "c");
				break;

			case 'p':
				Append2("p");
				break;

			case 'v':
				verbose = TRUE;
				if(deflt)
					(void)strcpy(want, "lwcp");
				break;

			case 's':
				pagelen = 0;
				while(Isdigit(*(p + 1)))
					pagelen = 10 * pagelen + Ctod(*++p);
				break;

			default:
				fprintf(stderr, "%s: unknown option -%c\n",
								progname, *p);
			}
		}
	}

if(verbose)
	{
	for(p = want; *p != '\0'; p++)
		{
		switch(*p)
			{
			case 'l':
				printf("   lines");
				break;

			case 'w':
				printf("   words");
				break;

			case 'c':
				printf("   chars");
				break;

			case 'p':
				printf("   pages");
				break;
			}
		}

	putchar('\n');
	}

if(argi >= argc)
	wc("", 0);
else	{
	totals = (argi + 1) < argc;

	for(; argi < argc; argi++)
		{
		if((fd = open(argv[argi], 0)) < 0)
			{
			fprintf(stderr, "%s: can't open %s\n", progname,
								argv[argi]);
			perror("");
			errs++;
			continue;
			}
		wc(argv[argi], fd);
		(void)close(fd);
		}

	if(totals)
		{
		printit(totlines, totwords, totchars, totpages);
		printf(" total\n");
		}
	}

exit(errs);
}

#define Set(flag)	flag++
#define Clear(flag)	flag = FALSE

#define Checkline()	if(*p == '\n')					\
				lines++

#define Checkword()	if(' ' < *p && *p < '\177')			\
				{					\
				if(!inword)				\
					{				\
					words++;			\
					Set(inword);			\
					}				\
				continue;				\
				}

#define Checkword2()	else if(*p != ' ' && *p != '\t') 		\
				continue;				\
			Clear(inword)

#define Checkword3()	if(*p == ' ' || *p == '\n' || *p == '\t')	\
				Clear(inword)

#define Dochars()	chars += r

wc(name, fd)
char *name;
int fd;
{
char buf[BUFSIZ];
register char *bufend;
int r;
long int lines, words, chars, pages;
register char *p;
register int inword;

lines = words = chars = pages = 0;

Clear(inword);

switch(count)
	{
	case LINES:
		while((r = read(fd, buf, BUFSIZ)) > 0)
			{
			bufend = buf + r;
			for(p = buf; p < bufend; p++)
				Checkline();
			}
		break;

	case WORDS:
		while((r = read(fd, buf, BUFSIZ)) > 0)
			{
			bufend = buf + r;
			for(p = buf; p < bufend; p++)
				{
				Checkword();
				Checkword3();
				}
			}
		break;

	case CHARS:
		while((r = read(fd, buf, BUFSIZ)) > 0)
			Dochars();
		break;

	case LINES|CHARS:
		while((r = read(fd, buf, BUFSIZ)) > 0)
			{
			Dochars();

			bufend = buf + r;
			for(p = buf; p < bufend; p++)
				Checkline();
			}
		break;

	case LINES|WORDS:
		while((r = read(fd, buf, BUFSIZ)) > 0)
			{
			bufend = buf + r;
			for(p = buf; p < bufend; p++)
				{
				Checkword();
				Checkline();
				Checkword2();
				}
			}
		break;

	case WORDS|CHARS:
		while((r = read(fd, buf, BUFSIZ)) > 0)
			{
			Dochars();

			bufend = buf + r;
			for(p = buf; p < bufend; p++)
				{
				Checkword();
				Checkword3();
				}
			}
		break;

	case LINES|WORDS|CHARS:
		while((r = read(fd, buf, BUFSIZ)) > 0)
			{
			Dochars();

			bufend = buf + r;
			for(p = buf; p < bufend; p++)
				{
				Checkword();
				Checkline();
				Checkword2();
				}
			}
		break;
	}

if(r < 0)
	{
	fprintf(stderr, "%s: %s: read error\n", progname,
				*name != '\0' ? name : "standard input");
	perror("");
	errs++;
	}

pages = lines / pagelen + (lines % pagelen != 0 ? 1 : 0);

printit(lines, words, chars, pages);

if(*name != '\0')
	printf(" %s", name);
putchar('\n');

totlines += lines;
totwords += words;
totchars += chars;
totpages += pages;
}

printit(lines, words, chars, pages)
long int lines, words, chars, pages;
{
char *p;

for(p = want; *p != '\0'; p++)
	{
	switch(*p)
		{
		case 'l':
			printf(" %7ld", lines);
			break;

		case 'w':
			printf(" %7ld", words);
			break;

		case 'c':
			printf(" %7ld", chars);
			break;

		case 'p':
			printf(" %7ld", pages);
			break;
		}
	}
}
