home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / cube.zip / CUBE.CMD next >
OS/2 REXX Batch file  |  1993-12-20  |  36KB  |  712 lines

  1. /*
  2.     ╔═══════════════════════════════════════════════════════════════════════╗
  3.     ║                    Config Update/Batch Editing                        ║
  4.     ║                                                                       ║
  5.     ║  Batch update of CONFIG.SYS-like files. CUBE modifies a Target ASCII  ║
  6.     ║  file, given a set of commands in a Procedure file.                   ║
  7.     ║                                                                       ║
  8.     ║  04/06/93: V2.6 - Add 'procedure read from QUEUE' (from Steve Farrell)║
  9.     ║  03/06/93: V2.5 - Add IF/IFNOT to xLINE cmds (generalize N. Marks req)║
  10.     ║                   Correct ADDSTRING BEFORE option  (Neil Marks)       ║
  11.     ║  07/04/93: V2.4 - Corrected ADDBOTTOM/ADDTOP in ADDSTRING (Per Hertz) ║
  12.     ║                   Address cmd + CHECK Option + new exit rtne          ║
  13.     ║  21/01/93: V2.3 - Added user defined string delimiter in CUBE cmds    ║
  14.     ║  21/12/92: V2.2 - Added conditionnal command processing (WHEN)        ║
  15.     ║  26/11/92: V2.1 - RS() for DL, DS (desinstallation case)              ║
  16.     ║                   New LINEID command (strip leading chars)            ║
  17.     ║                   ADDTOP,ADDBOTTOM for AS (W. Pachl requirement)      ║
  18.     ║                   Fix Whereis (Walter Pachl).                         ║
  19.     ║                   Exit with SaveFile return code (Walter Pachl)       ║
  20.     ║  18/11/92: V2.0 - Changes with environment variable substitution (RS) ║
  21.     ║                       (AS, RS, AL & AL now all have same RS() option) ║
  22.     ║                   Logging of all changes made to Target File          ║
  23.     ║                   Adapt/Include some of Walter Pachl's enhancements:  ║
  24.     ║                       Single CUBE command on command line             ║
  25.     ║                       Add'l string substitution at command line level ║
  26.     ║                       PAUSE option (debugging purposes)               ║
  27.     ║  05/11/92: V1.5 - AL with pre substitution                            ║
  28.     ║  03/11/92: V1.4 - AS with substitution ; fix RS recursion.            ║
  29.     ║  02/11/92: V1.3 - Bug fixes & cmds abbrev, thanks to Walter Pachl.    ║
  30.     ║                   Target Backup & lineid no more limited to col 1.    ║
  31.     ║  30/10/92: V1.2 - Added env variable substitution + version #         ║
  32.     ║  31/08/92: V1.1 - Bug fix                                             ║
  33.     ║  21/07/92: V1.0 - Initial revision                                    ║
  34.     ║  Didier LAFON - LAFON at CBEPROFS                                     ║
  35.     ╚═══════════════════════════════════════════════════════════════════════╝ */
  36.  
  37. '@echo off'
  38. version = '2.6'
  39. pf = 0
  40. if left(arg(1),1) = '{' then parse arg '{'PFile'}' TFile Bkup . '(' Opt
  41. else do
  42.   parse arg PFile TFile Bkup . '(' Opt
  43.   pf = 1
  44. end
  45. parse upper source source                          /* who am I ?              */
  46.  
  47. if pf then do
  48.   if PFile = '' then call Exit 0 'no procedure !'    /* no or missing PFile     */
  49.   if Pfile <> 'QUEUE' then if exists(PFile)='' then call Exit 0 PFile 'not found'
  50. end
  51. if TFile = ''  then call Exit 0 'no target !'      /* no or missing Tfile     */
  52. if exists(TFile)='' then call Exit 0 TFile 'not found'
  53.  
  54. pause=(wordpos('PAUSE',translate(Opt))>0)          /* Pause mode ?            */
  55. chkmd=(wordpos('CHECK',translate(Opt))>0)          /* Check mode ?            */
  56. ap = wordpos('MAKE',translate(Opt))                /* make  specified ?       */
  57. if ap>0 then Make=translate(word(Opt,ap+1))        /* ...when                 */
  58.         else Make = '*'                            /* default make = all      */
  59. MWhen='*'                                          /* Default when = all      */
  60. dlm = wordpos('DLM',translate(Opt))                /* New delimiter specified?*/
  61. if dlm>0 then _d_=left(word(Opt,dlm+1),1)          /*   yes use it            */
  62.          else _d_ = '"'                            /* else use default        */
  63. OnErr = 'STOP'                                     /* Default OnError setting */
  64. LStrip= ''                                         /* No lineid strip         */
  65. CaseM = 'I'                                        /* String compare default  */
  66. call get_cmdrs(Opt)
  67. NumberOfChanges = 0
  68.  
  69. say arg
  70. say 'CUBE' version 'applying' PFile 'to' TFile 'on' date() time()
  71. if Bkup <> "" then do
  72.    address cmd 'copy' Tfile Bkup '1>nul 2>nul'
  73.    if rc = 0 then say Tfile 'backup is:' Bkup
  74. end
  75.  
  76. Proc. = ''
  77. if pf then do
  78.    If PFile = 'QUEUE' then Do
  79.      i = 1
  80.      Do Queued()
  81.        Parse Pull procline
  82.        proc.i = proc.i || upkw(procline)
  83.        if right(Proc.i,1) = ','                       /*   continuation char ?   */
  84.          then proc.i=left(proc.i,length(proc.i)-1)' ' /*     yes: blank it out   */
  85.          else  i = i + 1                              /*     no: new Proc line   */
  86.      end
  87.      Proc.0 = i-1                                     /* Proc.0 = # of lines     */
  88.      if Proc.0 <= 0 then call Exit 0 PFile 'empty'
  89.   end
  90.   Else do
  91.      i = 1 ;                                          /* current Proc line: null */
  92.      do while lines(PFile)                            /* for all PFile's lines   */
  93.        Proc.i = Proc.i || upkw(linein(PFile))         /*   concat to Proc line   */
  94.        if right(Proc.i,1) = ','                       /*   continuation char ?   */
  95.          then proc.i=left(proc.i,length(proc.i)-1)' ' /*     yes: blank it out   */
  96.          else  i = i + 1                              /*     no: new Proc line   */
  97.      end
  98.      Proc.0 = i-1                                     /* Proc.0 = # of lines     */
  99.      call close PFile
  100.      if Proc.0 <= 0 then call Exit 0 PFile 'empty'
  101.   End
  102. end
  103. else do
  104.   Proc.0 = 1
  105.   Proc.1 = upkw(Pfile)
  106. end
  107.  
  108. i = 0
  109. do while lines(TFile)                              /* for all TFile's lines   */
  110.   i = i + 1                                        /*   get line in           */
  111.   Target.i = linein(TFile)                         /*   Target. stem          */
  112. end
  113. Target.0 = i                                       /* Target.0 = # of lines   */
  114. call close Tfile
  115.  
  116. /*  ╔═══════════════════════════════════════════════════════════════════════╗
  117.     ║The real thing: go thru procedure file, interpret/execute its commands ║
  118.     ║sequentially.                                                          ║
  119.     ╚═══════════════════════════════════════════════════════════════════════╝ */
  120.  
  121. p = 0                                              /* Proc lines index        */
  122. do while p <= Proc.0                               /* for all PFile's lines   */
  123.   p = p + 1                                        /*   index next line       */
  124.   if Proc.p = '' then iterate                      /*   ignore null lines     */
  125.   parse var Proc.p Verb Parms                      /*   Isolate command verb  */
  126.   say ''
  127.   say '>>>' Proc.p
  128.   Verb = translate(Verb)
  129.   Select                                           /*   Process verb          */
  130.     When left(Verb,1) = '*'   then iterate
  131.     When left(Verb,2) = '--'  then iterate
  132.                /* commands that always get executed  */
  133.     When Verb = 'WHEN'        then call APPLYWHEN
  134.     When Verb = 'ONERROR'     then call ONERROR
  135.     When Verb = 'CASE'        then call CASE
  136.     When Verb = 'LINEID'      then call SLINEID
  137.     When wordpos(Make,MWhen)=0 then iterate
  138.                /* commands executed when WHEN/MAKE match */
  139.     When Verb = 'REPLINE'     | verb = 'RL'  then call REPLINE
  140.     When Verb = 'ADDLINE'     | verb = 'AL'  then call ADDLINE
  141.     When Verb = 'ADDSTRING'   | verb = 'AS'  then call ADDSTRING
  142.     When Verb = 'DELSTRING'   | verb = 'DS'  then call DELSTRING
  143.     When Verb = 'REPSTRING'   | verb = 'RS'  then call REPSTRING
  144.     When Verb = 'COMMENTLINE' | verb = 'CL'  then call COMMENTL
  145.     When Verb = 'DELLINE'     | verb = 'DL'  then call DELLINE
  146.     Otherwise rc=OnErrorDo(p,"Don't know what to do")
  147.   end
  148.   if pause then Pull .
  149. end
  150. if chkmd = 0 then call exit 1 source 'ended.'      /* It's OVER !! and OK !!  */
  151. if chkmd = 1 then call exit 2 source 'ended.'      /*                         */
  152.  
  153. /* ╒═══════════════════════════════════════════════════════════════════════╕
  154.    │  Error report and action (based on Onerr setting, from ONERROR cmd)   ╞
  155.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  156. OnErrorDo:
  157.   parse arg line,msg
  158.   say PFile', line' line':' msg
  159.   if OnErr = 'STOP' then call Exit 0 source 'stopped.'
  160.                     else return 0
  161.  
  162. /* ╒═══════════════════════════════════════════════════════════════════════╕
  163.    │  Searches All or First or Last lines in Target starting with string   ╞
  164.    │  Returns the line number(s) found.                                    ╞
  165.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  166. Whereis: procedure expose Target. CaseM LStrip
  167.  parse arg string,direction,mode
  168.  if wordpos(direction,'F A')>0 then do; de=1; a=Target.0; par=1; end
  169.                                else do; de=Target.0; a=1; par=-1; end
  170.  stringlength=length(string); ret = ''
  171.  do i = de to a by par
  172.    If CaseM = 'S' then do; T = Target.i ; S = string ; end
  173.       else do; T = translate(Target.i) ; S = translate(string) ; end
  174.    If length(LStrip) = 1 then T = strip(T,'L',Lstrip)
  175.    if mode=1 then do
  176.      if left(T,stringlength)=S then do
  177.        ret = ret i
  178.        if direction \= 'A' then leave
  179.      end
  180.    end
  181.    else do
  182.      if pos(S,T) > 0 then do
  183.        ret = ret i
  184.        if direction \= 'A' then leave
  185.      end
  186.    end
  187.  end
  188.  return ret
  189.  
  190. /* ╒═══════════════════════════════════════════════════════════════════════╕
  191.    │  Update Target file from Target. stem. Remove '       ' lines ╞
  192.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  193. SaveFile:
  194.  address cmd 'erase' TFile
  195.  src = rc
  196.  if src = 0 then do
  197.    do i = 1 to Target.0
  198.      if Target.i = '       ' then iterate
  199.      rc=lineout(TFile,Target.i)
  200.    end
  201.    call close Tfile
  202.  end
  203.  return src
  204.  
  205. /* ╒═══════════════════════════════════════════════════════════════════════╕
  206.    │  Insert a line in Target file (stem) after line number i.             ╞
  207.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  208. Insert: procedure  expose Target. NumberOfChanges
  209.  parse arg i string
  210.  if i = Target.0 then k = Target.0 + 1
  211.  else do
  212.    do j = Target.0 to i+1 by -1
  213.      k = j + 1
  214.      Target.k = Target.j
  215.    end
  216.    k = i + 1
  217.  end
  218.  Target.k = string
  219.  Target.0 = Target.0 + 1
  220.  say 'Inserted line' k ': "'Target.k'"'
  221.  NumberOfChanges = NumberOfChanges + 1
  222.  return
  223.  
  224. /* ╒═══════════════════════════════════════════════════════════════════════╕
  225.    │  returns a procedure command line  with all strings uppercased, except╞
  226.    │  doubled-quoted strings.                                              ╞
  227.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  228. upkw: procedure expose vn. vv. _d_
  229.   parse arg sentence
  230.   sentence = strip(sentence)
  231.   phrase = ""
  232.   do forever
  233.     if sentence = '' then leave
  234.     if left(word(sentence,1),1) = _d_ then do
  235.        parse var sentence (_d_) y (_d_) sentence
  236.        phrase = phrase _d_ || y || _d_
  237.     end
  238.     else do
  239.        parse var sentence y sentence
  240.        phrase = phrase translate(y)
  241.     end
  242.   end
  243.   return phrase
  244.  
  245. /* ╒═══════════════════════════════════════════════════════════════════════╕
  246.    │  apply command line-specified substitutions within a string           ╞
  247.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  248. Cmdrs: procedure expose vn. vv.
  249.  parse arg y
  250.  do i = 1 to vn.0
  251.    out = ''
  252.    do forever
  253.      if pos(vn.i,y) > 0 then do
  254.         parse var y x (vn.i) y
  255.         out = out || x || vv.i
  256.      end
  257.      else leave
  258.    end
  259.  y = out || y
  260.  end
  261.  return y
  262.  
  263. /* ╒═══════════════════════════════════════════════════════════════════════╕
  264.    │  apply env. variables substitutions to STRING if req. in OPTION.      ╞
  265.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  266. Envrs: procedure
  267.   Parse arg String,Option
  268.   out = ''
  269.   parse var Option x 'RS('c')' .
  270.   if length(c) = 1 then do
  271.      do forever
  272.         parse var String x (c) name (c) String
  273.         if name = "" then leave
  274.         out = out || x || value(name,,'OS2ENVIRONMENT')
  275.      end
  276.      String = out || x
  277.   end
  278.   return String
  279.  
  280. /* ╒═══════════════════════════════════════════════════════════════════════╕
  281.    │ All that must be done to quit and more: say msg, save Target file if  ╞
  282.    │ necessary (type=1).                                                   ╞
  283.    ╘═══════════════════════════════════════════════════════════════════════╛  */
  284. Exit:
  285.  parse arg type msg
  286.  src=0
  287.  Select
  288.    When type=1 then do
  289.         src = SaveFile()
  290.         if src <> 0 then msg = msg 'Error writing' TFile
  291.                     else msg = msg NumberOfChanges 'changes applied'
  292.         end
  293.    When type=2 then do
  294.         src = NumberOfChanges
  295.         msg = msg NumberOfChanges 'changes applied'
  296.         end
  297.    Otherwise nop
  298.  End
  299.  say msg
  300.  Exit src
  301.  
  302. /*      ┌─────────────────────────────────────────────────────────┐
  303.         │ ONERROR [CONTINUE] [STOP] : what to do on syntax errors │
  304.         └─────────────────────────────────────────────────────────┘           */
  305. ONERROR:
  306.  if wordpos(translate(Parms),'CONTINUE STOP')>0 then OnErr = translate(Parms)
  307.     else rc=OnErrorDo(p,'On Error what ?')
  308.  return
  309.  
  310. /*      ┌─────────────────────────────────────────────────────────┐
  311.         │ WHEN     ... wordlist of when codes ...                 │
  312.         └─────────────────────────────────────────────────────────┘           */
  313. APPLYWHEN:
  314.  MWhen = strip(translate(Parms),'B')
  315.  if MWhen = '*' then MWhen = Make
  316.  return
  317.  
  318. /*      ┌─────────────────────────────────────────────────────────┐
  319.         │ CASE [SENSITIVE] [IGNORE] : string compare mode         │
  320.         └─────────────────────────────────────────────────────────┘           */
  321. CASE:
  322.  if wordpos(translate(Parms),'SENSITIVE IGNORE')>0 then CaseM = translate(left(Parms,1))
  323.     else rc=OnErrorDo(p,'Case what ?')
  324.  return
  325.  
  326. /*      ┌─────────────────────────────────────────────────────────┐
  327.         │ LINEID [NOSTRIP] [STRIP "x"]                            │
  328.         └─────────────────────────────────────────────────────────┘           */
  329. SLINEID:
  330.  Select
  331.    When word(translate(Parms),1) = 'NOSTRIP' then Lstrip = ""
  332.    When word(translate(Parms),1) = 'STRIP' then do
  333.       Parse var Parms 'STRIP' (_d_) ww (_d_)
  334.       if length(ww) <> 1 then rc=OnErrorDo(p,'Strip leading what ?')
  335.                          else Lstrip = ww
  336.       end
  337.    Otherwise rc=OnErrorDo(p,'Lineid what ?')
  338.  end
  339.  return
  340.  
  341. /*      ┌─────────────────────────────────────────────────────────┐
  342.         │ REPLINE lineid WITH replacement [( options]             │
  343.         └─────────────────────────────────────────────────────────┘           */
  344. REPLINE:
  345.   parse var Parms (_d_) Lineid (_d_)  'WITH' (_d_) With (_d_)  '(' Opt
  346.   if Lineid = '' then do                           /* No line identifier      */
  347.     rc=OnErrorDo(p,'Replace what line ?')
  348.     return
  349.   end
  350.   if With = '' then do                             /* No replacement string   */
  351.     rc=OnErrorDo(p,'Replace line with ?')          /*   process error         */
  352.     return                                         /*   ignore command        */
  353.   end
  354.   With = Cmdrs(With)                               /* cmd substitution        */
  355.   With = Envrs(With,Opt)                           /* env substitution if req */
  356.   dir = Searchdir(opt)                             /* What target lines ?     */
  357.   mod = Lidmod(opt)                                /* floating line id ?      */
  358.   select                                           /* What if no target lines?*/
  359.     when wordpos('ADDTOP',Opt)>0 then after=0      /* add after line 0        */
  360.     when wordpos('ADDBOTTOM',Opt)>0 then after=Target.0 /* add after last line     */
  361.     when wordpos('DONTADD',Opt)>0 then after=-1    /* don't add               */
  362.     otherwise after=-1                             /* don't add is the default*/
  363.   end
  364.   if ififnot() then return                         /* Process only when       */
  365.   where = Whereis(Lineid,dir,mod)                  /* Get target lines numbers*/
  366.   if where \= '' then do                           /* if target(s) found      */
  367.     do until where = ''                            /*   process all targets   */
  368.       parse var where w where                      /*     1 at a time         */
  369.       was = Target.w                               /* save old value for log  */
  370.       Target.w = With                              /*     target = replacmnt. */
  371.       call logrep w,was,Target.w                   /*     log action          */
  372.       if dir \= 'A' then leave                     /*     quit if not ALL     */
  373.     end
  374.   end
  375.   else if after>-1 then call insert after With     /* if no target, try add   */
  376.   return
  377.  
  378. /*      ┌───────────────────────────────────────────────────────────┐
  379.         │ ADDLINE     line  [( options]                             │
  380.         └───────────────────────────────────────────────────────────┘         */
  381. ADDLINE:
  382.   parse var Parms (_d_) Line (_d_)  '(' Opt
  383.   if Line = '' then do                             /* No line identifier      */
  384.     rc=OnErrorDo(p,'Add what line ?')              /*    process error        */
  385.     return                                         /*    ignore command       */
  386.   end
  387.   select                                           /* When to add ?           */
  388.     when wordpos('IFNEW',Opt)>0 then always=0      /*   if not already there  */
  389.     when wordpos('ALWAYS',Opt)>0 then always=1     /*   even if already there */
  390.     otherwise always=0                             /*   IFNEW is the default  */
  391.   end
  392.   Line = Cmdrs(Line)                               /* cmd substitution        */
  393.   Line = envrs(Line,Opt)                           /* env substitution if req */
  394.   mod = Lidmod(opt)                                /* floating line id ?      */
  395.   exist = Whereis(Line,'F',mod)                    /* If this line exists and */
  396.   if exist \= '' & always = 0 then return          /* IFNEW , don't add !     */
  397.   if ififnot() then return
  398.  
  399.   select                                           /* Where to add ?          */
  400.     when wordpos('AFTER',Opt)>0 then do;           /* 1) After a given line   */
  401.       parse var Opt 'AFTER' (_d_) astr (_d_)  .    /*    line identifier ?    */
  402.       if astr = '' then after = Target.0           /*    no id = add bottom   */
  403.       else after = Whereis(astr,'F',mod)           /*    else get # of 1st    */
  404.       parse var after after .                      /*    line with this id.   */
  405.       if after='' then do                          /*    no match found       */
  406.         if wordpos('ONLY',Opt)>0 then after=-1     /*      if ONLY, don't add */
  407.            else after=Target.0                     /*      else add bottom    */
  408.       end
  409.     end
  410.     when wordpos('BEFORE',Opt)>0 then do;          /* 2) Before a given line  */
  411.       parse var Opt 'BEFORE' (_d_) bstr (_d_)  .   /*    line identifier ?    */
  412.       if bstr = '' then after = 0                  /*    no id = add top      */
  413.       else after = Whereis(bstr,'F',mod)           /*    else get # of 1st    */
  414.       parse var after after .                      /*    line with this id.   */
  415.       if after ='' then do                         /*    no match found       */
  416.         if wordpos('ONLY',Opt)>0 then after=-1     /*      if ONLY don't add  */
  417.            else after=0                            /*      else add top       */
  418.       end
  419.       else after=max(0,after-1)                    /*    match found          */
  420.     end
  421.     otherwise after=Target.0                       /* 3) default = add bottom */
  422.   end
  423.   if after \= -1 then call insert after Line       /* add the line            */
  424.   return
  425.  
  426. /*      ┌───────────────────────────────────────────────────────────┐
  427.         │ ADDSTRING string IN lineid [(Options]                     │
  428.         └───────────────────────────────────────────────────────────┘         */
  429. ADDSTRING:
  430.   parse var Parms (_d_) With (_d_)  'IN' (_d_) Lineid (_d_)   '(' Opt
  431.   if Lineid = '' then do                           /* No line identifier      */
  432.     rc=OnErrorDo(p,'Add string where ?')           /*    process error        */
  433.     return                                         /*    ignore command       */
  434.   end
  435.   if With = '' then do                             /* No string to add        */
  436.     rc=OnErrorDo(p,'Add what string ?')            /*    process error        */
  437.     return                                         /*    ignore command       */
  438.   end
  439.   With = Cmdrs(With)                               /* cmd substitution        */
  440.   With = Envrs(With,Opt)                           /* env substitution if req */
  441.   dir=Searchdir(opt)                               /* Which target line ?     */
  442.   mod=Lidmod(opt)                                  /* floating line id ?      */
  443.   where = Whereis(Lineid,dir,mod)                  /* Select target(s)        */
  444.   if where \= '' then do                           /*   if target found       */
  445.     do until where = ''                            /*     process target(s)   */
  446.       parse var where w where                      /*     1 at a time         */
  447.       if CaseM = 'S' then do
  448.          Tar= Target.w; Wi = With; end             /*     string compare mode */
  449.       else do
  450.          Tar = translate(Target.w); Wi=translate(With); end  /*     string compare mode */
  451.       if pos(Wi,Tar) > 0 & ,                       /* String already there &  */
  452.          wordpos('ALWAYS',Opt) = 0 then leave      /* ALWAYS not specified.   */
  453.  
  454.       select                                       /* Where to add ?          */
  455.         when wordpos('AFTER',Opt)>0 then do        /* 1) After a given string */
  456.           astr=''                                  /*    defaulted to null    */
  457.           parse var Opt 'AFTER' (_d_) astr (_d_)  .  /*    what is this string  */
  458.           If CaseM = 'I' then astr=translate(astr)
  459.           was = Target.w                           /*    save for logging     */
  460.           if astr = '' | pos(astr,Tar)=0           /* if no string or no match*/
  461.              then Target.w = Target.w || With      /*    add at end of target */
  462.           else do
  463.              parse var Tar xx (astr) rest          /* insert string after     */
  464.              Target.w = xx || astr || With || rest /*   specified string      */
  465.           end
  466.           call logrep w,was,Target.w               /*   log action            */
  467.         end
  468.         when wordpos('BEFORE',Opt)>0 then do       /* 2) Before a given string*/
  469.           bstr=''                                  /*    defaulted to null    */
  470.           parse var Opt 'BEFORE' (_d_) bstr (_d_)  . /*    what is this string  */
  471.           If CaseM = 'I' then bstr=translate(bstr)
  472.           was = Target.w                           /*    save for logging     */
  473.           if bstr = '' | pos(bstr,Tar)=0           /* if no string or no match*/
  474.           then do                                  /* add at beginning        */
  475.             If CaseM = 'I' then Lid=translate(Lineid)
  476.             Parse var Tar (Lid) rest               /* but after identifier    */
  477.             Target.w = Lineid || With || rest      /*                         */
  478.           end
  479.           else do
  480.              parse var Tar xx (bstr) rest          /* insert string before    */
  481.              Target.w = xx || With || bstr || rest /*   specified string      */
  482.           end
  483.           call logrep w,was,Target.w               /*   log action            */
  484.         end
  485.         otherwise nop
  486.       end
  487.       if dir \= 'A' then leave                     /* leave if not ALL targets*/
  488.     end
  489.   end
  490.   else do                                          /* no target : add line ?  */
  491.     if wordpos('ADDTOP',Opt)>0 then call insert 0 Lineid || With
  492.     if wordpos('ADDBOTTOM',Opt)>0 then call insert Target.0 Lineid || With
  493.   end
  494.   return
  495.  
  496. /*      ┌───────────────────────────────────────────────────────────┐
  497.         │ DELSTRING string [IN lineid] [(Options]                   │
  498.         └───────────────────────────────────────────────────────────┘           */
  499. DELSTRING:
  500.   parse var Parms (_d_) What (_d_)  'IN' (_d_) Lineid (_d_)  '(' Opt
  501.   if Lineid = '' then parse var Parms (_d_) What (_d_)  '(' Opt
  502.   if What = '' then do                             /* No string to del        */
  503.     rc=OnErrorDo(p,'Delete what string ?')         /*    process error        */
  504.     return                                         /*    ignore command       */
  505.   end
  506.   What = Cmdrs(What)                               /* cmd substitution        */
  507.   What = envrs(What,Opt)                           /* env substitution if req */
  508.   dir=Searchdir(opt)                               /* Which target line ?     */
  509.   mod=Lidmod(opt)                                  /* floating line id ?      */
  510.   where = Whereis(Lineid,dir,mod)                  /* Select target(s)        */
  511.   if where \= '' then do                           /*   if target found       */
  512.     do until where = ''                            /*     process target(s)   */
  513.       parse var where w where                      /*     1 at a time         */
  514.       do forever                                   /* for all occurences      */
  515.         if CaseM = 'S' then do
  516.            Tar = Target.w; Wh=What; end            /*     string compare mode */
  517.         else do
  518.            Tar = translate(Target.w); Wh=translate(What); end  /*     string compare mode */
  519.         if pos(Wh,Tar) > 0 then do                 /*     String is there     */
  520.            was = Target.w                          /*     save for logging    */
  521.            parse var Tar xx (Wh) rest              /*     isolate string      */
  522.            Target.w = xx || rest                   /*     delete string       */
  523.            call logrep w,was,Target.w              /*     log action          */
  524.         end
  525.         else leave
  526.       end
  527.       if dir \= 'A' then leave                     /* leave if not ALL targets*/
  528.     end
  529.   end
  530.   return
  531.  
  532. /*      ┌───────────────────────────────────────────────────────────┐
  533.         │ REPSTRING ostring [WITH nstring] [IN lineid] [(Options]   │
  534.         └───────────────────────────────────────────────────────────┘           */
  535. REPSTRING:
  536.   parse var Parms (_d_) Ostr (_d_)  'WITH' (_d_) With (_d_)  'IN' (_d_) Lineid (_d_)  '(' Opt
  537.   if Ostr = '' then do                             /* No old string specif.   */
  538.     rc=OnErrorDo(p,'Replace what string ?')        /*    process error        */
  539.     return                                         /*    ignore command       */
  540.   end
  541.   if With = '' then parse var Parms (_d_) Ostr (_d_)  'IN' (_d_) Lineid (_d_)  '(' Opt
  542.   if Lineid = '' then parse var Parms (_d_) Ostr (_d_)  '(' Opt
  543.   if With = '' then With = Ostr                    /* No rep string specif.   */
  544.   With = Cmdrs(With)                               /* cmd substitution        */
  545.   With = envrs(With,Opt)                           /* env substitution if req.*/
  546.   dir=Searchdir(opt)                               /* Which target line ?     */
  547.   mod=Lidmod(opt)                                  /* floating line id ?      */
  548.   where = Whereis(Lineid,dir,mod)                  /* Select target(s)        */
  549.   do while where \= ''                             /*   if target found       */
  550.       parse var where w where                      /*     1 at a time         */
  551.       was = Target.w
  552.       newtar = ''
  553.       do forever                                   /* for all occurences      */
  554.         if CaseM = 'S' then do
  555.            Tar = Target.w; Os=Ostr; end            /*     string compare mode */
  556.         else do
  557.            Tar = translate(Target.w); Os=translate(Ostr); end  /*     string compare mode */
  558.         if pos(Os,Tar) > 0 then do                 /*     String is there     */
  559.            parse var Tar xx (Os) rest              /*     isolate string      */
  560.            newtar = newtar || xx || With           /*     replace occurrence  */
  561.            Target.w = rest                         /*     next                */
  562.         end
  563.         else leave
  564.       end
  565.       Target.w = newtar || target.w
  566.       call logrep w,was,Target.w                   /* log action              */
  567.       if dir \= 'A' then leave                     /* leave if not ALL targets*/
  568.   end
  569.   return
  570.  
  571. /*      ┌─────────────────────────────────────────────────────────┐
  572.         │ COMMENTLINE lineid WITH type [(options ]                │
  573.         └─────────────────────────────────────────────────────────┘           */
  574. COMMENTL:
  575.   parse var Parms (_d_) Lineid (_d_)  'WITH' (_d_) cmnt (_d_)  '(' Opt
  576.   if Lineid = '' then do                           /* No identifier           */
  577.     rc=OnErrorDo(p,'Comment what ?')               /*    process error        */
  578.     return                                         /*    ignore command       */
  579.   end
  580.   if cmnt = '' then do                             /* No comment string       */
  581.     rc=OnErrorDo(p,'Comment how ?')                /*    process error        */
  582.     return                                         /*    ignore command       */
  583.   end
  584.   dir=Searchdir(opt)                               /* Which target lines ?    */
  585.   mod=Lidmod(opt)                                  /* floating line id ?      */
  586.   if ififnot() then return
  587.   where= whereis(Lineid,dir,mod)                   /* get target lines #s     */
  588.   if where \= '' then do                           /* if match(es) found      */
  589.     do until where = ''                            /*   process target(s)     */
  590.       parse var where w where                      /*   1 at a time           */
  591.       was = Target.w                               /*  save for logging       */
  592.       Target.w = cmnt Target.w                     /*   comment target        */
  593.       call logrep w,was,Target.w                   /*   log action            */
  594.       if dir \= 'A' then leave                     /* leave if not ALL targets*/
  595.     end
  596.   end
  597.   return
  598.  
  599. /*      ┌─────────────────────────────────────────────────────────┐
  600.         │ DELLINE lineid [(options ]                              │
  601.         └─────────────────────────────────────────────────────────┘           */
  602. DELLINE:
  603.   parse var Parms (_d_) Lineid (_d_)   '(' Opt
  604.   if Lineid = '' then do                           /* No identifier           */
  605.     rc=OnErrorDo(p,'Delete what line ?')           /*    process error        */
  606.     return                                         /*    ignore command       */
  607.   end
  608.   Lineid = Cmdrs(Lineid)                           /* cmd substitution        */
  609.   Lineid = envrs(Lineid,Opt)                       /* env substitution if req */
  610.   dir=Searchdir(opt)                               /* Which target line(s) ?  */
  611.   mod=Lidmod(opt)                                  /* floating line id ?      */
  612.   if ififnot() then return
  613.   where= whereis(Lineid,dir,mod)                   /* Get target lin(s) #s    */
  614.   if where \= '' then do                           /* if match(es) found      */
  615.     do until where = ''                            /*    process all targets  */
  616.       parse var where w where                      /*    one at a time        */
  617.       say 'Deleted line' w
  618.       say '  was:' Target.w
  619.       NumberOfChanges = NumberOfChanges + 1
  620.       Target.w = '       '                 /*    mark for delete      */
  621.       if dir \= 'A' then leave                     /* leave if not ALL targets*/
  622.     end
  623.   end
  624.   return
  625.  
  626. /*      ┌─────────────────────────────────────────────────────────┐
  627.         │ SEARCHDIR: Direction for line search in Target File     │
  628.         └─────────────────────────────────────────────────────────┘           */
  629. Searchdir: procedure
  630.   select                                           /* What target lines ?     */
  631.     when wordpos('LAST',arg(1))>0 then dir='L'     /* set reverse search      */
  632.     when wordpos('FIRST',arg(1))>0 then dir='F'    /* set forward search      */
  633.     otherwise dir='A'                              /* default is all lines    */
  634.   end
  635.   return dir
  636.  
  637. /*      ┌─────────────────────────────────────────────────────────┐
  638.         │ LIDMOD: Identify line at 1st col or anywhere in line    │
  639.         └─────────────────────────────────────────────────────────┘           */
  640. Lidmod: procedure
  641.   if wordpos('*ID',arg(1))>0 then return(0)
  642.                              else return(1)
  643.  
  644.  
  645. /*      ┌─────────────────────────────────────────────────────────┐
  646.         │ Stream functions (close & exists)                       │
  647.         └─────────────────────────────────────────────────────────┘           */
  648. close:
  649.   return stream(arg(1),'C','CLOSE')
  650.  
  651. exists:
  652.   return stream(arg(1),'C','QUERY EXISTS')
  653.  
  654. /*      ┌─────────────────────────────────────────────────────────┐
  655.         │ get substitution variables and their values in cmdline. │
  656.         │ ( RS(#aaa#=aaa #bbb#=bbb)                               │
  657.         │Output: vn.i = number and names of subst. variables      │
  658.         │        vv.i = values of these variables                 │
  659.         │Adapted from Walter Pachl's                              │
  660.         └─────────────────────────────────────────────────────────┘           */
  661. get_cmdrs: procedure expose vn. vv.
  662.   parse arg Opt
  663.   vn.0 = 0
  664.   p = pos('RS(',translate(Opt))
  665.   if p > 0 then do
  666.     rs = substr(Opt,p+3)
  667.     Parse var rs rs ')'
  668.     Do i=1 By 1 While rs<>''
  669.       rs=strip(rs,'L')
  670.       Parse Var rs vn '=' vv rs
  671.       if left(vn,1) = right(vn,1) then do
  672.         vn.i = vn
  673.         vv.i = vv
  674.       end
  675.       else call Exit 0 'Invalid substitution variables ('vn vv')'
  676.     End
  677.     vn.0=i-1
  678.   end
  679.   Return
  680.  
  681. /*      ┌─────────────────────────────────────────────────────────┐
  682.         │ Log a change in a target line                           │
  683.         └─────────────────────────────────────────────────────────┘           */
  684. logrep: procedure expose NumberOfChanges
  685. parse arg ln,old,new
  686. if old<>new then do
  687.   say 'Changed line' ln
  688.   say '  old: "'old'"'
  689.   say '  new: "'new'"'
  690.   NumberOfChanges = NumberOfChanges + 1
  691. end
  692.  
  693. return
  694.  
  695. /*      ┌─────────────────────────────────────────────────────────┐
  696.         │ Process IF/IFNOT logic for Lines commands               │
  697.         │    returns 1 if IF/IFNOT condition is false !!          │
  698.         │    returns 0 otherwise                                  │
  699.         └─────────────────────────────────────────────────────────┘           */
  700. ififnot:
  701. if wordpos('IFNOT',Opt)>0 Then Do                /* Process only when       */
  702.   parse var Opt 'IFNOT' (_d_) istr (_d_) .       /* another line doesn't    */
  703.   iexist = Whereis(istr,'F',mod)                 /* exists                  */
  704.   if iexist \= '' then return(1)                 /*                         */
  705. end                                              /*                         */
  706. if wordpos('IF',Opt)>0 Then Do                   /* Process only when       */
  707.   parse var Opt 'IF' (_d_) istr (_d_) .          /* another line exists     */
  708.   iexist = Whereis(istr,'F',mod)                 /*                         */
  709.   if iexist  = '' then return(1)                 /*                         */
  710. end                                              /*                         */
  711. return(0)
  712.