Category Archives: (oo)REXX

MD5 digest in OORexx

usemd5.rex – a sample MD5 calculation with md5.cls below

#!/usr/bin/rexx
 
parse arg ifn   -- get input filename
input_stream = .stream~new(ifn)
message_digest = .md5~new
do while input_stream~chars>0
  -- calculate digest in chunks of 256 bytes
  message_digest~update(input_stream~charin(,256))
end
say message_digest~digest
exit
 
::requires "md5.cls"

md5.cls – a MD5 implementation in OORexx

#!/usr/bin/rexx
 
-- requires OORexx 4.2.0 or later
-- standard numeric digits of 9 is not enough in this case
::options digits 20
 
-- Implementation mainly based on pseudocode in https://en.wikipedia.org/wiki/MD5
::class md5 public
 
::method init
  expose a0 b0 c0 d0 count buffer index K. s  -- instance variables
  use strict arg chunk=""
  -- Initialize message digest
  a0 = .int32~new('67452301'x,"C")   -- A
  b0 = .int32~new('efcdab89'x,"C")   -- B
  c0 = .int32~new('98badcfe'x,"C")   -- C
  d0 = .int32~new('10325476'x,"C")   -- D
  -- The 512 bit chunk buffer
  buffer = .mutablebuffer~new('00'x~copies(64),64)
  -- The position in the buffer to insert new input
  index = 1
  -- message bytecount 
  count = 0 
  -- initialize leftrotate amounts
  nrs = .array~of(7,12,17,22)
  s = nrs~union(nrs)~union(nrs)~union(nrs)
  nrs = .array~of(5,9,14,20)
  s = s~union(nrs)~union(nrs)~union(nrs)~union(nrs)
  nrs = .array~of(4,11,16,23)
  s = s~union(nrs)~union(nrs)~union(nrs)~union(nrs)
  nrs = .array~of(6,10,15,21)
  s = s~union(nrs)~union(nrs)~union(nrs)~union(nrs)
  -- initialize sinus derived constants.
  -- sin function from RXMath Library shipped with OORexx
  -- see ::routine directive at the end of the code 
  do i=0 to 63
    K.i = .int32~new(((2**32)*(sin(i+1,16,R)~abs))~floor) 
  end
  -- process initial string if any
  self~update(chunk)
exit
 
::method update
  expose a0 b0 c0 d0 count buffer index K. s  -- instance variables
  use strict arg chunk
  count += chunk~length
  if chunk~length<65-index then do
    buffer~overlay(chunk,index)
    index += chunk~length
  end
  else do
    split = 65-index+1
    parse var chunk part =(split) chunk
    buffer~overlay(part,index)
    index = 65
  end
  -- Only proces completely filled buffer
  do while index=65
    A = a0
    B = b0
    C = c0
    D = d0
    do i=0 to 63
      select 
        when i<16 then do
          F = D~xor(B~and(C~xor(D)))
          g = i
        end
        when i<32 then do
          F = C~xor(D~and(B~xor(C)))
          g = (5*i+1)//16
        end
        when i<48 then do
          F = B~xor(C)~xor(D)
          g = (3*i+5)//16
        end
        otherwise do
          F = C~xor(B~or(D~not))
          g = (7*i)//16
        end
      end
      M = .int32~new(buffer~substr(g*4+1,4)~reverse,"C")  -- 32bit word in little-endian
      dTemp = D
      D = C
      C = B
      B = (B + (A+F+K.i+M)~bitrotate(s[i+1]))
      A = dTemp
    end
    a0 = a0+A
    b0 = b0+B
    c0 = c0+C
    d0 = d0+D
    parse var chunk part 65 chunk
    index = part~length+1
    buffer~overlay(part,1,part~length)
  end
exit
 
::method digest
  expose a0 b0 c0 d0 count buffer index K s -- instance variables
  padlen = 64
  if index<57 then padlen = 57-index
  if index>57 then padlen = 121-index
  padding = '00'x~copies(padlen)~bitor('80'x)
  bitcount = count*8//2**64
  lowword = bitcount//2**32
  hiword = bitcount%2**32
  lowcount = lowword~d2c(4)~reverse -- make it little-endian
  hicount = hiword~d2c(4)~reverse   -- make it little-endian
  self~update(padding || lowcount || hicount)
