implement Look; # # Copyright © 2002 Lucent Technologies Inc. # transliteration of the Plan 9 command; subject to the Lucent Public License 1.02 # -r option added by Caerwyn Jones to print a range # include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "arg.m"; Look: module { init: fn(nil: ref Draw->Context, nil: list of string); }; filename := "/lib/words"; dfile: ref Iobuf; bout: ref Iobuf; debug := 0; fold, direc, exact, iflag, range: int; rev := 1; # -1 for reverse-ordered file, not implemented compare: ref fn(a, b: string): int; tab := '\t'; entry: string; word: string; key: string; latin_fold_tab := array[64] of { # Table to fold latin 1 characters to ASCII equivalents # based at Rune value 0xc0 # # À Á Â Ã Ä Å Æ Ç # È É Ê Ë Ì Í Î Ï # Ð Ñ Ò Ó Ô Õ Ö × # Ø Ù Ú Û Ü Ý Þ ß # à á â ã ä å æ ç # è é ê ë ì í î ï # ð ñ ò ó ô õ ö ÷ # ø ù ú û ü ý þ ÿ # 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'd', 'n', 'o', 'o', 'o', 'o', 'o', 0, 'o', 'u', 'u', 'u', 'u', 'y', 0, 0, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'd', 'n', 'o', 'o', 'o', 'o', 'o', 0, 'o', 'u', 'u', 'u', 'u', 'y', 0, 'y', }; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; arg := load Arg Arg->PATH; lastkey: string; arg->init(args); arg->setusage("look -[dfinx] [-r lastkey] [-t c] [string] [file]"); compare = acomp; while((c := arg->opt()) != 0) case c { 'D' => debug = 1; 'd' => direc++; 'f' => fold++; 'i' => iflag++; 'n' => compare = ncomp; 't' => tab = (arg->earg())[0]; 'x' => exact++; 'r' => range++; lastkey = rcanon(arg->earg()); * => arg->usage(); } args = arg->argv(); arg = nil; bin := bufio->fopen(sys->fildes(0), Sys->OREAD); bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); orig: string; if(!iflag){ if(args != nil){ orig = hd args; args = tl args; }else iflag++; } if(args == nil){ direc++; fold++; }else filename = hd args; if(!iflag) key = rcanon(orig); if(debug) sys->fprint(sys->fildes(2), "orig %s key %s %s\n", orig, key, filename); dfile = bufio->open(filename, Sys->OREAD); if(dfile == nil){ sys->fprint(sys->fildes(2), "look: can't open %s\n", filename); raise "fail:no dictionary"; } if(!iflag) if(!locate() && !range) raise "fail:not found"; do{ if(iflag){ bout.flush(); if((orig = bin.gets('\n')) == nil) exit; key = rcanon(orig); if(!locate()) continue; } if(range){ if(compare(key, word) <= 0 && compare(word, lastkey) <= 0) bout.puts(entry); }else if(!exact || acomp(word, orig) == 0) bout.puts(entry); Matches: while((entry = dfile.gets('\n')) != nil){ word = rcanon(entry); if(range) n := compare(word, lastkey); else n = compare(key, word); if(debug) sys->print("compare %d %q\n", n, word); case n { -2 => if(!range) break Matches; bout.puts(entry); -1 => if(exact) break Matches; bout.puts(entry); 0 => if(!exact || acomp(word, orig) == 0) bout.puts(entry); * => break Matches; } } }while(iflag); bout.flush(); } locate(): int { bot := big 0; top := dfile.seek(big 0, 2); mid: big; Search: for(;;){ mid = (top+bot)/big 2; if(debug) sys->fprint(sys->fildes(2), "locate %bd %bd %bd\n", top, mid, bot); dfile.seek(mid, 0); c: int; do c = dfile.getc(); while(c >= 0 && c != '\n'); mid = dfile.offset(); if((entry = dfile.gets('\n')) == nil) break; word = rcanon(entry); if(debug) sys->fprint(sys->fildes(2), "mid %bd key: %s entry: %s\n", mid, key, word); n := compare(key, word); if(debug) sys->fprint(sys->fildes(2), "compare: %d\n", n); case n { -2 or -1 or 0 => if(top <= mid) break Search; top = mid; 1 or 2 => bot = mid; } } if(debug) sys->fprint(sys->fildes(2), "locate %bd %bd %bd\n", top, mid, bot); bot = dfile.seek(big bot, 0); while((entry = dfile.gets('\n')) != nil){ word = rcanon(entry); if(debug) sys->fprint(sys->fildes(2), "seekbot %bd key: %s entry: %s\n", bot, key, word); n := compare(key, word); if(debug) sys->fprint(sys->fildes(2), "compare: %d\n", n); case n { -2 => return 0; -1 => return !exact; 0 => return 1; 1 or 2 => ; } } return 0; } # # acomp(s, t) returns: # -2 if s strictly precedes t # -1 if s is a prefix of t # 0 if s is the same as t # 1 if t is a prefix of s # 2 if t strictly precedes s # acomp(s, t: string): int { if(s == t) return 0; l := len s; if(l > len t) l = len t; cs, ct: int; for(i := 0; i < l; i++) { cs = s[i]; ct = t[i]; if(cs != ct) break; } if(i == len s) return -1; if(i == len t) return 1; if(cs < ct) return -2; return 2; } rcanon(s: string): string { if(s != nil && s[len s - 1] == '\n') s = s[0: len s - 1]; o := 0; for(i := 0; i < len s && (r := s[i]) != tab; i++){ if(islatin1(r) && (mr := latin_fold_tab[r-16rc0]) != 0) r = mr; if(direc) if(!(isalnum(r) || r == ' ' || r == '\t')) continue; if(fold) if(isupper(r)) r = tolower(r); if(r != s[o]) # avoid copying s unless necessary s[o] = r; o++; } if(o != i) return s[0:o]; return s; } sgn(v: int): int { if(v < 0) return -1; if(v > 0) return 1; return 0; } ncomp(s: string, t: string): int { while(len s > 0 && isspace(s[0])) s = s[1:]; while(len t > 0 && isspace(t[0])) t = t[1:]; ssgn := tsgn := -2*rev; if(s != nil && s[0] == '-'){ s = s[1: ]; ssgn = -ssgn; } if(t != nil && t[0] == '-'){ t = t[1:]; tsgn = -tsgn; } for(i := 0; i < len s && isdigit(s[i]); i++) ; is := s[0:i]; js := s[i:]; for(i = 0; i < len t && isdigit(t[i]); i++) ; it := t[0:i]; jt := t[i:]; a := 0; i = len is; j := len it; if(ssgn == tsgn){ while(j > 0 && i > 0) if((b := it[--j] - is[--i]) != 0) a = b; } while(i > 0) if(is[--i] != '0') return -ssgn; while(j > 0) if(it[--i] != '0') return tsgn; if(a) return sgn(a)*ssgn; s = js; if(len s > 0 && s[0] == '.') s = s[1: ]; t = jt; if(len t > 0 && t[0] == '.') t = t[1: ]; if(ssgn == tsgn) while((len s > 0 && isdigit(s[0])) && (len t > 0 && isdigit(t[0]))){ if(a = t[0] - s[0]) return sgn(a)*ssgn; s = s[1:]; t = t[1:]; } for(; len s > 0 && isdigit(s[0]); s = s[1:]) if(s[0] != '0') return -ssgn; for(; len t > 0 && isdigit(t[0]); t = t[1:]) if(t[0] != '0') return tsgn; return 0; } isupper(c: int): int { return c >= 'A' && c <= 'Z'; } islower(c: int): int { return c >= 'a' && c <= 'z'; } isalpha(c: int): int { return islower(c) || isupper(c); } islatin1(c: int): int { return c >= 16rC0 && c <= 16rFF; } isdigit(c: int): int { return c >= '0' && c <= '9'; } isalnum(c: int): int { return isdigit(c) || islower(c) || isupper(c); } isspace(c: int): int { return c == ' ' || c == '\t' || c >= 16r0A && c <= 16r0D; } tolower(c: int): int { return c-'A'+'a'; }