implement Fmt; ## Copyright © 2002 Lucent Technologies Inc.# based on the Plan 9 command; subject to the Lucent Public License 1.02# this Vita Nuova variant uses Limbo channels and processes to avoid accumulating words# ## block up paragraphs, possibly with indentation# include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "arg.m"; Fmt: module{ init: fn(nil: ref Draw->Context, nil: list of string);}; extraindent := 0; # how many spaces to indent all linesindent := 0; # current value of indent, before extra indentlength := 70; # how many columns per output linejoin := 1; # can lines be joined?maxtab := 8;bout: ref Iobuf; Word: adt { text: string; indent: int; bol: int;}; init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; arg := load Arg Arg->PATH; arg->init(args); arg->setusage("fmt [-j] [-i indent] [-l length] [file...]"); while((c := arg->opt()) != 0) case(c){ 'i' => extraindent = int arg->earg(); 'j' => join = 0; 'w' or 'l' => length = int arg->earg(); * => arg->usage(); } args = arg->argv(); if(length <= extraindent){ sys->fprint(sys->fildes(2), "fmt: line length<=indentation\n"); raise "fail:length"; } arg = nil; err := ""; bout = bufio->fopen(sys->fildes(1), Bufio->OWRITE); if(args == nil){ bin := bufio->fopen(sys->fildes(0), Bufio->OREAD); fmt(bin); }else for(; args != nil; args = tl args){ bin := bufio->open(hd args, Bufio->OREAD); if(bin == nil){ sys->fprint(sys->fildes(2), "fmt: can't open %s: %r\n", hd args); err = "open"; }else{ fmt(bin); if(tl args != nil) bout.putc('\n'); } } bout.flush(); if(err != nil) raise "fail:"+err;} fmt(f: ref Iobuf){ words := chan of ref Word; spawn parser(f, words); printwords(words);} parser(f: ref Iobuf, words: chan of ref Word){ while((s := f.gets('\n')) != nil){ if(s[len s-1] == '\n') s = s[0:len s-1]; parseline(s, words); } words <-= nil;} parseline(line: string, words: chan of ref Word){ ind: int; (line, ind) = indentof(line); indent = ind; bol := 1; for(i:=0; i < len line;){ # find next word if(line[i] == ' ' || line[i] == '\t'){ i++; continue; } # where does this word end? for(l:=i; l < len line; l++) if(line[l]==' ' || line[l]=='\t') break; words <-= ref Word(line[i:l], indent, bol); bol = 0; i = l; } if(bol) words <-= ref Word("", -1, bol);} indentof(line: string): (string, int){ ind := 0; for(i:=0; i < len line; i++) case line[i] { ' ' => ind++; '\t' => ind += maxtab; ind -= ind%maxtab; * => return (line, ind); } # plain white space doesn't change the indent return (line, indent);} printwords(words: chan of ref Word){ # one output line per loop nw := <-words; while((w := nw) != nil){ # if it's a blank line, print it if(w.indent == -1){ bout.putc('\n'); nw = <-words; continue; } # emit leading indent col := extraindent+w.indent; printindent(col); # emit words until overflow; always emit at least one word for(n:=0;; n++){ bout.puts(w.text); col += len w.text; if((nw = <-words) == nil) break; # out of words if(nw.indent != w.indent) break; # indent change nsp := nspaceafter(w.text); if(col+nsp+len nw.text > extraindent+length) break; # fold line if(!join && nw.bol) break; for(j:=0; j<nsp; j++) bout.putc(' '); # emit space; another word will follow col += nsp; w = nw; } bout.putc('\n'); }} printindent(w: int){ while(w >= maxtab){ bout.putc('\t'); w -= maxtab; } while(--w >= 0) bout.putc(' ');} # give extra space if word ends with punctuationnspaceafter(s: string): int{ if(len s < 2) return 1; if(len s < 4 && s[0] >= 'A' && s[0] <= 'Z') return 1; # assume it's a title, not full stop if((c := s[len s-1]) == '.' || c == '!' || c == '?') return 2; return 1;}
implement Fmt;
#
# Copyright © 2002 Lucent Technologies Inc.
# based on the Plan 9 command; subject to the Lucent Public License 1.02
# this Vita Nuova variant uses Limbo channels and processes to avoid accumulating words
#
#
# block up paragraphs, possibly with indentation
#
include "sys.m";
sys: Sys;
include "draw.m";
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "arg.m";
Fmt: module
{
init: fn(nil: ref Draw->Context, nil: list of string);
};
extraindent := 0; # how many spaces to indent all lines
indent := 0; # current value of indent, before extra indent
length := 70; # how many columns per output line
join := 1; # can lines be joined?
maxtab := 8;
bout: ref Iobuf;
Word: adt {
text: string;
indent: int;
bol: int;
};
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
bufio = load Bufio Bufio->PATH;
arg := load Arg Arg->PATH;
arg->init(args);
arg->setusage("fmt [-j] [-i indent] [-l length] [file...]");
while((c := arg->opt()) != 0)
case(c){
'i' =>
extraindent = int arg->earg();
'j' =>
join = 0;
'w' or 'l' =>
length = int arg->earg();
* =>
arg->usage();
}
args = arg->argv();
if(length <= extraindent){
sys->fprint(sys->fildes(2), "fmt: line length<=indentation\n");
raise "fail:length";
}
arg = nil;
err := "";
bout = bufio->fopen(sys->fildes(1), Bufio->OWRITE);
if(args == nil){
bin := bufio->fopen(sys->fildes(0), Bufio->OREAD);
fmt(bin);
}else
for(; args != nil; args = tl args){
bin := bufio->open(hd args, Bufio->OREAD);
if(bin == nil){
sys->fprint(sys->fildes(2), "fmt: can't open %s: %r\n", hd args);
err = "open";
}else{
fmt(bin);
if(tl args != nil)
bout.putc('\n');
}
}
bout.flush();
if(err != nil)
raise "fail:"+err;
}
fmt(f: ref Iobuf)
{
words := chan of ref Word;
spawn parser(f, words);
printwords(words);
}
parser(f: ref Iobuf, words: chan of ref Word)
{
while((s := f.gets('\n')) != nil){
if(s[len s-1] == '\n')
s = s[0:len s-1];
parseline(s, words);
}
words <-= nil;
}
parseline(line: string, words: chan of ref Word)
{
ind: int;
(line, ind) = indentof(line);
indent = ind;
bol := 1;
for(i:=0; i < len line;){
# find next word
if(line[i] == ' ' || line[i] == '\t'){
i++;
continue;
}
# where does this word end?
for(l:=i; l < len line; l++)
if(line[l]==' ' || line[l]=='\t')
break;
words <-= ref Word(line[i:l], indent, bol);
bol = 0;
i = l;
}
if(bol)
words <-= ref Word("", -1, bol);
}
indentof(line: string): (string, int)
{
ind := 0;
for(i:=0; i < len line; i++)
case line[i] {
' ' =>
ind++;
'\t' =>
ind += maxtab;
ind -= ind%maxtab;
* =>
return (line, ind);
}
# plain white space doesn't change the indent
return (line, indent);
}
printwords(words: chan of ref Word)
{
# one output line per loop
nw := <-words;
while((w := nw) != nil){
# if it's a blank line, print it
if(w.indent == -1){
bout.putc('\n');
nw = <-words;
continue;
}
# emit leading indent
col := extraindent+w.indent;
printindent(col);
# emit words until overflow; always emit at least one word
for(n:=0;; n++){
bout.puts(w.text);
col += len w.text;
if((nw = <-words) == nil)
break; # out of words
if(nw.indent != w.indent)
break; # indent change
nsp := nspaceafter(w.text);
if(col+nsp+len nw.text > extraindent+length)
break; # fold line
if(!join && nw.bol)
break;
for(j:=0; j<nsp; j++)
bout.putc(' '); # emit space; another word will follow
col += nsp;
w = nw;
}
bout.putc('\n');
}
}
printindent(w: int)
{
while(w >= maxtab){
bout.putc('\t');
w -= maxtab;
}
while(--w >= 0)
bout.putc(' ');
}
# give extra space if word ends with punctuation
nspaceafter(s: string): int
{
if(len s < 2)
return 1;
if(len s < 4 && s[0] >= 'A' && s[0] <= 'Z')
return 1; # assume it's a title, not full stop
if((c := s[len s-1]) == '.' || c == '!' || c == '?')
return 2;
return 1;
}