/* EditFile.cmd
 *
 * usage: EditFile queuename, <filename>
 *
 * This program designed to be run as a detached process
 * if it is run with a queuename of nul, it runs in interactive mode
 *
 * Commands
 *   ReadConfigFile
 *   AppendToPath <directory>
 *   PrependToPath <directory>
 *   AppendToLibPath <directory>
 *   PrependToLibPath <directory>
 *   AppendToDPath <directory>
 *   AppendToBookshelf <directory>
 *   AppendToClasspath <directory>
 *   AppendToHelp <directory>
 *   AppendToSomir <som file>
 *   AppendToNLSPath <template>
 *   InsertInLIB <directory>
 *   InsertInINCLUDE <directory>
 *   RemoveFromPath <directory>
 *   RemoveFromLibPath <directory>
 *   RemoveFromDPath <directory>
 *   RemoveFromHelp <directory>
 *   RemoveFromBookshelf <directory>
 *   RemoveFromSomir <som file>
 *   RemoveFromNLSPath <template>
 *   RemoveFromSetLIB <directory>
 *   RemoveFromINCLUDE <directory>
 *   AppendLine <line>
 *   InsertLine <token> <line>
 *   ReplaceLine <token> <line>
 *   InsertSection <line>
 *   AppendSection <line>
 *   RemoveLine <token>
 *   RemarkLine <token>
 *   AddLine <line number>, <line>
 *   DeleteLine <line number>
 *   Set <var>=<value>
 *   WriteConfigFile
 *   exit
 *
 * Many of these commands only make sense if the file is config.sys
 *
 * Copyright (C) 2018 Blonde Guy
 * All Rights Reserved
 */

debugEditFile = 0 
parse arg queueName fileName

if queueName = '/?' | queueName = '-?' | fileName = '' then do
   call LogCdi "Usage: EditFile <queueName> <fileName>"
   call LogCdi "if queueName is 'NUL' then run in interactive mode"
   return
end

if translate(queueName) = 'NUL' then do
   queueName = ''
end

if queueName \= '' then do
   call rxQueue 'Set', queueName
end

Config.0 = 0

do forever
   parse pull command option
   if command = 'ReadConfigFile' | command = 'WriteConfigFile' then do
      option = filename
   end
   if debugEditFile = 1 then call LogCdi "EditFile(" || command || ", " || option || ")"
   select
      when command = 'ReadConfigFile' then do
         call ReadConfigFile fileName
      end
      when command = 'AppendToPath' then do
         call AppendToPath option
      end
      when command = 'PrependToPath' then do
         call PrependToPath option
      end
      when command = 'AppendToLibPath' then do
         call AppendToLibPath option
      end
      when command = 'PrependToLibPath' then do
         call PrependToLibPath option
      end
      when command = 'AppendToDPath' then do
         call AppendToDPath option
      end
      when command = 'AppendToBookshelf' then do
         call AppendToBookshelf option
      end
      when command = 'AppendToClasspath' then do
         call AppendToClasspath option
      end
      when command = 'AppendToHelp' then do
         call AppendToHelp option
      end
      when command = 'AppendToSomir' then do
         call AppendToSomir option
      end
      when command = 'AppendToNLSPath' then do
         call AppendToNLSPath option
      end
      when command = 'InsertInLIB' then do
         call InsertInLIB option
      end
      when command = 'InsertInINCLUDE' then do
         call InsertInINCLUDE option
      end
      when command = 'RemoveFromPath' then do
         call RemoveFromPath option
      end
      when command = 'RemoveFromLibPath' then do
         call RemoveFromLibPath option
      end
      when command = 'RemoveFromDPath' then do
         call RemoveFromDPath option
      end
      when command = 'RemoveFromHelp' then do
         call RemoveFromHelp option
      end
      when command = 'RemoveFromBookshelf' then do
         call RemoveFromBookshelf option
      end
      when command = 'RemoveFromSomir' then do
         call RemoveFromSomir option
      end
      when command = 'RemoveFromNLSPath' then do
         call RemoveFromNLSPath option
      end
      when command = 'RemoveFromSetLIB' then do
         call RemoveFromSetLIB option
      end
      when command = 'RemoveFromINCLUDE' then do
         call RemoveFromINCLUDE option
      end
      when command = 'AppendLine' then do
         call AppendLine option
      end
      when command = 'InsertLine' then do
         call InsertLine option
      end
      when command = 'ReplaceLine' then do
         call ReplaceLine option
      end
      when command = 'InsertSection' then do
         call InsertSection option
      end
      when command = 'AppendSection' then do
         call AppendSection option
      end
      when command = 'RemoveLine' then do
         call RemoveLine option
      end
      when command = 'RemarkLine' then do
         call RemarkLine option
      end
      when command = 'AddLine' then do
         call AddLine option
      end
      when command = 'DeleteLine' then do
         call DeleteLine option
      end
      when command = 'Set' then do
         call Set option
      end
      when command = 'WriteConfigFile' then do
         call WriteConfigFile fileName
      end
      when command = 'exit' then do
         if queueName \= '' then do
            call RxQueue 'Delete', queueName
         end
         return
      end
      otherwise do
         if queueName = '' then do
            call LogCdi "Unknown command" command
         end
      end
   end
