/* Migration script for Symantec Antivirus */
/* Created 22 May 03 by Chris Clayton */
/* Free for all to use, either in its entirety or portions, */
/* but will appreciate credit */

/*  User modifiable definitions:  */
  /*  create WPS folders, but could substitute another such an */
  /* OD enhanced folder */
  clFolderClass = 'WpFolder'  /* ''TSEnhFolder' */

  /* log file - change to '' if not needed */
  fLogFile = 'SymaMigr.log'

  /* How to commit config.sys changes */
  /* 'Y' = save changes to config.sys (old as config.mig) */
  /* 'N' = save changes to config.mig (config.sys unchanged) */
  sCommitCgs = 'N'

  /* INI file to change - Symantec uses os2.ini: 'USER' */
  /* but changed it to 'symatest.ini' for testing */
  fDestINI = 'symatest.ini'

  /* Symantec AntiVirus program and virus definition directories that are */
  /* saved to the INI file- modify as required */
  sSymantec = 'e:\symantec'
  sDefsDir = sSymantec||'\common\shared\virusdef'
  sVirusDir1 = sDefsDir||'\20001113.002'
  sVirusDir2 = sDefsDir||'\20001113.002'
  sVirusDir3 = sDefsDir||'\20030326.002'  /* use the 'newest' here (look at DEFINFO.DAT) */
  sVirusDir4 = sDefsDir||'\20001113.002'

  /*  my icons directory */
  sIcons = 'E:\UTILITY\ICONS\'

/* find the OS/2 drive letter (system booted to) */
sBootDrive =
Substr(Translate(Value('PATH',,'OS2ENVIRONMENT')),Pos('\OS2\SYSTEM',Translate(Value('PATH',,'OS2ENVIRONMENT')))-2,2)
/* config.sys location - normally sBootdrive but use '.' for testing*/
sCfigDrive = '.' /* sBootDrive */

/*  load all of the REXX utility functions, even though we only use a few */
Call RxFuncAdd 'SysLoadFuncs', 'REXXUTIL', 'SysLoadFuncs';
Call SysLoadFuncs;

/*  other global definitions */
/*  variables that will contain config.sys additions to: */
sNewBook = ''           /* BOOKSHELF environment variable */
sNewDpath = ''              /* DPATH environment variable */
sNewHelp = ''                /* HELP environment variable */
sNewLib = ''                         /* LIBPATH directive */
sNewPath = ''                /* PATH environment variable */

/* the following stem variable is for additional config.sys entries */
stAddlCfg.0 = 0     /* the first will contain the number of entries */
                   /* .1 .. .n will contain the value of each entry */

bOK = 'FALSE'                            /* did the migration work? */

/* First create a destination folder on the Desktop */
/* Enter a unique object id and a folder name. Or you can specify an */
/* existing folder.  You just need to know its object id.  Unimaint has a */
/* a WPS tool that will display the properties of WPS objects. In this case */
/* the following code segment may be deleted and the variable sUtility must */
/* be set to the object id of the desired folder */
sFolder = '<CMC_MY_PROGS>'
sName = 'My Folder'
rc = Message(  'Creating destination folder: '||sName )
rc = SysSetObjectData(sFolder,'' )              /* see if the objects exists */
if RC = 1 THEN
  DO
    rc = Message( ' Unable to create folder '||sName||' because object ID '||sFolder||' exists' )
    EXIT
  END
ELSE
  DO
    sOptions = 'OBJECTID='||sFolder||';CONCURRENTVIEW=NO;'
    sOptions = sOptions||'VIEWBUTTON=MINIMIZE;MINWIN=HIDE;'
    sOptions = sOptions||'ICONVIEW=NONGRID,NORMAL;ICONVIEWPOS=25,10,50,70'
    rc = SysCreateObject( clFolderClass, sName, '<WP_DESKTOP>', sOptions, 'fail' )
    IF RC = 0 THEN
      DO
        rc = Message(  '  Unable to create folder '||sName )
        rc = Message(  '  Make sure it is deleted and retry the command.' )
        EXIT
      END
  END  /* sObject else */

