--undollar.e by CChris , 01/13/2005 --Removes the $ symbols and replaces with the lengths of correct sequence --so as to be able to run some Eu 2.5 programs under 2.4 --can't do anything about crash_routine() though. --Usage: --for individual files: --ex[ uw] undollar.e [/root rootdir] [infile] [outfile] --or ex[ uw] undollar.e /out outfile --If outfile is omitted, standard output is assumed. --If infile is left out as well, standard input and output are assumed. --Use the /out option to specify outfile while getting data from standard input. --Failing to specify outfile with the /out option causes it to be ignored. --for directories: --ex[ uw] undollar.e [/root rootdir] /dir indir outdir [extensions] --extensions is a list of file extensions you want to process. --If omitted, defaults to {".e",".ew",".ex",".exu",".exw"}. --If it starts with a comma, the above defaults will add to the supplied list --Subdirectories are scanned recursively. --All filenames are used without prior prepending any path to them. To transiently change current_dir(), use the /root rootdir option. --Addditionally, the undollar(inhandle,outhandle) and process_dir(indir,outdir,extensions) --procedure that do the job are global, so that this file can be used as an include module. include get.e include file.e include machine.e with trace --trace(1) function findFrom(object x,sequence s,integer pos) integer result result=find(x,s[pos..length(s)]) if result then result+=(pos-1) end if return result end function function matchFrom(sequence x,sequence s,integer pos) integer result result=match(x,s[pos..length(s)]) if result then result+=(pos-1) end if return result end function function ischar(integer c) if c>'z' then return 0 elsif c>='a' then return 1 elsif c>'Z' then return c='_' elsif c>='A' then return 1 elsif c>'9' then return c=':' elsif c>='0' then return 1 else return 0 end if end function function levels(sequence s,integer openc,integer closec) sequence result integer p,q result=s p=0 if openc!=closec then for i=1 to length(s) do q=s[i] if q=openc then p+=1 elsif q=closec then p-=1 end if result[i]=p end for else q=1 for i=1 to length(s) do if s[i]=openc then p+=q q=-q end if result[i]=p end for end if return result end function constant whitespace=" \t", --whitespace chars that may appear in a line returned by gets() specials="\"$[]" sequence plines,plevels plines="" plevels="" function clean(sequence work) integer p,q sequence z for i=1 to length(specials) do --get any occurrence of '$',']','[','"' out of the way q=specials[i] p=1 while 1 do p=matchFrom({39,q,39},work,p) if p=0 then exit end if work[p..p+2]=0 end while end for p=1 while 1 do --mask any escaped "s as well p=matchFrom("\\\"",work,p) if p=0 then exit end if q=p-1 while q and work[q]='\\' do q-=1 end while if and_bits(p-q,1) then work[p..p+1]=0 end if --otherwise, "server$\\\\C:\\" would not register as a string, and the $ inside wrongly processed end while --neutralize strings, now that the only " left delimit strings z=levels(work,34,34) work*=(1-z) --ending dquotes are still there, but never mind, strings don't show up any more --neutralize comments p=match("--",work) if p then return work[1..p-1] else return work end if end function function augment(sequence line,integer nline) sequence s if not plevels[nline] then s=clean(plines[nline]) plines[nline]=s plevels[nline]=1 else s=plines[nline] end if return s&line end function procedure undollar_(integer in,integer out) object line sequence work,blevels,line2 integer p,q,cdollar,sStart,sEnd,curline,plevel while 1 do line=gets(in) if atom(line) then exit else p=(line[length(line)]='\n') work=line[1..length(line)-p] p=find('$',line) plevel=(p>0) if p then --real stuff coming now --neutralize special cases, like comments, strings,... work=clean(work) --now work has relevant $, [ and ]s only. See if it has any $ left cdollar=find('$',work) --a daily activity :) Convert into €/£/¥ as appropriate. --resolve every $ sign, now that they all matter while cdollar do q=cdollar --temp $ position line2=work --temp text buffer curline=1 --first augment line index while 1 do --build the sequence of bracket levels blevels=levels(line2,'[',']') p=blevels[q] --inside how many [] is that $, relative to start of line? --find the base sequence name sEnd=q-1 while sEnd and blevels[sEnd]!=p-1 do sEnd-=1 end while --sEnd points at the char to the left of the inner [ nesting current $ --the name of the sequence the length of which $ represents ends there. --chars at this nesting level are either ] of intermediate indexes, or the name of the base sequence, possibly with an extra [ or operator on the left sStart=sEnd while sStart and (line2[sStart]=']' or blevels[sStart]>=p or find(line2[sStart],whitespace)) do sStart-=1 end while --now sStart points at the last char of the base sequence, find its starting point while sStart and ischar(line2[sStart]) do sStart-=1 end while --sStart points to the left of the complete name if ischar(line2[sStart+1]) then exit end if --found it --gotta find the base sequence in some previous line line2=augment(line2,curline) curline+=1 q=find('$',line2) end while --now perform substitution in both work and line, so that indexes always match --$ gets replaced by "length(@)", where @ is the string delimited by sStart and sEnd line=line[1..cdollar-1] & "length(" & line2[sStart+1..sEnd] & ")" & line[cdollar+1..length(line)] work=work[1..cdollar-1] & "length(" & line2[sStart+1..sEnd] & ")" & work[cdollar+1..length(work)] --next in line, please cdollar=findFrom('$',work,cdollar+7+sEnd-sStart) --7 is length("length()")-length("$") end while end if puts(out,line) --output processed file line, which kept comments, strings and stuff if plevel or (match("--",work)=0 and find('\"',work)=0) then q=1 p=match("end ",work) else q=0 end if --an "end " statement is an index fence; use this to limit the growth of plines if q=0 or p=0 or (p>1 and ischar(work[p-1])) then plines=prepend(plines,work) plevels=prepend(plevels,plevel) else plines={work[p+4..length(work)]} plevels={plevel} end if end if end while close(in) close(out) end procedure global procedure undollar(object in,object out) if sequence(in) then in=open(in,"r") end if if sequence(out) then out=open(out,"w") end if if in>=0 and out>=0 then undollar_(in,out) end if end procedure function iF(object a,object b,object c) if sequence(a) then a=(find(0,a)=0) and length(a) end if --a sequence is true iff it has no 0 inside and is not empty if a then return b else return c end if end function constant defext={".e",".ew",".ex",".exu",".exw"}, slash=iF(platform()=3,'/','\\'), slashes=iF(platform()=3,"/","\\/") sequence exts,dirout integer dirstatus dirstatus=0 function exfilter(sequence path,sequence direntry) --callback for walk_dir() integer inhandle,outhandle,p sequence ext,s,fname s=direntry[D_ATTRIBUTES] if find('d',s) or find('v',s) then return 0 end if fname=direntry[D_NAME] p=length(fname) path&=slash while p and fname[p]!='.' do p-=1 end while if p=0 then p=length(fname)+1 end if ext=fname[p..length(fname)] if not find(ext,exts) then return 0 end if --go process next file inhandle=open(path&fname,"r") if inhandle=-1 then dirstatus=1 return 1 end if --stop here, some file couldn't be opened --read failed, access denied or memory issues outhandle=open(dirout&fname,"w") if outhandle=-1 then dirstatus=1 return 1 end if --stop here, some file couldn't be opened --write failed, possibly becaue outdir doesn't exist undollar_(inhandle,outhandle) return 0 end function function process_dir(sequence indir,sequence outdir,sequence extensions) integer wstatus exts=extensions if not find(outdir[length(outdir)],slashes) then outdir&=slash end if dirout=outdir wstatus=walk_dir(indir,routine_id("exfilter"),1) return dirstatus or wstatus end function function tokens(sequence s) --transforms a comma separated string into a sequence of substrings integer p,q sequence result p=1 q=1 result={} while 1 do p=findFrom(',',s,q) if p=0 then return result end if result=append(result,s[q..p-1]) q=p+1 end while end function procedure ignore(object x) end procedure sequence cmd object prevdir integer inhandle,outhandle,lcmd,status status=0 prevdir=0 cmd=command_line() lcmd=length(cmd) inhandle=0 outhandle=1 --so as infile < undollar.e > outfile to work under Linux (not tested) if lcmd>=3 then if not compare(cmd[3],"/root") then --change base dir prevdir=current_dir() ignore(chdir(cmd[4])) cmd=cmd[1..2] & cmd[5..lcmd] lcmd-=2 end if if not compare(cmd[3],"/dir") then if lcmd>=6 and cmd[6][1]!=',' then abort(process_dir(cmd[4],cmd[5],tokens(cmd[6]))) elsif lcmd=6 then abort(process_dir(cmd[4],cmd[5],tokens(cmd[6][2..length(cmd[6])]) & defext)) elsif lcmd=5 then abort(process_dir(cmd[4],cmd[5],defext)) elsif lcmd=4 then abort(process_dir(current_dir(),cmd[4],defext)) end if else if compare(cmd[3],"/out") then inhandle=open(cmd[3],"r") end if if lcmd>3 then outhandle=open(cmd[4],"w") end if end if end if --process_dir aborts out, so we get here only when individual files are processed status=1-(inhandle>=0 and outhandle>=0) if status=0 then undollar_(inhandle,outhandle) else if inhandle=-1 then puts(2,"Can't read "&cmd[3]&'\n') end if if outhandle=-1 then puts(2,"Can't write to "&cmd[4]&'\n') end if end if if sequence(prevdir) then ignorechdir(prevdir)) end if abort(status)