end

/* never go here */

return

/* read the config.sys file */
ReadConfigFile:

parse arg cf
i = 0
do while lines(cf)
   i = i + 1
   Config.i = linein(cf)
end

Config.0 = i
call lineout(cf)

if debugEditFile = 1 then call LogCdi "EditFile: read" Config.0 "lines from" cf

return

/* write the config.sys file */
WriteConfigFile:

parse arg cf

if Config.0 = 0 then do
   call LogCdi "WriteConfigFile called, but file has 0 lines."
   return
end

BackupFile = cf
if lastpos('.', cf) > 0 then do
   BackupFile = substr(cf,1,lastpos('.',cf)-1)
end
BackupFile = BackupFile || '.Cdi' 
address cmd '@copy' cf BackupFile '> nul'
call SysFileDelete cf

do i=1 to Config.0
   call lineout cf, Config.i
end

call stream cf, 'c', 'close'

if debugEditFile = 1 then call LogCdi "EditFile: wrote" Config.0 "lines to" cf

return

/* append to path */
AppendToPath:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,9)) = "SET PATH=" then do
      call DisasseblePath config.i
      notinpath = 1
      do h=1 to Dir.0
         if translate(Dir.h) = translate(newDir) then do
            notinpath = 0
         end
      end
      if notinpath then do
         j = Dir.0
         j = j + 1
         Dir.j = newDir
         Dir.0 = j
         config.i = AssemblePath("SET PATH=")
         if debugEditFile = 1 then call LogCdi "Done."
      end
      else do
         if debugEditFile = 1 then call LogCdi newDir "is already on the PATH."
      end
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET PATH= not found in file."
return

/* prepend to path */
PrependToPath:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,9)) = "SET PATH=" then do
      call DisasseblePath config.i
      notinpath = 1
      do h=1 to Dir.0
         if translate(Dir.h) = translate(newDir) then do
            notinpath = 0
         end
      end
      if notinpath then do
         do h=Dir.0 to 1 by -1
            hp = h + 1
            Dir.hp = Dir.h
         end
         Dir.1 = newDir
         Dir.0 = Dir.0 + 1
         config.i = AssemblePath("SET PATH=")
         if debugEditFile = 1 then call LogCdi "Done."
      end
      else do
         if debugEditFile = 1 then call LogCdi newDir "is already on the PATH."
      end
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET PATH= not found in file."
return

/* append to libpath */
AppendToLibPath:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,8)) = "LIBPATH=" then do
      call DisasseblePath config.i
      notinpath = 1
      do h=1 to Dir.0
         if translate(Dir.h) = translate(newDir) then do
            notinpath = 0
         end
      end
      if notinpath then do
         j = Dir.0
         j = j + 1
         Dir.j = newDir
         Dir.0 = j
         config.i = AssemblePath("LIBPATH=")
         if debugEditFile = 1 then call LogCdi "Done."
      end
      else do
         if debugEditFile = 1 then LogCdi newDir "is already on the LIBPATH."
      end
      return
   end
end

if debugEditFile = 1 then call LogCdi "LIBPATH= not found in file."
return

/* prepend to libpath */
PrependToLibPath:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,8)) = "LIBPATH=" then do
      call DisasseblePath config.i
      notinpath = 1
      do h=1 to Dir.0
         if translate(Dir.h) = translate(newDir) then do
            notinpath = 0
         end
      end
      if notinpath then do
         do h=Dir.0 to 1 by -1
            hp = h + 1
            Dir.hp = Dir.h
         end
         Dir.1 = newDir
         Dir.0 = Dir.0 + 1
         config.i = AssemblePath("LIBPATH=")
         if debugEditFile = 1 then call LogCdi "Done."
      end
      else do
         if debugEditFile = 1 then call LogCdi newDir "is already on the LIBPATH."
      end
      return
   end
end

if debugEditFile = 1 then call LogCdi "LIBPATH= not found in file."
return