/*  Now create subfolders */
sApplicat = CreateFolder( sFolder, 'Application', '<CMC_MY_APPS>' )
sUtility = CreateFolder( sFolder, 'Utility', '<CMC_MY_UTIL>' )

/* Migrate Symantec Antivirus if it exists */
fFile =  'E:\SYMANTEC\NAVOS2.EXE'
rc = stream( fFile, 'c', 'query exist' )
if rc = fFile then
 DO
  sName = 'Symantec Antivirus'
  /* create a new folder in the sUtility folder */
  sFolder = CreateFolder( sUtility, sName, '<SKC_AV_FOLDER>' )
  IF sFolder = sUtility THEN   /* folder creation didn't work */
    rc = Message( '      So not migrating '||sName||' applications' )
  ELSE  /* could create the desired folder so add program objects */
   DO
    /* Symantec Antivirus program */
    /* This is a WPS program that does not need any additional object */
    /* properties.  So just supply a destination folder (sFolder), an */
    /* object title (sName), an unique object id (<SKC_AV_PROG>) and */
    /* the program type:  PM for WPS program.  The extra parameters */
    /* is passed as a null string '' */
    rc = CreateApp( sFolder, sName, fFile, '<SKC_AV_PROG>', 'PM', '' )

    /* Installation Utility */
    /* Here we are using a program that is started in a command shell, */
    /* Notice that the entire path to cmd.exe is specified. This is */
    /* because CreateApp checks to see if the program exists before it */
    /* will create an object.  So it needs to know where the program is */
    /* Here the program type is WINDOWABLEVIO for a windowed OS/2 */
    /* session.  Finally, we need to specify the contents of the */
    /* 'parameters' field so the extra object properties variable sParms */
    /* is used.  We also specify an icon using a ';' to separate object */
    /* property items */
    sParms = 'PARAMETERS=/c e:\Symantec\epfinsts.exe /c:e:\Symantec\navos2.icf /o:drive;'
    sParms = sParms||'ICONFILE=E:\SYMANTEC\EPFIICIS.ICO'
    rc = CreateApp( sFolder, 'Installation Utility', sBootDrive||'\OS2\CMD.EXE', '<SKC_AV_INST>', 'WINDOWABLEVIO', sParms )

    /* Live Update */
    rc = CreateApp( sFolder, 'Live Update', 'E:\SYMANTEC\NAVLUOS2.EXE', '<SKC_AV_UPDT>', 'PM', '' )

    /* Scan a: object */
    sParms = 'PARAMETERS=a:\*.* /doallfiles /repair /s+;ICONFILE='||sIcons||'VIRUS.ICO'
    rc = CreateApp( sFolder, 'Scan a:', 'E:\SYMANTEC\NAVDXOS2.EXE', '<SKC_AV_SCANA>', 'WINDOWABLEVIO', sParms )

    /* create Startup Norton Program Scheduler  program object */
    rc = CreateApp( sFolder, 'Startup Norton Program Scheduler', 'E:\SYMANTEC\OS2SCHED.EXE', '<SKC_AV_STRT>', 'PM', '' )

    /* Now create Uninstall Norton Antivirus  program object */
    rc = CreateApp( sFolder, 'Uninstall Norton Antivirus', 'E:\SYMANTEC\VDEFINST.EXE', '<SKC_AV_UNINST>', 'PM', 'PARAMETERS=/u' )

    /* config.sys changes Symantec Antivirus doesn't actually need these */
    /* entries.  They are included as an example of how to change: */
    /* a) the various path environment entries.  The next function */
    /* adds a path that is to be added to the LIBPATH= statement.  */
    /* The sNewLib variable is both passed and returned.  This is */
    /* because the path entries are accumulated together as additional */
    /* applications are migrated later on (ConcactPath knows to separate */
    /* entries using a ';')  Since this function is used to process */
    /* different path variables (see the list of definitions above), */
    /* the string 'LIBPATH' is passed for logging purposes */
    sNewLib = ConcactPath( sNewLib, 'E:\SYMANTEC', 'LIBPATH' )
    sNewHelp = ConcactPath( sNewHelp, 'E:\SYMANTEC', 'HELP' )
    sNewPath = ConcactPath( sNewPath, 'E:\SYMANTEC', 'PATH' )
    /* b) AddEntry is used to save entire config.sys lines in a stem */
    /* variable for addition to the end of the new config.sys file. */
    /* Here we add a comment and an environment variable.  We could */
    /* also add anything else an application may need, */
    /* such as device drivers */
    rc = AddCfgEntry( 'REM  Symantec Antivirus' )
    rc = AddCfgEntry( 'SET SYMANTEC=E:\SYMANTEC' )

    /* create INI file entries */
    /* Adding entries to an INI files is easy, if you're not faint of heart */
    /* You need to specify an INI file:  'USER' for os2.ini, 'SYSTEM' for */
    /* os2sys.ini (most apps shouldn't mess with this one), or a file name */
    /* Here we specified fDestINI.  If the file does not exist, it will be */
    /* created for you using the path specified in fDerstINI. Note, Symantec */
    /* expects these entries to be in the OS/2 user file, os2.ini.  In this */
    /* example code, a file name is specified for testing purposes. When all */
    /* is working then fDestINI is set to 'USER' */
    /* The next variable in CreateINIentry is the application name, followed */
    /* by the key name. The routine checks to see if this application and key */
    /* combination exist in the specified INI file. If they do, the key value */
    /* is read and returned by the function to rc unchanged.  If they do not */
    /* exist, the the application and key are added with the specified key */
    /* value, here the contents of sDefsDir.  REXX strings are not null */
    /* terminated, so the the final variable specified whether a null is to */
    /* add to the key value ('Y') or not ('N').  Upon return rc contains */
    /* a null string '' is the key creation was successful or 'ERROR:' if */
    /* it was not */
    rc = CreateINIentry( fDestINI, 'SymantecInstalledApps', 'AVENGEDEFS', sDefsDir, 'Y' )
    rc = CreateINIentry( fDestINI, 'SymantecNAV', 'AVDefsDir', sDefsDir, 'Y' )
    rc = CreateINIentry( fDestINI, 'SymantecNAV', 'InstallDir', sSymantec, 'Y' )
    rc = CreateINIentry( fDestINI, 'SymantecNAV', 'InstallVersion', '5.0', 'Y' )
    rc = CreateINIentry( fDestINI, 'SymantecSharedDefs', 'NAVOS2_50_AP1', sVirusDir1, 'Y' )
    rc = CreateINIentry( fDestINI, 'SymantecSharedDefs', 'NAVOS2_50_AP2', sVirusDir2, 'Y' )
    rc = CreateINIentry( fDestINI, 'SymantecSharedDefs', 'NAVOS2_50_NAVOS2', sVirusDir3, 'Y' )
    rc = CreateINIentry( fDestINI, 'SymantecSharedDefs', 'NAVOS2_50_QUAR', sVirusDir4, 'Y' )

    /* looks like the migration worked, */
    /* but then I didn't do a lot of checking of returns */
    bOK = 'TRUE'
   END /* create folder else */
 END  /*  Symantec AntiVirus folder */

