using System.Console; using System.IO; using Nemerle.IO; using System; using System.Collections; namespace Filter { class EndOfFile : Exception { public this() { } } class Error : Exception { mutable message : string; public this(message:string) { this.message=message; } new public Message : string { get { message } } } variant Token { | Identifier { name : string; } | Text { value : string; } | Integer { value : int; } | EndOfFile | Begin | End | Break } class Lekser { private mutable stream : StreamReader; private mutable token : Token; private mutable row : int; private mutable col : int; public this(stream : StreamReader) { this.stream=stream; row=1; col=1; nextToken(); } public getToken() : Token { token; } private Get() : char { mutable ch=array(1); if (stream.Read(ch,0,1)!=1) { throw EndOfFile(); } else { if(ch[0]=='\n') { row=row+1; col=1; } else { col=col+1; }; ch[0] } } new public ToString() : string { "("+row.ToString()+","+col.ToString()+")" } public nextToken() : void { mutable ch=' '; try { ch=Get(); // pass white space while(Char.IsWhiteSpace(ch)) ch=Get(); } catch { _ is EndOfFile => token= Token.EndOfFile() }; unless (token is Token.EndOfFile) { // check on what you are in if(ch=='"') { // text mutable str=""; ch=Get(); try { while(ch!='"') { str=str+ch.ToString(); ch=Get(); }; ch=Get(); } catch { _ is EndOfFile => throw Error("End of file found - expected \"") }; token= Token.Text(str); } else if(Char.IsDigit(ch)) { mutable str=""; try { while(Char.IsLetterOrDigit(ch)) { str=str+ch.ToString(); ch=Get(); } } catch { _ is EndOfFile => () }; try { token= Token.Integer(Int32.Parse(str)); } catch { _ is Exception => throw Error("Invalid integer value") }; } else if(Char.IsLetter(ch)) { mutable str=""; try { while(Char.IsLetterOrDigit(ch)) { str=str+(Char.ToLower(ch)).ToString(); ch=Get(); } } catch { _ is EndOfFile => () }; token= Token.Identifier(str); } else if(ch=='{') { token= Token.Begin(); } else if(ch=='}') { token= Token.End(); } else if(ch==';') { token= Token.Break(); } else if(ch=='#') { try { while(ch!='\n') { ch=Get(); } } catch { _ is EndOfFile => () }; nextToken(); } else { throw Error("Invalid character!"); } } } public GetInt() : Int32 { nextToken(); match(token) { | Token.Integer(value) => value | _ => throw Error("Expected integer value") } } public GetStr() : string { nextToken(); match(token) { | Token.Text(value) => value | _ => throw Error("Expected text value") } } public GetIdentifier() : string { nextToken(); match(token) { | Token.Identifier(value) => value | _ => throw Error("Expected identifier") } } } enum Comparision { | cLess | cLessEq | cEq | cMoreEq | cMore } variant Property { | Size { comparision : Comparision; size : Int64; } | Ext {ext : string; } | Name { name : string; } | Date {comparision : Comparision; date : DateTime; } | Text { txt : string; } } class Rule { mutable property : list [Property]; mutable name : string; // parse data from input stream public this(lekser : Lekser, name : string ) { this.name=name; lekser.nextToken(); if(lekser.getToken() is Token.Begin) { def GetComp() { lekser.nextToken(); match(lekser.getToken()) { | Token.Identifier("less") => Comparision.cLess | Token.Identifier("lesseq") => Comparision.cLessEq | Token.Identifier("eq") => Comparision.cEq | Token.Identifier("moreeq") => Comparision.cMoreEq | Token.Identifier("more") => Comparision.cMore | _ => throw Error("Expected comparision identifier (less, lesseq, eq, moreeq, more)") } }; def Parse() { lekser.nextToken(); match(lekser.getToken()) { | Token.EndOfFile => throw Error("Error unexpected end of file - expected }.") | Token.Identifier(pr) => match(pr) { | "size" => Property.Size(GetComp(),Convert.ToInt64(lekser.GetInt()))::Parse() | "ext" => Property.Ext(lekser.GetStr())::Parse() | "name" => Property.Name(lekser.GetStr())::Parse() | "date" => Property.Date(GetComp(),DateTime.Parse(lekser.GetStr()))::Parse() | "text" => Property.Text(lekser.GetStr())::Parse() | _ => throw Error("Wrong property name") } | Token.End => [] | _ => throw Error("Unexpected value - expected property name") } }; property=Parse(); } else { throw Error("Expected {"); } } public RunFile(fi : FileInfo) : bool { def work(l) { | x::xs => if(match(x) { | Property.Size(comp,size) => if(comp==Comparision.cLess) fi.Length=size else fi.Length>size | Property.Ext(ext) => fi.Extension==ext // it should be replaced with regural expressions | Property.Name(name) => fi.Name==name // it should be replaced with regular expressions | Property.Date(comp,date) => if(comp==Comparision.cLess) fi.CreationTime=date else fi.CreationTime>date | Property.Text(_text) => throw Error("Searching in files not implemented yet!") }) work(xs) else false | [] => true }; work(property) } public RunDir(dir : DirectoryInfo ) : bool { def work(l) { | x::xs => if(match(x : Property) { | Property.Size(_,_) => printf("Ignoring size for directory\n"); true | Property.Ext(ext) => dir.Extension==ext // it should be replaced with regular expression | Property.Name(name) => dir.Name==name // it should be replaced with regular expression | Property.Date(comp,date) => if(comp==Comparision.cLess) dir.CreationTime=date else dir.CreationTime>date | Property.Text(_) => printf("Ignoring text search for directory\n"); true }) work(xs) else false | [] => true }; work(property) } } class Rules { private mutable rule : Hashtable; public this() { rule=Hashtable(); } public Add(name : string, rule : Rule) : void { this.rule.Add(name, rule) } public Get(name : string) : Rule { (rule[name] :> Rule); } } enum ConditionType { | tFile | tDirectory } variant ConditionResult { | Add | AddAll | Drop | Script {script : Filter.Script;} | ScriptX { script : string; } | RunRec {script : Filter.Script;} | RunRecX { script : string; } } class Condition { mutable typ : ConditionType; mutable rules : list [Rule]; mutable result : ConditionResult; public this(lekser : Lekser, typ : ConditionType, rules : Rules) { this.typ=typ; def Parse() { lekser.nextToken(); match(lekser.getToken()) { | Token.Identifier("do") => if(typ==ConditionType.tFile) { match(lekser.GetIdentifier()) { | "add" => result=ConditionResult.Add() | "drop" => result=ConditionResult.Drop() | n => result=ConditionResult.ScriptX(n) } } else { match(lekser.GetIdentifier()) { | "addall" => result=ConditionResult.AddAll() | "drop" => result=ConditionResult.Drop() | "runrec" => result=ConditionResult.RunRecX(lekser.GetIdentifier()) | n => result=ConditionResult.ScriptX(n) } }; [] | Token.Identifier(filter) => (rules.Get(filter))::Parse() | _ => throw Error("Expected filters names or do") } }; this.rules=Parse(); } public Check(scripts : Scripts) : void { match(result) { | ConditionResult.ScriptX(name) => result=ConditionResult.Script(scripts.Get(name)) | ConditionResult.RunRecX(name) => result=ConditionResult.RunRec(scripts.Get(name)) | _ => () } } public RunFile(fileinfo : FileInfo) : bool { if(typ==ConditionType.tFile) { def work(l) { | x::xs => if ((x : Rule).RunFile(fileinfo)) work(xs) else false | [] => match(result) { | ConditionResult.Add => printf("%s\n",fileinfo.FullName) | ConditionResult.Drop => /*printf("Droped %s\n",Prog.Info(fileinfo))*/() | ConditionResult.Script(sc) => /*printf("Run with %s file %s\n",sc.Name,fileinfo.Name);*/ ignore(sc.RunFile(fileinfo)) | _ => throw Error("Internal error 1") }; true }; work(rules) } else false } public RunDir(dir : DirectoryInfo) : bool { if(typ==ConditionType.tDirectory) { def work(l) { | x::xs => if ((x : Rule).RunDir(dir)) work(xs) else false | [] => match(result) { | ConditionResult.AddAll => /*printf("Add all %s\n",dir.Name);*/ // needs impelemntation () | ConditionResult.Drop => /*printf("Droped %s\n",dir.Name)*/ () | ConditionResult.Script(sc) => /*printf("Run with %s directory %s\n",sc.Name,dir.Name);*/ ignore(sc.RunDir(dir)) | ConditionResult.RunRec(sc) => /*printf("Add rec %s directory with %s script\n",dir.Name,sc.Name);*/ def files=dir.GetFiles(); for(mutable i=0;i throw Error("Internal error 2") }; true }; work(rules) } else false } } class Script { mutable name : string; mutable test : list [Condition]; public this(lekser : Lekser, name : string, rules : Rules) { this.name=name; lekser.nextToken(); if(lekser.getToken() is Token.Begin) { def Parse() { lekser.nextToken(); match(lekser.getToken()) { | Token.Identifier("onfile") => Condition(lekser, ConditionType.tFile, rules)::Parse() | Token.Identifier("ondirectory") => Condition(lekser, ConditionType.tDirectory, rules)::Parse() | Token.End => [] | _ => throw Error("Error expected }") } }; test=Parse(); } else { throw Error("Error excepted {"); } } public Check(scripts : Scripts) : void { def check(l) { | x::xs => (x : Condition).Check(scripts); check(xs) | _ => () }; check(test) } public RunFile( fileinfo : FileInfo ) : bool { def run(l) { | x::xs => if((x : Condition).RunFile(fileinfo)) true else run(xs) | [] => false }; run(test) } public RunDir( dir : DirectoryInfo ) : bool { def run(l) { | x::xs => if((x : Condition).RunDir(dir)) true else run(xs) | [] => false }; run(test) } public Run(item : string) : bool { // check if file or directory if(Directory.Exists(item)) { // it is a directory RunDir(DirectoryInfo(item)) } else if(File.Exists(item)) { // it is a file RunFile(FileInfo(item)) } else { // bad path - ignore false } } } class Scripts { private mutable script : Hashtable; public this() { script=Hashtable(); } public Add(name : string, script : Script) : void { this.script.Add(name, script) } public Get(name : string) : Script { (script[name] :> Script); } public CheckScripts() : void { def values=script.GetEnumerator(); while(values.MoveNext()) { (values.Value :> Script).Check(this); } } } class Filter { mutable rules : Rules; mutable scripts : Scripts; public this(lekser : Lekser) { mutable run=false; rules=Rules(); scripts=Scripts(); def CheckRun() { when (run) throw Error("After run you can't add more rules or filters") }; while(!(lekser.getToken() is Token.EndOfFile)) { match(lekser.getToken()) { | Token.Identifier(typ) => match(typ) { | "filter" => CheckRun(); def name=lekser.GetIdentifier(); rules.Add(name,Rule(lekser,name)) | "script" => CheckRun(); def name=lekser.GetIdentifier(); scripts.Add(name,Script(lekser,name,rules)) | "run" => when(!run) { run=true; scripts.CheckScripts(); }; def name=lekser.GetIdentifier(); def item=lekser.GetStr(); ignore(scripts.Get(name).Run(item)) | _ => throw Error("Error unknow type") } | _ => throw Error("Error expected type identifier") }; lekser.nextToken(); } } } module Prog { /* public Info(fi : FileInfo) : string { "Name: "+fi.Name+", Size: "+fi.Length.ToString()+", Date: "+fi.CreationTime.ToString()+", Path: "+fi.DirectoryName; } public Info(di : DirectoryInfo) : string { "Name: "+di.Name+", Date: "+di.CreationTime.ToString()+", Path: "+di.FullName; } */ public Help() : void { printf("Usage: filter [OP Token.ION]... FILE\n"); printf("Executes a filter script to generate file list.\n"); printf("Example: filter -s cdcopy.f\n"); printf("\n"); printf("Options:\n"); printf("\t-h help - this message\n"); } public Main(args : array [string] ) : int { if(args.Length==0) { Help(); } else { mutable i=0; mutable stdout=false; mutable file=""; mutable help=false; mutable quiet=false; mutable err=false; mutable infile=""; while((i stdout=true | "-o" => file=args[i+1]; i=i+1 | "-h" => help=true | "-q" => quiet=true; | s => if(infile=="") infile=s else { err=true; printf("Unknow option %s\n",s); } }; i=i+1; }; if(help) { Help(); } else if(infile=="") { printf("No input file!\n"); } else { mutable fs=FileStream(infile,FileMode.Open); mutable sr=StreamReader(fs); try { mutable lekser=Lekser(sr); try { ignore(Filter(lekser)) } catch{ e is Error => printf("%s at %s\n",e.Message,lekser.ToString()) }; } finally { sr.Close(); } } }; 0 } } }