--GENERAL PURPOSE SPIFFY routines for HamsterSpeak --(C) Copyright 1997-2005 James Paige, Ralph Versteegen, and the OHRRPGCE Developers --Dual licensed under the GNU GPL v2+ and MIT Licenses. Read LICENSE.txt for terms and disclaimer of liability. --------------------------------------------------------------------------- -- All of the routines in this file are general-purpose routines that -- are used by HSPEAK.EX but dont do anything HamsterSpeak-specific --------------------------------------------------------------------------- include misc.e include file.e include get.e --------------------------------------------------------------------------- ---constants--- constant true=1 constant false=0 global constant stdin=0 global constant stdout=1 global constant failure=-1 --globals-- global integer PATH_SLASH ifdef WINDOWS then PATH_SLASH='\\' elsedef PATH_SLASH='/' end ifdef --redundant to the standard upper() and lower() functions I know, but I --use them constantly, and almost never use the other wildcard functs. --this avoids including wildcard.e -- convert atom or sequence to lower case global function hs_lower(object x) return x + (x >= 'A' and x <= 'Z') * ('a' - 'A') end function -- convert atom or sequence to upper case global function hs_upper(object x) return x - (x >= 'a' and x <= 'z') * ('a' - 'A') end function -- convert unprintable characters to \x escape codes global function escape_string(sequence s) sequence result result="" for i=1 to length(s) do if s[i]=10 then result&="\\n" elsif s[i]<32 or s[i]>=127 then result&=sprintf("\\x%02x",{s[i]}) else result&={s[i]} end if end for return result end function ---convert a delimited string into a sequence of strings--- global function explode(sequence s,sequence delim) sequence result integer pos result={} pos=match(delim,s) if pos then while pos do result=append(result,s[1..pos-1]) s=s[pos+length(delim)..length(s)] pos=match(delim,s) end while end if result=append(result,s) return result end function --merge a sequence of strings into a single delimited string-- global function implode (sequence s,sequence glue) sequence output output={} if length(s) then output=s[1] for i=2 to length(s) do output &= glue & s[i] end for end if return output end function ---exclude specified chars from a string sequence--- global function exclude(sequence s,sequence c) sequence result result={} for i=1 to length(s) do if not find(s[i],c) then result=append(result,s[i]) end if end for return(result) end function --wraps value() and returns a default if it fails global function string_to_object(sequence s,object default) object result result=value(s) if result[1]!=GET_SUCCESS then result[2]=default end if return result[2] end function ---collapse reundant elements of a sequence--- global function redundx(sequence s,object c) sequence n n="" for i=1 to length(s) do if compare(s[i],c)=0 then if i=start do seq=seq[1..at-1] & new & seq[at+length(old)..length(seq)] start=at+length(new) at=match(old,repeat(0,start-1) & seq[start..length(seq)]) end while return(seq) end function --count the number of occurrences of an object in a sequence-- global function count(object it, sequence s) integer cnt integer i cnt=0 i=find(it,s) while i do cnt+=1 i=find_from(it,s,i+1) end while return cnt end function --find from the end of a sequence-- global function find_rev(object it, sequence s) for i=length(s) to 1 by -1 do if equal(s[i],it) then return i end if end for return 0 end function --normalize a pathname to use forward slashes-- --NOTE: on Unix this breaks the ability for \ to appear in a filename --appearing on the commandline, but we do definitely want to allow using --\ as a path separator in include paths. global function filenamix(sequence s) return substitute(s,'\\','/') end function --normalize a pathname to use back slashes-- global function filenamos(sequence s) return substitute(s,'/','\\') end function --normalise a pathname to use the platform specific path delimiter-- global function normalize_filename(sequence s) ifdef WINDOWS then return filenamos(s) elsedef return filenamix(s) end ifdef end function --make a file path absolute if it is not (note this leaves .'s and ..'s intact), --relative to the current dir by default global function absolutize_path(sequence s,sequence basedir="") if equal(basedir,"") then basedir=current_dir() end if ifdef WINDOWS then --FIXME: breaks on UNC paths if length(s)<3 or equal(s[2..3],":\\")=0 then if length(basedir)=3 then --special case: basedir has a trailing \ return basedir&s else return basedir&"\\"&s end if end if elsedef if s[1]!='/' then return basedir&"/"&s end if end ifdef return s end function --extract only portion of a string after the last of a delimiter-- global function get_suffix(sequence s,sequence delim) sequence broken broken=explode(s,delim) return broken[length(broken)] end function --extract all of a string except what follows the last of a delimiter-- global function without_suffix(sequence s,sequence delim) sequence broken broken=explode(s,delim) if length(broken)>1 then return implode(broken[1..length(broken)-1],delim)&delim else return "" end if end function --exclude the path from a fully qualified filename-- global function file_only(sequence filename) return get_suffix(filenamix(filename),"/") end function --return only the path from a fully qualified filename-- global function path_only(sequence filename) return without_suffix(filenamix(filename),"/") end function --return only the extension of a filename-- global function extension_only(sequence filename) return get_suffix(filename,".") end function --exclude the extension from a filename-- global function without_extension(sequence filename) sequence result result=without_suffix(filename,".") if length(result) then return result else return filename&"." end if end function --returns a filename with the extension changed-- global function alter_extension(sequence filename,sequence newext) return without_extension(filename) & newext end function --return the larger of two integers-- global function large(integer n1,integer n2) if n1>n2 then return n1 else return n2 end if end function --return the smaller of two integers-- global function small(integer n1,integer n2) if n1=-2147483648 and o<=2147483647 then return(true) end if return(false) end type global function string_is_int32(sequence s) object o o=value(s) if o[1]=GET_SUCCESS then --translated to object if int32(o[2]) then --object is an integer --if compare(s,sprintf("%d",{o[2]}))=0 then --strict checking is disabled because 7 = 007 --is exact match return(true) --end if end if end if return(false) end function --------------------------------------------------------------------------- --returns a number as a four-byte sequence in absurd byte order {3,4,1,2} function absurd_byte_order(integer n) integer b1,b2,b3,b4 b1=and_bits(n,#FF) b2=and_bits(n,#FF00)/#100 b3=and_bits(n,#FF0000)/#10000 b4=and_bits(n,#FF000000)/#1000000 return{b3,b4,b1,b2} end function --------------------------------------------------------------------------- global function write_lump(integer filehandle,sequence name,sequence data) if length(name)>50 then --fail if name is too long return(false) end if puts(filehandle,hs_upper(name)&0) puts(filehandle,absurd_byte_order(length(data))) puts(filehandle,data) return(true) end function --------------------------------------------------------------------------- --alternative to write_lump. Call lumphandle=begin_lump(...), then write --data to the file and call end_lump(lumphandle) global function begin_lump(integer filehandle,sequence name) sequence handle if length(name)>50 then --fail if name is too long return(false) end if puts(filehandle,hs_upper(name)&0) handle={filehandle,where(filehandle)} puts(filehandle,{0,0,0,0}) return(handle) end function --------------------------------------------------------------------------- global function end_lump(sequence lumphandle) integer curpos curpos=where(lumphandle[1]) if seek(lumphandle[1],lumphandle[2]) then return false end if puts(lumphandle[1],absurd_byte_order(curpos-lumphandle[2]-4)) if seek(lumphandle[1],curpos) then return false end if return(true) end function --------------------------------------------------------------------------- --returns the first x characters, avoiding word break. --this is used to do the word-wrapping of long-lines global function before_wrap_point(sequence string) sequence line,word,result integer size size=79 result=string line=explode(string,"\n") if length(line) then word=explode(line[1]," ") result="" if length(word) then while length(word) do if length(result & word[1]) <= size then result &= word[1] word=decapitate(word) if length(word) then result &= " " end if else if length(word[1])>size then result &= word[1][1..large(0,size-length(result))] end if exit end if end while else result&=line[1] end if end if return result end function --------------------------------------------------------------------------- --returns the remainder of a string after word wrapping one line --this is used to do the word-wrapping of long-lines global function after_wrap_point(sequence string) sequence result result=string[large(length(before_wrap_point(string)),1)+1..length(string)] if length(result) then if result[1]='\n' then result=decapitate(result) end if end if return result end function --------------------------------------------------------------------------- --replace the end of a string with ellipsis if too long global function shorten_string(sequence s, integer len) integer quotes quotes=0 if length(s)>0 and s[1]='"' and s[$]='"' then quotes=1 s=s[2..$-1] len-=2 end if if length(s)>len then s=s[1..len-3]&"..." end if if quotes then return("\""&s&"\"") else return(s) end if end function