/* if the migration worked then make changes to config.sys */
/* The sCommitCfs flag specifies whether to commit changes */
/* to config.sys or config.mig.  See above */
IF bOK = 'TRUE' THEN rc = UpdateConfigSys( sCommitCgs )

EXIT bOK \= 'TRUE'

/* The following are the functions that do the work of the migration process */
/* Being an old 'C' programmer, I always return something back to the */
/* calling program.  Here the returns are nearly all indications of how */
/* successful the operation was.  Any variables that are in these routines */
/* that are not parsed as arguments are global variable (used across the */
/* program) and care needs to be taken to be sure they are properly defined */
/* before the routine is called. Also a few, such as rc, are used internally */
/* and their contents may change.  So caution is required when using these */
/* variable with the main body of the program */

/*---------------------------------------------------------------------------
  rc = Message( sMessage )

  types a message onto the console and outputs it to a log file (if defined)

  arguments
        sMessage        Info to display.  Note, use the concation operator (||) to
                        include multiple strings and REXX variables
  returned
        rc              the return code from LINOUT ( 0 = ok, anything else ?)
  global
        fLogFile        set to the log file name or '' if not needed
  internal
        rc              LINEOUT return code
  ---------------------------------------------------------------------------*/
Message:
  PARSE ARG sMessage

  SAY sMessage
  rc = 0
  IF fLogFile \= '' THEN DO
    rc = LINEOUT( fLogFile, sMessage )   /* open file & write message */
    rc = LINEOUT( fLogFile )              /* close file */
  END  /* fLogFile IF */
  return rc  /* end of Message */