/* append to Dpath */
AppendToDPath:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,10)) = "SET DPATH=" then do
      call DisasseblePath config.i
      j = Dir.0
      j = j + 1
      Dir.j = newDir
      Dir.0 = j
      config.i = AssemblePath("SET DPATH=")
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET DPATH= not found in file."
return

/* remove from path */
RemoveFromPath:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,9)) = "SET PATH=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET PATH=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET PATH= not found in file."
return

/* remove from libpath */
RemoveFromLibPath:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,8)) = "LIBPATH=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("LIBPATH=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "LIBPATH= not found in file."
return

/* remove from Dpath */
RemoveFromDPath:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,10)) = "SET DPATH=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET DPATH=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET DPATH= not found in file."
return

/* remove from Help */
RemoveFromHelp:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,9)) = "SET HELP=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET HELP=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET HELP= not found in file."
return

/* remove from Bookshelf */
RemoveFromBookshelf:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,14)) = "SET BOOKSHELF=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET BOOKSHELF=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET BOOKSHELF= not found in file."
return

/* remove from Somir */
RemoveFromSomir:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,10)) = "SET SOMIR=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET SOMIR=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET SOMIR= not found in file."
return

/* remove from NLSPath */
RemoveFromNLSPath:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,12)) = "SET NLSPATH=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET NLSPATH=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET NLSPATH= not found in file."
return

/* append to bookshelf */
AppendToBookshelf:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,14)) = "SET BOOKSHELF=" then do
      call DisasseblePath config.i
      j = Dir.0
      j = j + 1
      Dir.j = newDir
      Dir.0 = j
      config.i = AssemblePath("SET BOOKSHELF=")
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET BOOKSHELF= not found in file."
return

/* append to classpath */
AppendToClasspath:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,14)) = "SET CLASSPATH=" then do
      call DisasseblePath config.i
      j = Dir.0
      j = j + 1
      Dir.j = newDir
      Dir.0 = j
      config.i = AssemblePath("SET CLASSPATH=")
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET CLASSPATH= not found in file."
return

/* append to help */
AppendToHelp:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,9)) = "SET HELP=" then do
      call DisasseblePath config.i
      j = Dir.0
      j = j + 1
      Dir.j = newDir
      Dir.0 = j
      config.i = AssemblePath("SET HELP=")
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET HELP= not found in file."
return

/* append to Somir */
AppendToSomir:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,10)) = "SET SOMIR=" then do
      call DisasseblePath config.i
      j = Dir.0
      j = j + 1
      Dir.j = newDir
      Dir.0 = j
      config.i = AssemblePath("SET SOMIR=")
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET SOMIR= not found in file."
return

/* append to NLSPath */
AppendToNLSPath:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,12)) = "SET NLSPATH=" then do
      call DisasseblePath config.i
      j = Dir.0
      j = j + 1
      Dir.j = newDir
      Dir.0 = j
      config.i = AssemblePath("SET NLSPATH=")
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET NLSPATH= not found in file."
return

/* remove from LIB */
RemoveFromSetLIB:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,8)) = "SET LIB=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET LIB=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET LIB= not found in file."
return

/* insert in LIB */
InsertInLIB:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,8)) = "SET LIB=" then do
      currentLIB = substr(config.i, 9)
      config.i = "Set LIB=" || newDir || ";" || currentLIB
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET LIB= not found in file."
return

/* insert in INCLUDE */
InsertInINCLUDE:

parse arg newDir

do i=1 to config.0
   if translate(left(config.i,12)) = "SET INCLUDE=" then do
      currentINCLUDE = substr(config.i, 13)
      config.i = "Set INCLUDE=" || newDir || ";" || currentINCLUDE
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET INCLUDE= not found in file."
return

/* remove from INCLUDE */
RemoveFromINCLUDE:

parse arg delDir

do i=1 to config.0
   if translate(left(config.i,12)) = "SET INCLUDE=" then do
      call DisasseblePath config.i
      do j=1 to Dir.0
         if translate(Dir.j) = translate(delDir) then do
            Dir.j = ''
            if debugEditFile = 1 then call LogCdi "Done."
         end
      end
      config.i = AssemblePath("SET INCLUDE=")
      return
   end
end

if debugEditFile = 1 then call LogCdi "SET INCLUDE= not found in file."
return

DisasseblePath:

parse arg thePath
parse var thePath . "=" thePath

j = 0
do while thePath \= ''
   parse var thePath aDir ';' thePath
   if aDir \= '' then do
      j = j + 1
      Dir.j = aDir
   end
end
Dir.0 = j

return

AssemblePath:

parse arg newPath

do j=1 to Dir.0
   if Dir.j \= '' then do
      newPath = newPath || Dir.j || ';'
   end
end

return newPath

