|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T w
Length: 5961 (0x1749) Types: TextFile Names: »wc.web«
└─⟦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«
\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; } }