/*---------------------------------------------------------------------------
  sReturn = CreateApp( sDest, sName, fFile, sObject, sType, sExtras )

  creates a program object named sName within folder sDest
  Note:  This function automatically sets the Startup Directory value to
         the program path.  If another directory is desired, you must use
         SysSetObjectData to make the correction:
           rc = SysSetObjectData( sDest, 'STARTUPDIR=path' )

  arguments
        sDest           Destination folder object id - must exist
        sName           icon title
        fFile           program file name with complete path
        sObject         object id - must be unique
        sType           program type such as (see WinCreateObject for more):
                                PM - WPS program
                                WINDOWABLEVIO - OS/2 windowed
                                WINDOWEDVDM - DOS windowed
        sExtras         additions to object creation options, such as parameters
                        format is:  option1=value1;option2=value2; ...
                        values may contain blanks but must be terminated by ';'
  returned
        sReturn         1 if successful, 0 otherwise
  internal
        rc              return code from function calls
        sDir            program path
        sOptions        object creation options
        sReturn         return from SysCreateObject() or  0
  ---------------------------------------------------------------------------*/
CreateApp:
  PARSE ARG sDest, sName, fFile, sObject, sType, sExtras

  sReturn = 0
  rc = stream( fFile, 'c', 'query exist' )
  if rc = fFile then
   DO
    rc = SysSetObjectData( sObject, '' )    /* see if the object exists */
    if rc = 1 THEN
      rc = Message( ' Unable to create '||sName||' because object ID '||sObject||' exists' )
    ELSE
     DO
      sDir = filespec( "drive", fFile )||filespec( "path", fFile)
      rc = Message(  'Creating '||sName||' object' )
      sOptions = 'OBJECTID='||sObject||';STARTUPDIR='sDir';EXENAME='||fFile||';'
      sOptions = sOptions||'PROGTYPE='||sType||';CONCURRENTVIEW=NO;MINWIN=VIEWER'
      IF sExtras \= '' THEN sOptions = sOptions||';'||sExtras
      sReturn = SysCreateObject( 'WpProgram', sName, sDest, sOptions, 'replace' )
      IF sReturn = 0 THEN
        rc = Message(  '  Unable to create '||sName||' object' )
     END   /* sObject else */
   END  /* fFile if */
  ELSE
    rc = Message(  sName||' does not exist - program object not created' )

  return sReturn  /* end of CreateApp */

/*---------------------------------------------------------------------------
  sReturn = CreateFolder( sDest, sName, sObject )

  creates a folder named sName within folder sDest
  Note:  this routine may replace a folders with the same name if one
  currently exists within the destination folder.  The object id is
  checked to make sure it is not a duplicate of an existing one, but
  the name is not.  THe WPS may be smart enough to take care of this
  duplicate name problem, but this has not been tested

  arguments
        sDest           Destination folder object id - must exist
        sName           folder name
        sObject         object id for the desired folder - must be unique
  returned
        sReturn         object id: if successful the created folder's
                        otherwise the destination's
  global
        clFolderClass   class of the folder type to be created
  internal
        rc              return from function calls
        sObject         folder object id
        sOptions        folder creation options
  ---------------------------------------------------------------------------*/