return a0~string || b0~string || c0~string || d0~string
 
-- A convenience class to encapsulate operations on non OORexx-like
-- things such as little-endian 32-bit words  
::class int32 private
 
::attribute arch class
 
::method init class
  self~arch = "little-endian"   -- can be adapted for multiple architectures 
 
-- Method to create an int32 like object
-- Input can be a OORexx whole number (type="I") or
-- a character string of 4 bytes (type="C")
-- input truncated or padded to 32-bit word/string
::method init
  expose char4 int32
  use strict arg input, type="Integer"
  -- type must be one of "I"nteger or "C"haracter
  t = type~subchar(1)~upper
  select
    when t=='I' then do
      char4 = input~d2c(4)
      int32 = char4~c2d
    end
    when t=='C' then do
      char4 = input~right(4,'00'x)
      int32 = char4~c2d
    end
    otherwise do
      raise syntax 93.915 array("IC",type)
    end
  end
exit
 
::method xor  -- wrapper for OORexx bitxor method
  expose char4
  use strict arg other
return .int32~new(char4~bitxor(other~char),"C")
 
::method and  -- wrapper for OORexx bitand method
  expose char4
  use strict arg other
return .int32~new(char4~bitand(other~char),"C")
 
::method or   -- wrapper for OORexx bitor method
  expose char4
  use strict arg other
return .int32~new(char4~bitor(other~char),"C")
 
::method not   -- OORexx not implementation
  expose char4
return .int32~new(char4~bitxor('ffffffff'x),"C")
 
::method bitleft -- OORexx shift (<<) implementation
  expose char4 
  use strict arg bits
  bstring = char4~c2x~x2b 
  bstring = bstring~substr(bits+1)~left(bstring~length,'0') 
return .int32~new(bstring~b2x~x2d) 
 
::method bitright -- OORexx shift (>>) implementation
  expose char4
  use strict arg bits, signed=.false
  bstring = char4~c2x~x2b
  fill = '0'
  if signed then fill = bstring~subchar(1)
  bstring = bstring~left(bstring~length-bits)~right(bstring~length,fill)
return .int32~new(bstring~b2x~x2d)
 
::method bitrotate  -- OORexx (left) rotate method
  expose char4
  use strict arg bits, direction='left'
  d = direction~subchar(1)~upper
  if d=='L' then do
    leftpart = self~bitleft(bits)
    rightpart = self~bitright(32-bits)
  end
  else do
    leftpart = self~bitleft(32-bits)
    rightpart = self~bitright(bits)
  end
return rightpart~or(leftpart)
 
::method int  -- retrieve integer as number
  expose int32
return int32
 
::method char -- retrieve integer as characters
  expose char4
return char4
 
::method string -- retrieve integer as hexadecimal string
  expose char4
return char4~reverse~c2x~lower
 
::method '+'  -- OORexx method to add 2 .int32 instances
  expose int32
  use strict arg other
return .int32~new(int32+other~int)
 
-- Simplify function names for the necessary 'RxMath' functions	
::routine sin EXTERNAL "LIBRARY rxmath RxCalcSin"
Posted in (oo)REXX Tagged

ooRexx 4.1.3 – Classes & Methods

An online reference for ooRexx (Release 4.1.3) classes and their methods.

Posted in (oo)REXX

REXX Lost and Found

A little REXX widget I wrote about 10 years ago, that I found back on an old disk:

#!/usr/bin/rexx
--
-- A Generalized Rexx Filter that can be used in a pipe
--
-- Usage: rexx filter.rex builtin-or-external-function-name(arg1, arg2, ...)
--    or: rexx filter.rex < inputfile
--    or: rexx filter.rex (boolean expression) 
--
-- Note 1:
--
--    rexx (to invoke filter.rex) may or may not be required to get 
--    the redirection and piping working, dependent on the operating
--    system and the REXX implementation and/or installation setup.  
--  
-- External functions can have a simple structure, for instance: 
--          
--    rexx filter.rex "cut.rex(!line,10:5)" or filter.rex "cut(!line,10:5)"
--
--    parse arg line,poslen
--    parse var poslen pos ':' length .
--    /* possibly more logic, but return value should be a (modified)
--       line or a boolean if the function is called as an expression */
--    return delstr(line,pos,length)  
--  
-- Argument(s)/Option(s) or even all of the filter have to be quoted generally.
-- On Windows: It's best to use '"' for all of the filtere and "'" for arguments.
-- On *n?x: It's best to use "'" for all of the filter and '"' for arguments.
-- On *n?x: Redirection (< and > or >> ) doesn't seem to work all of the time.
--
-- Pipe variables:
--
-- The input line from the pipe is represented by '!line'. 
-- The running line count is represented by '!count' 
-- The total nr of lines is respresented by '!lines'
-- Using '!lines' in the filter implies buffering the input stream before 
-- any output is passed on to the next pipe stage.
--
-- Example 1: rexx filter.rex changestr(needle,!line,newneedle)
--
-- Example 2: rexx filter.rex strip(!line,'L','>')
--
-- Example 3: rexx filter.rex < somefile.txt  - just echoes the input
--
-- Example 4: rexx filter.rex (pos('>',!line)>0) - a boolean expression 
--
-- Example 5: rexx filter.rex "(!count>10)" - skips first 10 lines of input
--
-- Example 6: rexx filter.rex "(!count>!lines-10)" - shows last 10 lines of input   
--
--    rexx filter.rex <filter.rex | ,
--    rexx filter.rex (pos('-',!line)=1) | , 
--    rexx filter.rex strip(!line,'L','-')
-- 
-- will first read and echo filter.rex, 
-- then output only input lines that start with '-',
-- then strip leading comment identifier '--'. 
--
-- No copyright, no license, no guarantees, use at your own risk.
--
-- And you just read the result of the pipe above.
--
parse source . . me  
parse arg !filter
do (!filter='?' | !filter='-?' | !filter='/?' | !filter='h' | !filter='-h' | !filter='/h' | !filter='--help')
  pipe = 'rexx "'me'"<"'me'" | rexx "'me'" (pos(''-'',!line)=1) | rexx "'me'" strip(!line,''L'',''-'')'
  pipe
  exit
end
!filter = strip(strip(strip(!filter,'B',' '),'B',"'"),'B','"')  -- strip spaces and quotes
do until (lines()>0)  -- a buffered stage can delay the input stream 
  nop
end 
!buffered = (pos('!lines',!filter)>0)
if (!buffered) then do  -- buffer input stream on standard queue
  do !lines=1 by 1 while (lines()>0)
    queue linein()
  end
  !lines = queued()
  !queued = !lines
  if (!queued>0) then do  -- prefetch the 1st line queue
    parse pull !line
  end  
end
else do
  !queued = lines()
  if (!queued>0) then do  -- prefetch the 1st line from stdin
    !line = linein()
  end) 
end
do !count=1 by 1 while (!queued>0)
  if (!filter = '') then do  -- no filter just echoes the input
    !out = !line
  end
  else do 
    if (substr(!filter,1,1)='(') then do
      drop !out    -- to verify !out is a var later on            
      interpret '!test =' !filter  -- apply the test
      if (!test) then do   -- if test yields .true it goes out
        !out = !line
      end
    end
    else do    -- apply the filter
      interpret '!out =' !filter  
    end
  end
   if (var("!out")) then do  -- only write if we have something 
     call lineout ,!out     
  end
  if (!buffered) then do  -- if buffered, next line from queue
  !queued = queued()
  if (!queued>0) then do
    parse pull !line
  end
  end
  else do  -- not buffered, next line from stdin
    !queued = lines()
  if (!queued>0) then do
    !line = linein()
  end
  end
end
exit

The single line comment ( “–” ) style, may limit the use of the above to ooREXX and Regina REXX.

Posted in (oo)REXX Tagged , , , , ,