1implement Wc; 2 3# 4# wc -- count things in utf-encoded text files 5# Bugs: 6# The only white space characters recognized are ' ', '\t' and '\n', even though 7# ISO 10646 has many more blanks scattered through it. 8# Should count characters that cannot occur in any rune (hex f0-ff) separately. 9# Should count non-canonical runes (e.g. hex c1,80 instead of hex 40). 10# 11 12include "sys.m"; 13 sys: Sys; 14 15include "draw.m"; 16 17Wc: module 18{ 19 init: fn(ctxt: ref Draw->Context, args: list of string); 20}; 21 22NBUF: con 8*1024; 23 24stderr: ref Sys->FD; 25nline, tnline, pline: int; 26nword, tnword, pword: int; 27nchar, tnchar, pchar: int; 28nbadr, tnbadr, pbadr: int; 29nbyte, tnbyte, pbyte: int; 30 31init(nil: ref Draw->Context, argv: list of string) 32{ 33 sys = load Sys Sys->PATH; 34 stderr = sys->fildes(2); 35 36 for(argv = tl argv; argv != nil; argv = tl argv){ 37 arg := hd argv; 38 if(len arg < 2 || arg[0] != '-' || arg[1] == '-') 39 break; 40 for(i := 1; i < len arg; i++){ 41 case arg[i]{ 42 'l' => pline++; 43 'w' => pword++; 44 'c' => pchar++; 45 'e' => pbadr++; 46 'b' => pbyte++; 47 * => 48 sys->fprint(stderr, "usage: wc [-lwcbe] [file ...]\n"); 49 raise "fail:usage"; 50 } 51 } 52 } 53 if(pline+pword+pchar+pbadr+pbyte == 0) 54 pline = pword = pchar = 1; 55 argc := len argv; 56 if(argc == 0) 57 count(sys->fildes(0), ""); 58 else{ 59 for(; argv != nil; argv = tl argv){ 60 name := hd argv; 61 f := sys->open(name, sys->OREAD); 62 if(f == nil) 63 sys->fprint(stderr, "wc: can't open %s: %r\n", name); 64 else{ 65 count(f, name); 66 tnline += nline; 67 tnword += nword; 68 tnchar += nchar; 69 tnbadr += nbadr; 70 tnbyte += nbyte; 71 f = nil; 72 } 73 } 74 if(argc > 1) 75 report(tnline, tnword, tnchar, tnbadr, tnbyte, "total"); 76 } 77 exit; 78} 79report(nline, nword, nchar, nbadr, nbyte: int, fname: string) 80{ 81 line := ""; 82 if(pline) 83 line += sys->sprint(" %7d", nline); 84 if(pword) 85 line += sys->sprint(" %7d", nword); 86 if(pchar) 87 line += sys->sprint(" %7d", nchar); 88 if(pbadr) 89 line += sys->sprint(" %7d", nbadr); 90 if(pbyte) 91 line += sys->sprint(" %7d", nbyte); 92 if(fname != nil) 93 line += sys->sprint(" %s", fname); 94 sys->print("%s\n", line[1:]); 95} 96# 97# How it works. Start in statesp. Each time we read a character, 98# increment various counts, and do state transitions according to the 99# following table. If we're not in statesp or statewd when done, the 100# file ends with a partial rune. 101# | character 102# state |09,20| 0a |00-7f|80-bf|c0-df|e0-ef|f0-ff 103# -------+-----+-----+-----+-----+-----+-----+----- 104# statesp|ASP |ASPN |AWDW |AWDWX|AC2W |AC3W |AWDWX 105# statewd|ASP |ASPN |AWD |AWDX |AC2 |AC3 |AWDX 106# statec2|ASPX |ASPNX|AWDX |AWDR |AC2X |AC3X |AWDX 107# statec3|ASPX |ASPNX|AWDX |AC2R |AC2X |AC3X |AWDX 108# 109 # actions 110 AC2, # enter statec2 111 AC2R, # enter statec2, don't count a rune 112 AC2W, # enter statec2, count a word 113 AC2X, # enter statec2, count a bad rune 114 AC3, # enter statec3 115 AC3W, # enter statec3, count a word 116 AC3X, # enter statec3, count a bad rune 117 ASP, # enter statesp 118 ASPN, # enter statesp, count a newline 119 ASPNX, # enter statesp, count a newline, count a bad rune 120 ASPX, # enter statesp, count a bad rune 121 AWD, # enter statewd 122 AWDR, # enter statewd, don't count a rune 123 AWDW, # enter statewd, count a word 124 AWDWX, # enter statewd, count a word, count a bad rune 125 AWDX: # enter statewd, count a bad rune 126 con byte iota; 127 128statesp := array[256] of{ # looking for the start of a word 129AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 00-07 130AWDW, ASP, ASPN, AWDW, AWDW, AWDW, AWDW, AWDW, # 08-0f 131AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 10-17 132AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 18-1f 133ASP, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 20-27 134AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 28-2f 135AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 30-37 136AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 38-3f 137AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 40-47 138AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 48-4f 139AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 50-57 140AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 58-5f 141AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 60-67 142AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 68-6f 143AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 70-77 144AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, # 78-7f 145AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# 80-87 146AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# 88-8f 147AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# 90-97 148AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# 98-9f 149AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# a0-a7 150AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# a8-af 151AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# b0-b7 152AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# b8-bf 153AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, # c0-c7 154AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, # c8-cf 155AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, # d0-d7 156AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, # d8-df 157AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, # e0-e7 158AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, # e8-ef 159AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# f0-f7 160AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,# f8-ff 161}; 162statewd := array[256] of { # looking for the next character in a word 163AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 00-07 164AWD, ASP, ASPN, AWD, AWD, AWD, AWD, AWD, # 08-0f 165AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 10-17 166AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 18-1f 167ASP, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 20-27 168AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 28-2f 169AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 30-37 170AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 38-3f 171AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 40-47 172AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 48-4f 173AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 50-57 174AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 58-5f 175AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 60-67 176AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 68-6f 177AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 70-77 178AWD, AWD, AWD, AWD, AWD, AWD, AWD, AWD, # 78-7f 179AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 80-87 180AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 88-8f 181AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 90-97 182AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 98-9f 183AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # a0-a7 184AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # a8-af 185AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # b0-b7 186AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # b8-bf 187AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, # c0-c7 188AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, # c8-cf 189AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, # d0-d7 190AC2, AC2, AC2, AC2, AC2, AC2, AC2, AC2, # d8-df 191AC3, AC3, AC3, AC3, AC3, AC3, AC3, AC3, # e0-e7 192AC3, AC3, AC3, AC3, AC3, AC3, AC3, AC3, # e8-ef 193AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # f0-f7 194AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # f8-ff 195}; 196statec2 := array[256] of { # looking for 10xxxxxx to complete a rune 197AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 00-07 198AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX, # 08-0f 199AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 10-17 200AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 18-1f 201ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 20-27 202AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 28-2f 203AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 30-37 204AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 38-3f 205AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 40-47 206AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 48-4f 207AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 50-57 208AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 58-5f 209AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 60-67 210AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 68-6f 211AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 70-77 212AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 78-7f 213AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # 80-87 214AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # 88-8f 215AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # 90-97 216AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # 98-9f 217AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # a0-a7 218AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # a8-af 219AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # b0-b7 220AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, # b8-bf 221AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # c0-c7 222AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # c8-cf 223AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # d0-d7 224AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # d8-df 225AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, # e0-e7 226AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, # e8-ef 227AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # f0-f7 228AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # f8-ff 229}; 230statec3 := array[256] of { # looking for 10xxxxxx,10xxxxxx to complete a rune 231AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 00-07 232AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX, # 08-0f 233AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 10-17 234AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 18-1f 235ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 20-27 236AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 28-2f 237AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 30-37 238AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 38-3f 239AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 40-47 240AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 48-4f 241AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 50-57 242AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 58-5f 243AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 60-67 244AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 68-6f 245AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 70-77 246AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # 78-7f 247AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # 80-87 248AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # 88-8f 249AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # 90-97 250AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # 98-9f 251AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # a0-a7 252AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # a8-af 253AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # b0-b7 254AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, # b8-bf 255AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # c0-c7 256AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # c8-cf 257AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # d0-d7 258AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, # d8-df 259AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, # e0-e7 260AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, # e8-ef 261AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # f0-f7 262AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, # f8-ff 263}; 264buf := array[NBUF] of byte; 265count(f: ref Sys->FD, name: string) 266{ 267 state := statesp; 268 nline = nword = nchar = nbadr = nbyte = 0; 269 n := 0; 270 for(;;){ 271 n = sys->read(f, buf, NBUF); 272 if(n <= 0) 273 break; 274 nbyte += n; 275 nchar += n; # might be too large, gets decreased later 276 i := 0; 277 do{ 278 case int state[int buf[i++]]{ 279 int AC2 => state = statec2; 280 int AC2R => state = statec2; nchar--; 281 int AC2W => state = statec2; nword++; 282 int AC2X => state = statec2; nbadr++; 283 int AC3 => state = statec3; 284 int AC3W => state = statec3; nword++; 285 int AC3X => state = statec3; nbadr++; 286 int ASP => state = statesp; 287 int ASPN => state = statesp; nline++; 288 int ASPNX => state = statesp; nline++; nbadr++; 289 int ASPX => state = statesp; nbadr++; 290 int AWD => state = statewd; 291 int AWDR => state = statewd; nchar--; 292 int AWDW => state = statewd; nword++; 293 int AWDWX => state = statewd; nword++; nbadr++; 294 int AWDX => state = statewd; nbadr++; 295 } 296 }while(i < n); 297 } 298 if(state!=statesp && state!=statewd) 299 nbadr++; 300 if(n < 0) 301 sys->fprint(stderr, "wc: error reading %s: %r\n", name); 302 report(nline, nword, nchar, nbadr, nbyte, name); 303} 304