CreateFolder:
  PARSE ARG sDest, sName, sObject

  sReturn = sDest
  rc = Message(  '  Creating folder: '||sName )
  rc = SysSetObjectData( sObject,'' )            /* see if the object exists */
  if rc = 1 THEN
    rc = Message( '    Unable to create folder '||sName||' because object ID '||sObject||' exists' )
  ELSE
   DO
     sOptions = 'OBJECTID='||sObject||';CONCURRENTVIEW=NO;VIEWBUTTON=MINIMIZE;'
     sOptions = sOptions||'MINWIN=HIDE;ICONVIEW=NONGRID,NORMAL'
     rc = SysCreateObject( clFolderClass, sName, sDest, sOptions, 'replace' )
     IF rc = 0 THEN
       rc = Message(  '    Unable to create folder '||sName )
     ELSE
       sReturn = sObject
   END  /* sObject else */

  return sReturn  /* end of CreateFolder */

/*---------------------------------------------------------------------------
   sResult = MoveObject( sSource, sDest, sName )

   A routine to move objects from one location to another
   arguments
           sSource      Object ID to be moved
        sDest           Object ID of location to move to
        sName           Source object description (for log)
   returned
        sResult         operation success:  1 = moved, 0 = didn't
   internal
	rc	return code from Message
	sResult	Object function return codes
  ---------------------------------------------------------------------------*/
MoveObject:
  PARSE ARG sSource, sDest, sName

  rc = Message(  '  Moving object: '||sName )
  sResult = SysSetObjectData( sSource, '' )      /* see if the object exists */
  if sResult = 0 THEN
    rc = Message( "    Not moved - doesn't exist!" )
  ELSE
   DO
    sResult = SysMoveObject( sSource, sDest )
    IF sResult = 0 THEN
      rc = Message( '    Not moved - check ids' )
   END

  RETURN sResult  /* end of MoveObject */

/*---------------------------------------------------------------------------
   sResult = ShadowObject( sSource, sDest, sName )

   A routine to create a shadow of an object 
   arguments
        sSource         Object ID to be shadowed
        sDest           Object ID of the location of the new shadow
        sName           Source object description (for log)
   returned
        sResult         operation success:  1 = shadowed, 0 = didn't
   internal
	rc	return code from Message
	sResult	Object function return codes
  ---------------------------------------------------------------------------*/
ShadowObject:
  PARSE ARG sSource, sDest, sName

  rc = Message(  '  Shadowing object: '||sName )
  sResult = SysSetObjectData( sSource, '' )       /* see if the object exists */
  if sResult = 0 THEN
    rc = Message( "    Not shadowed - doesn't exist!" )
  ELSE
   DO
    sResult = SysCreateShadow( sSource, sDest )
    IF sResult = 0 THEN
      rc = Message( '    Not shadowed - check ids' )
   END

  RETURN sResult  /* end of ShadowObject */

/*---------------------------------------------------------------------------
  sReturn = CreateINIentry( sINIfile, sApp, sKey, sVal, sNull )

  tries to create an INI file entry for Application sAdd with keyword sKey and
  value sVal.  If sKey exists, then the current value is returned and no
  changes are made.

  input:
        sINIfile        INI file to modify:  'USER', 'SYSTEM', or a file name
                        Note:  if file name doesn't exist, it will be created
        sApp            Application name used to store profile information
        sKey            Keyword name holding profile information
        sVal            Application keyword value
        sNull           add a NULL terminator to key value?  'Y' = yes, 'N' = no
  returned
        sReturn         return from SysIni
  internal
        rc              return code from message
        sReturn         return code from SysIni
  ---------------------------------------------------------------------------*/