/* append line to end of config.sys file */
AppendLine:

parse arg newLine

i = Config.0
i = i + 1
Config.i = newLine
Config.0 = i

return

/* insert a line in config.sys */
InsertLine:

parse arg token replacement

do i=1 to Config.0
   if abbrev(translate(strip(Config.i, 'Leading')), translate(token)) = 1 then do
      call addLine i, replacement
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "InsertLine did not find this token:" token

return

/* replace a line in config.sys */
ReplaceLine:

parse arg token replacement

found = 0
do i=1 to Config.0
   if abbrev(translate(strip(Config.i, 'Leading')), translate(token)) = 1 then do
      leadingSpace = pos(translate(token), translate(Config.i)) - 1
      Config.i = copies(' ', leadingSpace) || replacement
      if debugEditFile = 1 then call LogCdi "Done."
      found = 1
   end
end

if found = 0 then do
   if debugEditFile = 1 then call LogCdi "ReplaceLine did not find this token:" token
end

return

/* replace a line with a section */
InsertSection:

parse arg oldLine

do i=1 to Config.0
   if translate(Config.i) = translate(oldLine) then do
      parse pull replacement
      Config.i = replacement
      parse pull replacement
      do while translate(replacement) \= translate(endSectionMarker)
         i = i + 1
         call addLine i, replacement
         parse pull replacement
      end
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "InsertSection did not find this line:" oldLine

return

/* append a section to a line */
AppendSection:

parse arg oldLine

do i=1 to Config.0
   if abbrev(translate(strip(Config.i, 'Leading')), translate(oldLine)) = 1 then do
      parse pull replacement
      do while translate(replacement) \= translate(endSectionMarker)
         i = i + 1
         call addLine i, replacement
         parse pull replacement
      end
      if debugEditFile = 1 then call LogCdi "Done."
      return
   end
end

if debugEditFile = 1 then call LogCdi "AppendSection did not find this line:" oldLine

return

/* remove a line from the file */
RemoveLine:

parse arg token

do i=1 to Config.0
   if abbrev(translate(Config.i), translate(token)) = 1 then do
      call DeleteLine i
      if debugEditFile = 1 then call LogCdi "Done."
   end
end

return

/* remove a line from config.sys */
RemarkLine:

parse arg token

do i=1 to Config.0
   if abbrev(translate(Config.i), translate(token)) = 1 then do
      Config.i = 'REM ' || Config.i
      if debugEditFile = 1 then call LogCdi "Done."
   end
end

return

/* add a line to the file */
AddLine:

parse arg lineNumber, lineText

if pos(',', lineNumber) > 0 then do
   tmpNumber = lineNumber
   parse var tmpNumber lineNumber ',' lineText
   lineText = strip(lineText)
end

if lineNumber > Config.0 then do
   if debugEditFile = 1 then call LogCdi "AddLine called to add line number" lineNumber "to a file with" Config.0 "lines"
   return
end

if lineNumber < 1 then do
   if debugEditFile = 1 then call LogCdi "AddLine called to add Line number" lineNumber
   return
end

stepsize = -1
do j = Config.0 to lineNumber by stepsize
   k = j + 1
   Config.k = Config.j
end

Config.lineNumber = lineText
Config.0 = Config.0 + 1

return

/* remove a line from the file */
DeleteLine:

parse arg lineNumber

if lineNumber > Config.0 then do
   if debugEditFile = 1 then call LogCdi "DeleteLine called to add line number" lineNumber "to a file with" Config.0 "lines"
   return
end

do j = lineNumber to Config.0
   k = j + 1
   Config.j = Config.k
end

Config.0 = Config.0 - 1

return

/* edit a set statement */
Set:

parse arg setStatement

if pos('=', setStatement) = 0 then do
   call LogCdi "In" setStatement "no equals sign (=) was found"
   return
end

parse var setStatement setVar '=' setValue

if setVar = '' then do
   call LogCdi "In" setStatement "no variable is given"
   return
end

found = 0
do j = 1 to Config.0
   if abbrev(translate(Config.j), translate('Set' setVar)) = 1 then do
      if setValue = '' then do
         do m = j to Config.0
            k = m + 1
            Config.m = Config.k
         end
         Config.0 = Config.0 - 1
         j = j - 1
         if debugEditFile = 1 then call LogCdi "Deleted."
      end
      else do
         Config.j = 'Set' setVar || '=' || setValue
         if debugEditFile = 1 then call LogCdi "Modified."
      end
      setValue = ''
      found = 1
   end
end

if found = 0 then do
   call AppendLine 'Set' setVar || '=' || setValue
   if debugEditFile = 1 then call LogCdi "Added."
end

return
