home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / OS2 / RXMULCH.ZIP / rxMulch.cmd < prev    next >
OS/2 REXX Batch file  |  1994-02-02  |  49KB  |  1,144 lines

  1. /* 
  2. program: rxMulch.cmd
  3. type:    REXXSAA-OS/2, version 2.x
  4. purpose: find/change strings in a file, allow for hex-characters
  5.          (program to find/replace characters (strings) in file; allows
  6.          definition of hexadecimal or decimal value of single characters)
  7.  
  8. version: 1.0
  9. date:    1994-01-08
  10.  
  11. author:  Rony G. Flatscher,
  12.          Wirtschaftsuniversität/Vienna
  13.          Rony.Flatscher@wu-wien.ac.at
  14.  
  15. needs:   DATERGF.CMD, all RexxUtil-function loaded (automatically loaded, if needed)
  16.    
  17. usage:
  18.     rxMulch [infile] [outfile] {[-]controlfile | /[-]switch}
  19.       infile:      if missing from STDIN:                         
  20.    
  21.       outfile:     if missing, RxMulch will replace infile;       
  22.                    if no infile than output to STDOUT:            
  23.    
  24.       controlfile OR switch MUST be present:
  25.  
  26.       controlfile: change for every search-string/replace-string line the  
  27.                    'search-string' into 'replace-string'; if more than one 
  28.                    search/replace-line is given, subsequent search/replaces
  29.                    start from the beginning.                      
  30.                    If the controlfile is preceded with a minus (-) the  
  31.                    meaning of the 'search-string' and 'replace-string' is  
  32.                    swapped.                                       
  33.                    If a line is empty or if there is a semi-colon (;) at the very
  34.                    first column, the line is treated as a comment.
  35.                                                                   
  36.                                                                   
  37.       switch:      If the switch is preceded with a minus (-) the meaning of  
  38.                    the 'search-string' and 'replace-string' is swapped. 
  39.    
  40.                    'C'search-string/replace-string                
  41.                    ... Change all occurrences of 'search-string' to  
  42.                        'replace-string'.                          
  43.    
  44.                    '[L[1|2|3|4|5]][H[1|2]]'                       
  45.                    ...  change low-/high-characters to any of the following
  46.                         representations:                          
  47.    
  48.                         L: change all low-char values c2d(0-32)   
  49.                             L .... defaults to L1                 
  50.                             L1 ... char(0-32) to decimal          
  51.                             L2 ... char(0-32) to hexadecimal      
  52.                             L3 ... char(0-32) to control-sequence 
  53.                             L4 ... char(0-32) to abbreviated comm-characters  
  54.                             L5 ... char(0-32) to all representations above 
  55.    
  56.                         H: change all high-char values c2d(128-255)  
  57.                             H  ... defaults to H1                 
  58.                             H1 ... char(128-255) to decimal       
  59.                             H2 ... char(128-255) to hexadecimal   
  60.    
  61.                         The appropriate search-string/replace-string pairs are
  62.                         generated automatically.                  
  63.    
  64.                    'F'search-string/replace-string                
  65.                    ... count the number of occurrences of 'search-string'  
  66.    
  67.       search-string/replace-string:                               
  68.                   (delimiter)search-values(delimiter)replace-values(delimiter)
  69.    
  70.             delimiter:                                            
  71.                    very first character in search-string/replace-string IMMEDIATELY
  72.                    following switch-character
  73.    
  74.             search-values                                         
  75.             replace-values:                                       
  76.                    any ASCII-string intermixed with the following escape-codes
  77.    
  78.                    escape-codes:                                  
  79.                        @C    ... CR                               
  80.                        @L    ... LF                               
  81.                        @T    ... TAB                              
  82.                        @E    ... ESC                              
  83.                        @Z    ... CTL-Z                            
  84.                        @@    ... @ (escape for @)                 
  85.                        @Xnn                                       
  86.                        @Hnn  ... char with the hexadecimal of value 'nn'
  87.                        @Dnnn ... char with the decimal value 'nnn'
  88.  
  89.    RxMulch can be called as a function from another REXX-program, e.g.
  90.  
  91.       some_variable = RxMulch(REXXstring, "[/][-]switch")
  92.  
  93.    
  94.    examples:                                                          
  95.    
  96.        rxMulch infile outfile controlfile                            
  97.            ... change 'infile' according to 'controlfile', place results into 
  98.                'outfile'
  99.    
  100.        rxMulch infile controlfile                                    
  101.            ... change 'infile' according to 'controlfile', place results into 
  102.                'infile' (i.e. replace 'infile' itself)               
  103.    
  104.        rxMulch < some_in_file > some_out_file controlfile            
  105.            ... change 'some_in_file' according to 'controlfile', place results
  106.                into 'some_out_file'; 'some_in_file' and 'some_out_file' are
  107.                redirected ('<' and '>'). rxMulch therefore can be used in pipes
  108.                too.                                                  
  109.    
  110.        rxMulch infile outfile1 /C.Microsoft Excel.Lotus 1-2-3.       
  111.            ... change 'infile' according to commandline switch (replace all
  112.                occurrences of 'Microsoft Excel' with 'Lotus 1-2-3'), place 
  113.                results into 'outfile1'                               
  114.    
  115.        rxMulch outfile1 outfile2 /-C.Microsoft Excel.Lotus 1-2-3.    
  116.            ... change 'outfile1' according to commandline switch (replace all 
  117.                occurrences of 'Lotus 1-2-3' with 'Microsoft Excel', note the  
  118.                minus (-) right before the switch-character), place results into  
  119.                'outfile2'; could be also expressed as:               
  120.                      rxMulch outfile1 outfile2 /C.Lotus 1-2-3.Microsoft Excel.
  121.    
  122.        rxMulch infile /C.;.@c@l.                                     
  123.            ... change 'infile' according to commandline switch (replace 
  124.                semicolons (;) with a carriage-return/linefeed), replace 
  125.                'infile'; could be also expressed as:                 
  126.                      rxMulch infile /C.;.@xd@xa.                     
  127.                      rxMulch infile /C.;.@x0d@x0a.                   
  128.                      rxMulch infile /C.;.@d13@d10.                   
  129.    
  130.        rxMulch infile /C.@c@l@c@l.@c@l.                              
  131.            ... change 'infile' according to commandline switch (replace 
  132.                consecutive carriage-return/linefeeds with one        
  133.                carriage-return/linefeed, i.e. remove one empty line), replace 
  134.                'infile'; could be also expressed as:                 
  135.                      rxMulch infile /C.@xd@xa@xd@xa.@xd@xa.          
  136.                      rxMulch infile /C!@d13@d10@d13@d10!@d13@d10!    
  137.                      rxMulch infile /C/@d13@d10@d13@d10/@c@l/        
  138.    
  139.        rxMulch infile /-C.@c@l@c@l.@c@l.                             
  140.            ... change 'infile' according to commandline switch (replace a  
  141.                carriage-return/linefeed with two consecutive         
  142.                carriage-return/linefeeds, i.e. insert an empty line after each
  143.                line), replace 'infile'; could be also expressed as:  
  144.                      rxMulch infile /C,@c@l,@c@l@c@l,                
  145.                      rxMulch infile /C=@x0d@x0a=@x0d@x0a@x0dx@0a=    
  146.                      rxMulch infile /C=@d13@d10=@d13@d10@x0dx@0a=    
  147.    
  148.        rxMulch infile /C=@x00@x00@x00@x00=@x01@x01@x01@x01=          
  149.            ... change 'infile' according to commandline switch (replace all
  150.                hexadecimal strings of 0x00000000 with 0x01010101), replace 
  151.                'infile'; could be also expressed as:                 
  152.                      rxMulch infile /C=@x0@x0@x0@x0=@x1@x1@x1@x1=    
  153.                      rxMulch infile /C/@d0@d0@d0@d0/@d1@d1@d1@d1/    
  154.    
  155.        rxMulch infile /F.OS/2.                                       
  156.            ... count occurrences of string 'OS/2' in 'infile'          
  157.    
  158.        rxMulch infile /F.@c@l@c@l.                                   
  159.            ... count number of lines in 'infile', which are immediately
  160.                followed by a blank line  
  161.  
  162.    examples for calling RxMulch from a REXX-procedure:
  163.        string1 = 'this is nice'
  164.        string2 = RxMulch(string1, '/c.this.that.')  /* change 'this' to 'that' */
  165.            ... string2 = 'that is nice'
  166.        string2 = RxMulch(string2, '/-c.this.that.') /* change 'that' to 'this' */
  167.            ... string2 = 'this is nice'
  168.        occurrences = RxMulch(string2, 'f.this.')    /* count 'this' in string2 */
  169.            ... occurrences = 1
  170.  
  171.  
  172. All rights reserved, copyrighted 1994, no guarantee that it works without
  173. errors, etc. etc.
  174.  
  175. donated to the public domain granted that you are not charging anything (money
  176. etc.) for it and derivates based upon it, as you did not write it,
  177. etc. if that holds you may bundle it with commercial programs too
  178.  
  179. you may freely distribute this program, granted that no changes are made
  180. to it and that DATERGF.CMD is being distributed with it.
  181.  
  182. Please, if you find an error, post me a message describing it, I will
  183. try to fix and rerelease it to the net.
  184.  
  185. */
  186.  
  187.  
  188. SIGNAL ON ERROR
  189. SIGNAL ON HALT
  190.  
  191. global.         = ""            /* default for empty array-elements */
  192. global.eTotalOfNeedles = 0      /* number of search/replace-needles */
  193. global.eTotalOfChanges = 0      /* sum of all changes */
  194. global.eDirection = 1           /* regular, i.e. first needle to be replaced with second */
  195. delimiter = ""
  196. mode = ""                       /* "F" find, "C" change, i.e. replacement */
  197. writeFile = 1                   /* write results to file */
  198. readFile  = 1                   /* read from file */
  199.  
  200. PARSE SOURCE op_sys global.eCall_type proc_name .
  201.  
  202. /* check whether RxFuncs are loaded, if not, load them */    
  203. IF RxFuncQuery('SysLoadFuncs') THEN                          
  204. DO                                                           
  205.     /* load the load-function */                             
  206.     CALL RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  207.                                                              
  208.     /* load all Sys* utilities in RexxUtil */
  209.     CALL SysLoadFuncs
  210. END                                                          
  211.  
  212.  
  213. IF global.eCall_type = "FUNCTION" THEN
  214. DO
  215.    tmpVar1 = ARG(1)             /* data to be worked on */
  216.    IF LEFT(ARG(2), 1) <> "/" THEN       /* in case "/" is not supplied */
  217.       arg2 = ARG(2)
  218.    ELSE
  219.       arg2    = SUBSTR(ARG(2), 2)       /* command-string without leadin "/" */
  220.  
  221.    writeFile = 0                /* no file to be produced */
  222.    readFile  = 0                /* do not read from file */
  223. END
  224. ELSE
  225.    PARSE ARG arg1 "/"arg2          /* control-string on command-line ?     */
  226.  
  227.  
  228. IF arg2 <> "" THEN              /* parse arguments with control-string on command-line */
  229. DO
  230.    full_control_string = "/" || arg2
  231.    IF LEFT(arg2, 1) = "-" THEN
  232.    DO
  233.       global.eDirection = 0      /* inverse, i.e. swap meaning of from-needle and to-needle */
  234.       arg2 = SUBSTR(arg2, 2)
  235.    END
  236.  
  237.    tmp1 = TRANSLATE(LEFT(arg2, 1))
  238.  
  239.    IF tmp1 = "C" THEN        /* needle & replacement is given on command-line */
  240.    DO
  241.      active_switch = "C"
  242.      delimiter = SUBSTR(arg2, 2, 1)
  243.      /* without "C" */
  244.      CALL parse_replacement_string SUBSTR(arg2, 2), "command line"
  245.      mode = "C"
  246.    END
  247.    ELSE IF tmp1 = "F" THEN      /* count occurrences of given needle */
  248.    DO
  249.      delimiter = SUBSTR(arg2, 2, 1)                             /* get delimiter */
  250.      tmp_string = SUBSTR(arg2, 2) || delimiter || delimiter     /* make sure, that enough delimiters are supplied */
  251.      CALL parse_replacement_string tmp_string, "command"
  252.      mode = "F"                 /* mode is "find" all occurrences */
  253.      writeFile = 0              /* don't write to a file */
  254.    END
  255.    ELSE                         /* needles & replacements to built automatically */
  256.    DO
  257.       tmp2 = TRANSLATE(SUBSTR(arg2, 2, 1))
  258.       tmp3 = TRANSLATE(SUBSTR(arg2, 3, 1))
  259.       tmp4 = TRANSLATE(SUBSTR(arg2, 4, 1))
  260.  
  261.       tmpL = (tmp1 = 'L') | (tmp2 = 'L') | (tmp3 = 'L') 
  262.       tmpH = (tmp1 = 'H') | (tmp2 = 'H') | (tmp3 = 'H') 
  263.  
  264.       IF tmpL THEN              /* replace low-chars: d2c(0-32) */
  265.       DO
  266.          arg2 = TRANSLATE(arg2)
  267.          /* is "L" followed by a number ? If so, extract it */
  268.          tmpLnr = SUBSTR(arg2, POS("L", SUBSTR(arg2, 1, 3)) + 1, 1)
  269.  
  270.          IF \DATATYPE(tmpLnr, "NUMERIC") THEN tmpLnr = ""
  271.  
  272.          CALL build_low tmpLnr          /* build the translation strings */
  273.       END
  274.  
  275.       IF tmpH THEN              /* replace high-chars: d2c(128-255) */
  276.       DO
  277.          arg2 = TRANSLATE(arg2)
  278.          /* is "H" followed by a number ? If so, extract it */
  279.          tmpHnr = SUBSTR(arg2, POS("H", SUBSTR(arg2, 1, 3)) + 1, 1)
  280.  
  281.          IF \DATATYPE(tmpHnr, "NUMERIC") THEN tmpHnr = ""
  282.          CALL build_high tmpHnr         /* build the translation strings */
  283.       END
  284.  
  285.       IF \(tmpL | tmpH) THEN            /* wrong switch ! */
  286.          SIGNAL usage
  287.  
  288.      mode = "CLH"
  289.    END
  290.  
  291.    global.eControl = ""                 /* no control-file */
  292.    PARSE VAR arg1 global.eFilein global.eFileout .
  293. END
  294. ELSE                            /* parse a control file ************************/
  295. DO
  296.    PARSE ARG global.eFilein global.eFileout global.eControl
  297.  
  298.    IF global.eControl = "" THEN         /* no control-file given ? */
  299.    DO
  300.       IF global.eFileout = "" THEN      /* no output-file given */
  301.       DO
  302.          IF global.eFilein = "" THEN    /* no input-file given, now we are in trouble */
  303.          DO
  304.             SIGNAL usage
  305.          END
  306.          ELSE                   /* assign files */
  307.          DO
  308.             global.eControl = global.eFilein    /* last file is control-file */
  309.             global.eFilein  = ""        /* user wants to read/write stdin/stdout */
  310.          END
  311.       END
  312.       ELSE
  313.       DO
  314.          global.eControl = global.eFileout                 /* last file is control-file */
  315.          global.eFileout = ""                      /* user wants to overwrite global.eFilein */
  316.       END
  317.    END
  318.  
  319.    IF LEFT(global.eControl, 1) = "-" THEN
  320.    DO
  321.       global.eDirection = 0                /* inverse */
  322.       global.eControl = SUBSTR(global.eControl, 2)         /* get rid of "-" */
  323.    END
  324.  
  325.    IF global.eFileout = global.eFilein THEN global.eFileout = ""   /* replace input file */
  326. END
  327.  
  328. /* remove leading & trailing spaces */
  329. global.eFilein  = STRIP(global.eFilein)
  330. global.eFileout = STRIP(global.eFileout)
  331. global.eControl = STRIP(global.eControl)                   
  332.  
  333. IF readFile & (global.eFilein <> "") THEN       /* check, whether input file exists */
  334. DO
  335.    IF (STREAM(global.eFilein, "C", "QUERY EXISTS") = "") THEN
  336.       CALL say_error global.eFilein || ": input-file does not exist !", -1
  337. END
  338.  
  339. /* erase output-file, if it exists */
  340. IF (global.eFileout <> "") & writeFile THEN
  341. DO
  342.    IF (STREAM(global.eFileout, "C", "QUERY EXISTS") <> "") THEN
  343.    DO
  344.       CALL BEEP 550, 250
  345.       CALL say2stderr "Output file [" || global.eFileout || "] exists already !"
  346.       CALL say2stderr "Overwrite it ? (Y/N)"
  347.       IF TRANSLATE(SysGetKey()) <> "Y" THEN
  348.          EXIT
  349.    
  350.       ADDRESS CMD "@del" global.eFileout
  351.       CALL say2stderr 
  352.       CALL say2stderr "Output File [" || global.eFileout || "] deleted."
  353.       CALL say2stderr 
  354.    END
  355. END
  356.  
  357. IF global.eControl <> "" THEN           /* check whether control-file exists */
  358. DO
  359.    IF STREAM(global.eControl, "C", "QUERY EXISTS") = "" THEN
  360.    DO
  361.       /* control-file not found: 
  362.            if control file was given *WITHOUT* path and drive, lookup the drive and
  363.            path of RxMulch.CMD and try to get the control-file from there
  364.       */
  365.       IF FILESPEC("Name", global.eControl) = global.eControl THEN
  366.       DO
  367.          PARSE SOURCE . . this_proc
  368.          global.eControl = FILESPEC("Drive", this_proc) || FILESPEC("PATH", this_proc) || global.eControl
  369.       END
  370.  
  371.       IF STREAM(global.eControl, "C", "QUERY EXISTS") = "" THEN
  372.          CALL say_error global.eFilein || ": control-file does not exist !", -2
  373.    END
  374.    CALL parse_control_file global.eControl      /* build search/replace-values from control-file */
  375. END
  376. /*
  377. ELSE
  378.    CALL parse_control_file global.eControl      /* build search/replace-values from control-file */
  379. */
  380.  
  381.  
  382. IF readFile & global.eFilein <> "" THEN
  383. DO
  384.    CALL STREAM global.eFilein, "C", "OPEN READ"    /* open input-file */
  385.  
  386.    IF global.eFileout = "" & writeFile THEN
  387.    DO
  388.       global.eTmp = SysTempFileName("tmp???")
  389.       global.eFileout     = global.eTmp
  390.    END
  391. END
  392.  
  393. IF global.eFileout <> "" & writeFile THEN
  394.    CALL STREAM global.eFileout, "C", "OPEN WRITE"  /* open output-file */
  395.  
  396. start_read = DATE("S") TIME()
  397.  
  398. IF readFile & global.eFilein = "" THEN     /* read from stdin: */
  399. DO
  400.    tmpVar1 = ""
  401.    DO WHILE STREAM('STDIN:','S') == 'READY'     /* read from stdin: */
  402.         tmpVar1 = tmpVar1 || CHARIN("STDIN:", , 131072)     /* read 4096 * 32 = 128KB to speed things up */
  403.    END
  404.  
  405.    IF tmpVar1 = "" THEN
  406.       CALL say_error "no data from stdin: received", -100
  407.  
  408.   global.eFilein  = "STDIN:"
  409.   global.eFileout = "STDOUT:"
  410. END
  411. ELSE IF readFile THEN  /* read entire file into variable */
  412.     tmpVar1  = CHARIN(global.eFilein, 1, STREAM(global.eFilein, "C", "QUERY SIZE"))
  413.  
  414. end_read = DATE("S") TIME()
  415.  
  416. IF global.eCall_type <> "FUNCTION" THEN
  417. DO
  418.    IF readFile THEN
  419.       CALL say2stderr RIGHT("Input-File:", 30)   "[" || global.eFilein  || "]"
  420.    IF writeFile THEN
  421.       CALL say2stderr RIGHT("Output-File:", 30)  "[" || global.eFileout || "]"
  422.    
  423.    IF global.eControl <> "" THEN
  424.    DO
  425.       CALL say2stderr RIGHT("global.eControl-File:", 30) "[" || global.eControl || "]"
  426.       CALL say2stderr
  427.       CALL say2stderr RIGHT("", 30) global.eTotalOfNeedles "needle(s) to be replaced."
  428.    END
  429.    ELSE
  430.    DO
  431.       CALL say2stderr RIGHT("global.eControl-Command Line:", 30) "[" || full_control_string || "]"
  432.       IF \(delimiter == "") THEN
  433.          CALL say2stderr RIGHT("", 30) "needle-delimiter: [" || delimiter || "]"
  434.       CALL say2stderr 
  435.  
  436.       IF mode = "C" THEN                /* change mode */
  437.       DO
  438.          CALL say2stderr RIGHT("replacing:", 41) "[" || global.1.eDebug_In || "]"
  439.          CALL say2stderr RIGHT("with:", 41) "[" || global.1.eDebug_Out || "]"
  440.          CALL say2stderr 
  441.          CALL say2stderr RIGHT("", 30) global.eTotalOfNeedles "needle(s) to be replaced."
  442.       END
  443.       ELSE IF mode = "CLH" THEN         /* automatic change mode, low/high values */
  444.       DO
  445.          CALL say2stderr RIGHT("replacing:", 41) "[CHAR(0-32) or/and CHAR(128-255)]"
  446.          CALL say2stderr 
  447.          CALL say2stderr RIGHT("", 30) global.eTotalOfNeedles "needle(s) to be replaced."
  448.       END
  449.       ELSE IF mode = "F" THEN   
  450.       DO
  451.          CALL say2stderr RIGHT("looking for:", 43) "[" || global.1.eDebug_In || "]"
  452.       END
  453.    END
  454. END
  455.  
  456.  
  457. start_replace = DATE("S") TIME()
  458.  
  459. /*
  460.    save space on large files, hence minimize usage of variable assignments
  461. */
  462. bDirty = 0                               /* bDirty, if replacements happened */
  463. IF mode = "F" THEN                      
  464. DO
  465.    DO i = 1 TO global.eTotalOfNeedles   /* find all search/replace-strings */
  466.       CALL find_needles global.i.eNeedle_In, global.i.eNeedle_Out
  467.    END
  468. END
  469. ELSE
  470. DO
  471.    tmpVar2 = ""
  472.    bWorkOnTmpVar1 = 1                   /* work on variable "tmpVar1" or "tmpVar2" */
  473.    DO i = 1 TO global.eTotalOfNeedles   /* process all search/replace-strings */
  474.       IF bWorkOnTmpVar1 THEN
  475.          CALL work_on_tmpVar1 global.i.eNeedle_In, global.i.eNeedle_Out
  476.       ELSE
  477.          CALL work_on_tmpVar2 global.i.eNeedle_In, global.i.eNeedle_Out
  478.    
  479.       IF bDirty THEN
  480.          bWorkOnTmpVar1 = \bWorkOnTmpVar1
  481.    END
  482. END
  483.  
  484. end_replace = DATE("S") TIME()
  485.  
  486. IF readFile THEN
  487.    CALL STREAM global.eFilein, "C", "CLOSE"     /* close input file */
  488.  
  489. start_write = DATE("S") TIME()
  490.  
  491.  
  492. IF global.eCall_type = "FUNCTION" THEN          /* was called as a function */
  493. DO
  494.    IF mode <> "F" THEN
  495.    DO
  496.      IF bWorkOnTmpVar1 THEN             /* result in variable "tmpVar1" */
  497.         RETURN tmpVar1
  498.      ELSE                               /* result in variable "tmpVar2" */
  499.         RETURN tmpVar2
  500.    END
  501.  
  502.    RETURN global.eTotalOfChanges        /* return number of occurrences found */
  503. END
  504.  
  505. IF writeFile THEN                       /* write output file */
  506. DO
  507.    IF mode <> "F" THEN
  508.    DO
  509.      IF global.eTotalOfChanges <> 0 THEN
  510.      DO
  511.         IF bWorkOnTmpVar1 THEN             /* result in variable "tmpVar1" */
  512.            CALL CHAROUT global.eFileout, tmpVar1
  513.         ELSE                               /* result in variable "tmpVar2" */
  514.            CALL CHAROUT global.eFileout, tmpVar2
  515.      END
  516.      ELSE       /* no replacements took place, erase output-file */
  517.      DO
  518.         IF STREAM(global.eFileout) = "READY" THEN
  519.            CALL STREAM global.eFileout, "C", "CLOSE"    /* close output file */
  520.  
  521.         ADDRESS CMD "@del" global.eFileout
  522.      END
  523.    END
  524.    
  525.    IF STREAM(global.eFileout) = "READY" THEN
  526.       CALL STREAM global.eFileout, "C", "CLOSE"    /* close output file */
  527. END
  528.  
  529. end_write = DATE("S") TIME()
  530.  
  531. IF global.eTmp <> "" & writeFile THEN   /* output file was temporary, replacement of input file sought */
  532. DO
  533.    CALL SysFileDelete(global.eFilein)           /* delete input file */
  534.    ADDRESS CMD "@ren" global.eFileout global.eFilein    /* rename output to input name */
  535. END
  536.  
  537. total_time   = calc_time(start_read, end_write)
  538. read_time    = calc_time(start_read, end_read)
  539.  
  540. IF writeFile THEN
  541.    write_time   = calc_time(start_write, end_write)
  542.  
  543. replace_time = calc_time(start_replace, end_replace)
  544.  
  545. IF mode <> "F" THEN
  546.    CALL say2stderr RIGHT("", 30) global.eTotalOfChanges "occurrence(s) replaced."
  547. ELSE
  548.    CALL say2stderr RIGHT("", 30) global.eTotalOfChanges "occurrence(s) found."
  549.  
  550. CALL say2stderr 
  551. CALL say2stderr RIGHT("time for reading file:", 30)  read_time
  552.  
  553. IF mode <> "F" THEN
  554.    CALL say2stderr RIGHT("time for replacing:", 30)     replace_time
  555.  
  556. IF writeFile THEN
  557.    CALL say2stderr RIGHT("time for writing file:", 30)  write_time
  558.  
  559. CALL say2stderr
  560. CALL say2stderr RIGHT("total time:", 30)             total_time
  561.  
  562. EXIT
  563.  
  564.  
  565.  
  566.  
  567. /*********************************************************************************
  568.    search string and replace it, if found, return tmpVar2 string
  569.  
  570.    EXPOSE both strings, so no copies of them need to be produced in this procedure
  571.    (could be deadly on multi-MB-files in terms of speed)
  572. */
  573. WORK_ON_TMPVAR1: PROCEDURE EXPOSE global. tmpVar1 tmpVar2 bDirty
  574.     from_needle  = ARG(1)
  575.     to_needle    = ARG(2)
  576.  
  577.     from_needle_length = LENGTH(from_needle)      /* get length of search-string */
  578.     start = 1           /* start position in tmpVar1 */
  579.     bDirty = 0           /* does a change occur ? */
  580.     tmpVar2   = ""
  581.  
  582.     DO FOREVER
  583.        pos = POS(from_needle, tmpVar1, start)         /* get position of search-string */
  584.        IF pos = 0 THEN                          /* search-string not found */
  585.        DO
  586.           IF bDirty THEN
  587.              tmpVar2 = tmpVar2 || SUBSTR(tmpVar1, start)
  588.           LEAVE            /* done, no from_needle found */
  589.        END
  590.  
  591.        tmpVar2 = tmpVar2 || SUBSTR(tmpVar1, start, pos-start) || to_needle
  592.        global.eTotalOfChanges = global.eTotalOfChanges + 1
  593.        start = pos + from_needle_length
  594.        bDirty = 1                /* change occurred ! */
  595.     END
  596.  
  597.     RETURN
  598.  
  599.  
  600. WORK_ON_TMPVAR2: PROCEDURE EXPOSE global. tmpVar2 tmpVar1 bDirty
  601.     from_needle = ARG(1)
  602.     to_needle   = ARG(2)
  603.  
  604.     from_needle_length = LENGTH(from_needle)      /* get length of search-string */
  605.     start = 1           /* start position in tmpVar2 */
  606.     bDirty = 0           /* does a change occur ? */
  607.     tmpVar1   = ""
  608.  
  609.     DO FOREVER
  610.        pos = POS(from_needle, tmpVar2, start)         /* get position of search-string */
  611.  
  612.        IF pos = 0 THEN                          /* search-string not found */
  613.        DO
  614.           IF bDirty THEN
  615.              tmpVar1 = tmpVar1 || SUBSTR(tmpVar2, start)
  616.           LEAVE            /* done, no from_needle found */
  617.        END
  618.  
  619.        tmpVar1 = tmpVar1 || SUBSTR(tmpVar2, start, pos-start) || to_needle
  620.        global.eTotalOfChanges = global.eTotalOfChanges + 1
  621.        start = pos + from_needle_length
  622.        bDirty = 1                /* change occurred ! */
  623.     END
  624.  
  625.     RETURN
  626.  
  627.  
  628.  
  629. /*********************************************************************************
  630.    find string for number of occurences
  631. */
  632. FIND_NEEDLES: PROCEDURE EXPOSE global. tmpVar1 bDirty
  633.     from_needle  = ARG(1)
  634.  
  635.     from_needle_length = LENGTH(from_needle)      /* get length of search-string */
  636.     start = 1           /* start position in tmpVar1 */
  637.     bDirty = 0           /* does a change occur ? */
  638.  
  639.     DO FOREVER
  640.        pos = POS(from_needle, tmpVar1, start)         /* get position of search-string */
  641.        IF pos = 0 THEN                          /* search-string not found */
  642.        DO
  643.           LEAVE            /* done, no from_needle found anymore */
  644.        END
  645.  
  646.        global.eTotalOfChanges = global.eTotalOfChanges + 1
  647.        start = pos + from_needle_length
  648.        bDirty = 1                /* changes occurred ! */
  649.     END
  650.  
  651.     RETURN
  652.  
  653.  
  654.  
  655.  
  656. /*********************************************************************************
  657.     parse control file, setup needle_in-/needle_out-array
  658.     ARG(1) ... control-file-name
  659. */
  660. PARSE_CONTROL_FILE: PROCEDURE EXPOSE global.
  661.     linecount = 0
  662.     DO WHILE LINES(ARG(1)) > 0
  663. /*                                      allow a blank as a delimiter !
  664.        line = STRIP(LINEIN(ARG(1)))
  665. */
  666.        line = LINEIN(ARG(1))
  667.  
  668.        linecount = linecount + 1        /* count processed lines */
  669.        IF line = "" THEN ITERATE
  670.        delimiter = LEFT(line, 1)        /* get delimiter character */
  671.        IF delimiter = ";" THEN iterate  /* comment encountered */
  672.        CALL parse_replacement_string line, linecount
  673.     END
  674.  
  675.     CALL STREAM ARG(1), "C", "CLOSE"    /* close the control file */
  676.  
  677.     RETURN
  678.  
  679.  
  680. /*
  681.    parse the replacement string in hand, extract replacement-strings
  682. */
  683. PARSE_REPLACEMENT_STRING: PROCEDURE EXPOSE global. 
  684.     line      = ARG(1)
  685.     linecount = ARG(2)
  686.     delimiter = LEFT(line, 1)        /* get delimiter character */
  687.  
  688.     /* get last delimiter and check whether it truly is a delimiter */
  689.     PARSE VAR line (delimiter) needle_in (delimiter) needle_out (delimiter) +0 checkDelimiter +1 .
  690.  
  691.     IF checkDelimiter <> delimiter THEN
  692.     DO
  693.        IF global.eCall_type <> "FUNCTION" THEN
  694.        DO
  695.           CALL say2stderr "Delimiter error in control-string:"
  696.           CALL say2stderr "   line # ["linecount"]"
  697.           CALL say2stderr "   line   ["line"]"
  698.        END
  699.        CALL say_error  "   last delimiter (" || delimiter || ") is missing !", -5
  700.     END
  701.  
  702.     i = global.eTotalOfNeedles + 1
  703.  
  704.  
  705.     IF global.eDirection THEN        /* regular */
  706.     DO
  707.        global.i.eNeedle_In  = parse_needle(needle_in, linecount, "search-string")
  708.        global.i.eNeedle_Out = parse_needle(needle_out, linecount, "replace-string")
  709.        global.i.eDebug_in  = needle_in
  710.        global.i.eDebug_out = needle_out
  711.     END
  712.     ELSE                             /* inverse (switch strings) */
  713.     DO
  714.        global.i.eNeedle_In  = parse_needle(needle_out, linecount, "search-string")
  715.        global.i.eNeedle_Out = parse_needle(needle_in, linecount, "replace-string")
  716.        global.i.eDebug_in  = needle_out
  717.        global.i.eDebug_out = needle_in
  718.     END
  719.  
  720.     global.eTotalOfNeedles = i
  721.  
  722.     RETURN
  723.  
  724.  
  725. /*********************************************************************************
  726.     parse needles and replaces the special character-sequences ... with:
  727.        @C    ... CR
  728.        @L    ... LF
  729.        @T    ... TAB
  730.        @E    ... ESC
  731.        @Z    ... CTL-Z
  732.        @@    ... @ (escape for @)
  733.        @Xnn  ... with the hexadecimal "nn" value
  734.        @Hnn  ... with the hexadecimal "nn" value
  735.        @Dnnn ... with the decimal "nnn" value
  736.  
  737.     ARG(1)   ... contains string to be parsed
  738.     ARG(2)   ... contains linenumber in control-file
  739.     ARG(3)   ... contains hint which part is in error
  740. */
  741. PARSE_NEEDLE: PROCEDURE
  742.      needle = ARG(1)
  743.      error_msg_string = ARG(3) "[" || ARG(1) || "] in line [" || ARG(2) || "] in error !"
  744.  
  745.      new_needle = ""
  746.      DO FOREVER
  747.         PARSE VAR needle left_side "@" needle
  748.         new_needle = new_needle || left_side
  749.  
  750.         IF needle <> "" THEN
  751.         DO
  752.            a = TRANSLATE(LEFT(needle, 1))       /* translate into uppercase */
  753.            value = ""
  754.            needle = SUBSTR(needle, 2)           /* remove lead-in */
  755.            SELECT
  756.               WHEN a = "C" THEN value = "0D"X   /* CR */
  757.               WHEN a = "L" THEN value = "0A"X   /* LF */
  758.               WHEN a = "T" THEN value = "09"X   /* TAB */
  759.               WHEN a = "E" THEN value = "1B"X   /* ESCape */
  760.               WHEN a = "Z" THEN value = "1A"X   /* CTL-Z == EOF */
  761.               WHEN a = "@" THEN value = "@"     /* character "@" itself */
  762.               WHEN a = "H" | a = "X" THEN       /* get character from hex-value */
  763.                    DO
  764.                       /* check whether a valid hex-string, two hex-digits mandatory ! */
  765.                       i = 2
  766.                       tmp = TRANSLATE(SUBSTR(needle, 1, i))
  767.  
  768.                       IF VERIFY(tmp, "0123456789ABCDEF") <> 0 THEN      /* hexadecimal value ? */
  769.                          CALL say_error "looking for 2-hex-digits, not a hex-value:" error_msg_string, -10
  770.  
  771.                       value = X2C(tmp)
  772.                       needle = SUBSTR(needle, i + 1)
  773.                    END
  774.  
  775.               WHEN a = "D" THEN                 /* get character from decimal-value */
  776.                    DO
  777.                       /* check for numeric datatype, 3-digits a must ! */
  778.                       i = 3
  779.                       tmp = TRANSLATE(SUBSTR(needle, 1, i))
  780.  
  781.                       IF \DATATYPE(SUBSTR(needle, 1, i), "N") THEN      /* numeric value ? */
  782.                          CALL say_error "looking for 3-digits, not a decimal-value:" error_msg_string, -10
  783.  
  784.                       value = D2C(tmp)
  785.                       needle = SUBSTR(needle, i + 1)
  786.                    END
  787.               OTHERWISE 
  788.                       CALL say_error "unknown control-value:" error_msg_string, -10
  789.  
  790.            END
  791.               new_needle = new_needle || value
  792.         END
  793.         ELSE
  794.            LEAVE
  795.  
  796.      END
  797.  
  798.      RETURN new_needle
  799.  
  800.  
  801. /*********************************************************************************
  802.   build low-control needles (00x --> 20x)
  803. */
  804. BUILD_LOW: PROCEDURE EXPOSE global.
  805.    type = ARG(1)        /* should it be decimal (1), hexadecimal (2),
  806.                                         control-sequence (3),
  807.                                         abbreviation of comm-characters (4) or
  808.                                         all representations (5)
  809.                          */
  810.  
  811.    /* default to 1 */
  812.    IF type <> "1" & type <> "2" & type <> "3" & type <> "4" & type <> "5" THEN
  813.       type = 1
  814.  
  815.    set.   = ""                  /* default to empty string */
  816.    /*      decimal pure : decimal : hexadecimal : control-chars : comm-chars */
  817.    set.0  = "32 d032 x20 ^  SP"    /* at first place, so no space-replacements take place at the end */
  818.    set.1  = " 1 d001 x01 ^A SOH"
  819.    set.2  = " 2 d002 x02 ^B STX"
  820.    set.3  = " 3 d003 x03 ^C ETX"
  821.    set.4  = " 4 d004 x04 ^D EOT"
  822.    set.5  = " 5 d005 x05 ^E ENQ"
  823.    set.6  = " 6 d006 x06 ^F ACK"
  824.    set.7  = " 7 d007 x07 ^G BEL"
  825.    set.8  = " 8 d008 x08 ^H BS"
  826.    set.9  = " 9 d009 x09 ^I HT"
  827.    set.10 = "10 d010 x0A ^J LF"
  828.    set.11 = "11 d011 x0B ^K VT"
  829.    set.12 = "12 d012 x0C ^L FF"
  830.    set.13 = "13 d013 x0D ^M CR"
  831.    set.14 = "14 d014 x0E ^N SO"
  832.    set.15 = "15 d015 x0F ^O SI"
  833.    set.16 = "16 d016 x10 ^P DLE"
  834.    set.17 = "17 d017 x11 ^Q DC1"
  835.    set.18 = "18 d018 x12 ^R DC2"
  836.    set.19 = "19 d019 x13 ^S DC3"
  837.    set.20 = "20 d020 x14 ^T DC4"
  838.    set.21 = "21 d021 x15 ^U NAK"
  839.    set.22 = "22 d022 x16 ^V SYN"
  840.    set.23 = "23 d023 x17 ^W ETB"
  841.    set.24 = "24 d024 x18 ^X CAN"
  842.    set.25 = "25 d025 x19 ^Y EM"
  843.    set.26 = "26 d026 x1A ^Z SUB"
  844.    set.27 = "27 d027 x1B ^[ ESC"
  845.    set.28 = "28 d028 x1C ^\ FS"
  846.    set.29 = "29 d029 x1D ^] GS"
  847.    set.30 = "30 d030 x1E ^^ RS"
  848.    set.31 = "31 d031 x1F ^_ US"
  849.    set.32 = " 0 d000 x00 ^@ NUL"
  850.  
  851.    DO i=0 TO 32
  852.       representation = ""
  853.  
  854.       set = SUBSTR(set.i, 4)            /* get encoding strings */
  855.       iChar = WORD(set.i, 1)            /* get decimal value of char in hand */
  856.  
  857.       SELECT 
  858.          WHEN type = 5 THEN representation = "<@" || set || ">"       /* all meaningful infos */
  859.          OTHERWISE representation = "<@" || WORD(set, type) || ">"
  860.       END  
  861.  
  862.       j = global.eTotalOfNeedles + 1
  863.  
  864.       IF global.eDirection THEN         /* from char ---> representation */
  865.       DO
  866.          global.j.eNeedle_In  = D2C(iChar)
  867.          global.j.eNeedle_Out = representation
  868.       END
  869.       ELSE
  870.       DO
  871.          global.j.eNeedle_In  = representation
  872.          global.j.eNeedle_Out = D2C(iChar)
  873.       END
  874.       global.eTotalOfNeedles = j
  875.    END
  876.  
  877.    RETURN
  878.  
  879.  
  880. /*********************************************************************************
  881.   build high-char needles (80x-FFx)
  882. */
  883. BUILD_HIGH: PROCEDURE EXPOSE global.
  884.    type = ARG(1)                /* should it be decimal (1) or hexadecimal (2) ? */
  885.    IF type <> "1" & type <> "2" THEN
  886.       type = 1
  887.  
  888.    DO i=128 TO 255
  889.       IF type = 1 THEN          /* decimal representation */
  890.          representation = "<@d" || RIGHT(i, 3, 0) || ">"
  891.       ELSE
  892.          representation = "<@x" || d2x(i) || ">"
  893.  
  894.       j = global.eTotalOfNeedles + 1
  895.  
  896.       IF global.eDirection THEN         /* from char ---> representation */
  897.       DO
  898.          global.j.eNeedle_In  = D2C(i)
  899.          global.j.eNeedle_Out = representation
  900.       END
  901.       ELSE
  902.       DO
  903.          global.j.eNeedle_In  = representation
  904.          global.j.eNeedle_Out = D2C(i)
  905.       END
  906.       global.eTotalOfNeedles = j
  907.    END
  908.  
  909.    RETURN
  910.  
  911.  
  912.  
  913.  
  914. /*********************************************************************************
  915.     display errormessage and exit
  916.     ARG(1) ... message
  917.     ARG(2) ... error-level
  918. */
  919. SAY_ERROR: 
  920.     CALL BEEP 550, 250
  921.     CALL say2stderr ARG(1)
  922.     EXIT ARG(2)
  923.  
  924. /*********************************************************************************/
  925. HALT:
  926.    IF global.eFilein <> "" THEN
  927.       IF STREAM(global.eFilein, "STATUS") = "READY" THEN
  928.          CALL STREAM global.eFilein, "C", "CLOSE"          /* close input file */
  929.  
  930.    IF global.eFileout <> "" THEN
  931.       IF STREAM(global.eFileout, "STATUS") = "READY" THEN
  932.          CALL STREAM global.eFileout, "C", "CLOSE"         /* close output file */
  933.    
  934.    IF global.eControl <> "" THEN
  935.       IF STREAM(global.eControl, "STATUS") = "READY" THEN
  936.          CALL STREAM global.eFileout, "C", "CLOSE"         /* close output file */
  937.    
  938.    IF global.eTmp <> "" THEN                    /* output file was temporary */
  939.       IF STREAM(global.eTmp, "STATUS") = "READY" THEN
  940.           CALL SysFileDelete(global.eTmp)           /* delete temporary file */
  941.    ELSE
  942.       IF global.eFileout <> "" THEN
  943.          CALL SysFileDelete(global.eFileout)            /* delete output file */
  944.  
  945.    CALL say2stderr "CTL-C pressed, aborting..."
  946.    EXIT
  947.  
  948.  
  949. /*********************************************************************************/
  950. ERROR:
  951.    myrc = RC                                                                   
  952.    CALL say2stderr 'REXX error' myrc 'in line' SIGL':' ERRORTEXT(myrc)
  953.    CALL say2stderr SUBSTR('     ',1,6-LENGTH(SIGL))(SIGL)' *-*   'SOURCELINE(SIGL)
  954.    EXIT -999
  955.  
  956. /*
  957.    calculate elapsed time
  958. */
  959. CALC_TIME: PROCEDURE
  960.    start = ARG(1)
  961.    end   = ARG(2)
  962.    diff = DATERGF(end, "-S", start)
  963.    
  964.    IF diff >= 1 THEN day = diff % 1 "day(s) "
  965.                 ELSE day = ""
  966.    
  967.    RETURN day || DATERGF(diff, "FR")
  968.  
  969.  
  970.  
  971. /*
  972.    replacement for SAY, which uses stdout:
  973.    =
  974.    use stderr: rather than stdout:, so redirection works
  975. */
  976. SAY2STDERR: PROCEDURE
  977.    CALL LINEOUT "STDERR:", ARG(1)
  978.    RETURN
  979.  
  980. USAGE:
  981.    CALL say2stderr "rxMulch.cmd: program to find/replace characters (strings) in file; allows"
  982.    CALL say2stderr "             definition of hexadecimal or decimal value of single  "
  983.    CALL say2stderr "             characters                                            "
  984.    CALL say2stderr 
  985.    CALL say2stderr "usage:"
  986.    CALL say2stderr
  987.    CALL say2stderr " rxMulch [infile] [outfile] {[-]controlfile | /[-]switch}      "
  988.    CALL say2stderr "   infile:      if missing from STDIN:                         "
  989.    CALL say2stderr 
  990.    CALL say2stderr "   outfile:     if missing, RxMulch will replace infile;       "
  991.    CALL say2stderr "                if no infile than output to STDOUT:            "
  992.    CALL say2stderr 
  993.    CALL say2stderr "   controlfile OR switch MUST be present:"
  994.    CALL say2stderr 
  995.    CALL say2stderr "   controlfile: change for every search-string/replace-string line the  "
  996.    CALL say2stderr "                'search-string' into 'replace-string'; if more than one "
  997.    CALL say2stderr "                search/replace-line is given, subsequent search/replaces"
  998.    CALL say2stderr "                start from the beginning.                      "
  999.    CALL say2stderr "                If the controlfile is preceded with a minus (-) the  "
  1000.    CALL say2stderr "                meaning of the 'search-string' and 'replace-string' is  "
  1001.    CALL say2stderr "                swapped.                                       "
  1002.    CALL say2stderr "                If a line is empty or if there is a semi-colon (;) at the very"
  1003.    CALL say2stderr "                first column, the line is treated as a comment."
  1004.    CALL say2stderr "                                                               "
  1005.    CALL say2stderr "                                                               "
  1006.    CALL say2stderr "   switch:      If the switch is preceded with a minus (-) the meaning of  "
  1007.    CALL say2stderr "                the 'search-string' and 'replace-string' is swapped. "
  1008.    CALL say2stderr 
  1009.    CALL say2stderr "                'C'search-string/replace-string                "
  1010.    CALL say2stderr "                ... Change all occurrences of 'search-string' to  "
  1011.    CALL say2stderr "                    'replace-string'.                          "
  1012.    CALL say2stderr 
  1013.    CALL say2stderr "                '[L[1|2|3|4|5]][H[1|2]]'                       "
  1014.    CALL say2stderr "                ...  change low-/high-characters to any of the following"
  1015.    CALL say2stderr "                     representations:                          "
  1016.    CALL say2stderr 
  1017.    CALL say2stderr "                     L: change all low-char values c2d(0-32)   "
  1018.    CALL say2stderr "                         L .... defaults to L1                 "
  1019.    CALL say2stderr "                         L1 ... char(0-32) to decimal          "
  1020.    CALL say2stderr "                         L2 ... char(0-32) to hexadecimal      "
  1021.    CALL say2stderr "                         L3 ... char(0-32) to control-sequence "
  1022.    CALL say2stderr "                         L4 ... char(0-32) to abbreviated comm-characters  "
  1023.    CALL say2stderr "                         L5 ... char(0-32) to all representations above "
  1024.    CALL say2stderr 
  1025.    CALL say2stderr "                     H: change all high-char values c2d(128-255)  "
  1026.    CALL say2stderr "                         H  ... defaults to H1                 "
  1027.    CALL say2stderr "                         H1 ... char(128-255) to decimal       "
  1028.    CALL say2stderr "                         H2 ... char(128-255) to hexadecimal   "
  1029.    CALL say2stderr 
  1030.    CALL say2stderr "                     The appropriate search-string/replace-string pairs are"
  1031.    CALL say2stderr "                     generated automatically.                  "
  1032.    CALL say2stderr 
  1033.    CALL say2stderr "                'F'search-string/replace-string                "
  1034.    CALL say2stderr "                ... count the number of occurrences of 'search-string'  "
  1035.    CALL say2stderr 
  1036.    CALL say2stderr "   search-string/replace-string:                               "
  1037.    CALL say2stderr "               (delimiter)search-values(delimiter)replace-values(delimiter)"
  1038.    CALL say2stderr 
  1039.    CALL say2stderr "         delimiter:                                            "
  1040.    CALL say2stderr "                very first character in search-string/replace-string "
  1041.    CALL say2stderr 
  1042.    CALL say2stderr "         search-values                                         "
  1043.    CALL say2stderr "         replace-values:                                       "
  1044.    CALL say2stderr "                any ASCII-string intermixed with the following escape-codes"
  1045.    CALL say2stderr 
  1046.    CALL say2stderr "                escape-codes:                                  "
  1047.    CALL say2stderr "                    @C    ... CR                               "
  1048.    CALL say2stderr "                    @L    ... LF                               "
  1049.    CALL say2stderr "                    @T    ... TAB                              "
  1050.    CALL say2stderr "                    @E    ... ESC                              "
  1051.    CALL say2stderr "                    @Z    ... CTL-Z                            "
  1052.    CALL say2stderr "                    @@    ... @ (escape for @)                 "
  1053.    CALL say2stderr "                    @Xnn                                       "
  1054.    CALL say2stderr "                    @Hnn  ... char with the hexadecimal of value 'nn'"
  1055.    CALL say2stderr "                    @Dnnn ... char with the decimal value 'nnn'"
  1056.    CALL say2stderr 
  1057.    CALL say2stderr "RxMulch can be called as a function from another REXX-program, e.g."
  1058.    CALL say2stderr 
  1059.    CALL say2stderr '   some_variable = RxMulch(REXXstring, "[/][-]switch")'
  1060.    CALL say2stderr 
  1061.    CALL say2stderr "examples:                                                          "
  1062.    CALL say2stderr 
  1063.    CALL say2stderr "    rxMulch infile outfile controlfile                            "
  1064.    CALL say2stderr "        ... change 'infile' according to 'controlfile', place results into "
  1065.    CALL say2stderr "            'outfile'"
  1066.    CALL say2stderr 
  1067.    CALL say2stderr "    rxMulch infile controlfile                                    "
  1068.    CALL say2stderr "        ... change 'infile' according to 'controlfile', place results into "
  1069.    CALL say2stderr "            'infile' (i.e. replace 'infile' itself)               "
  1070.    CALL say2stderr 
  1071.    CALL say2stderr "    rxMulch < some_in_file > some_out_file controlfile            "
  1072.    CALL say2stderr "        ... change 'some_in_file' according to 'controlfile', place results"
  1073.    CALL say2stderr "            into 'some_out_file'; 'some_in_file' and 'some_out_file' are"
  1074.    CALL say2stderr "            redirected ('<' and '>'). rxMulch therefore can be used in pipes"
  1075.    CALL say2stderr "            too.                                                  "
  1076.    CALL say2stderr 
  1077.    CALL say2stderr "    rxMulch infile outfile1 /C.Microsoft Excel.Lotus 1-2-3.       "
  1078.    CALL say2stderr "        ... change 'infile' according to commandline switch (replace all"
  1079.    CALL say2stderr "            occurrences of 'Microsoft Excel' with 'Lotus 1-2-3'), place "
  1080.    CALL say2stderr "            results into 'outfile1'                               "
  1081.    CALL say2stderr 
  1082.    CALL say2stderr "    rxMulch outfile1 outfile2 /-C.Microsoft Excel.Lotus 1-2-3.    "
  1083.    CALL say2stderr "        ... change 'outfile1' according to commandline switch (replace all "
  1084.    CALL say2stderr "            occurrences of 'Lotus 1-2-3' with 'Microsoft Excel', note the  "
  1085.    CALL say2stderr "            minus (-) right before the switch-character), place results into  "
  1086.    CALL say2stderr "            'outfile2'; could be also expressed as:               "
  1087.    CALL say2stderr "                  rxMulch outfile1 outfile2 /C.Lotus 1-2-3.Microsoft Excel."
  1088.    CALL say2stderr 
  1089.    CALL say2stderr "    rxMulch infile /C.;.@c@l.                                     "
  1090.    CALL say2stderr "        ... change 'infile' according to commandline switch (replace "
  1091.    CALL say2stderr "            semicolons (;) with a carriage-return/linefeed), replace "
  1092.    CALL say2stderr "            'infile'; could be also expressed as:                 "
  1093.    CALL say2stderr "                  rxMulch infile /C.;.@xd@xa.                     "
  1094.    CALL say2stderr "                  rxMulch infile /C.;.@x0d@x0a.                   "
  1095.    CALL say2stderr "                  rxMulch infile /C.;.@d13@d10.                   "
  1096.    CALL say2stderr 
  1097.    CALL say2stderr "    rxMulch infile /C.@c@l@c@l.@c@l.                              "
  1098.    CALL say2stderr "        ... change 'infile' according to commandline switch (replace "
  1099.    CALL say2stderr "            consecutive carriage-return/linefeeds with one        "
  1100.    CALL say2stderr "            carriage-return/linefeed, i.e. remove one empty line), replace "
  1101.    CALL say2stderr "            'infile'; could be also expressed as:                 "
  1102.    CALL say2stderr "                  rxMulch infile /C.@xd@xa@xd@xa.@xd@xa.          "
  1103.    CALL say2stderr "                  rxMulch infile /C!@d13@d10@d13@d10!@d13@d10!    "
  1104.    CALL say2stderr "                  rxMulch infile /C/@d13@d10@d13@d10/@c@l/        "
  1105.    CALL say2stderr 
  1106.    CALL say2stderr "    rxMulch infile /-C.@c@l@c@l.@c@l.                             "
  1107.    CALL say2stderr "        ... change 'infile' according to commandline switch (replace a  "
  1108.    CALL say2stderr "            carriage-return/linefeed with two consecutive         "
  1109.    CALL say2stderr "            carriage-return/linefeeds, i.e. insert an empty line after each"
  1110.    CALL say2stderr "            line), replace 'infile'; could be also expressed as:  "
  1111.    CALL say2stderr "                  rxMulch infile /C,@c@l,@c@l@c@l,                "
  1112.    CALL say2stderr "                  rxMulch infile /C=@x0d@x0a=@x0d@x0a@x0dx@0a=    "
  1113.    CALL say2stderr "                  rxMulch infile /C=@d13@d10=@d13@d10@x0dx@0a=    "
  1114.    CALL say2stderr 
  1115.    CALL say2stderr "    rxMulch infile /C=@x00@x00@x00@x00=@x01@x01@x01@x01=          "
  1116.    CALL say2stderr "        ... change 'infile' according to commandline switch (replace all"
  1117.    CALL say2stderr "            hexadecimal strings of 0x00000000 with 0x01010101), replace "
  1118.    CALL say2stderr "            'infile'; could be also expressed as:                 "
  1119.    CALL say2stderr "                  rxMulch infile /C=@x0@x0@x0@x0=@x1@x1@x1@x1=    "
  1120.    CALL say2stderr "                  rxMulch infile /C/@d0@d0@d0@d0/@d1@d1@d1@d1/    "
  1121.    CALL say2stderr 
  1122.    CALL say2stderr "    rxMulch infile /F.OS/2.                                       "
  1123.    CALL say2stderr "        ... count occurrences of string 'OS/2' in 'infile'          "
  1124.    CALL say2stderr 
  1125.    CALL say2stderr "    rxMulch infile /F.@c@l@c@l.                                   "
  1126.    CALL say2stderr "        ... count number of lines in 'infile', which are immediately"
  1127.    CALL say2stderr "            followed by a blank line  "
  1128.    CALL say2stderr 
  1129.    CALL say2stderr 
  1130.    CALL say2stderr "examples for calling RxMulch from a REXX-procedure:"
  1131.    CALL say2stderr 
  1132.    CALL say2stderr "    string1 = 'this is nice'"
  1133.    CALL say2stderr "    string2 = RxMulch(string1, '/c.this.that.')  /* change 'this' to 'that' */"
  1134.    CALL say2stderr "        ... string2 = 'that is nice'"
  1135.    CALL say2stderr "    string2 = RxMulch(string2, '/-c.this.that.') /* change 'that' to 'this' */"
  1136.    CALL say2stderr "        ... string2 = 'this is nice'"
  1137.    CALL say2stderr "    occurrences = RxMulch(string2, 'f.this.')    /* count 'this' in string2 */"
  1138.    CALL say2stderr "        ... occurrences = 1"
  1139.  
  1140.    EXIT
  1141.  
  1142.  
  1143.             
  1144.