CreateINIentry:
  PARSE ARG sINIfile, sApp, sKey, sVal, sNull

    rc = Message(  '    checking for ini file entry "'sApp'"' )
    sReturn = SysIni( sIniFile, sApp, sKey )
    IF sReturn = 'ERROR:' THEN
    DO
      rc = Message(  '      creating ini entry for key "'sKey'"' )
      IF TRANSLATE( sNull ) = 'Y' THEN sVal = sVal||'0'x
      sReturn = SysIni( sIniFile, sApp, sKey, sVal )
      IF sReturn = '' THEN
         rc = Message(  '      successfully added "'sVal'"' )
      ELSE
         rc = Message(  '      not successful:  SysIni return = "'sReturn'"' )
    END
    ELSE  rc = Message(  '      entry exists: "'sReturn'"' )

  return sReturn  /* end of CreateINIentry */

/*---------------------------------------------------------------------------
   sDest = Concactpath( sDest. sSource, sPlace )

   A routine to concact a single directory path onto a variable that contains a string
   of paths to be added to a given config.sys environment variable

   arguments
        sDest           the variable that contains the paths to be added
        sSource         the path to add on
        sPlace          the environment variable name to add to (such as PATH)
                        this is used for logging puposes
   returned
        sDest           the new path additions
   internal
        dPos            position of ';' in sSource
        dLen            length of sSource
        rc              return code from function calls
        sCurDir         current working directory
        sNewDir         directory that DIRECTORY changes to
  ---------------------------------------------------------------------------*/
ConcactPath:
  PARSE ARG sDest, sSource, sPlace

  IF sSource \= '' THEN DO                      /*  some to add on, so do it */
    rc = Message( '    Adding "'||sSource||'" to '||sPlace )
    sCurDir = DIRECTORY()   /* check that sSource is a directory that exists */
    sNewDir = DIRECTORY( sSource )                    /* try to change to it */
    IF TRANSLATE( sNewDir ) = TRANSLATE( sSource ) THEN             /* could */
       rc = DIRECTORY( sCurDir )                /* it exists, so change back */
    ELSE                    /* doen't exist, so issue a warning & use anyway */
       rc = Message( '      WARNING: '||sSource||' does not exist' )
    sDest = sDest||sSource             /* add on new path to existing string */
    dPos = LASTPOS( ';', sSource )        /* check for a terminating ';' */
    dLen = LENGTH( sSource )
    IF dPos \= dLen THEN sDest = sDest||';' /* no ';' at end, so add */
  END  /* sSource if */

  return  sDest /* end of ConcactPath */

/*---------------------------------------------------------------------------
  rc = AddCfgEntry( sEntry )

  adds a single line to the stem list that contains entries to be
  added to config.sys

  arguments
        sEntry          the string to be added
  returned
        stAddlCfg.0     the numbers of entries in the stem list
  internal
        i               no. of entries
  ---------------------------------------------------------------------------*/
AddCfgEntry:
  PARSE ARG sEntry

  IF sEntry \= '' THEN DO  /* something to add */
    i = stAddlCfg.0  /* find out how many are already there */
    i = i + 1          /* increment to the next slot */
    stAddlCfg.i = sEntry  /*  save line in next slot */
    stAddlCfg.0 = i  /* update the no. of entries counter */
    rc = Message( '    added config.sys line: "'||sEntry||'"' )
  END  /* sEntry if */

  return  stAddlCfg.0 /* end of AddCfgEntry */

