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 implementedcompare: 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';}
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';
}