DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T w

⟦2b616f728⟧ TextFile

    Length: 5961 (0x1749)
    Types: TextFile
    Names: »wc.web«

Derivation

└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
    └─⟦c319c2751⟧ »unix3.0/TeX3.0.tar.Z« 
        └─⟦036c765ac⟧ 
            └─⟦this⟧ »TeX3.0/CWEB/examples/wc.web« 
└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89
    └─⟦this⟧ »./tex82/CWEB/examples/wc.web« 

TextFile

\def\9#1{}
@* An example of {\tt WEB}.  This example presents the ``word count''
program from UNIX.  It was rewritten in \.{WEB} to demonstrate
literate programming in C.  The level of detail is high for didactic
purposes; many of the things spelled out here don't have to be
explained in other programs.

@ Most \.{CWEB} programs share a common structure.  It's probably a
good idea to have one module explicitly stating this common structure,
even though if the functions don't need to be introduced in any
special order they can all be unnamed modules.

@c
@<Global |#include|s@>@/
@<Global variables@>@/
@<All functions@>@/
@<Main@>

@ We must include the standard I/O definitions to use formatted output
to |stdout| and |stderr|.  The file \.{stdio.h} includes a typedef for
the identifier |FILE|, which is not, strictly speaking, part of C.
It turns out \.{WEAVE} knows that |FILE| is a reserved word (after all,
|FILE| is almost as common as |int|);
but if you're using other types like {\bf caddr\_t},
@:caddr_t}{\bf caddr_t@>
which is defined in \.{/usr/include/sys/types.h}, you should let
\.{WEAVE} know that this is a type, either by including the \.{.h} file
at \.{WEB} time (saying \.{@@i /usr/include/sys/types.h}), or by
using \.{WEB}'s format command (saying \.{@@f caddr\_t int}).  Either of
these will make {\bf caddr\_t} be treated in the same way as |int|. 

@<Global |#in...@>=
#include <stdio.h>

@ Now we come to the general layout of the |main| function.  The
|status| variable communicates to the operating system if the run was
successful or not, and |prog_name| is used in case there's an error message to
be printed.

@d OK 0
@d usage_error 1
@d cannot_open_file 2

@<Global variables@>=
int status=OK; /* exit status of command */
char *prog_name;

@ @<Main@>=
main (argc,argv)
char **argv;
{
	@<Variables local to |main|@>@;@/
	prog_name=argv[0];
	@<Set up option selection@>;@/
	@<Process all the files@>;@/
	@<Print the grand totals if there were multiple files @>;@/
	exit(status);
}

@ If the first argument begins with a \.{'-'} the user is choosing
the counts that he wants.  Each selection is given by the initial character
(lines, words or characters).  We do not process this string now.  It is
sufficient just to suppress unwanted figures at output time.

@<Var...@>=
int filect; /* how many files there are */
char *which; /* which counts to print */

@ @<Set up o...@>=
which="lwc"; /* if no option is given, print all three values */
if (argc>1 && *argv[1] == '-') { which=++argv[1]; argc--; argv++; }
filect=argc-1;

@ Now we scan the remaining arguments and try to open a file, if
possible.  The file is processed and its statistics are given.
We use the |do| \dots\ |while| loop because if no file name is given
we should read from the standard input.

@<Process...@>=
argc--;
do {
	@<If a file is given try to open it@>;@/
	@<Initialize pointers and counters@>;@/
	@<Scan file@>;@/
	@<Write statistics for file@>;@/
	@<Close file@>;@/
	@<Update grand totals@>; /* even if there is only one file */
} while (--argc>0);

@ Here's the code to open the file.  A special trick allows us to
handle input from |stdin| when no name is given.
Recall that the file descriptor to |stdin| is 0, so that's what we
initialize our file descriptor to.

@<Variabl...@>=
int fd=0; /* file descriptor, initialized to |stdin| */

@ @<If a fi...@>=
if (filect>0 && (fd=open(*(++argv),0))<0) {
	fprintf (stderr, "%s: cannot open file %s\n", prog_name, *argv);
@.cannot open file@>
	status+=cannot_open_file;
	continue;
}

@ @<Close file@>=
close(fd);

@ Since buffered I/O speeds things up very much we use it, but we do
the buffering ourselves.  To do this we set up appropriate pointers
and counters.

@d buf_size BUFSIZ /* \.{stdio.h}'s |BUFSIZ| is chosen for efficiency*/

@<Var...@>=
char buffer[buf_size]; /* we read the input into this */
register char *ptr, *buf_end; /* pointers into |buffer| */
register int c; /* character read or |EOF| */
int in_word; /* are we within a word? */
long wordct, linect, charct; /* number of words, lines and chars in a file */
long twordct, tlinect, tcharct; /* total number of words, lines and chars */

@ @<Init...@>=
ptr=buf_end=buffer; linect=wordct=charct=0; in_word=0;

@ @<Scan...@>=
while (1) {
	@<Fill |buffer| if it is empty@>;
	c=*ptr++;
	if (c>' ' && c<0177) { /* visible ASCII codes */
		if (!in_word) {wordct++; in_word++;}
		continue;
	}
	if (c=='\n') linect++;
	else if (c!=' ' && c!='\t') continue;
	in_word=0;
}

@ Using buffered I/O makes it very easy to count the number of
characters, almost for free.

@<Fill |buff...@>=
if (ptr>=buf_end) {
	ptr=buffer; c=read(fd,ptr,buf_size);
	if (c<=0) break;
	charct+=c; buf_end=buffer+c;
}

@ Output of the statistics is done by means of a function.  This makes
it easy to use it for the totals, too.  Additionally we must decide
here if we know the name of the file we have processed of if it was
just |stdin|.

@<Write...@>=
wc_print(which, charct, wordct, linect);
if (filect) printf (" %s\n", *argv);
else printf ("\n");

@ @<Upda...@>=
tlinect+=linect;
twordct+=wordct;
tcharct+=charct;

@ @<Print the...@>=
if (filect>1) {
	wc_print(which, tcharct, twordct, tlinect);
	printf(" total\n");
}

@ Here now is the function that prints the values according to the
specified options.  If an invalid option character is found we inform
the reader about the usage of the command.

@d prt_value(n) printf("%7ld",n)

@<All f...@>=
wc_print(which, charct, wordct, linect)
char *which; /* which counts to print */
long charct, wordct, linect; /* number of words, lines and chars */
{
	static int usage_error_flag=0;
	while (*which) 
		switch (*which++) {
		case 'l': prt_value(linect); break;
		case 'w': prt_value(wordct); break;
		case 'c': prt_value(charct); break;
		default: if (usage_error_flag++==0) {
		  fprintf (stderr, "usage: %s [-clw] [name ...]\n", prog_name);
@.usage: ...@>
		  status+=usage_error;
		}
		continue;
		}
}