/*---------------------------------------------------------------------------
  rc = UpdateConfigSys( sReplace )

  Updates config.sys sNew.... path variable additions
  and adds any entries in stAddlCfg
  Note:  this routine is modified code from the IBM Works migration script
  (and now more readable and compact)

  arguments
        sReplace        Y = change entries and save to sBootDrive\config.sys
                            (original config.sys is saved as config.mig)
                        N = save changes in sBootDrive\config.mig
                            (original config.sys is unchanged)
  returned
        sReturn         the return code from LINOUT ( 0 = ok, anything else ?)
  global
        sBootDrive      current OS/2 system drive (or '.' if testing)
        sNewBook        BOOKSHELF paths to add on
        sNewDpath       DPATH paths to add on
        sNewPath        PATH paths to add on
        sNewLib         LIBPATH paths to add on
        stAddlCfg       stem variable containing the lines to add to config.sys
  internal
        fCurConfig      original config.sys file
        fNewConfig      config file to be created
        tmpconfig       a temporary file for copying purposes
        rc              return from function calls
        sReturn         return from LINOUT
        sInLine         a config.sys line
        sOS2var         the config.sys variable (part before '=')
        sOS2value       corresponding value (after '"' )
  ---------------------------------------------------------------------------*/
UpdateConfigSys:
  PARSE ARG sReplace

  fCurConfig = sCfigDrive||'\CONFIG.SYS'           /* the current config.sys */
  fNewConfig = sCfigDrive||'\CONFIG.MIG'         /* the new one with changes */
  tmpconfig = sCfigDrive||'\IWTMP.SYS' /* temp file (could use REXX routine) */

  '@del 'fNewConfig ' 2>nul >nul'    /* make sure that config.mig is deleted */

  DO WHILE LINES( fCurConfig )              /*  scan through the entire file */
   sInLine = LINEIN( fCurConfig )         /*   reading in one line at a time */
                                              /* separate variable and value */
    PARSE VALUE sInLine WITH sOS2var '=' sOS2value
    sOS2var = TRANSLATE( sOS2var )               /*  make sure all uppercase */
    SELECT
       WHEN sOS2var = 'SET BOOKSHELF' THEN         /*  add BOOKSHELF entries */
         sReturn = UpdateConfLine( fNewConfig, sInLine, sNewBook )
       WHEN sOS2var = 'SET DPATH' THEN                 /*  add DPATH entries */
         sReturn = UpdateConfLine( fNewConfig, sInLine, sNewDpath )
       WHEN sOS2var = 'SET HELP' THEN                   /*  add HELP entries */
         sReturn = UpdateConfLine( fNewConfig, sInLine, sNewHelp )
       WHEN sOS2var = 'SET PATH' THEN                   /*  add PATH entries */
         sReturn = UpdateConfLine( fNewConfig, sInLine, sNewPath )
       WHEN sOS2var = 'LIBPATH' THEN                 /*  add LIBPATH entries */
         sReturn = UpdateConfLine( fNewConfig, sInLine, sNewLib )
/*---------------------------------------------------------------------------
   Since you are already scanning through the config.sys file, this is a good
   opportunity to tailor existing lines at their current location, or add
   additional lines before or after them.  Here are a few examples

       WHEN sOS2var = 'PROTSHELL' THEN DO       /*  add c-a-d PROTSHELL entry */
         sReturn = lineout( fNewConfig, sInLine )
         sReturn = lineout( fNewConfig, 'rem ** PROTSHELL=E:\CADCMDR\CADCMDR.EXE' )
        END
       WHEN sOS2var = 'SWAPPATH' THEN               /*  change SWAPPATH entry */
         sReturn = lineout( fNewConfig, 'SWAPPATH=E:\ 4096 98304' )
   Notice the following example uses value portion of the line
       WHEN sOS2value = 'USBMSD.ADD' THEN       /*  fixup USB rem media entry */
         sReturn = lineout( fNewConfig, 'BASEDEV=USBMSD.ADD /FLOPPIES:0 /REMOVABLES:1' )

   end of examples
  ---------------------------------------------------------------------------*/
       OTHERWISE       /* write the exising line out to the new config file  */
           sReturn = lineout( fNewConfig, sInLine )
    END  /* select */
  END /* LINES DO */

  IF stAddlCfg.0 > 0 THEN DO  /* add additional config entries */
    DO i = 1 TO stAddlCfg.0
      sReturn = LINEOUT( fNewConfig, stAddlCfg.i )
   END  /* i DO */
  END  /* stAddlCfg IF */

  sReturn = LINEOUT( fNewConfig )                          /* close the files */
  sReturn = LINEOUT( fCurConfig )

                             /* replace the orig config.sys with the new one */
  IF TRANSLATE( sReplace ) = 'Y' THEN DO
    '@erase 'tmpconfig' 2>nul >nul'        /* make sure temp file is deleted */
    '@rename 'fCurConfig' 'tmpconfig' >nul'        /* rename current to temp */
    '@rename 'fNewConfig' 'fCurConfig' >nul'     /* rename new to config.sys */
    '@rename 'tmpconfig' 'fNewConfig' 2>nul >nul'      /* rename temp to new */
    rc = Message( '  '||fCurConfig||' updated succesfully.  Your old file has been renamed to '||fNewConfig )
  END
  ELSE rc = Message( '  '||fNewConfig||' contains the changes.  Your original '||fCurConfig||' is unchanged.' )

return  sReturn /* end of UpdateConfigSys */

/*---------------------------------------------------------------------------
  rc = UpdateConfLine( fOutFile, sInput, sAddition )

  Add text to an existing line and write the result to the new config.sys

  arguments
        fOutFile        output file name to write modified entry to
        sInput          current value of line in config.sys
        sAddition       string to add to sInput
  returned
        sReturn         the return code from LINOUT ( 0 = ok, anything else ?)
  internal
        sLen            length of a string
        sPos            position of a substring in another string
        sReturn         return code from LINEOUT
  ---------------------------------------------------------------------------*/
UpdateConfLine:
  PARSE ARG fOutFile, sInput, sAddition

  IF sAddition \= '' THEN DO               /*  something to add on, so do it */
    /* the following line checks to see if the additional path is already in */
    /* the existing path.  This is nice, but only works if the entire string */
    /* is duplicated.  Really should check each path entry for duplication */
    /* Not hard to do, but messy.  Might implement in the future */
    sPos = POS( TRANSLATE( sAddition ), TRANSLATE( sInput ))
    IF sPos = 0 THEN DO                              /* not there, so add on */
      sPos = LASTPOS( ';', sInput )           /* check for a terminating ';' */
      sLen = LENGTH( sInput )
      IF sPos \= sLen THEN sInput = sInput||';'         /*  no ';' so add it */
      sInput = sInput||sAddition                           /* add on changes */
    END  /* pos if */
  END  /* sAddition IF */

  sReturn = LINEOUT( fOutFile, sInput )    /* write result to new config file */

return result  /* end of UpdateConfLine */

/*---------------------------------------------------------------------------
  sDest = ConcactTabID( sDest, iTab, sObjID, sPlace )

  A routine to concact a Tab Launchbar object onto a variable that contains
  a string of Tab objects

  arguments
	sDest	contains the tab object ids to be added
	iTab	tab number to add to
	sObjID	object id to add on
	sPlace	folder/program name to add (for logging)
  returned
	sDest           the new object id additions
  internal
	rc	function return codes    ---------------------------------------------------------------------------*/
ConcactTabID:
  PARSE ARG sDest, iTab, sObjID, sPlace

  IF sObjID \= '' THEN DO                  /*  something to add on, so do it */
    rc = Message( '    Adding "'||sPlace||'" to tab '||iTab )
    rc = SysSetObjectData(sObjID,'' )            /* see if the object exists */
    IF rc = 0 THEN                                     /* doesn't, so ignore */
      rc = Message( '      WARNING: '||sPlace||' does not exist, not added' )
    ELSE                             /* does, so add onto desired tab string */
      sDest = sDest||iTab||'='||sObjID||','
  END  /* sObjID if */

  return  sDest /* end of ConcactTabID */
