home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / o / os2_util.zip / SHOWINI2.ZIP / SHOWINI.CMD < prev    next >
OS/2 REXX Batch file  |  1992-07-01  |  145KB  |  3,917 lines

  1. /*
  2. program: showini.cmd
  3. type:    REXXSAA-OS/2, OS/2 2.0
  4. purpose: allow for viewing, editing, backing up & restoring of OS/2-INI-files
  5. version: 2.0
  6. date:    1992-06-01
  7. changed: 1992-06-02, RGF, changed text appearing on key-value-menu
  8.          1992-06-10, RGF, removed last debug-information from error-message
  9.          1992-06-20, RGF, introduced ability to have fewer than the default of
  10.                           10 generations for backups
  11.          1992-07-01, RGF, introduced batch-file execution on entire filesystems
  12.                           and on OS2.INI and OS2SYS.INI.
  13.  
  14. author:  Rony G. Flatscher
  15.          RONY@AWIWUW11.BITNET
  16.          rony@wu-wien.ac.at
  17.  
  18. usage:   
  19.    SHOWINI: allow to view, edit, print, backup, restore OS/2-INI-files
  20.   
  21.    showini        ... allow to work interactively
  22.   
  23.    showini /switch[generations]  {filename | /modifier}  ... batch-mode execution
  24.  
  25.        switch:    B[T]   ... make a BACKUP of an OS/2-INI-file
  26.                   U[T]   ... UPDATE original OS/2-INI using a backup
  27.                   R[T]   ... RESTORE original OS/2-INI using a backup, i.e. delete
  28.                              keys not found in backup
  29.                     T    ... backup is a text-file (i.e. ASCII-file)
  30.  
  31.        generations: a number between 1-10, indicating how many backup-files you
  32.                     want, respectively, which backup you wish to use
  33.  
  34.        filename: filename of OS/2-INI-file or the filename of the backup
  35.        ---or---
  36.        modifier: look for all OS/2-INI-files on the filesystem[s]:
  37.  
  38.                  L[OCAL]  ... only LOCAL filesystems are scanned
  39.                  R[EMOTE] ... only REMOTE filesystems are scanned
  40.                  A[LL] ...... both, LOCAL and REMOTE filesystems are scanned
  41.                  D[RIVES]:letters ... only the given driveletters are scanned,
  42.                                       where letters is e.g. ACDEH
  43.  
  44.                  process OS/2-system INI-files:
  45.  
  46.                  S[YSTEM] ... "OS2SYS.INI" only
  47.                  U[SER] ..... "OS2.INI" only
  48.                  B[OTH] ..... both, "OS2SYS.INI" and "OS2.INI"
  49.  
  50. needs:   all RxUtil-functions loaded, BOXEDIT.CMD
  51.  
  52.  
  53.  
  54.  
  55. 1) Working interactively with OS/2-INI-files
  56. ============================================
  57.  
  58. SHOWINI.CMD allows you to interactively
  59.  
  60.     a) view
  61.     b) print to text-(ASCII)-file
  62.     c) edit
  63.     d) move/copy
  64.     e) delete
  65.     f) configure SHOWINI.CMD
  66.  
  67. Toplevel entries ("Application entries") and key-entries in OS/2-INI-files. Just
  68. enter:
  69.  
  70.      showini
  71.  
  72. Initially you will **not** see the menu choices for editing, moving/copying and
  73. deleting INI-entries for safety reasons. In addition there is an option to log 
  74. backup/update/restore operations; if you set this option to no an existing 
  75. logfile will be erased (it has a name of "SHOWINI.LOG" and resides in the same 
  76. directory as SHOWINI.CMD itself).
  77.  
  78. Also, you will get a choice to work with OS2.INI (USER) and OS2SYS.INI (SYSTEM).  
  79. With "s" for scan you can have SHOWINI.CMD to automatically scan for valid 
  80. OS/2-INI-files on all local and/or remote drives or a specific drive, it will 
  81. successfully ignore Winodows-INI-files.  
  82.  
  83. Once you configure one of the above manipulative functions the configuration 
  84. choice will be shown, if you reset them, it will not be shown anymore.  
  85. Therefore you could safely leave this program on a machine for end-users.
  86.  
  87. In order to activate the edit, move/copy and/or delete functions, you need to 
  88. enter "c" (configure) on the main menu.  This option will allways be accessible 
  89. from the INI- and TopLevel-menus, no matter whether it is displayed or not.  All 
  90. these settings will be stored in OS2.INI under the TopLevel-entry called "RGF 
  91. Showini.cmd".  Hint:  If there are many entries in an INI-file, use the 
  92. MODE-command to get more lines or more columns and/or lines:
  93.  
  94.        e.g. "MODE co80,100" for 100 lines or
  95.             "MODE co132,50" for 132 columns and 50 lines on an XGA-adapter
  96.  
  97. Hint: Wherever it is possible from the program logic, you may immeditiately end 
  98. SHOWINI.CMD by typing "q" (quit). Attention: if quitting the program, changes to
  99. the settings are not stored in OS2.INI.
  100.  
  101.  
  102. 2) Batchfile-commands
  103. =====================
  104.  
  105. SHOWINI.CMD allows for
  106.  
  107.         backing up and ***restoring*** INI-files while the system is running ! 
  108.  
  109. This means that you can backup even OS2.INI and OS2SYS.INI while the system is 
  110. up and restore them from a backup while the system is running. SHOWINI.CMD 
  111. by default produces **10-generation** backups.
  112.  
  113.  
  114. a) syntax:
  115. ----------
  116.  
  117.    showini /switch[generations]  {filename | /modifier}  
  118.  
  119.        switch:    B[T]   ... make a BACKUP of an OS/2-INI-file
  120.                   U[T]   ... UPDATE original OS/2-INI using a backup
  121.                   R[T]   ... RESTORE original OS/2-INI using a backup, i.e. 
  122.                              delete keys not found in backup
  123.                     T    ... backup is a text-file (i.e. ASCII-file), else 
  124.                              backup is a valid OS/2-INI-file
  125.  
  126.        generations: an optional number between 1-10, indicating how many
  127.                     backup-files you want, respectively, which backup you 
  128.                     wish to use; please note: backups will be numbered from
  129.                     0 (= 1. generation) thru 9 (= 10. generation), e.g. 
  130.                     ".IN0", ".TX9"
  131.  
  132.        filename: filename of OS/2-INI-file or the filename of the backup
  133.        --- or ---
  134.        modifier: 
  135.                 look for all OS/2-INI-files on the filesystem[s]:
  136.  
  137.                  L[OCAL]  ... only LOCAL filesystems are scanned
  138.                  R[EMOTE] ... only REMOTE filesystems are scanned
  139.                  A[LL] ...... both, LOCAL and REMOTE filesystems are scanned
  140.                  D[RIVES]:letters ... only the given driveletters are scanned,
  141.                                       where letters is e.g. ACDEH
  142.  
  143.                  process OS/2-system INI-files:
  144.  
  145.                  S[YSTEM] ... "OS2SYS.INI" affected only
  146.                  U[SER] ..... "OS2.INI" afftected only
  147.                  B[OTH] ..... both, "OS2SYS.INI" and "OS2.INI" affected
  148.  
  149.  
  150.  
  151. b) examples (pertaining to single files):
  152. -----------------------------------------
  153.  
  154.    showini /b d:\os2\os2.ini
  155.         ... make a backup of OS2.INI, resulting backup will be in an 
  156.             OS/2-INI-format ("/B") and will have an extension of ".IN0", 
  157.             ".IN1", ".IN2", ".IN3", ".IN4", ".IN5", ".IN6", ".IN7", ".IN8",
  158.             ".IN9" depending on how many backups exist already.
  159.  
  160.    showini /bt4 e:\os2\os2sys.ini
  161.         ... make a backup of OS2SYS.INI, resulting backup will be in a
  162.             TEXT-format ("/BT", i.e. ASCII, editable by any text-editor) and 
  163.             will have an extension of ".TX0", ".TX1", ".TX2", ".TX3", depending
  164.             on how many backups exist already.
  165.  
  166.             Note: There are four generations desired ("/BT4") only. In case
  167.                   there are more generations present, because beforehand you
  168.                   used the default of 10 generations, all superfluos backups
  169.                   will be deleted (oldest first) !
  170.  
  171.    showini /u c:\mamma_mia\mutter.in9
  172.         ... update "mutter.ini" according to the backup-values in "mutter.in9" 
  173.             which is in an OS/2-INI-format ("/U"). 
  174.  
  175.             Note: If "mutter.ini" does not exist, SHOWINI.CMD prompts the user
  176.                   whether to create it !
  177.  
  178.    showini /rt q:vater.tx5
  179.         ... restore "vater.ini" according to the backup-values in "vater.tx5"
  180.             which is in TEXT-format ("/RT", i.e. ASCII-format, editable by 
  181.             any text-editor).
  182.  
  183.             Note: The "restore"-operation deletes all Toplevels and Keys in the 
  184.                   original OS/2-INI-file, which are not found in the backup. If 
  185.                   you do not want to delete those entries, use the "update"-mode
  186.                   instead !
  187.  
  188.             Note: If "vater.ini" does not exist, SHOWINI.CMD prompts the user
  189.                   whether to create it !
  190.  
  191.             Note: If the name of the original OS/2-INI-file in the backup 
  192.                   "vater.tx5" is another name like "father.ini", SHOWINI.CMD 
  193.                   will work on that INI-file.
  194.  
  195. The switches B, U, R pertain to OS/2-INI-backup-files, BT, UT, RT to 
  196. TEXT-backup-files.
  197.  
  198.  
  199.  
  200. c) examples (pertaining to filesystems, or OS2.INI, OS2SYS.INI):
  201. ----------------------------------------------------------------
  202.  
  203.    showini /b /local
  204.    --- same as:
  205.    showini /b /l
  206.  
  207.         ... make a backup of all OS/2 INI-files on all local drives, resulting
  208.             backups will be in an OS/2-INI-format ("/B") and will have an
  209.             extension of ".IN0", ".IN1", ".IN2", ".IN3", ".IN4", ".IN5", 
  210.             ".IN6", ".IN7", ".IN8", ".IN9" depending on how many backups exist
  211.             already.
  212.  
  213.    showini /b5 /local
  214.    --- same as:
  215.    showini /b5 /l
  216.  
  217.         ... make a backup of all OS/2 INI-files on all local drives, resulting
  218.             backups will be in an OS/2-INI-format ("/B") and will have an
  219.             extension of ".IN0", ".IN1", ".IN2", ".IN3", ".IN4", depending on
  220.             how many backups exist already.
  221.  
  222.             Note: There are five generations desired ("/BT5") only. In case
  223.                   there are more generations present, because beforehand you
  224.                   used the default of 10 generations, all superfluos backups
  225.                   will be deleted (oldest first) !
  226.  
  227.    showini /bt /local
  228.    --- same as:
  229.    showini /bt /l
  230.  
  231.         ... make a backup of all OS/2 INI-files on all local drives, resulting
  232.             backups will be in TEXT-format ("/BT") and will have an
  233.             extension of ".TX0", ".TX1", ".TX2", ".TX3", ".TX4", ".TX5", 
  234.             ".TX6", ".TX7", ".TX8", ".TX9" depending on how many backups exist
  235.             already.
  236.  
  237.    showini /b /remote
  238.    --- same as:
  239.    showini /b /r
  240.  
  241.         ... make a backup of all OS/2 INI-files on all remote drives, resulting
  242.             backups will be in an OS/2-INI-format ("/B") and will have an
  243.             extension of ".IN0", ".IN1", ".IN2", ".IN3", ".IN4", ".IN5", 
  244.             ".IN6", ".IN7", ".IN8", ".IN9" depending on how many backups exist
  245.             already.
  246.  
  247.    showini /bt1 /all
  248.    --- same as:
  249.    showini /bt1 /a
  250.  
  251.         ... make a backup of all OS/2 INI-files on all drives, local and remote,
  252.             resulting backups will be in TEXT-format ("/BT") and will have an
  253.             extension of ".TX0".
  254.  
  255.             Note: There is one generation desired ("/BT1") only. In case there
  256.                   are more generations present, because beforehand you used more
  257.                   than one generation, all superfluos backups will be deleted
  258.                   (oldest first) !
  259.  
  260.    showini /b /drives:adf
  261.    --- same as:
  262.    showini /b /d:adf
  263.  
  264.         ... make a backup of all OS/2 INI-files on drives "A", "D", "F",
  265.             resulting backups will be in an OS/2-INI-format ("/B") and will have
  266.             an extension of ".IN0", ".IN1", ".IN2", ".IN3", ".IN4", ".IN5",
  267.             ".IN6", ".IN7", ".IN8", ".IN9" depending on how many backups exist
  268.             already.
  269.  
  270.  
  271.     showini /u /a
  272.  
  273.         ... update all OS/2-INI-files found on all drives, remote and local, 
  274.             with backups in OS/2-INI-format. 
  275.  
  276.             Note: The latest backup will be used for updating the original 
  277.                   OS/2-INI-files.
  278.  
  279.     showini /ut5 /a
  280.  
  281.         ... update all OS/2-INI-files found on all drives, remote and local, 
  282.             with backups in TEXT-format. SHOWINI.CMD uses the 5th backup
  283.             (i.e. extension ".TX4"), if not found any backup which is before the
  284.             5th.
  285.  
  286.  
  287.     showini /r /a
  288.  
  289.         ... update all OS/2-INI-files found on all drives, remote and local, 
  290.             with backups in OS/2-INI-format.
  291.  
  292.             Note: The "restore"-operation deletes all Toplevels and Keys in the 
  293.                   original OS/2-INI-file, which are not found in the backup. If 
  294.                   you do not want to delete those entries, use the "update"-mode
  295.                   instead !
  296.  
  297.             Note: The latest backup will be used for updating the original 
  298.                   OS/2-INI-files.
  299.  
  300.  
  301.    showini /b /user
  302.    --- same as:
  303.    showini /b /u
  304.  
  305.         ... backup "OS2.INI".
  306.  
  307.    showini /rt4 /system
  308.    --- same as:
  309.    showini /rt4 /s
  310.  
  311.         ... restore "OS2SYS.INI" from a TEXT-backup. Use the fourth, if not 
  312.             found a younger, generation.
  313.  
  314.    showini /ut /both
  315.    --- same as:
  316.    showini /ut /b
  317.  
  318.         ... update both, "OS2.INI" and "OS2SYS.INI", with the latest 
  319.             TEXT-backup.
  320.  
  321. 3) EXIT-codes
  322. =============
  323.  
  324.  0 ... everything went o.k.
  325. -1 ... user aborted program
  326. -2 ... wrong switch or invalid filename
  327. -3 ... invalid backup-file
  328.  
  329.  
  330. 4) minimal layout of text-(ASCII)-backup-files
  331. ==============================================
  332.  
  333.         ; a line starting with a semi-column is a comment and is ignored
  334.         ; blank lines are ignored as well
  335.  
  336.         ; the file entry must be included and be given before the TopLevel- and
  337.         ; key-entries; it may span multiple lines (for long filenames) and has
  338.         ; the principal layout
  339.         ;
  340.         ;          "File [file name]"
  341.         ; delimiter for the value is allways an opening and ending square 
  342.         ; bracket
  343.  
  344.         File  [D:\work\klondike.ini] 
  345.         
  346.         ; A TopLevel (application) entry starts with the keyword "Top"; is being
  347.         ; followed by the datatype [A], [A0] or [H] for ASCII, ASCII-Z, resp.
  348.         ; hexadecimal; the last entry is the value enclosed in square brackets.
  349.         ; 
  350.         ; The same syntax applies to the key-names ("Key") and finally to the
  351.         ; values themselves ("Val").
  352.         ;
  353.         ; Any Value for TopLevel-names, Key-names and Key-values may span 
  354.         ; multiple lines; if so, subsequent lines must not contain a key-word,
  355.         ; but the data-type and the value.
  356.  
  357.  
  358.         Top [A]  [PATIENCE]
  359.             Key  [A]  [CardBack]
  360.  
  361.         ; the key-value is of ASCII-string, terminated by \0; note that the 
  362.         ; terminating '00'x is not contained within the value part:
  363.  
  364.                  Val  [A0] [2]
  365.  
  366.         ; the following key-value spans two lines:
  367.  
  368.             Key  [A]  [ColorSet]
  369.                  Val  [A0] [13]
  370.                       [A0] [03]
  371.         
  372.         ; this is an example for hexadecimal values for all three, 
  373.         ;TopLevel-name, key-name and key-value:
  374.  
  375.         Top [H]  [01020304050607]
  376.             Key  [H]  [08091011]
  377.                  Val  [H]  [12131415]
  378.  
  379.         ; note values enclosed in the square-bracket-delimiters may contain
  380.         ; square brackets themselves:
  381.  
  382.         Top [A]  [This is another TopLevel-entry [yes, another]]
  383.             Key  [A]  [This is another key-entry]
  384.                  Val  [A]  [This is a plain ASCII-entry.]
  385.             Key  [A]  [This is the second key-entry, within this TopLevel.]
  386.                  Val  [A0] [This is an ASCII-Z entry.]
  387.  
  388. For further examples of the syntax of the text-(ASCII)-file see any printout or
  389. text-(ASCII)-backup.
  390.         
  391. All rights reserved, copyrighted 1992, no guarantee that it works without
  392. errors, etc. etc.
  393.  
  394. donated to the public domain granted that you are not charging anything (money
  395. etc.) for it and derivates based upon it, as you did not write it,
  396. etc. if that holds you may bundle it with commercial programs too
  397.  
  398. you may freely distribute this program, granted that no changes are made
  399. to it
  400.  
  401. Please, if you find an error, post me a message describing it, I will
  402. try to fix and rerelease it to the net.
  403.  
  404. */
  405.  
  406. SIGNAL ON HALT
  407. SIGNAL ON ERROR
  408.  
  409. global. = ""                    /* default to empty string */
  410.  
  411.  
  412. /* place logfile into the same directory as this program */
  413. PARSE SOURCE . . name_path      /* get full name & path of this program */
  414. global.eLogFile = SUBSTR(name_path, 1, LASTPOS(".", name_path)) || "LOG"
  415.  
  416. CALL initialize                 /* initialize array "global."           */
  417.  
  418. IF ARG(1) <> "" THEN         /* arguments are given */
  419.    CALL backup_restore_update ARG(1)
  420.  
  421. CALL show_inis                  /* show OS/2-INI-files                  */
  422. CALL write_ini_settings         /* write settings for this program      */
  423.  
  424. EXIT 0                          /* normal exit */
  425.  
  426. /****************************************************************/
  427.  
  428. INITIALIZE: PROCEDURE EXPOSE global.
  429.     /* check whether RxFuncs are loaded, if not, load them */
  430.     IF RxFuncQuery('SysLoadFuncs') THEN
  431.     DO
  432.         /* load the load-function */
  433.         CALL RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'       
  434.  
  435.         /* load the Sys* utilities */
  436.         CALL SysLoadFuncs                                                 
  437.     END
  438.  
  439.     /* define some ANSI.SYS-colors */
  440.     esc    = '1B'x          /* escape-char */
  441.     
  442.     
  443.     /* define ANSI-colors */
  444.     global.iRedWhite    = esc||"[31;47m"    /* ANSI.SYS-control for red foreground */
  445.     global.iYellow = esc||"[33;40m"    /* ANSI.SYS-control for yellow foreground */
  446.     global.iCyan   = esc||"[36;40m"    /* ANSI.SYS-control for cyan foreground */
  447.     global.iNormal = esc||"[0;40m"     /* ANSI.SYS-control for resetting attributes to normal */
  448.     global.iScrLength = LENGTH(global.iRedWhite)
  449.  
  450.     /* hint for options */
  451.     global.defaultHint = global.iCyan || "(" || global.iYellow || "default" || global.iCyan || ")"
  452.     /* hint for default yes or no */
  453.     global.yesHint = global.iCyan || " (" || global.iYellow || "Y" || global.iCyan || "/N) " || global.iYellow
  454.     global.noHint  = global.iCyan || " (Y/" || global.iYellow || "N" || global.iCyan || ") " || global.iYellow
  455.  
  456.     /* get screen dimensions */
  457.     PARSE VALUE SysTextScreenSize() WITH row columns
  458.     global.iRows = row
  459.     global.iColumns = columns
  460.     global.iBlankLine = global.iRedWhite || RIGHT("", columns - 1) || global.iCyan
  461.     
  462.     global.iNonPrintable = XRANGE("00"x, "1F"x) || D2C(255)
  463.     global.iFilter       = COPIES("FA"x, 256)
  464.  
  465.     /* define leadin for menus */
  466.     global.enterString = global.iCyan || "Enter:" global.iYellow || "1" || global.iCyan || "-" || global.iYellow
  467.  
  468.     /* define data-type strings */
  469.     global.dType.H        = "[H]"
  470.     global.dType.H.long   = "hexadecimal"
  471.     global.dType.A        = "[A]"
  472.     global.dType.A.long   = "ASCII"
  473.     global.dType.A0       = "[A0]"
  474.     global.dType.A0.long  = "ASCII delimited by \0"
  475.     global.dType.F        = "[F]"
  476.     global.dType.longest  = 4           /* length of "[A0]" */
  477.  
  478.     /* define leadins for printout */
  479.     global.format.forTop        = "Top "
  480.     global.format.forTop.Length = LENGTH(global.format.forTop)
  481.     global.format.forTop.LeadIn = COPIES(" ", global.format.forTop.Length) 
  482.     global.format.forKey        = "    Key  "
  483.     global.format.forKey.Length = LENGTH(global.format.forKey)
  484.     global.format.forKey.LeadIn = COPIES(" ", global.format.forKey.Length) 
  485.     global.format.forVal        = "         Val  "
  486.     global.format.forVal.Length = LENGTH(global.format.forVal)
  487.     global.format.forVal.LeadIn = COPIES(" ", global.format.forVal.Length) 
  488.  
  489.  
  490.     /* define information for Level of depth */
  491.     global.depthOfOutput.T = "TopLevels only"
  492.     global.depthOfOutput.K = "TopLevels + Keys"
  493.     global.depthOfOutput.V = "TopLevels + Keys + Key-Values"
  494.  
  495.     /* define information for range of printout */
  496.     global.rangeOfPrintout.AI   = "ALL OS/2-INI-files in hand"
  497.     global.rangeOfPrintout.AT   = "ALL TopLevel-entries"
  498.     global.rangeOfPrintout.AK   = "ONE TopLevel-entry, ALL keys"
  499.     global.rangeOfPrintout.K    = "ONE Key"
  500.  
  501.     /* define information for output type, if hexadecimal value in hand */
  502.     global.hextypeOfOutput.H = "hexadecimal-string only"
  503.     global.hextypeOfOutput.F = "filtered ASCII-string only"
  504.     global.hextypeOfOutput.B = "both, hexadecimal- and filtered ASCII-string"
  505.  
  506.  
  507.     /* get defaults from previous settings of user from USER ("OS2.INI") */
  508.     global.config.showTopLevel      = "RGF Showini.cmd"   /* TopLevel name for this application */
  509.     global.config.add.indTopLevel   = 0         /* configuration defaults */
  510.     global.config.add.indKey        = 0         /* configuration defaults */
  511.     global.config.change.indKey     = 0         /* configuration defaults */
  512.     global.config.delete.indAllKeys = 0         /* configuration defaults */
  513.     global.config.delete.indKey     = 0         /* configuration defaults */
  514.     global.config.move.indAllKeys   = 0         /* configuration defaults */
  515.     global.config.move.indKey       = 0         /* configuration defaults */
  516.     global.config.copy.indAllKeys   = 0         /* configuration defaults */
  517.     global.config.copy.indKey       = 0         /* configuration defaults */
  518.     CALL read_ini_settings                      /* read previous values from USER */
  519.     CALL check_option_set                       /* show configuration option ? */
  520.  
  521.     /* Keys for backup-entries into USER resp. backup */
  522.     global.bkp.iOName      = "backup.info1.origName"
  523.     global.bkp.iBName      = "backup.info2.bkpName"
  524.     global.bkp.iTLentries  = "backup.info3.# of TopLevels"
  525.     global.bkp.iDTimeStart = "backup.info4.date/time.start"
  526.     global.bkp.iDTimeEnd   = "backup.info5.date/time.end"
  527.  
  528.     /* batch-mode-switches */
  529.     global.eMultipleFiles = 0                   /* batch mode with multiple files ? */
  530.  
  531.     RETURN
  532.  
  533. /*
  534.    check whether configuration-option should be displayed
  535. */
  536. CHECK_OPTION_SET: PROCEDURE EXPOSE global.
  537.     /* if any of the options are set, show configuration option in first menu */
  538.     global.showConfigure = global.config.add.indTopLevel | global.config.add.indKey | global.config.change.indKey |,
  539.                            global.config.delete.indAllKeys | global.config.delete.indKey | global.config.move.indAllKeys |,
  540.                            global.config.move.indKey | global.config.copy.indAllKeys | global.config.copy.indKey          
  541.     RETURN
  542.  
  543.  
  544. /* 
  545.    read settings stored in OS2.INI and define default settings
  546. */
  547. READ_INI_SETTINGS: PROCEDURE EXPOSE global.
  548.  
  549.     tmp = SysIni("USER", global.config.showTopLevel, "activateLog")     /* keep a LOG-file ? */
  550.     IF tmp <>"ERROR:" THEN global.config.eLog = tmp
  551.                       ELSE global.config.eLog = "1"                     /* default: log */
  552.  
  553.     tmp = SysIni("USER", global.config.showTopLevel,"add.TopLevel")
  554.     IF tmp <>"ERROR:" THEN global.config.add.indTopLevel = tmp
  555.  
  556.     tmp = SysIni("USER", global.config.showTopLevel,"add.Key")
  557.     IF tmp <>"ERROR:" THEN global.config.add.indKey = tmp
  558.  
  559.     tmp = SysIni("USER", global.config.showTopLevel,"change.Key")
  560.     IF tmp <>"ERROR:" THEN global.config.change.indKey = tmp
  561.  
  562.  
  563.     tmp = SysIni("USER", global.config.showTopLevel,"delete.AllKeys")
  564.     IF tmp <>"ERROR:" THEN global.config.delete.indAllKeys  = tmp
  565.  
  566.     tmp = SysIni("USER", global.config.showTopLevel,"delete.Key")
  567.     IF tmp <>"ERROR:" THEN global.config.delete.indKey  = tmp
  568.  
  569.     tmp = SysIni("USER", global.config.showTopLevel,"move.AllKeys")
  570.     IF tmp <>"ERROR:" THEN global.config.move.indAllKeys = tmp
  571.  
  572.     tmp = SysIni("USER", global.config.showTopLevel,"move.Key")
  573.     IF tmp <>"ERROR:" THEN global.config.move.indKey = tmp
  574.  
  575.     tmp = SysIni("USER", global.config.showTopLevel,"copy.AllKeys")
  576.     IF tmp <>"ERROR:" THEN global.config.copy.indAllKeys = tmp
  577.  
  578.     tmp = SysIni("USER", global.config.showTopLevel,"copy.Key")
  579.     IF tmp <>"ERROR:" THEN global.config.copy.indKey = tmp
  580.  
  581.  
  582.     tmp = SysIni("USER", global.config.showTopLevel, "showDepth")
  583.     IF tmp <> "ERROR:" THEN global.config.showDepth = tmp
  584.                        ELSE global.config.showDepth = "K"      /* default: TopLevels + Keys to show in output */
  585.  
  586.     tmp = SysIni("USER", global.config.showTopLevel, "showHexAs")
  587.     IF tmp <> "ERROR:" THEN global.config.showHexAs = tmp
  588.                        ELSE global.config.showHexAs = "H"      /* default: show them as hexadecimal-string */
  589.  
  590.     tmp = SysIni("USER", global.config.showTopLevel, "showLineLength")
  591.     IF tmp <> "ERROR:" THEN global.config.showLineLength = tmp
  592.  
  593.     IF tmp = "ERROR:" | tmp < "40" THEN
  594.        global.config.showLineLength = 120
  595.  
  596.     global.old.config.eLog              = global.config.eLog 
  597.     global.old.config.add.indTopLevel   = global.config.add.indTopLevel    
  598.     global.old.config.add.indKey        = global.config.add.indKey         
  599.     global.old.config.change.indKey     = global.config.change.indKey      
  600.     global.old.config.delete.indAllKeys = global.config.delete.indAllKeys  
  601.     global.old.config.delete.indKey     = global.config.delete.indKey      
  602.     global.old.config.move.indAllKeys   = global.config.move.indAllKeys    
  603.     global.old.config.move.indKey       = global.config.move.indKey        
  604.     global.old.config.copy.indAllKeys   = global.config.copy.indAllKeys    
  605.     global.old.config.copy.indKey       = global.config.copy.indKey        
  606.     global.old.config.showDepth         = global.config.showDepth
  607.     global.old.config.showHexAs         = global.config.showHexAs
  608.     global.old.config.showLineLength    = global.config.showLineLength
  609.  
  610.     RETURN
  611.  
  612.  
  613. /* 
  614.    write settings into OS2.INI, if values were changed
  615. */
  616. WRITE_INI_SETTINGS: PROCEDURE EXPOSE global.
  617.  
  618.  
  619.     IF global.old.config.eLog <> global.config.eLog THEN
  620.        CALL SysIni"USER", global.config.showTopLevel,"activateLog", global.config.eLog
  621.  
  622.     IF global.old.config.add.indTopLevel   <> global.config.add.indTopLevel    THEN
  623.        CALL SysIni"USER", global.config.showTopLevel,"add.TopLevel", global.config.add.indTopLevel
  624.  
  625.     IF global.old.config.add.indKey        <> global.config.add.indKey         THEN
  626.        CALL SysIni"USER", global.config.showTopLevel,"add.Key", global.config.add.indKey
  627.  
  628.     IF global.old.config.change.indKey     <> global.config.change.indKey      THEN
  629.        CALL SysIni"USER", global.config.showTopLevel,"change.Key", global.config.change.indKey
  630.  
  631.     IF global.old.config.delete.indAllKeys <> global.config.delete.indAllKeys  THEN
  632.        CALL SysIni"USER", global.config.showTopLevel,"delete.AllKeys", global.config.delete.indAllKeys
  633.  
  634.     IF global.old.config.delete.indKey     <> global.config.delete.indKey      THEN
  635.        CALL SysIni"USER", global.config.showTopLevel,"delete.Key", global.config.delete.indKey
  636.  
  637.     IF global.old.config.move.indAllKeys   <> global.config.move.indAllKeys    THEN
  638.        CALL SysIni"USER", global.config.showTopLevel,"move.AllKeys", global.config.move.indAllKeys
  639.  
  640.     IF global.old.config.move.indKey       <> global.config.move.indKey        THEN
  641.        CALL SysIni"USER", global.config.showTopLevel,"move.Key", global.config.move.indKey
  642.  
  643.     IF global.old.config.copy.indAllKeys   <> global.config.copy.indAllKeys    THEN
  644.        CALL SysIni"USER", global.config.showTopLevel,"copy.AllKeys", global.config.copy.indAllKeys
  645.  
  646.     IF global.old.config.copy.indKey       <> global.config.copy.indKey        THEN
  647.        CALL SysIni"USER", global.config.showTopLevel,"copy.Key", global.config.copy.indKey
  648.  
  649.  
  650.     IF global.old.config.showDepth <> global.config.showDepth THEN
  651.        CALL SysIni "USER", global.config.showTopLevel, "showDepth", global.config.showDepth 
  652.  
  653.     IF global.old.config.showHexAs <> global.config.showHexAs THEN
  654.        CALL SysIni "USER", global.config.showTopLevel, "showHexAs", global.config.showHexAs 
  655.  
  656.     IF global.old.config.showLineLength <> global.config.showLineLength THEN
  657.        CALL SysIni "USER", global.config.showTopLevel, "showLineLength", global.config.showLineLength 
  658.     RETURN
  659.  
  660.  
  661.  
  662. /*
  663.    show argument, if any, &
  664.    get yes/no answer
  665. */
  666. GET_YES_NO: PROCEDURE EXPOSE global.
  667.     IF ARG(1) <> "" THEN
  668.        CALL CHAROUT , ARG(1)
  669.  
  670.     CALL CHAROUT , global.iYellow
  671.     answer = get_answer("QYN")
  672.     CALL CHAROUT , global.iCyan
  673.  
  674.     SELECT
  675.        WHEN answer = "Q"   THEN SIGNAL halt
  676.        WHEN answer = "1B"x THEN RETURN 0                /* abort module */
  677.        WHEN answer = ""    THEN NOP                     /* do not change value */
  678.        OTHERWISE 
  679.             INTERPRET ARG(2) " = (answer = 'Y')"
  680.     END  
  681.  
  682.     RETURN 1            /* everything went o.k. */
  683.  
  684. /*
  685.    allow changing the settings for menus
  686. */
  687. SETTINGS_MENU: PROCEDURE EXPOSE global.
  688.     SAY
  689.     SAY
  690.     tmp = "Keep a logfile for backup/restore/update ?"
  691.     IF global.config.eLog THEN tmp = tmp global.yesHint
  692.                           ELSE tmp = tmp global.noHint
  693.     IF \get_yes_no(tmp, "global.config.eLog") THEN
  694.        RETURN
  695.     /* try to delete the LOG-file */
  696.     IF \global.config.eLog THEN '@ERASE "' || global.eLogFile ||'" 2>nul'
  697.  
  698.  
  699.     SAY
  700.     SAY
  701.     SAY "Settings for menu abilities:"
  702.     SAY
  703.  
  704.     tmp = "Allow adding a new TopLevel entry ?"
  705.     IF global.config.add.indTopLevel THEN tmp = tmp global.yesHint
  706.                                      ELSE tmp = tmp global.noHint
  707.     IF \get_yes_no(tmp, "global.config.add.indTopLevel") THEN
  708.        RETURN
  709.     SAY
  710.  
  711.     tmp = "Allow deleting ALL Keys in a TopLevel (= deleting a TopLevel) ?"
  712.     IF global.config.delete.indAllKeys THEN tmp = tmp global.yesHint
  713.                                        ELSE tmp = tmp global.noHint
  714.     IF \get_yes_no(tmp, "global.config.delete.indAllKeys") THEN
  715.        RETURN
  716.  
  717.     tmp = "Allow moving ALL Keys to another TopLevel ?"
  718.     IF global.config.move.indAllKeys THEN tmp = tmp global.yesHint
  719.                                      ELSE tmp = tmp global.noHint
  720.     IF \get_yes_no(tmp, "global.config.move.indAllKeys") THEN
  721.        RETURN
  722.  
  723.     tmp = "Allow copying ALL Keys to another TopLevel ?"
  724.     IF global.config.copy.indAllKeys THEN tmp = tmp global.yesHint
  725.                                      ELSE tmp = tmp global.noHint
  726.     IF \get_yes_no(tmp, "global.config.copy.indAllKeys") THEN
  727.        RETURN
  728.  
  729.     SAY
  730.  
  731.  
  732.  
  733.  
  734.     tmp = "Allow adding a new Key entry ?"
  735.     IF global.config.add.indKey THEN tmp = tmp global.yesHint
  736.                                 ELSE tmp = tmp global.noHint
  737.     IF \get_yes_no(tmp, "global.config.add.indKey") THEN
  738.        RETURN
  739.  
  740.     tmp = "Allow deleting a single Key ?"
  741.     IF global.config.delete.indKey THEN tmp = tmp global.yesHint
  742.                                    ELSE tmp = tmp global.noHint
  743.     IF \get_yes_no(tmp, "global.config.delete.indKey") THEN
  744.        RETURN
  745.  
  746.     tmp = "Allow moving a single Key ?"
  747.     IF global.config.move.indKey THEN tmp = tmp global.yesHint
  748.                                  ELSE tmp = tmp global.noHint
  749.     IF \get_yes_no(tmp, "global.config.move.indKey") THEN
  750.        RETURN
  751.  
  752.     tmp = "Allow copying a single Key ?"
  753.     IF global.config.copy.indKey THEN tmp = tmp global.yesHint
  754.                                  ELSE tmp = tmp global.noHint
  755.     IF \get_yes_no(tmp, "global.config.copy.indKey") THEN
  756.        RETURN
  757.  
  758.     tmp = "Allow changing (editing) a Key value ?"
  759.     IF global.config.change.indKey THEN tmp = tmp global.yesHint
  760.                                    ELSE tmp = tmp global.noHint
  761.     IF \get_yes_no(tmp, "global.config.change.indKey") THEN
  762.        RETURN
  763.  
  764.  
  765.     RETURN
  766.  
  767.  
  768.  
  769.  
  770. /*
  771.    allow changing the settings for printout
  772. */
  773. SETTINGS_PRINTOUT: PROCEDURE EXPOSE global.
  774.     SAY
  775.     SAY "Settings for formatting the printout:"
  776.     SAY
  777.  
  778.     /* how much information should be produced ? */
  779.     SAY global.iCyan || "How much information to include in output:" global.iYellow
  780.     SAY
  781.     choice = ""
  782.  
  783.     tmp = "       " global.iYellow || "T" global.iCyan global.depthOfOutput.T
  784.     IF global.config.showDepth = "T" THEN 
  785.        tmp = tmp global.defaultHint
  786.     SAY  tmp
  787.  
  788.     /* if no previous preferences, then this is the default */
  789.     tmp = "       " global.iYellow || "K" global.iCyan global.depthOfOutput.K
  790.     IF global.config.showDepth = "K" THEN 
  791.        tmp = tmp global.defaultHint
  792.     SAY tmp
  793.  
  794.     tmp = "       " global.iYellow || "V" global.iCyan global.depthOfOutput.V
  795.     IF global.config.showDepth = "V" THEN 
  796.        tmp = tmp global.defaultHint
  797.     SAY tmp
  798.  
  799.     SAY "       " global.iCyan   || "   [Esc] to return"
  800.  
  801.     /* define choices and get answer from user */
  802.     CALL CHAROUT , global.iYellow
  803.     answer = get_answer(choice || "TKVQ")
  804.     CALL CHAROUT , global.iCyan
  805.  
  806.     SELECT
  807.        WHEN answer = "1B"x THEN RETURN    /* Escape was pressed */
  808.        WHEN answer = "Q" THEN SIGNAL halt
  809.        WHEN answer = "" THEN NOP          /* do not change present setting */
  810.        OTHERWISE global.config.showDepth = answer
  811.     END  
  812.  
  813.     /* should hex value be presented additionally with a filtered ASCII-string ? */
  814.  
  815.     SAY global.iCyan || "hexadecimal values should be showed as:"
  816.     SAY
  817.     tmp = "       " global.iYellow || "H" global.iCyan global.hextypeOfOutput.H
  818.     IF global.config.showHexAs = "H" THEN
  819.        tmp = tmp global.defaultHint
  820.     SAY tmp
  821.  
  822.     tmp = "       " global.iYellow || "F" global.iCyan global.hextypeOfOutput.F
  823.     IF global.config.showHexAs = "F" THEN
  824.        tmp = tmp global.defaultHint
  825.     SAY tmp
  826.  
  827.     tmp = "       " global.iYellow || "B" global.iCyan global.hextypeOfOutput.B
  828.     IF global.config.showHexAs = "B" THEN
  829.        tmp = tmp global.defaultHint
  830.     SAY tmp
  831.  
  832.     SAY "       " global.iCyan   || "   [Esc] to return"
  833.  
  834.     /* define choices and get answer from user */
  835.     CALL CHAROUT , global.iYellow
  836.     answer = get_answer("HFBQ")
  837.     CALL CHAROUT , global.iCyan
  838.  
  839.     SELECT
  840.        WHEN answer = "1B"x THEN RETURN
  841.        WHEN answer = ""    THEN NOP
  842.        WHEN answer = "Q"   THEN SIGNAL halt
  843.        OTHERWISE global.config.showHexAs = answer       /* assign new value */
  844.     END
  845.  
  846.  
  847.     /* get desired line-length */
  848.     DO FOREVER
  849.        SAY global.iCyan || "How many characters per line (" || global.iYellow || global.config.showLineLength || global.iCyan "default, 40 minimum) ? " global.iYellow
  850.        /* define choices and get answer from user */
  851.        CALL CHAROUT , global.iYellow
  852.        answer = get_answer("Q", "999999")
  853.        CALL CHAROUT , global.iCyan
  854.  
  855.        SELECT
  856.           WHEN answer = "Q"   THEN SIGNAL halt
  857.           WHEN answer = "1B"x THEN RETURN
  858.           WHEN answer = ""    THEN LEAVE        /* do not change setting */
  859.           OTHERWISE
  860.                IF answer >= 40 THEN          /* minimum line-length >= 40 characters */
  861.                DO
  862.                   global.config.showLineLength = answer  /* new value */
  863.                   LEAVE
  864.                END
  865.        END  
  866.  
  867.        SAY
  868.        CALL error_msg "A minimum line-length of 40 characrters is required !", "come back"
  869.        SAY
  870.        SAY "please retry."
  871.        SAY
  872.     END
  873.  
  874.     RETURN
  875.  
  876.  
  877.  
  878.  
  879. /* 
  880.    determine largest entry
  881. */
  882. LARGEST_GENERIC: PROCEDURE EXPOSE global. stemIni. stemTopLevel. stemKey.
  883.     largest = 0
  884.     dynCode =         "DO i = 1 TO" ARG(1) || ".0;"
  885.     dynCode =         dynCode "largest = MAX(largest, LENGTH(" ARG(1) || ".i));"
  886.     dynCode = dynCode "END;"
  887.     dynCode = dynCode ARG(1) || ".iLargest = MIN(largest, (global.iColumns - (2+ LENGTH(" || ARG(1) || ".0))))"
  888.     INTERPRET dynCode                /* execute REXX-code in variable dynCode */
  889.  
  890.     RETURN 
  891.  
  892.  
  893. /* 
  894.   display stem-contents on screen
  895. */
  896. SHOW_GENERIC: PROCEDURE EXPOSE global. stemIni. stemTopLevel. stemKey.
  897.     stemName = ARG(1)
  898.  
  899.     tmpANSILength = global.iScrLength * 2       /* length of the two ANSI-color control chars */
  900.  
  901.     dyncode =         "nrLength = LENGTH(" || stemName || ".0);"
  902.     dyncode = dyncode "columns = global.iColumns % (" || stemName || ".iLargest + nrLength + 2);"
  903.  
  904.     dyncode = dyncode "IF columns < 1 THEN columns = 1;"
  905.  
  906.     dynCode = dyncode "j = (" || stemName || ".0 + columns - 1) % columns;"
  907.  
  908.     dynCode = dynCode "DO i = 1 TO j;"
  909.     dynCode = dynCode "   tmp = '';" 
  910.     dynCode = dynCode "   DO k = 1 TO columns;" /*  m = (i - 1) * columns + k    /* order in lines */ */
  911.     dynCode = dynCode "       m = (k - 1) * j + i;"
  912.     dynCode = dynCode "       IF " stemName || ".m <> '' THEN;"
  913.     dynCode = dynCode "       tmp = tmp LEFT((global.iCyan || RIGHT(m, nrLength) global.iYellow || "stemName ||".m), (" stemName || ".iLargest + nrLength + 1 + tmpANSILength));"
  914.     dynCode = dynCode "    END;" 
  915.     dynCode = dynCode "    CALL CHAROUT , tmp;"
  916.     dynCode = dyncode "    IF (LENGTH(tmp) - tmpANSILength * columns) < global.iColumns THEN SAY;"
  917.  
  918.     dynCode = dyncode "END"
  919.  
  920.     INTERPRET dynCode
  921.  
  922.     RETURN
  923.  
  924.  
  925.  
  926. /* 
  927.    one of Knuth's algorithms to sort
  928.    ARG(1) ... stemname of array to sort
  929.    ARG(2) ... optional, if given, exact comparison (no forced uppercase), taking leading
  930.               and trailing blanks into account
  931. */
  932. SORT_GENERIC: PROCEDURE EXPOSE stemIni. stemTopLevel. stemKey. stemTargetTopLevel. stemTargetKey.
  933.  
  934.    /* define M for passes, build REXX-code */
  935.    dynCode = "M = 1; DO WHILE (9 * M + 4) <" ARG(1) || ".0 ; M = M * 3 + 1; END"
  936.    INTERPRET dynCode                /* execute REXX-code in variable dynCode */
  937.  
  938.    /* sort stem, build REXX-code */
  939.    dynCode = "DO WHILE M > 0; K = " ARG(1) || ".0 - M; DO J = 1 TO K; Q = J;"
  940.    dynCode = dynCode "DO WHILE Q > 0; L = Q + M;"
  941.  
  942.    IF ARG() < 2 THEN    /* uppercase comparison, ignore leading and trailing blanks */
  943.       dynCode = dynCode "IF TRANSLATE(" || ARG(1) || ".Q) <= TRANSLATE(" || ARG(1) || ".L) THEN LEAVE;"
  944.    ELSE                 /* exact comparison */ 
  945.       dynCode = dynCode "IF" ARG(1) || ".Q <<=" ARG(1) || ".L THEN LEAVE;"
  946.  
  947.    dynCode = dynCode "tmp =" ARG(1) || ".Q; tmp.origValue =" ARG(1) || ".Q.origValue;"
  948.    dynCode = dynCode "tmp.type =" ARG(1) || ".Q.type; tmp.filterValue =" ARG(1) || ".Q.filterValue;"
  949.  
  950.    dynCode = dynCode ARG(1) || ".Q =" ARG(1) || ".L;" ARG(1) || ".Q.origValue =" ARG(1) || ".L.origValue;"
  951.    dynCode = dynCode ARG(1) || ".Q.type =" ARG(1) || ".L.type;" ARG(1) || ".Q.filterValue =" ARG(1) || ".L.filterValue;"
  952.  
  953.    dynCode = dynCode ARG(1) || ".L = tmp;" ARG(1) || ".L.origValue = tmp.origValue;"
  954.    dynCode = dynCode ARG(1) || ".L.type = tmp.type;" ARG(1) || ".L.filterValue  = tmp.filterValue;"
  955.  
  956.    dynCode = dynCode "Q = Q - M; END; END; M = M % 3; END"
  957.  
  958.    INTERPRET dynCode                /* execute REXX-code in variable dynCode */
  959.  
  960.    RETURN
  961.  
  962.  
  963. /*
  964.     get answer from user, if a 3rd argument is supplied, then force an entry
  965.     ARG(1) ... string of valid letters
  966.     ARG(2) ... upper bound of an arithmetic value
  967.     ARG(3) ... if given, force user to enter at least one character
  968. */
  969. GET_ANSWER: PROCEDURE EXPOSE global.
  970.     validLetters = ARG(1)
  971.     upperBound   = ARG(2)       /* 0 - upperBound */
  972.  
  973.     i = 0
  974.     answer = ""
  975.  
  976.     DO FOREVER
  977.        tmp = TRANSLATE(SysGetKey("noecho"))
  978.  
  979.        IF tmp = "0D"x THEN                      /* CR-was pressed */
  980.        DO
  981.           IF ARG(3) = "" | i > 0 THEN LEAVE
  982.           CALL BEEP 2000, 250
  983.           ITERATE
  984.        END
  985.  
  986.        IF tmp = "1B"x THEN                      /* Escape was pressed */
  987.        DO
  988.           answer = tmp
  989.           LEAVE
  990.        END
  991.  
  992.        IF tmp = "08"x THEN                      /* Backspace was pressed */
  993.        DO
  994.           IF i = 0 THEN                         /* already at first position */
  995.           DO
  996.              CALL BEEP 2000, 250
  997.              ITERATE
  998.           END
  999.  
  1000.           CALL CHAROUT , tmp                    /* backspace */
  1001.           CALL CHAROUT , " "                    /* erase character */
  1002.           CALL CHAROUT , tmp
  1003.           i = i - 1
  1004.  
  1005.           IF i = 0 THEN answer = ""             /* adjust value of answer */
  1006.           ELSE answer = SUBSTR(answer, 1, i)
  1007.  
  1008.           ITERATE
  1009.        END
  1010.  
  1011.        IF POS(tmp, validLetters) > 0 THEN
  1012.        DO
  1013.           IF answer = "" THEN
  1014.           DO
  1015.              answer = tmp
  1016.              CALL CHAROUT , answer
  1017.              LEAVE
  1018.           END
  1019.  
  1020.           CALL BEEP 2000, 250
  1021.           ITERATE
  1022.        END
  1023.  
  1024.        IF upperBound <> "" THEN
  1025.        DO
  1026.           IF i = 0 THEN
  1027.           DO
  1028.              IF POS(tmp, "0123456789") > 0 & tmp <= upperBound THEN
  1029.              DO
  1030.                 CALL CHAROUT , tmp
  1031.                 answer = tmp
  1032.                 i = i + 1
  1033.                 IF answer = 0 | LENGTH(upperBound) = 1 | (answer || "0" > upperBound) THEN LEAVE
  1034.                 ITERATE
  1035.              END
  1036.           END
  1037.           ELSE
  1038.           DO
  1039.              IF POS(tmp, "0123456789") > 0 THEN
  1040.              DO
  1041.                 IF answer || tmp <= upperBound THEN
  1042.                 DO
  1043.                    CALL CHAROUT , tmp 
  1044.                    answer = answer || tmp
  1045.                    i = i + 1
  1046.    
  1047.                    IF LENGTH(answer) = LENGTH(upperBound) | (answer || "0" > upperBound) THEN LEAVE
  1048.                    ITERATE
  1049.                 END
  1050.              END
  1051.           END
  1052.        END
  1053.  
  1054.        CALL BEEP 2000, 250
  1055.     END
  1056.     SAY
  1057.  
  1058.     RETURN answer
  1059.  
  1060.  
  1061. /* supply defaults */
  1062. INI_DEFAULT: PROCEDURE EXPOSE stemIni. global.
  1063.     DROP stemIni.
  1064.     stemIni.   = ""
  1065.  
  1066.     IF ARG(1) = "1" THEN        /* show user INI-names themselves */
  1067.     DO
  1068.        stemIni.1 = "USER (OS2.INI)"   /* OS2.INI */
  1069.        stemIni.2 = "SYSTEM (OS2SYS.INI)"/* OS2SYS.INI */
  1070.     END
  1071.     ELSE
  1072.     DO
  1073.        stemIni.1 = "USER"               /* OS2.INI */
  1074.        stemIni.2 = "SYSTEM"             /* OS2SYS.INI */
  1075.     END
  1076.  
  1077.     stemIni.0 = 2                       /* number of elements in array */
  1078.  
  1079.     CALL largest_generic("stemIni")
  1080.     RETURN
  1081.  
  1082.  
  1083.  
  1084. SEARCH_INI: PROCEDURE EXPOSE global. stemIni.
  1085.     SAY
  1086.     SAY global.iCyan || "Enter:"
  1087.     SAY
  1088.     SAY "       " global.iYellow "1" global.iCyan " to scan all attached drives" global.defaultHint
  1089.     SAY "       " global.iYellow "2" global.iCyan " to scan all local drives"
  1090.     SAY "       " global.iYellow "3" global.iCyan " to scan remote drives"
  1091.     SAY "       " global.iYellow "->" global.iCyan 'or any valid drive letter from "' || global.iYellow || 'A' || global.iCyan || '"-"' || global.iYellow || 'Z' || global.iCyan || '"'
  1092.  
  1093.     /* allow for choices 1-3 and drive letters A-Z */
  1094.     CALL CHAROUT , global.iYellow
  1095.     answer = get_answer("123ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  1096.     CALL CHAROUT , global.iCyan
  1097.  
  1098.     SELECT
  1099.        WHEN answer = "1B"x THEN                 /* user aborted, return unchanged array */
  1100.             RETURN 
  1101.        WHEN answer = "1" | answer = "" THEN
  1102.              map = SysDriveMap(, "USED")        /* get a list of all used drives */
  1103.        WHEN answer = "2" THEN
  1104.              map = SysDriveMap(, "LOCAL")       /* get a list of all local drives */
  1105.        WHEN answer = "3" THEN
  1106.              map = SysDriveMap(, "REMOTE")      /* get a list of all remote drives */
  1107.        OTHERWISE
  1108.              map = answer || ":"                /* drive - letter */
  1109.     END  
  1110.  
  1111.     /* drop existing array */
  1112.     DROP stemIni.
  1113.     stemIni.  = ""
  1114.     stemIni.0 = 0
  1115.  
  1116.     DO WHILE map <> ""
  1117.        PARSE VAR map drive map
  1118.  
  1119.        SAY global.iCyan || "scanning drive" global.iYellow ||drive||global.iCyan "for accessible INI-files..."
  1120.        CALL SysFileTree drive || "\*.ini", "file", "FSO"
  1121.  
  1122.        DO i = 1 TO file.0
  1123.           /* check whether accessible from OS/2, i.e. an OS/2 INI-file */
  1124.           ok = SysIni(file.i, 'ALL:', 'TopLevel')
  1125.           IF ok <> "ERROR:" & TopLevel.0 > 0 THEN
  1126.           DO
  1127.              SAY global.iCyan || "     file" global.iYellow || file.i || global.iCyan "found..."
  1128.              stemIni.0 = stemIni.0 + 1
  1129.              j = stemIni.0
  1130.              stemIni.j = file.i
  1131.           END
  1132.        END
  1133.        SAY 
  1134.     END
  1135.  
  1136.     CALL largest_generic("stemIni")
  1137.     RETURN 
  1138.  
  1139.  
  1140. /* 
  1141.         Display INI-files which are accessible
  1142. */
  1143. SHOW_INIS: PROCEDURE EXPOSE global. stemIni. stemTopLevel. stemKey.
  1144.  
  1145.    stemIni.0 = 0        /* no INI-files initially, setup */
  1146.  
  1147.    DO FOREVER
  1148.       IF WORD(stemIni.1, 1) = "USER" | stemIni.0 = 0 THEN
  1149.          CALL ini_default("1")     /* show file-names of system-INIs */
  1150.  
  1151.       SAY
  1152.       SAY global.iRedWhite || CENTER("accessible OS/2-INI-files", global.iColumns) || global.iCyan
  1153.       SAY 
  1154.       SAY global.iCyan'The following' global.iYellow || stemIni.0 global.iCyan'OS/2 INI-files are accessible:'
  1155.       SAY
  1156.  
  1157.       CALL show_generic "stemIni"      /* show stem contents */
  1158.  
  1159.       IF WORD(stemIni.1, 1) = "USER" THEN       /* if initial settings, then */
  1160.          CALL ini_default                       /* get symbolic-names only for RxUtils-functions */
  1161.  
  1162.       SAY
  1163.       tmp = global.enterString || stemIni.0 global.iCyan || 'to view INI-file,',
  1164.             global.iYellow || '0' || global.iCyan 'to end;',
  1165.             global.iYellow || 's' || global.iCyan || 'can drive[s] for INI-files,',
  1166.             global.iYellow || 'p' || global.iCyan || 'rint all INI-files'
  1167.  
  1168.       IF global.showConfigure THEN              /* show configuration option ? */
  1169.           tmp = tmp || ", " ||  global.iYellow || 'c' || global.iCyan || 'onfigure'
  1170.  
  1171.       SAY tmp
  1172.  
  1173.       /* define choices and upper numeric boundary and get answer from user */
  1174.       CALL CHAROUT , global.iYellow
  1175.       answer = get_answer("PQCS", stemIni.0)
  1176.       CALL CHAROUT , global.iCyan
  1177.       SAY
  1178.  
  1179.       SELECT
  1180.          WHEN answer = "" | answer = 0 | answer = "1B"x THEN LEAVE
  1181.  
  1182.          WHEN answer = "Q" THEN SIGNAL halt
  1183.    
  1184.          WHEN answer = "C" THEN         /* configure program */
  1185.               DO
  1186.                  CALL settings_printout
  1187.                  CALL settings_menu
  1188.                  CALL check_option_set  /* show configuration option ? */
  1189.               END
  1190.    
  1191.          WHEN answer = 'S' THEN         /* search for all OS/2-INI-files */
  1192.                CALL search_ini
  1193.                                        
  1194.          WHEN answer = 'P' THEN         /* print all OS/2-INI-files */
  1195.               CALL print
  1196.  
  1197.          OTHERWISE                      /* display top-levels of chosen INI-file */
  1198.               DO
  1199.                  CALL show_toplevel answer
  1200.                  SAY
  1201.               END
  1202.       END  
  1203.       SAY
  1204.       SAY
  1205.    END
  1206.    SAY global.iNormal
  1207.    RETURN
  1208.  
  1209.  
  1210. /*
  1211.         read TopLevel-entries
  1212. */
  1213. READ_TOPLEVEL: PROCEDURE EXPOSE stemIni. stemTopLevel. stemKey. global. 
  1214.     DROP stemTopLevel.
  1215.     stemTopLevel. = ""
  1216.  
  1217.     iIni = ARG(1)
  1218.     ok = SysIni(stemIni.iIni, 'ALL:', 'stemTopLevel')
  1219.     IF ok = "ERROR:" THEN
  1220.     DO
  1221.        stemTopLevel.0 = 0
  1222.     END
  1223.     ELSE
  1224.     DO i = 1 TO stemTopLevel.0
  1225.        CALL check_value stemTopLevel.i  /* determine and prepare value in hand */
  1226.        stemTopLevel.i.origValue   = stemTopLevel.i
  1227.        stemTopLevel.i             = strings.displayValue
  1228.        stemTopLevel.i.type        = strings.type
  1229.        stemTopLevel.i.filterValue = strings.filterValue
  1230.     END
  1231.  
  1232.     CALL largest_generic("stemTopLevel")
  1233.     RETURN 
  1234.  
  1235.  
  1236. /*
  1237.    ask for new TopLevel-name,
  1238.    return name, if it does not exist, NULL-string if it exists
  1239. */
  1240. CREATE_NEW_TOPLEVEL: PROCEDURE EXPOSE global. stemIni.
  1241.     iniName = VALUE("stemIni." || ARG(1))
  1242.  
  1243.     PARSE VALUE edit_value("A", "new Top-Level-name ('Application'):", , "H A", "Enter new Top-Level-Name") WITH tmpState tmpResult
  1244.  
  1245.     IF tmpState = "NOK" THEN RETURN ""          /* user aborted entry */
  1246.  
  1247.     /* check whether Top-Level exists already */
  1248.     ok = SysIni(iniName, tmpResult, 'ALL:', 'stemKey')
  1249.  
  1250.     IF ok <> "ERROR:" THEN
  1251.     DO
  1252.        CALL check_value tmpResult
  1253.        CALL error_msg "TopLevel:" || global.iCyan "[" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iRedWhite || "exists already !"
  1254.        RETURN ""
  1255.     END
  1256.  
  1257.     RETURN tmpResult
  1258.  
  1259.  
  1260. /*
  1261.    ask for new Key-name,
  1262.    return name, if it does not exist, NULL-string if it exists
  1263. */
  1264. CREATE_NEW_KEY: PROCEDURE EXPOSE global. stemIni. stemTopLevel.
  1265.     iniName = ARG(1)                            /* INI-name */
  1266.     topName = ARG(2)                            /* Top Level name */
  1267.  
  1268.     PARSE VALUE edit_value("A", "new Key-name:", , "H A", "Enter new Key-Name") WITH tmpState tmpResult
  1269.  
  1270.     IF tmpState = "NOK" THEN RETURN ""          /* user aborted entry */
  1271.  
  1272.     /* check whether Key exists already */
  1273.     ok = SysIni(iniName, topName, tmpResult)
  1274.  
  1275.     IF ok <> "ERROR:" THEN
  1276.     DO
  1277.        CALL check_value tmpResult
  1278.        CALL error_msg "Key:" || global.iCyan "[" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iRedWhite || "exists already !"
  1279.        RETURN ""
  1280.     END
  1281.  
  1282.     RETURN tmpResult
  1283.  
  1284.  
  1285.  
  1286. /* 
  1287.         Display TOPLEVEL-entries of chosen INI-file
  1288. */
  1289. SHOW_TOPLEVEL: PROCEDURE EXPOSE global. stemIni. stemTopLevel. stemKey. 
  1290.    iIni     = ARG(1)
  1291.    filename = stemIni.iIni
  1292.  
  1293.    CALL read_toplevel(iIni)               /* read all toplevel-entries */
  1294.    global.TopLSortState = "U"                  /* unsorted */
  1295.  
  1296.    DO FOREVER
  1297.       IF stemTopLevel.0 = 0 THEN
  1298.       DO
  1299.         
  1300.       END
  1301.  
  1302.       SAY
  1303.       SAY global.iRedWhite || CENTER("available TopLevel-entries (Application entries)", global.iColumns) || global.iCyan
  1304.       SAY
  1305.       SAY global.iCyan'INI-file:' global.iYellow ||filename
  1306.       SAY
  1307.       SAY global.iCyan'The following' global.iYellow||stemTopLevel.0 global.iCyan'TopLevel entries are available:'
  1308.       SAY
  1309.       CALL show_generic "stemTopLevel"      /* show stem contents */
  1310.  
  1311.       SAY
  1312.  
  1313.       choices = "CPSUQ"                  /* define choices */
  1314.  
  1315.       tmp = global.enterString || stemTopLevel.0 global.iCyan || 'to view TopLevel-entries,',
  1316.             global.iYellow || '0' || global.iCyan 'to return;'
  1317.  
  1318.       IF global.config.add.indTopLevel THEN
  1319.       DO
  1320.          tmp = tmp global.iYellow || 'a' || global.iCyan || 'dd,'
  1321.          choices = choices || "A"         /* add the adding choice */
  1322.       END
  1323.  
  1324.       tmp = tmp global.iYellow || 's' || global.iCyan || 'ort,',
  1325.             global.iYellow || 'u' || global.iCyan || 'nsort,',
  1326.             global.iYellow || 'p' || global.iCyan || 'rint'
  1327.  
  1328.       SAY tmp
  1329.  
  1330.       /* define choices and upper numeric boundary and get answer from user */
  1331.       CALL CHAROUT , global.iYellow
  1332.       answer = get_answer(choices, stemTopLevel.0)
  1333.       CALL CHAROUT , global.iCyan
  1334.       SAY
  1335.  
  1336.       SELECT
  1337.          WHEN answer = "" | answer = 0 | answer = "1B"x THEN LEAVE
  1338.  
  1339.          WHEN answer = "Q" THEN SIGNAL halt
  1340.  
  1341.          WHEN answer = 'P' THEN    /* print all TopLevel-entries */
  1342.               DO
  1343.                  CALL print iIni                        /* all TopLevel-entries will be sorted */
  1344.                  IF global.TopLSortState = "U" THEN
  1345.                     CALL read_toplevel(iIni)       /* reread all toplevel-entries */
  1346.  
  1347.               END
  1348.    
  1349.          WHEN answer = "C" THEN         /* configure program */
  1350.               DO
  1351.                  CALL settings_printout
  1352.                  CALL settings_menu
  1353.                  CALL check_option_set  /* show configuration option ? */
  1354.               END
  1355.  
  1356.          WHEN answer = 'S' THEN    /* sort TopLevel-entries */
  1357.               DO
  1358.                  CALL sort_generic "stemTopLevel"
  1359.                  global.TopLSortState = "S"
  1360.               END
  1361.     
  1362.          WHEN answer = 'U' THEN    /* unsort TopLevel-entries, i.e. reread */
  1363.               DO
  1364.                  CALL read_toplevel(iIni)          /* read all toplevel-entries */
  1365.                  global.TopLSortState = "U"  
  1366.               END
  1367.    
  1368.          WHEN answer = "A" THEN      /* add a new key-entry */
  1369.               DO
  1370.                  newTopLevel = create_new_toplevel(iIni)
  1371.                  IF newTopLevel = "" THEN ITERATE       /* no entry, or exists already */
  1372.         
  1373.                  CALL check_value newTopLevel
  1374.                  newTopLevel.displayValue = strings.displayValue
  1375.                  SAY
  1376.                  SAY "new Top-Level:" global.iRedWhite || newTopLevel.displayValue || global.iCyan
  1377.                  SAY
  1378.         
  1379.                  newKey = create_new_key(stemIni.iIni, newTopLevel)
  1380.                  IF newKey = "" THEN ITERATE            /* no entry, or exists already */
  1381.  
  1382.                  SAY
  1383.                  SAY "new Top-Level:" global.iRedWhite || newTopLevel.displayValue || global.iCyan
  1384.                  SAY
  1385.                  CALL check_value newKey
  1386.                  newKey.displayValue = strings.displayValue
  1387.         
  1388.                  PARSE VALUE edit_value("A", "new value for Key:" global.iRedWhite || newKey.displayValue || global.iCyan, , , "Enter new Key-Value") WITH tmpState tmpResult
  1389.                  IF tmpState = "NOK" THEN ITERATE
  1390.  
  1391.                  newKeyValue = tmpResult
  1392.         
  1393.                  val = SysIni(stemIni.iIni, newTopLevel, newKey, newKeyValue)
  1394.         
  1395.                  CALL read_toplevel(iIni)               /* read all toplevel-entries */
  1396.         
  1397.                  IF global.TopLSortState = "S" THEN
  1398.                     CALL sort_generic "stemTopLevel"
  1399.               END
  1400.     
  1401.          OTHERWISE              /* show keys of chosen TopLevel */
  1402.               DO
  1403.                  IF show_key(ARG(1), answer) THEN
  1404.                  DO
  1405.                     CALL read_toplevel(iIni)               /* read all toplevel-entries */
  1406.            
  1407.                     IF global.TopLSortState = "S" THEN
  1408.                        CALL sort_generic "stemTopLevel"
  1409.                  END
  1410.            
  1411.                  SAY
  1412.               END
  1413.       END  
  1414.       SAY
  1415.    END
  1416.    SAY global.iNormal
  1417.    RETURN
  1418.  
  1419.  
  1420. /*
  1421.      copy keys to another TopLevel
  1422. */
  1423. COPY_KEYS: PROCEDURE EXPOSE global. stemIni. stemTopLevel. stemKey.
  1424.     iIni     = ARG(1)           /* OS/2-ini file */
  1425.     iTopL    = ARG(2)           /* index into stemTopLevel */
  1426.     all_keys = ARG(3)           /* true if all keys to be copied, else false */
  1427.     iKey     = ARG(4)           /* index into stemKey., if only one key is to be copied */
  1428.  
  1429.     dirty = 0                   /* reread TopLevel entries, if one was added as a target */
  1430.  
  1431.     SAY
  1432.  
  1433.     DO FOREVER
  1434.        tmp = "copy" 
  1435.    
  1436.        IF all_keys THEN
  1437.           tmp = tmp "ALL keys ... "
  1438.        ELSE
  1439.           tmp = tmp "key ... "
  1440.    
  1441.        SAY global.iRedWhite 
  1442.        SAY tmp
  1443.        SAY global.iRedWhite || CENTER("available TopLevel-entries (Application entries)", global.iColumns) || global.iCyan
  1444.        SAY
  1445.    
  1446.        SAY global.iCyan'INI-file:' global.iYellow || stemIni.iIni
  1447.        SAY global.iCyan'TopLevel:' global.iYellow || stemTopLevel.iTopL
  1448.    
  1449.        IF \all_keys THEN        /* single key, show it */
  1450.        DO
  1451.           SAY global.iCyan'     Key:' global.iYellow || stemKey.iKey
  1452.        END
  1453.    
  1454.        SAY
  1455.        SAY global.iCyan'The following' global.iYellow||stemTopLevel.0 global.iCyan'TopLevel entries are available:'
  1456.        SAY
  1457.        CALL show_generic "stemTopLevel"      /* show stem contents */
  1458.        SAY
  1459.    
  1460.        tmp = global.iCyan || "Enter the TopLevel you wish to" global.iYellow|| "copy" || global.iCyan "to:",
  1461.              global.iYellow || "1" || global.iCyan || "-" ||,
  1462.              global.iYellow|| stemTopLevel.0 global.iCyan || 'to identify target TopLevel,',
  1463.              global.iYellow || '0' || global.iCyan 'to return;',
  1464.              global.iYellow || 'c' || global.iCyan || 'reate new TopLevel'
  1465.    
  1466.        SAY tmp
  1467.    
  1468.        /* define choices and upper numeric boundary and get answer from user */
  1469.        CALL CHAROUT , global.iYellow
  1470.        answer = get_answer("CQ", stemTopLevel.0)
  1471.        CALL CHAROUT , global.iCyan
  1472.    
  1473.        SAY
  1474.        SELECT
  1475.           WHEN answer = "" | answer = 0 | answer = "1B"x THEN LEAVE
  1476.    
  1477.           WHEN answer = "Q" THEN SIGNAL halt
  1478.    
  1479.           WHEN answer = "C" THEN              /* create new TopLevel */
  1480.                DO
  1481.                   newTopLevel = create_new_toplevel(iIni)
  1482.                   IF newTopLevel = "" THEN ITERATE    /* no entry, or exists already */
  1483.          
  1484.                   CALL check_value newTopLevel
  1485.                   SAY
  1486.                   SAY "new Top-Level:" global.iRedWhite || strings.displayValue || global.iCyan
  1487.                   SAY
  1488.    
  1489.                   targetTopLevel = newTopLevel
  1490.                   dirty = 1                   /* reread TopLevels */
  1491.                END
  1492.    
  1493.           WHEN answer = iTopL THEN    /* target same as source ? */
  1494.                DO
  1495.                   CALL error_msg "Target TopLevel:" || global.iCyan "[" || global.iYellow || stemTopLevel.iTopL || global.iCyan || "]" global.iRedWhite || "same as source TopLevel !"
  1496.                   ITERATE
  1497.                END
  1498.    
  1499.           OTHERWISE targetTopLevel = stemTopLevel.answer.origValue
  1500.        END
  1501.    
  1502.        /* copy keys to TopLevel */
  1503.        SAY global.iCyan || "Copying keys into TopLevel [" || global.iYellow || stemTopLevel.answer || global.iCyan || "] ..."
  1504.        DO i = 1 TO stemKey.0
  1505.           IF \all_keys THEN i = iKey       /* assign key-index */
  1506.    
  1507.           val = SysIni(stemIni.iIni, stemTopLevel.iTopL.origValue, stemKey.i.origValue)
  1508.           CALL SysIni stemIni.iIni, targetTopLevel, stemKey.i.origValue, val
  1509.  
  1510.           SAY global.iCyan"Key: [" || global.iYellow || stemKey.i || global.iCyan || "] copied."
  1511.    
  1512.           IF \all_keys THEN LEAVE          /* leave loop prematurely */
  1513.        END
  1514.        LEAVE
  1515.     END
  1516.  
  1517.     RETURN dirty
  1518.  
  1519.  
  1520.  
  1521. /*
  1522.         move keys to another TopLevel, if only one key, then allow for renaming it
  1523. */
  1524. MOVE_KEYS: PROCEDURE EXPOSE global. stemIni. stemTopLevel. stemKey.
  1525.     iIni     = ARG(1)           /* OS/2-ini file */
  1526.     iTopL    = ARG(2)           /* index into stemTopLevel */
  1527.     all_keys = ARG(3)           /* true if all keys to be moved, else false */
  1528.     iKey     = ARG(4)           /* index into stemKey., if only one key is to be moved */
  1529.  
  1530.     TopLevelCreated = 0         /* was a new TopLevel created ? */
  1531.     KeyMoved = 0                /* was a key moved ? */
  1532.  
  1533.     SAY
  1534.     DO FOREVER
  1535.        tmp = "move"
  1536.    
  1537.        IF all_keys THEN
  1538.           tmp = tmp "ALL keys ... "
  1539.        ELSE
  1540.           tmp = tmp "key ... "
  1541.  
  1542.        SAY global.iRedWhite 
  1543.        SAY tmp
  1544.        SAY global.iRedWhite || CENTER("available TopLevel-entries (Application entries)", global.iColumns) || global.iCyan
  1545.  
  1546.        SAY
  1547.        SAY global.iCyan'INI-file:' global.iYellow || stemIni.iIni
  1548.        SAY global.iCyan'TopLevel:' global.iYellow || stemTopLevel.iTopL
  1549.  
  1550.        IF \all_keys THEN        /* single key, show it */
  1551.        DO
  1552.           SAY global.iCyan'     Key:' global.iYellow || stemKey.iKey
  1553.        END
  1554.  
  1555.  
  1556.        SAY
  1557.        SAY global.iCyan'The following' global.iYellow||stemTopLevel.0 global.iCyan'TopLevel entries are available:'
  1558.        SAY
  1559.        CALL show_generic "stemTopLevel"      /* show stem contents */
  1560.        SAY
  1561.        choices = "CQ"
  1562.    
  1563.        tmp = global.iCyan || "Enter the TopLevel you wish to" global.iYellow|| "move" 
  1564.    
  1565.        IF all_keys THEN
  1566.           tmp = tmp || global.iCyan "these keys to:"
  1567.        ELSE
  1568.           tmp = tmp || global.iCyan "the key to:"
  1569.    
  1570.        tmp = tmp  global.iYellow || "1" || global.iCyan || "-" ||,
  1571.              global.iYellow|| stemTopLevel.0 global.iCyan || 'to identify target TopLevel,',
  1572.              global.iYellow || '0' || global.iCyan 'to return;'
  1573.    
  1574.        IF \all_keys THEN
  1575.        DO
  1576.           tmp = tmp global.iYellow || 'r' || global.iCyan || 'ename key,'
  1577.           choices = choices || "R"
  1578.        END
  1579.           tmp = tmp global.iYellow || 'c' || global.iCyan || 'reate new TopLevel'
  1580.    
  1581.        SAY tmp
  1582.    
  1583.        /* define choices and upper numeric boundary and get answer from user */
  1584.        CALL CHAROUT , global.iYellow
  1585.        answer = get_answer(choices, stemTopLevel.0)
  1586.        CALL CHAROUT , global.iCyan
  1587.    
  1588.        SAY
  1589.        SELECT
  1590.           WHEN answer = "" | answer = 0 | answer = "1B"x THEN LEAVE
  1591.    
  1592.           WHEN answer = "Q" THEN SIGNAL halt
  1593.    
  1594.           WHEN answer = "R" THEN              /* just rename present key */
  1595.                DO
  1596.                   PARSE VALUE edit_value("E", stemKey.iKey, , "H A", "Enter new Key-Name") WITH tmpState tmpResult
  1597.                   IF tmpState = "NOK" | tmpResult = "" THEN RETURN TopLevelCreated
  1598.                   newKey = tmpResult
  1599.                   CALL check_value newKey
  1600.                END
  1601.    
  1602.           WHEN answer = "C" THEN              /* create new TopLevel */
  1603.                DO
  1604.                   newTopLevel = create_new_toplevel(iIni)
  1605.                   IF newTopLevel = "" THEN ITERATE    /* no entry, or exists already */
  1606.          
  1607.                   CALL check_value newTopLevel
  1608.                   SAY
  1609.                   SAY "new Top-Level:" global.iRedWhite || strings.displayValue || global.iCyan
  1610.                   SAY
  1611.    
  1612.                   targetTopLevel = newTopLevel
  1613.                   targetTopLevel.displayValue = strings.displayValue
  1614.                   TopLevelCreated = 1
  1615.                END
  1616.    
  1617.           WHEN answer = iTopL THEN    /* target same as source ? */
  1618.                DO
  1619.                   CALL error_msg "Target TopLevel:" || global.iCyan "[" || global.iYellow || stemTopLevel.iTopL || global.iCyan || "]" global.iRedWhite || "same as source TopLevel !"
  1620.                   ITERATE
  1621.                END
  1622.    
  1623.           OTHERWISE 
  1624.                DO
  1625.                   targetTopLevel = stemTopLevel.answer.origValue
  1626.                   targetTopLevel.displayValue = stemTopLevel.answer
  1627.                END
  1628.        END
  1629.    
  1630.        /* move keys to target TopLevel */
  1631.    
  1632.        IF answer <> "R" THEN       /* regular move */
  1633.        DO
  1634.           SAY global.iCyan || "Moving keys into TopLevel [" || global.iYellow || targetTopLevel.displayValue || global.iCyan || "] ..."
  1635.           DO i = 1 TO stemKey.0
  1636.              IF \all_keys THEN i = iKey
  1637.  
  1638.              /* move key in hand */
  1639.              PARSE VALUE call_move_keys(stemIni.iIni, stemTopLevel.iTopL.origValue, targetTopLevel,,
  1640.                                         stemKey.i.origValue, stemKey.i),
  1641.                          WITH TopSuccess keySuccess
  1642.              TopLevelCreated = TopLevelCreated | TopSuccess
  1643.              KeyMoved =        KeyMoved | keySuccess
  1644.    
  1645.              IF \all_keys THEN LEAVE
  1646.           END
  1647.        END
  1648.        ELSE                        /* rename single key */
  1649.        DO
  1650.           /* move key in hand */
  1651.           i = iKey
  1652.           PARSE VALUE call_move_keys(stemIni.iIni,,
  1653.                                      stemTopLevel.iTopl.origValue, stemTopLevel.iTopl.origValue,,
  1654.                                      stemKey.i.origValue, strings.displayValue, newKey),
  1655.                       WITH TopLevelCreated keySuccess
  1656.        END
  1657.    
  1658.        IF KeyMoved THEN    /* at least one key was moved, reread keys into stemKey. */
  1659.        DO
  1660.           CALL read_key iIni, iTopL
  1661.           IF global.KeySortState = "S" THEN        /* resort */
  1662.              CALL sort_generic "stemKey"
  1663.  
  1664.           RETURN TopLevelCreated
  1665.        END
  1666.        LEAVE
  1667.     END
  1668.  
  1669.     RETURN 0            /* no move took place, therefore no new TopLevel was created */
  1670.  
  1671.  
  1672. /*
  1673.    do the actual move-operation
  1674. */
  1675. CALL_MOVE_KEYS: PROCEDURE EXPOSE global.
  1676.     iniFile          = ARG(1)   /* OS/2-INI-file */
  1677.     sourceTopLevel   = ARG(2)   /* source */
  1678.     targetTopLevel   = ARG(3)   /* target */
  1679.     sourceKey        = ARG(4)   /* key */
  1680.     sourceKeyDisplay = ARG(5)   /* displayable string for key-name */
  1681.  
  1682.     TopLevelCreated = 0
  1683.  
  1684.     IF ARG() = 6 THEN   /* only given, if a single key-move within the same target (rename of a key) */
  1685.         targetKey = ARG(6)
  1686.     ELSE
  1687.     DO
  1688.         targetKey = sourceKey   /* move to a different place, but leave key-name unchanged */
  1689.         TopLevelCreated = 1     /* expect a succesful move into a new directory */
  1690.     END
  1691.  
  1692.     KeyMoved = 1                /* expect a successful move */
  1693.  
  1694.     /* present value of key */
  1695.     val1 = SysIni(iniFile, sourceTopLevel, sourceKey)
  1696.  
  1697.     /* value of key in target, if it exists */
  1698.     val2 = SysIni(iniFile, targetTopLevel, targetKey)
  1699.  
  1700.     IF val2 <> "ERROR:" THEN    /* key exists in target ! */
  1701.     DO
  1702.        CALL error_msg "Key:" || global.iCyan "[" || global.iYellow || sourceKeyDisplay || global.iCyan || "]" global.iRedWhite || "exists already, not moved.", "no wait"
  1703.        TopLevelCreated = 0
  1704.        KeyMoved = 0
  1705.     END
  1706.     ELSE
  1707.     DO
  1708.        /* create new key */
  1709.        CALL SysIni iniFile, targetTopLevel, targetKey, val1
  1710.        /* delete original key */
  1711.        CALL SysIni iniFile, sourceTopLevel, sourceKey, "DELETE:"
  1712.        SAY global.iCyan"Key: [" || global.iYellow || sourceKeyDisplay || global.iCyan || "] moved."
  1713.     END
  1714.  
  1715.     RETURN TopLevelCreated KeyMoved
  1716.  
  1717.  
  1718.  
  1719.  
  1720.  
  1721.  
  1722.  
  1723. /*
  1724.         read KEY-entries
  1725. */
  1726. READ_KEY: PROCEDURE EXPOSE stemIni. stemTopLevel. stemKey. global. strings.
  1727.     DROP stemKey.
  1728.     stemKey. = ""
  1729.  
  1730.     iIni     = ARG(1)
  1731.     iTopL    = ARG(2)
  1732.  
  1733.     ok = SysIni(stemIni.iIni, stemTopLevel.iTopL.origValue, 'ALL:', 'stemKey')
  1734.     IF ok = "ERROR:" THEN
  1735.     DO
  1736.        stemKey.0 = 0
  1737.     END
  1738.     ELSE
  1739.     DO i = 1 TO stemKey.0
  1740.        CALL check_value stemKey.i  /* determine and prepare value in hand */
  1741.        stemKey.i.origValue   = stemKey.i
  1742.        stemKey.i             = strings.displayValue
  1743.        stemKey.i.type        = strings.type
  1744.        stemKey.i.filterValue = strings.filterValue
  1745.     END
  1746.  
  1747.     CALL largest_generic("stemKey")
  1748.     RETURN 
  1749.  
  1750.  
  1751. /* 
  1752.         Display KEY-entries of chosen INI-file
  1753. */
  1754. SHOW_KEY: PROCEDURE EXPOSE global. stemIni. stemTopLevel. stemKey.
  1755.    iIni     = ARG(1)
  1756.    iTopL    = ARG(2)
  1757.    filename = stemIni.iIni
  1758.    TopLevel = stemTopLevel.iTopL
  1759.  
  1760.    CALL read_key iIni, iTopL
  1761.    global.KeySortState = "U"            /* unsorted */
  1762.    dirty   = 0                          /* everything o.k., no need to reread TopLevel */
  1763.  
  1764.    DO FOREVER
  1765.       IF stemKey.0 = 0 THEN RETURN "1"       /* no keys left, reread Top-Level entries */
  1766.  
  1767.       SAY
  1768.       SAY global.iRedWhite || CENTER("available keys", global.iColumns) || global.iCyan
  1769.       SAY
  1770.       SAY global.iCyan'INI-file:' global.iYellow filename
  1771.       SAY global.iCyan'TopLevel:' global.iYellow TopLevel
  1772.       SAY
  1773.       SAY global.iCyan'The following' global.iYellow||stemKey.0 global.iCyan'Key entries are available:'
  1774.       SAY
  1775.       CALL show_generic "stemKey"      /* show stem contents */
  1776.       SAY
  1777.  
  1778.  
  1779.       choices = "PSUQ"          /* define choices */
  1780.  
  1781.       tmp = global.enterString || stemKey.0 global.iCyan || 'to view Key-value,',
  1782.             global.iYellow || '0' || global.iCyan 'to return;'
  1783.  
  1784.       IF global.config.add.indKey THEN
  1785.       DO
  1786.          tmp = tmp global.iYellow || 'a' || global.iCyan || 'dd,'
  1787.          choices = choices || "A"
  1788.       END
  1789.  
  1790.       IF global.config.delete.indAllKeys THEN
  1791.       DO
  1792.          tmp = tmp global.iYellow || 'd' || global.iCyan || 'elete ALL,'
  1793.          choices = choices || "D"
  1794.       END
  1795.  
  1796.       IF global.config.move.indAllKeys THEN
  1797.       DO
  1798.          tmp = tmp global.iYellow || 'm' || global.iCyan || 'ove ALL,'
  1799.          choices = choices || "M"
  1800.       END
  1801.  
  1802.       IF global.config.copy.indAllKeys THEN
  1803.       DO
  1804.          tmp = tmp global.iYellow || 'c' || global.iCyan || 'opy ALL,'
  1805.          choices = choices || "C"
  1806.       END
  1807.  
  1808.       tmp = tmp global.iYellow || 's' || global.iCyan || 'ort,',
  1809.           global.iYellow || 'u' || global.iCyan || 'nsort,',
  1810.           global.iYellow || 'p' || global.iCyan || 'rint'
  1811.  
  1812.       SAY tmp
  1813.  
  1814.       /* define choices and upper numeric boundary and get answer from user */
  1815.       CALL CHAROUT , global.iYellow
  1816.       answer = get_answer(choices, stemKey.0)
  1817.       CALL CHAROUT , global.iCyan
  1818.       SAY
  1819.  
  1820.       SELECT
  1821.          WHEN answer = "" | answer = 0 | answer = "1B"x THEN LEAVE
  1822.  
  1823.          WHEN answer = "Q" THEN SIGNAL halt
  1824.  
  1825.          WHEN answer = 'P' THEN         /* print all Key-entries of TopLevel */
  1826.               DO
  1827.                  CALL print iIni, iTopL
  1828.                  IF global.KeySortState = "U" THEN      /* reread all key-entries */
  1829.                     CALL read_key iIni, iTopL
  1830.               END
  1831.    
  1832.          WHEN answer = 'S' THEN         /* sort key-entries */
  1833.               DO
  1834.                  CALL sort_generic "stemKey"
  1835.                  global.KeySortState = "S"
  1836.               END
  1837.     
  1838.          WHEN answer = 'U' THEN         /* unsort key-entries, i.e. reread them */
  1839.               DO
  1840.                  CALL read_key iIni, iTopL
  1841.                  global.KeySortState = "U"
  1842.               END
  1843.    
  1844.          WHEN answer = "D" THEN         /* delete all keys and TopLevel-entry */
  1845.               DO
  1846.                  CALL BEEP 2000, 250
  1847.                  CALL CHAROUT , global.iRedWhite || "Do you really wish to delete ALL KEYs ???" || global.noHint 
  1848.                  answer = get_answer("QYN")
  1849.                  CALL CHAROUT , global.iCyan
  1850.         
  1851.                  IF answer = "Q" THEN SIGNAL halt
  1852.         
  1853.                  IF answer = "Y" THEN
  1854.                  DO
  1855.                     SAY global.iCyan || "Deleting ALL keys from TopLevel [" || global.iYellow || stemTopLevel.iTopL || global.iCyan || "] ..."
  1856.                     val = SysIni(stemIni.iIni, stemTopLevel.iTopL.origValue, "DELETE:")
  1857.                     SAY global.iCyan"done."
  1858.         
  1859.                     RETURN "1"                          /* return to TopLevel-entries */
  1860.                  END
  1861.               END
  1862.    
  1863.          WHEN answer = "C" THEN         /* copy all key entries to another TopLevel */
  1864.               DO
  1865.                 dirty = dirty | copy_keys(iIni, iTopL, "1")
  1866.               END
  1867.  
  1868.          WHEN answer = "M" THEN         /* move all key entries to another TopLevel */
  1869.               DO
  1870.                 dirty = dirty | move_keys(iIni, iTopL, "1")
  1871.               END
  1872.  
  1873.  
  1874.          WHEN answer = "A" THEN         /* add a new key-entry */
  1875.               DO
  1876.                  newKey = create_new_key(stemIni.iIni, stemTopLevel.iTopL.origValue)
  1877.                  IF newKey = "" THEN ITERATE
  1878.         
  1879.                  CALL check_value newKey
  1880.                  newKey.displayValue = strings.displayValue
  1881.         
  1882.                  PARSE VALUE edit_value("A", "new value for Key:" global.iRedWhite || newKey.displayValue || global.iCyan, , , "Enter new Key-Value") WITH tmpState tmpResult
  1883.                  IF tmpState = "NOK" THEN ITERATE
  1884.                  newKeyValue = tmpResult
  1885.  
  1886.                  /* create new key */
  1887.                  val = SysIni(stemIni.iIni, stemTopLevel.iTopL.origValue, newKey, newKeyValue)
  1888.         
  1889.                  CALL read_key iIni, iTopL              /* reread keys */
  1890.         
  1891.                  IF global.KeySortState = "S" THEN
  1892.                     CALL sort_generic "stemKey"
  1893.               END
  1894.  
  1895.          OTHERWISE      /* display value of chosen key */
  1896.               dirty = dirty | show_keyvalue(iIni, iTopL, answer)        /* TopLevel created ? */
  1897.               SAY
  1898.       END  
  1899.       SAY
  1900.    END
  1901.    SAY global.iNormal
  1902.    RETURN dirty
  1903.  
  1904.  
  1905. /*
  1906.         display value and allow for editing
  1907. */
  1908. SHOW_KEYVALUE: PROCEDURE EXPOSE global. strings. stemIni. stemTopLevel. stemKey.
  1909.     iIni     = ARG(1)
  1910.     iTopL    = ARG(2)
  1911.     iKey     = ARG(3)
  1912.     dirty    = 0                /* change in key-names ? */
  1913.     TopLevelCreated = 0         /* was a TopLevel created using copy, move ? */
  1914.  
  1915.     filename = stemIni.iIni
  1916.     TopLevel = stemTopLevel.iTopL
  1917.     Key      = stemKey.iKey
  1918.  
  1919.     val = SysIni(stemIni.iIni, stemTopLevel.iTopL.origValue, stemKey.iKey.origValue)
  1920.  
  1921.     CALL check_value val
  1922.     iType = strings.type        /* type could be A, A0 or H */
  1923.  
  1924.     value2show = strings.displayValue
  1925.  
  1926.  
  1927.     DO FOREVER
  1928.        SAY
  1929.        SAY global.iRedWhite || CENTER("key-value", global.iColumns) || global.iCyan
  1930.        SAY
  1931.        SAY global.iCyan'INI-file:' global.iYellow || filename
  1932.        SAY global.iCyan'TopLevel:' global.iYellow || TopLevel
  1933.        SAY global.iCyan'     Key:' global.iYellow || Key global.iCyan
  1934.        SAY
  1935.    
  1936.    
  1937.        SAY "data type:" global.iYellow || global.dType.iType global.iCyan "(" ||,
  1938.            global.iYellow || global.dType.iType.long || global.iCyan || ")"
  1939.        SAY "    value:" global.iNormal
  1940.        SAY global.iCyan || "[" || global.iYellow || value2show || global.iCyan || "]"
  1941.        SAY
  1942.  
  1943.        choices = ""
  1944.  
  1945.        SAY global.iCyan || "Enter:"
  1946.        SAY
  1947.  
  1948.        IF iType = "H" THEN
  1949.        DO
  1950.           choices = choices || "FH"
  1951.           SAY "       " global.iYellow || "H" global.iCyan "to show value in hex-digits"
  1952.           SAY "       " global.iYellow || "F" global.iCyan "to show filtered value"
  1953.        END
  1954.  
  1955.        IF global.config.change.indKey THEN
  1956.        DO
  1957.           SAY "       " global.iYellow || "E" global.iCyan "to edit (change) key-value"
  1958.           choices = choices || "E"
  1959.        END
  1960.  
  1961.        IF global.config.delete.indKey THEN
  1962.        DO
  1963.           SAY "       " global.iYellow || "D" global.iCyan "to delete key"
  1964.           choices = choices || "D"
  1965.        END
  1966.  
  1967.        IF global.config.move.indKey THEN
  1968.        DO
  1969.           SAY "       " global.iYellow || "M" global.iCyan "to move/rename key"
  1970.           choices = choices || "M"
  1971.        END
  1972.  
  1973.        IF global.config.copy.indKey THEN
  1974.        DO
  1975.           SAY "       " global.iYellow || "C" global.iCyan "to copy key"
  1976.           choices = choices || "C"
  1977.        END
  1978.  
  1979.        SAY "       " global.iYellow || "P" global.iCyan "to print key-value"
  1980.        SAY "       " global.iCyan   || "   [Esc] to return"
  1981.        /* define choices and get answer from user */
  1982.        CALL CHAROUT , global.iYellow
  1983.        answer = get_answer(choices || "PQ")
  1984.        CALL CHAROUT , global.iCyan
  1985.  
  1986.        SELECT
  1987.           WHEN answer = "Q" THEN SIGNAL halt
  1988.  
  1989.           WHEN answer = "H" THEN value2show = strings.displayValue
  1990.  
  1991.           WHEN answer = 'P' THEN    /* print the Key-value */
  1992.                CALL print iIni, iTopL, iKey
  1993.  
  1994.           WHEN answer = "F" THEN value2show = strings.filterValue
  1995.  
  1996.  
  1997.  
  1998.  
  1999.          WHEN answer = "C" THEN         /* copy key to another TopLevel */
  2000.               DO
  2001.                 TopLevelCreated = TopLevelCreated | copy_keys(iIni, iTopL, "0", iKey)
  2002.               END
  2003.  
  2004.          WHEN answer = "M" THEN         /* move key to another TopLevel or rename it */
  2005.               DO
  2006.                 TopLevelCreated = TopLevelCreated | move_keys(iIni, iTopL, "0", iKey)
  2007.                 dirty = 1
  2008.                 LEAVE
  2009.               END
  2010.  
  2011.           WHEN answer = "D" THEN                        /* delete key */
  2012.                DO
  2013.                   CALL BEEP 2000, 250
  2014.                   CALL CHAROUT , global.iRedWhite || "Do you really wish to delete this KEY ?" || global.noHint 
  2015.                   answer = get_answer("QYN")
  2016.                   CALL CHAROUT , global.iCyan
  2017.  
  2018.                   IF answer = "Q" THEN SIGNAL halt
  2019.  
  2020.                   IF answer = "Y" THEN
  2021.                   DO
  2022.                      val = SysIni(stemIni.iIni, stemTopLevel.iTopL.origValue, stemKey.iKey.origValue, "DELETE:")
  2023.                      dirty = 1
  2024.                      LEAVE
  2025.                   END
  2026.                END
  2027.  
  2028.           WHEN answer = "E" THEN                        /* change key-value */
  2029.                DO
  2030.                   PARSE VALUE edit_value("E", val, , , "Enter new Key-Value") WITH tmpState tmpResult
  2031.                   IF tmpState = "OK" THEN
  2032.                   DO
  2033.                      val = SysIni(stemIni.iIni, stemTopLevel.iTopL.origValue, stemKey.iKey.origValue, tmpResult)
  2034.                      val = tmpResult
  2035.                   END
  2036.  
  2037.                   CALL check_value val
  2038.                   iType = strings.type        /* type could be A, A0 or H */
  2039.                   value2show = strings.displayValue
  2040.               
  2041.                   choice = ""
  2042.                END
  2043.  
  2044.           OTHERWISE LEAVE
  2045.        END  
  2046.    
  2047.        SAY
  2048.     END
  2049.  
  2050.     SAY
  2051.     IF dirty THEN               /* was a key added, renamed/moved, deleted ? */
  2052.     DO                          /* if so, reread keys */
  2053.        CALL read_key iIni, iTopL
  2054.        IF global.KeySortState = "S" THEN              /* resort keys */
  2055.           CALL sort_generic "stemKey"
  2056.     END
  2057.     RETURN TopLevelCreated
  2058.  
  2059.  
  2060. /* 
  2061.    check data-type of argument and set up a structure containing different representations
  2062.    of it
  2063. */
  2064. CHECK_VALUE: PROCEDURE EXPOSE global. strings.
  2065.    DROP strings.
  2066.    strings. = ""
  2067.  
  2068.    length_arg1 = LENGTH(ARG(1))         /* length of ARG(1) */
  2069.  
  2070.    IF length_arg1 = 0 THEN              /* unknown key-value, key-value not set. */
  2071.    DO
  2072.       strings.type = "H"
  2073.       strings.displayValue = "x''"
  2074.       strings.filterValue = ""
  2075.       RETURN
  2076.    END
  2077.  
  2078.    /* last character \0 ? */
  2079.  
  2080.    IF SUBSTR(ARG(1), length_arg1, 1) = "00"x THEN    /* ASCIIZ ? */
  2081.    DO
  2082.       work = SUBSTR(ARG(1), 1, length_arg1 - 1)
  2083.       asciiz = "0"
  2084.    END
  2085.    ELSE                                                 
  2086.    DO
  2087.       work = ARG(1)
  2088.       asciiz = ""
  2089.    END
  2090.  
  2091.    ascii = (VERIFY(work, global.iNonPrintable, "Match") = 0)    /* plain ASCII ? */
  2092.  
  2093.    IF ascii THEN                                        /* ASCII-type value in hand */
  2094.    DO
  2095.       IF asciiz = "0" THEN strings.type = "A0"
  2096.                       ELSE strings.type = "A"
  2097.       strings.displayValue = work
  2098.    END
  2099.    ELSE                                                 /* hexadecimal value in hand */
  2100.    DO
  2101.       strings.type = "H"
  2102.       strings.displayValue = "x'" || C2X(ARG(1)) || "'"
  2103.       strings.filterValue = TRANSLATE(ARG(1), global.iFilter, global.iNonPrintable)
  2104.    END
  2105.    RETURN
  2106.  
  2107.  
  2108.  
  2109. /*
  2110.    allow for adding or editing a value
  2111.    ARG(1) ... A to add, E to edit
  2112.    ARG(2) ... title
  2113.    ARG(3) ... optional, if given, then only ASCII-values allowed
  2114.    ARG(4) ... optional, if given, then only HEX- or ASCII-editing allowed (for TopLevels and
  2115.               keynames
  2116.    ARG(5) ... optional, if given, then the prompt for BOXEDIT
  2117. */
  2118. EDIT_VALUE: PROCEDURE EXPOSE global. strings.
  2119.    editType = ARG(1)    /* A add new, E edit existing */
  2120.    editName = ARG(2)    /* if adding new: TopLevel-entry or Key-entry 
  2121.                            if editing: value to be added */
  2122.    editDataType = ARG(3) /* optional, determines data-type "A", "A0", "H" */
  2123.  
  2124.  
  2125.    IF editType = "A" THEN
  2126.    DO
  2127.       DROP strings.
  2128.       strings. = ""
  2129.       tmpValue = ""
  2130.  
  2131.       IF ARG() = 3 THEN strings.type = editDataType
  2132.       ELSE
  2133.          choices = "12"
  2134.          SAY global.iCyan || "Defining:" global.iYellow || editName || global.iCyan
  2135.          SAY
  2136.          SAY "choose data type:" 
  2137.          SAY
  2138.          SAY "       " global.iYellow || "1" global.iCyan global.dtype.H.long
  2139.          SAY "       " global.iYellow || "2" global.iCyan global.dtype.A.long global.defaultHint
  2140.  
  2141.          IF ARG() < 4 THEN      /* ASCIIZ (A0) allowed ? */
  2142.          DO
  2143.            SAY "       " global.iYellow || "3" global.iCyan global.dtype.A0.long
  2144.            choices = choices || "3"
  2145.          END
  2146.  
  2147.          SAY "       " global.iCyan   || "   [Esc] to return"
  2148.          /* define choices and get answer from user */
  2149.          CALL CHAROUT , global.iYellow
  2150.          answer = get_answer("123Q")
  2151.          CALL CHAROUT , global.iCyan
  2152.    
  2153.          SELECT
  2154.             WHEN answer = "Q" THEN SIGNAL halt
  2155.             WHEN answer = "1" THEN strings.type = "H"
  2156.             WHEN answer = "2" | answer = "" THEN strings.type = "A"
  2157.             WHEN answer = "3" THEN strings.type = "A0"
  2158.    
  2159.             OTHERWISE RETURN "NOK"
  2160.          END  
  2161.       DO
  2162.       END
  2163.  
  2164.  
  2165.     END
  2166.     ELSE        /* editing mode */
  2167.     DO
  2168.        val = editName
  2169.        CALL CHECK_VALUE val
  2170.  
  2171.        IF strings.type = "A" | strings.type = "A0" THEN
  2172.        DO
  2173.            IF ARG() = 3 THEN answer = SUBSTR(editDataType, 1, 1)
  2174.            ELSE
  2175.            DO
  2176.               SAY global.iCyan || "choose data type for editing:"
  2177.               SAY
  2178.               SAY "       " global.iYellow || "H" global.iCyan global.dtype.H.long
  2179.               SAY "       " global.iYellow || "A" global.iCyan global.dtype.A.long global.defaultHint
  2180.               SAY "       " global.iCyan   || "   [Esc] to return"
  2181.    
  2182.               /* define choices and get answer from user */
  2183.               CALL CHAROUT , global.iYellow
  2184.               answer = get_answer("HAQ")
  2185.               CALL CHAROUT , global.iCyan
  2186.    
  2187.               IF answer = "Q" THEN SIGNAL halt    
  2188.    
  2189.               IF answer = "1B"x THEN RETURN "NOK"  /* Escape was pressed */
  2190.            END
  2191.                                                
  2192.            IF answer = "" | answer = "A" THEN   /* edit as ASCII */
  2193.            DO
  2194.               tmpValue = strings.displayValue
  2195.            END
  2196.            ELSE
  2197.            DO
  2198.               strings.type = "H"
  2199.               tmpValue = C2X(val)
  2200.            END
  2201.  
  2202.        END
  2203.        ELSE tmpValue = C2X(val)
  2204.     END
  2205.  
  2206.  
  2207.     tmpLength = LENGTH(tmpValue)
  2208.  
  2209.     neededRows = TRUNC(tmpLength / (global.iColumns - 2) + 0.9999) + 2
  2210.  
  2211.     IF neededRows > global.iRows THEN
  2212.     DO
  2213.        SAY
  2214.        CALL error_msg "screen-size not large enough to edit value !"
  2215.        val = "NOK"
  2216.     END
  2217.     ELSE  /* try to add four more empty lines */
  2218.     DO
  2219.        DO 4
  2220.           IF (neededRows + 1) < global.iRows THEN neededRows = neededRows + 1
  2221.                                              ELSE LEAVE
  2222.        END
  2223.        /* get present position */
  2224.        PARSE VALUE SysCurPos() WITH cursor_row cursor_col
  2225.  
  2226.        startingRow = cursor_row - neededRows 
  2227.  
  2228.        IF startingRow < 0 THEN startingRow = 0
  2229.  
  2230.        tmpType = ""       /* ASCII-edit for default */
  2231.        IF strings.type = "H" THEN tmptype = "H"
  2232.        editResult = boxedit((startingRow 0 (startingRow + neededRows - 1) (global.iColumns - 1)),,
  2233.                              tmpValue, tmpType, ARG(5))
  2234.        editResult = STRIP(editResult, "T")        /* remove trailing blanks */
  2235.  
  2236.        val = "NOK"
  2237.        IF editResult <> tmpValue THEN
  2238.        DO
  2239.           SELECT
  2240.              WHEN strings.type = "H" THEN val = "OK" X2C(editResult)
  2241.              WHEN strings.type = "A0" THEN val = "OK" editResult || "00"x
  2242.              OTHERWISE val = "OK" editResult
  2243.           END  
  2244.        END
  2245.     END
  2246.     RETURN val
  2247.  
  2248.  
  2249.  
  2250. /***********************************************************************************************/
  2251. /*
  2252.     print to file
  2253. */
  2254. PRINT: PROCEDURE EXPOSE global. strings. stemIni. stemTopLevel. stemKey.
  2255.     iIni  = ARG(1)      /* INI-file to be printed */
  2256.     iTopL = ARG(2)      /* TopLevel to be printed */
  2257.     iKey  = ARG(3)      /* Key to be printed */
  2258.  
  2259.     IF global.iBackupMode = "" THEN    /* prompt user */
  2260.     DO
  2261.        SAY
  2262.        SAY "Following settings are active:"
  2263.        SAY
  2264.        SAY global.iCyan || "Level of information to produce:" global.iYellow || VALUE("global.depthOfOutput." || global.config.showDepth)
  2265.        SAY global.iCyan || "Hexadecimal values shown as:    " global.iYellow || VALUE("global.hextypeOfOutput." || global.config.showHexAs)
  2266.        SAY global.iCyan || "Line length in characters:      " global.iYellow || global.config.showLineLength
  2267.        SAY
  2268.        CALL CHAROUT , global.iCyan || "Do you wish to change these settings ?" global.noHint
  2269.    
  2270.        /* define choices and get answer from user */
  2271.        CALL CHAROUT , global.iYellow
  2272.        answer = get_answer("YNQ")
  2273.        CALL CHAROUT , global.iCyan
  2274.    
  2275.        IF answer = "1B"x THEN RETURN 0
  2276.        ELSE IF answer = "Q" THEN SIGNAL halt
  2277.    
  2278.        IF answer = "Y" THEN                /* Yes, change the settings in effect */
  2279.        DO
  2280.          CALL settings_printout
  2281.        END
  2282.    
  2283.        answer = ""
  2284.        /* output file exists, shall we append to it ? */
  2285.        IF global.outFile <> "" THEN
  2286.        DO
  2287.           SAY
  2288.           SAY global.iCyan || "You printed already to file" global.iYellow || global.outFile || global.iCyan || "."
  2289.           SAY
  2290.           SAY global.iCyan || "Do you wish to append to it?" global.yesHint
  2291.           /* define choices and get answer from user */
  2292.           CALL CHAROUT , global.iYellow
  2293.           answer = get_answer("YNQ")
  2294.           CALL CHAROUT , global.iCyan
  2295.    
  2296.           IF answer = "1B"x THEN RETURN 0
  2297.           ELSE IF answer = "Q" THEN SIGNAL halt
  2298.       
  2299.        END
  2300.    
  2301.        IF answer = "N" | global.outFile = "" THEN  /* get file name to print to */
  2302.           IF \get_filename() THEN RETURN           /* user aborted print-operation ? */
  2303.     END
  2304.  
  2305.     /* o.k., now let us start producing the output ! */
  2306.  
  2307.     SELECT
  2308.        WHEN ARG() = 0 THEN      /* all INI-files to be printed */
  2309.             DO
  2310.                DO i = 1 TO stemIni.0
  2311.                   CALL output_filename i, "AI"          /* print file-name */
  2312.                                                         
  2313.                   CALL read_toplevel i                  /* read top-levels */
  2314.                   CALL sort_generic "stemTopLevel"      /* sort them */
  2315.                   DO j = 1 TO stemTopLevel.0
  2316.                      CALL output_topic j                /* print topic-name */
  2317.                      IF POS(global.config.showDepth, "KV") > 0 THEN             /* show more than TopLevel ? */
  2318.                      DO
  2319.                         CALL read_key i, j              /* read keys */
  2320.                         CALL sort_generic "stemKey"     /* sort them */
  2321.                         DO k = 1 TO stemKey.0
  2322.                            CALL output_key k            /* print key-name */
  2323.    
  2324.                            IF POS(global.config.showDepth, "V") > 0 THEN      /* show key-values too ? */
  2325.                            DO
  2326.                               /* get key-value */
  2327.                               val = SysIni(stemIni.i, stemTopLevel.j.origValue, stemKey.k.origValue)
  2328.                               CALL output_val val       /* print value */
  2329.                            END
  2330.                         END
  2331.                         CALL LINEOUT global.outFile, "" /* write empty line */
  2332.                      END
  2333.                   END
  2334.                CALL LINEOUT global.outFile, ""          /* write empty line */
  2335.                END
  2336.             END
  2337.  
  2338.        WHEN ARG() = 1 THEN      /* one INI-file, all TopLevels to be printed */
  2339.             DO
  2340.                i = iIni
  2341.                CALL output_filename i, "AT"             /* print file-name */
  2342.                CALL sort_generic "stemTopLevel"         /* sort them */
  2343.                                                        
  2344.                DO j = 1 TO stemTopLevel.0              
  2345.                   CALL output_topic j                   /* print topic-name */
  2346.                   IF POS(global.config.showDepth, "KV") > 0 THEN    /* show more than TopLevel ? */
  2347.                   DO
  2348.                      CALL read_key i, j                 /* read keys */
  2349.                      CALL sort_generic "stemKey"        /* sort them */
  2350.                      DO k = 1 TO stemKey.0
  2351.                         CALL output_key k               /* print key-name */
  2352.  
  2353.                         IF POS(global.config.showDepth, "V") > 0 THEN      /* show key-values too ? */
  2354.                         DO
  2355.                            /* get key-value */
  2356.                            val = SysIni(stemIni.i, stemTopLevel.j.origValue, stemKey.k.origValue)
  2357.                            CALL output_val val          /* print value */
  2358.                         END
  2359.                      END
  2360.                      CALL LINEOUT global.outFile, ""    /* write empty line */
  2361.                   END
  2362.                END
  2363.                CALL LINEOUT global.outFile, ""          /* write empty line */
  2364.             END
  2365.  
  2366.        WHEN ARG() = 2 THEN      /* one INI-file, one TopLevel, all keys to be printed */
  2367.             DO
  2368.                i = iIni
  2369.                j = iTopL
  2370.                CALL output_filename i, "AK"             /* print file-name */
  2371.                CALL output_topic j                      /* print topic-name */
  2372.                                                         
  2373.                CALL read_key i, j                       /* read keys */
  2374.                CALL sort_generic "stemKey"              /* sort them */
  2375.                                                         
  2376.                DO k = 1 TO stemKey.0                    
  2377.                   CALL output_key k                     /* print key-name */
  2378.  
  2379.                   IF POS(global.config.showDepth, "V") > 0 THEN      /* show key-values too ? */
  2380.                   DO
  2381.                      /* get key-value */
  2382.                      val = SysIni(stemIni.i, stemTopLevel.j.origValue, stemKey.k.origValue)
  2383.                      CALL output_val val                /* print value */
  2384.                   END
  2385.                END
  2386.                CALL LINEOUT global.outFile, ""          /* write empty line */
  2387.             END
  2388.  
  2389.        WHEN ARG() = 3 THEN      /* one INI-file, one TopLevel, one key and its value to be printed */
  2390.             DO
  2391.                i = iIni
  2392.                j = iTopL
  2393.                k = iKey
  2394.  
  2395.                CALL output_filename i, "K"              /* print file-name */
  2396.                CALL output_topic j                      /* print topic-name */
  2397.                CALL output_key k                        /* print key-name */
  2398.  
  2399.                /* get key-value */
  2400.                val = SysIni(stemIni.i, stemTopLevel.j.origValue, stemKey.k.origValue)
  2401.                CALL output_val val                      /* print value */
  2402.                CALL LINEOUT global.outFile, ""          /* write empty line */
  2403.             END
  2404.  
  2405.        OTHERWISE NOP
  2406.     END  
  2407.  
  2408.     SAY
  2409.     CALL STREAM global.outFile, "C", "CLOSE"            /* close output file */
  2410.  
  2411.     RETURN
  2412.  
  2413.  
  2414. /*
  2415.    write Filename into File, argument: index of INI-file to be processed
  2416. */
  2417. OUTPUT_FILENAME: PROCEDURE EXPOSE global. stemIni. stemTopLevel.
  2418.     range = ARG(2)      /* range of printout (all INIs, all TopLevels, all Keys, one Key) */
  2419.     /* show date/time of printout */
  2420.     tmp = LEFT("Date  [" || DATE("S") TIME() || "] ", global.config.showLineLength, "=")
  2421.     CALL LINEOUT global.outFile, tmp
  2422.  
  2423.     CALL LINEOUT global.outFile, ""
  2424.  
  2425.     /* show range of printout */
  2426.     tmp = LEFT("Range [" || VALUE("global.rangeOfPrintout." || range) || "] ", global.config.showLineLength, "=")
  2427.     CALL LINEOUT global.outFile, tmp
  2428.  
  2429.     /* show depth of information */
  2430.     tmp = LEFT("Depth [" || VALUE("global.depthOfOutput." || global.config.showDepth) || "] ", global.config.showLineLength, "=")
  2431.     CALL LINEOUT global.outFile, tmp
  2432.  
  2433.     CALL LINEOUT global.outFile, ""
  2434.  
  2435.     /* show filename, no matter how long it is */
  2436.     filename = "File  [" || VALUE("stemIni." || ARG(1))
  2437.     filler   = "      ["
  2438.  
  2439.     SAY filename || "]"
  2440.  
  2441.     tmp = ""
  2442.  
  2443.     /* filename can be arbitrarily long */
  2444.     DO WHILE filename <> ''     
  2445.        IF LENGTH(filename) >= global.config.showLineLength THEN
  2446.        DO
  2447.           tmp = SUBSTR(filename, 1, global.config.showLineLength - 1) || "]"
  2448.           filename = filler || SUBSTR(filename, global.config.showLineLength)
  2449.        END
  2450.        ELSE
  2451.        DO
  2452.           tmp = filename || "] "
  2453.           tmp = LEFT(tmp, global.config.showLineLength, "=")
  2454.           filename = ""
  2455.        END
  2456.  
  2457.        CALL LINEOUT global.outFile, tmp 
  2458.     END
  2459.  
  2460.     /* show # of TopLevel entries */
  2461.     tmp = LEFT("TopL# [" || stemTopLevel.0 "TopLevel(s)] ", global.config.showLineLength, "=")
  2462.     CALL LINEOUT global.outFile, tmp
  2463.  
  2464.  
  2465.     CALL LINEOUT global.outFile, ""     /* empty line */
  2466.     RETURN
  2467.  
  2468. /*
  2469.    write TopLevel entry
  2470. */
  2471. OUTPUT_TOPIC: PROCEDURE EXPOSE global. stemTopLevel.
  2472.     iInd = ARG(1)
  2473.  
  2474.     tmpDisplay = stemTopLevel.iInd
  2475.     tmpFiltered = ""
  2476.  
  2477.     SAY LEFT(global.format.forTop || "[" || tmpDisplay || "]", global.iColumns - 1)
  2478.  
  2479.     IF stemTopLevel.iInd.type = "H" THEN
  2480.     DO
  2481.        IF global.config.showHexAs = "H" | global.config.showHexAs = "B" THEN
  2482.           tmpDisplay = SUBSTR(tmpDISPLAY, 3, LENGTH(tmpDisplay) - 3)
  2483.        ELSE
  2484.           tmpDisplay = ""
  2485.  
  2486.        IF global.config.showHexAs = "F" | global.config.showHexAs = "B" THEN
  2487.            tmpFiltered = stemTopLevel.iInd.FilterValue
  2488.     END
  2489.  
  2490.     global.config.showDataLength = global.config.showLineLength - (global.format.forTop.Length + 7)   /* "[A0] [" ..."]" longest append */
  2491.     CALL print_go_do_it_finally_god_damn_it global.format.forTop, global.format.forTop.LeadIn, stemTopLevel.iInd.type, tmpDisplay, tmpFiltered
  2492.     RETURN
  2493.  
  2494.  
  2495. /*
  2496.    write Key entry
  2497. */
  2498. OUTPUT_KEY: PROCEDURE EXPOSE global. stemKey.
  2499.     iInd = ARG(1)
  2500.  
  2501.     tmpDisplay = stemKey.iInd
  2502.     tmpFiltered = ""
  2503.  
  2504.     SAY LEFT(global.format.forKey || "[" || tmpDisplay || "]", global.iColumns - 1)
  2505.  
  2506.     IF stemKey.iInd.type = "H" THEN
  2507.     DO
  2508.        IF global.config.showHexAs = "H" | global.config.showHexAs = "B" THEN
  2509.           tmpDisplay = SUBSTR(tmpDISPLAY, 3, LENGTH(tmpDisplay) - 3)
  2510.        ELSE
  2511.           tmpDisplay = ""
  2512.  
  2513.        IF global.config.showHexAs = "F" | global.config.showHexAs = "B" THEN
  2514.            tmpFiltered = stemKey.iInd.FilterValue
  2515.     END
  2516.  
  2517.     global.config.showDataLength = global.config.showLineLength - (global.format.forKey.Length + 7)   /* "[A0] [" ..."]" longest append */
  2518.     CALL print_go_do_it_finally_god_damn_it global.format.forKey, global.format.forKey.LeadIn, stemKey.iInd.type, tmpDisplay, tmpFiltered
  2519.     RETURN
  2520.  
  2521.  
  2522. /*
  2523.    write Key value
  2524. */
  2525. OUTPUT_VAL: PROCEDURE EXPOSE global.
  2526.     CALL check_value ARG(1)
  2527.  
  2528.     tmpDisplay = strings.DisplayValue
  2529.     tmpFiltered = ""
  2530.  
  2531.     IF strings.type = "H" THEN
  2532.     DO
  2533.        IF global.config.showHexAs = "H" | global.config.showHexAs = "B" THEN
  2534.           tmpDisplay = SUBSTR(tmpDISPLAY, 3, LENGTH(tmpDisplay) - 3)
  2535.        ELSE
  2536.           tmpDisplay = ""
  2537.  
  2538.        IF global.config.showHexAs = "F" | global.config.showHexAs = "B" THEN
  2539.            tmpFiltered = strings.FilterValue
  2540.     END
  2541.  
  2542.     global.config.showDataLength = global.config.showLineLength - (global.format.forVal.Length + 7)   /* "[A0] [" ..."]" longest append */
  2543.     CALL print_go_do_it_finally_god_damn_it global.format.forVal, global.format.forVal.LeadIn, strings.type, tmpDisplay, tmpFiltered
  2544.     RETURN
  2545.  
  2546.  
  2547.  
  2548. PRINT_GO_DO_IT_FINALLY_GOD_DAMN_IT: PROCEDURE EXPOSE global.
  2549.     title    = ARG(1)
  2550.     leadin   = ARG(2)
  2551.     type     = ARG(3)
  2552.     display  = ARG(4)
  2553.     filtered = ARG(5)     
  2554.  
  2555.     tmp1 = title
  2556.     type_name = LEFT(VALUE("global.dtype." || type), 4) || " ["
  2557.  
  2558.     DO WHILE display <> ""
  2559.        IF LENGTH(display) > global.config.showDataLength THEN
  2560.        DO
  2561.           tmp = SUBSTR(display, 1, global.config.showDataLength)
  2562.           display = SUBSTR(display, global.config.showDataLength + 1)
  2563.        END
  2564.        ELSE
  2565.        DO
  2566.           tmp = display
  2567.           display = ""
  2568.        END
  2569.  
  2570.        CALL LINEOUT global.outFile, tmp1 || type_name || tmp || "]"
  2571.        tmp1 = leadin
  2572.     END
  2573.  
  2574.     IF type = "H" & filtered <> "" THEN
  2575.     DO
  2576.        type_name = LEFT(VALUE("global.dtype." || "F"), 4)  || " ["   /* for ASCII-filtered strings */
  2577.    
  2578.        DO WHILE filtered <> ""
  2579.           IF LENGTH(filtered) > global.config.showDataLength THEN
  2580.           DO
  2581.              tmp = SUBSTR(filtered, 1, global.config.showDataLength)
  2582.              filtered = SUBSTR(filtered, global.config.showDataLength + 1)
  2583.           END
  2584.           ELSE
  2585.           DO
  2586.              tmp = filtered
  2587.              filtered = ""
  2588.           END
  2589.    
  2590.           CALL LINEOUT global.outFile, tmp1 || type_name || tmp || "]"
  2591.           tmp1 = leadin
  2592.        END
  2593.     END
  2594.  
  2595.     RETURN
  2596.  
  2597.  
  2598.  
  2599. /* ask user for the filename into which output should be printed */
  2600. GET_FILENAME: PROCEDURE EXPOSE global.
  2601.     IF global.outFile = "" THEN
  2602.        tmpResult = DIRECTORY() || "\"   /* supply current directory as default */
  2603.     ELSE
  2604.        tmpResult = global.outFile
  2605.  
  2606.     DO FOREVER
  2607.        SAY
  2608.        SAY "Enter file name:"
  2609.        SAY
  2610.        PARSE VALUE edit_value("E", tmpResult, "A", , "Enter new File-name") WITH tmpState tmpResult
  2611.  
  2612.        IF tmpState = "OK" THEN          /* check whether file exists already */
  2613.        DO
  2614.           IF STREAM(tmpResult, "C", "QUERY EXISTS") <> "" THEN
  2615.           DO
  2616.              CALL BEEP 2000, 250
  2617.              SAY
  2618.              SAY global.iYellow || tmpResult || global.iCyan "exists already !"
  2619.  
  2620.              SAY
  2621.              SAY "       " global.iYellow || "D" global.iCyan "delete existing file"
  2622.              SAY "       " global.iYellow || "A" global.iCyan "append to existing file"
  2623.              SAY "       " global.iCyan   || "   [Esc] to return"
  2624.           
  2625.              /* define choices and get answer from user */
  2626.              CALL CHAROUT , global.iYellow
  2627.              answer = get_answer("DAQ", , "NO-CR")   /* force an entry */
  2628.              CALL CHAROUT , global.iCyan
  2629.       
  2630.              SELECT
  2631.                 WHEN answer = "1B"x THEN RETURN 0     /* Escape was pressed */
  2632.                 WHEN answer = "Q" THEN SIGNAL halt
  2633.                 WHEN answer = "D" THEN                /* delete existing file */
  2634.                      DO
  2635.                         ADDRESS CMD "@DEL" tmpResult /* let OS/2 erase it */
  2636.                         IF RC <> 0 THEN
  2637.                         DO
  2638.                            SAY
  2639.                            CALL error_msg tmpResult || global.iCyan "could not be deleted, aborting..."
  2640.                            RETURN 0
  2641.                         END
  2642.                      END
  2643.       
  2644.                 OTHERWISE NOP
  2645.              END  
  2646.              LEAVE
  2647.           END
  2648.           ELSE       /* file does not exist yet, create it */
  2649.           DO
  2650.              IF STREAM(tmpResult, "C", "Open Write") <> "READY:" THEN        /* error while creating file */
  2651.              DO
  2652.                 SAY
  2653.                 CALL error_msg global.iYellow || tmpResult global.iRedWhite || "could not be created !"
  2654.  
  2655.                 CALL CHAROUT , "Try another filename ?" global.yesHint
  2656.                 answer = get_answer("YNQ")
  2657.                 CALL CHAROUT , global.iCyan
  2658.          
  2659.                 SELECT
  2660.                    WHEN answer = "1B"x THEN RETURN 0     /* Escape was pressed */
  2661.                    WHEN answer = "Q" THEN SIGNAL halt
  2662.                    WHEN answer = "Y" || answer = "" THEN ITERATE
  2663.                    OTHERWISE RETURN 0
  2664.                 END
  2665.              END
  2666.           END
  2667.        END
  2668.        ELSE RETURN 0
  2669.  
  2670.        LEAVE
  2671.     END
  2672.     global.outFile = tmpResult
  2673.  
  2674.     RETURN 1
  2675.  
  2676. /***********************************************************************************************/
  2677.  
  2678. /*
  2679.    display error message passed in ARG(1)
  2680.    if ARG(2) given, then do not prompt user for keyboard-input, return immediately
  2681. */
  2682. ERROR_MSG: PROCEDURE EXPOSE global.
  2683.     SAY global.iRedWhite || ARG(1)  global.iCyan
  2684.     IF ARG() = 1 THEN
  2685.     DO
  2686.        CALL BEEP 2000, 250
  2687.        SAY "press any key to continue..."
  2688.        tmp = SysGetKey("NOECHO")
  2689. /*
  2690.        IF TRANSLATE(tmp) = "Q" THEN SIGNAL halt         /* quit immediately */
  2691. */
  2692.        SAY
  2693.     END
  2694.     RETURN
  2695.  
  2696.  
  2697. HALT:
  2698.    IF global.outFile <> "" THEN         /* interrupted while writing to file */
  2699.    DO
  2700.       CALL STREAM global.outFile, "C", "CLOSE"  /* close file */
  2701.    END
  2702.  
  2703.    IF global.eMultipleFiles & global.config.eLog THEN
  2704.    DO
  2705.       CALL LINEOUT global.eLogFile, DATE("S") TIME() "*** User interrupted application ! ***"
  2706.       CALL LINEOUT global.eLogFile, COPIES("*", 80)
  2707.       CALL STREAM  global.eLogFile, "C", "CLOSE"
  2708.    END
  2709.  
  2710.    SAY 
  2711.    SAY global.iNormal || "User interrupted application."
  2712.    EXIT -1                              /* user interrupt       */
  2713.  
  2714.  
  2715. /* ********************** ********************** *********************** ****************** */
  2716.  
  2717. BACKUP_RESTORE_UPDATE: PROCEDURE EXPOSE global.
  2718.  
  2719. command_line = ARG(1)
  2720.  
  2721. IF command_line = "?" | TRANSLATE(command_line) = "H" THEN SIGNAL usage
  2722.  
  2723. PARSE ARG WITH '/'switch filename
  2724.  
  2725. /* defaults */
  2726. stemFiles. = ""                 /* set default to empty */
  2727. stemFiles.0 = 0                 /* no element in array  */
  2728.  
  2729. /* defaults for ASCII-backup */
  2730. IF global.config.showDepth <> "V" THEN       /* show everything ? */
  2731.    global.config.showDepth = "V"     /* set switch to show everything */
  2732.  
  2733. IF global.config.showHexAs = "F" THEN        /* show filtered ASCII-string only ? */               
  2734.    global.config.showHexAs = "B"             /* show both, hexadecimal and filtered value */
  2735.  
  2736.  
  2737. /* check  arguments */
  2738. switch = TRANSLATE(switch)
  2739.  
  2740. /* extract number of generations */
  2741. generations = ""
  2742. DO i = LENGTH(switch) TO 1 BY -1
  2743.    tmp = SUBSTR(switch, i, 1)
  2744.    IF DATATYPE(tmp, "N") THEN generations = generations || tmp
  2745.    ELSE LEAVE
  2746. END
  2747.  
  2748.  
  2749. IF generations = "" THEN generations = 10               /* 10 generations, numbered from 0 thru 9 by default */
  2750. ELSE IF generations < 1 | generations > 10 THEN
  2751. DO
  2752.    SAY global.iYellow || "Invalid number of generations (minimum = 1, maximum = 10):" global.iYellow || generations || global.iNormal
  2753.    EXIT -2
  2754. END
  2755.  
  2756. switch = SUBSTR(switch, 1, i)           /* remaining switch */
  2757.  
  2758. /* check whether switch is valid */
  2759. IF switch <> "B" & switch <> "U" & switch <> "R" & switch <> "BT" & switch <> "UT" & switch <> "RT" THEN
  2760. DO
  2761.    SAY global.iYellow || "Invalid switch !" global.iYellow || switch || global.iNormal
  2762.    EXIT -2
  2763. END
  2764.  
  2765.  
  2766. IF global.config.eLog THEN
  2767. DO
  2768.    CALL log2file COPIES("-", 79), "no-indention"  /* insert empty line */
  2769.    CALL log2file "Processing of [" || command_line || "] started."
  2770. END
  2771.  
  2772. filename = STRIP(filename)              /* get rid of blanks */
  2773.  
  2774. global.eMultipleFiles = 1               /* default: multiple files to process */
  2775.  
  2776.  
  2777. IF LEFT(filename, 1) = "/" THEN         /* another switch is active */
  2778. DO
  2779.    tmp = TRANSLATE(filename)            /* force to uppercase */
  2780.    tmp = SUBSTR(tmp, 2, 1)              /* get first letter */
  2781.  
  2782.    SELECT
  2783.       WHEN tmp =  "A" THEN              /* scan ALL drives */
  2784.            map = SysDriveMap(, "USED")  /* get a list of all used drives */
  2785.  
  2786.       WHEN tmp =  "L" THEN              /* scan all LOCAL drives */
  2787.            map = SysDriveMap(, "LOCAL") /* get a list of all used drives */
  2788.  
  2789.       WHEN tmp =  "R" THEN              /* scan all REMOTE drives */
  2790.            map = SysDriveMap(, "REMOTE")/* get a list of all used drives */
  2791.  
  2792.       WHEN tmp = "D" THEN               /* scan defined DRIVES */
  2793.            DO
  2794.               PARSE UPPER VAR filename ":" map
  2795.  
  2796.               IF VERIFY(map, XRANGE("41"x, "5A"x)) > 0 THEN     /* range: "A" ... "Z" */
  2797.               DO
  2798.                  SAY "Error in argument: Illegal drive-letters !"
  2799.                  EXIT -2
  2800.               END
  2801.  
  2802.               tmp = ""                  /* build "A: B: C:" from ABC */
  2803.               DO WHILE map <> ""
  2804.                  PARSE VAR map 1 drive 2 map
  2805.                  tmp = tmp drive ||":"
  2806.               END
  2807.               map = STRIP(tmp)
  2808.            END
  2809.  
  2810.       OTHERWISE                         /* USER (OS2.INI), SYSTEM (OS2SYS.INI), or BOTH */
  2811.            DO
  2812.               map = ""                  /* indicate no checking on drives */
  2813.               tmp_user   = VALUE("USER_INI", , "OS2ENVIRONMENT")
  2814.               tmp_system = VALUE("SYSTEM_INI", , "OS2ENVIRONMENT")
  2815.  
  2816.               IF tmp_user = "" | tmp_system = "" THEN
  2817.               DO
  2818.                  CALL log2file COPIES("-", 79), "no-indention"  /* insert empty line */
  2819.                  SAY "Error: environment variables USER_INI or SYSTEM_INI not set !"
  2820.                  EXIT -2
  2821.               END
  2822.  
  2823.               stemFiles.0 = 1           /* default, one entry only     */
  2824.  
  2825.               IF tmp = "U" THEN         /* backup USER-ini   = OS2.INI */
  2826.                  stemFiles.1 = tmp_user
  2827.               ELSE IF tmp = "S" THEN    /* backup SYSTEM-ini = OS2SYS.INI */
  2828.                  stemFiles.1 = tmp_system
  2829.               ELSE IF tmp = "B" THEN    /* backup both, OS2.INI and OS2SYS.INI */
  2830.               DO
  2831.                  stemFiles.0 = 2        /* two files to backup */
  2832.                  stemFiles.1 = tmp_user
  2833.                  stemFiles.2 = tmp_system
  2834.               END
  2835.               ELSE
  2836.               DO
  2837.                  SAY "Error: wrong command-line switch !"
  2838.                  EXIT -2
  2839.               END
  2840.  
  2841.            END
  2842.    END
  2843.  
  2844.    DO WHILE map <> ""                /* search drives for valid INI-files */
  2845.       PARSE VAR map drive map
  2846.       drive = drive || "\*.ini"
  2847.  
  2848.       SAY global.iCyan || "scanning drive" global.iYellow ||drive||global.iCyan "for accessible INI-files..." global.iNormal
  2849.       CALL SysFileTree drive, "file", "FSO"
  2850.  
  2851.       DO i = 1 TO file.0
  2852.          /* check whether accessible from OS/2, i.e. an OS/2 INI-file */
  2853.          ok = SysIni(file.i, 'ALL:', 'TopLevel')
  2854.          IF ok <> "ERROR:" & TopLevel.0 > 0 THEN
  2855.          DO
  2856.             SAY global.iCyan || "     file" global.iYellow || file.i || global.iCyan "found..." global.iNormal
  2857.             stemFiles.0 = stemFiles.0 + 1
  2858.             j = stemFiles.0
  2859.             stemFiles.j = file.i
  2860.          END
  2861.       END
  2862.       SAY 
  2863.    END
  2864. END
  2865. ELSE                    /* single, specific file */
  2866. DO
  2867.    /* check whether file exists */
  2868.    IF filename = "" THEN sourceFile = ""
  2869.    ELSE sourceFile = STREAM(filename, "C", "QUERY EXISTS")
  2870.  
  2871.    IF sourceFile = "" THEN
  2872.    DO
  2873.       CALL log2file, "***Error: file does not exist !***", "no-date"
  2874.       SAY "Error: file does not exist !"
  2875.       EXIT -3
  2876.    END
  2877.  
  2878.    stemFiles.0 = 1              /* default, one entry only     */
  2879.    stemFiles.1 = sourceFile
  2880.    global.eMultipleFiles = 0    /* one distinct file to process */
  2881. END
  2882.  
  2883.  
  2884.  
  2885. global.iBackupMode = "Quiet !"
  2886.  
  2887. DO  iI = 1 TO stemFiles.0               /* cycle thru all given files */
  2888.    sourceFile = stemFiles.iI            /* assign file to work-variable */
  2889.    SAY COPIES("=", 79)
  2890.  
  2891.    global.iBackupMode.iDrive = FILESPEC("drive", sourceFile)
  2892.    global.iBackupMode.iPath  = FILESPEC("path",  sourceFile)
  2893.  
  2894.    
  2895.    SELECT
  2896.       WHEN switch = "B" THEN       /* backup to an OS2-INI-file */
  2897.            DO
  2898.               tmp = "BACKUP-MODE: produced backup wil be an OS/2-INI-file."
  2899.               SAY global.iCyan || tmp || global.iNormal
  2900.               SAY
  2901.  
  2902.               IF global.config.eLog THEN
  2903.               DO
  2904.                  CALL log2file tmp
  2905.                  CALL log2file "SOURCE:" sourceFile, "no-date"
  2906.               END
  2907.  
  2908.               IF check_if_ini(sourceFile) < 0 THEN
  2909.               DO
  2910.                  SAY global.iRedWhite || "***Error: [" || sourcefile || "] is not an OS/2-INI-file !" global.iNormal
  2911.  
  2912.                  IF global.config.eLog THEN
  2913.                      CALL log2file "***Error: not an OS/2-INI-file !***", "no-date"
  2914.               END
  2915.               ELSE
  2916.               DO
  2917.                  global.bkpFileName = get_next_file(sourceFile, generations)  /* get name of the new backup-file */
  2918.    
  2919.                  IF global.config.eLog THEN
  2920.                     CALL log2file "TARGET:" global.bkpFileName, "no-date"
  2921.    
  2922.                  CALL make_backup sourceFile, global.bkpFileName              /* backup the INI-file */
  2923.               END
  2924.    
  2925.            END
  2926.    
  2927.       WHEN switch = "BT" THEN      /* backup to an ASCII-file */
  2928.            DO
  2929.               tmp = "BACKUP-MODE: produced backup will be an ASCII-file."
  2930.               SAY global.iCyan || tmp || global.iNormal
  2931.               SAY
  2932.  
  2933.               IF global.config.eLog THEN
  2934.               DO
  2935.                  CALL log2file tmp
  2936.                  CALL log2file "SOURCE:" sourceFile, "no-date"
  2937.               END
  2938.  
  2939.               IF \global.eMultipleFiles & check_if_ini(sourceFile) < 0 THEN
  2940.               DO
  2941.                  SAY global.iRedWhite || "***Error: not an OS/2-INI-file !" global.iNormal
  2942.                  IF global.config.eLog THEN
  2943.                      CALL log2file "***Error: source file is not an OS/2-INI-file !***", "no-date"
  2944.               END
  2945.               ELSE      /* single file */
  2946.               DO
  2947.                  /* backup-ASCII-file gets a file extension of "TXT", hence 10 backups from "TX0" thru "TX9" */
  2948.                  tmpName  = FILESPEC("name",  sourceFile)
  2949.       
  2950.                  pointPos = LASTPOS(".", tmpName)
  2951.                  IF pointPos = 0 THEN tmpName = tmpName || ".TXT"
  2952.                                  ELSE tmpName = SUBSTR(tmpName, 1, pointPos) || "txt"
  2953.       
  2954.                  /* build name of the new backup-file */
  2955.                  global.outFile = get_next_file(global.iBackupMode.iDrive ||, /* get name of the new backup-file */
  2956.                                                 global.iBackupMode.iPath ||,
  2957.                                                 tmpName, generations)
  2958.    
  2959.                  IF global.config.eLog THEN
  2960.                     CALL log2file "TARGET:" global.outFile, "no-date"
  2961.       
  2962.                  stemIni.0 = 1                  /* number of entries */
  2963.                  stemIni.1 = sourceFile         /* assign OS/2-INI-file-name to be printed into an ASCII-file */
  2964.       
  2965.                  SAY
  2966.                  SAY global.iCyan || "Source (OS/2-INI-file):" global.iYellow || sourceFile
  2967.                  SAY global.iCyan || "Target (backup-file):  " global.iYellow || global.outFile || global.iNormal
  2968.                  SAY
  2969.       
  2970.                  CALL read_toplevel 1           /* read & prepare TopLevel entries, do not sort them */
  2971.       
  2972.                  CALL print 1                   /* call print-procedure, have all TopLevels printed */
  2973.               END
  2974.    
  2975.               CALL STREAM global.outFile, "C", "CLOSE"     /* close output file */
  2976.            END
  2977.    
  2978.       WHEN switch = "R" | switch = "U" THEN     /* restore or update from an OS/2-INI-file */
  2979.            DO
  2980.               IF switch = "R" THEN tmp = "RESTORE-MODE:"
  2981.                               ELSE tmp = "UPDATE-MODE:"
  2982.    
  2983.               tmp = tmp "backup is in OS/2-INI-file-format."
  2984.               SAY global.iCyan || tmp || global.iNormal
  2985.               SAY
  2986.  
  2987.               IF global.config.eLog THEN
  2988.                  CALL log2file tmp 
  2989.  
  2990.               IF global.eMultipleFiles THEN     /* if multiple files, get the true backup */
  2991.               DO
  2992.                  tmp = get_backup_to_use(sourceFile, generations, "INI")
  2993.  
  2994.                  IF tmp = "" THEN
  2995.                  DO
  2996.                     SAY global.iRedWhite || "***Error: No backup found for [" || sourcefile || "] !" global.iNormal
  2997.                     IF global.config.eLog THEN
  2998.                        CALL log2file "***Error: no backup file found for [" || sourceFile || "] ***", "no-date"
  2999.                     ITERATE
  3000.                  END
  3001.  
  3002.                  sourceFile = tmp
  3003.               END
  3004.  
  3005.               IF global.config.eLog THEN
  3006.                  CALL log2file "SOURCE:" sourceFile, "no_date"
  3007.  
  3008.               targetIni = getBkpData_Ini(sourceFile)       /* get target INI-name from INI-bkp */
  3009.  
  3010.               IF targetIni <> "" THEN
  3011.               DO
  3012.                  IF global.config.eLog THEN
  3013.                     CALL log2file "TARGET:" targetIni, "no_date"
  3014.    
  3015.                  CALL ini_restore_from_ini sourceFile, targetIni, switch     /* restore/update original */
  3016.               END
  3017.  
  3018.               CALL STREAM sourceFile, "C", "CLOSE"         /* close input file */
  3019.            END
  3020.    
  3021.       WHEN switch = "RT" | switch = "UT" THEN      /* restore or update from an ASCII-file */
  3022.            DO
  3023.               IF switch = "RT" THEN tmp = "RESTORE-MODE:"
  3024.                                ELSE tmp = "UPDATE-MODE:"
  3025.    
  3026.               tmp = tmp "backup is in ASCII-file-format."
  3027.               SAY global.iCyan || tmp  || global.iNormal
  3028.               SAY
  3029.    
  3030.               IF global.config.eLog THEN
  3031.                  CALL log2file tmp 
  3032.  
  3033.               IF global.eMultipleFiles THEN     /* if multiple files, get the true backup  */
  3034.               DO
  3035.                  tmp = get_backup_to_use(sourceFile, generations, "TXT")
  3036.  
  3037.                  IF tmp = "" THEN
  3038.                  DO
  3039.                     SAY global.iRedWhite || "***Error: No backup found for [" || sourcefile || "] !" global.iNormal
  3040.                     IF global.config.eLog THEN
  3041.                        CALL log2file "***Error: no backup file found for [" || sourceFile || "] ***", "no-date"
  3042.                     ITERATE
  3043.                  END
  3044.                  sourceFile = tmp
  3045.               END
  3046.  
  3047.               IF global.config.eLog THEN
  3048.                  CALL log2file "SOURCE:" sourceFile, "no_date" 
  3049.  
  3050.               CALL ini_restore_from_txt sourceFile, switch /* use ASCII-file for restore */
  3051.               CALL STREAM sourceFile, "C", "CLOSE"         /* close input file */
  3052.            END
  3053.    
  3054.    
  3055.       OTHERWISE NOP
  3056.    END  
  3057. END 
  3058.  
  3059.  
  3060. IF global.config.eLog THEN
  3061.    CALL log2file "Processing of [" || command_line || "] ended."
  3062.  
  3063. EXIT 0          /* finished batch-mode */
  3064.  
  3065.  
  3066.  
  3067.  
  3068.  
  3069. /*************************************************************************************/
  3070.  
  3071.  
  3072. /*
  3073.         find the backup according to the given generation
  3074.         if no backup with the given generation was found, try to locate a "younger" backup
  3075.         if no backup was found return an empty string
  3076. */
  3077. GET_BACKUP_TO_USE: PROCEDURE EXPOSE global.
  3078.    sourceFile = ARG(1)
  3079.    generations = ARG(2)
  3080.    extension = ARG(3)
  3081.  
  3082.    tmp = SUBSTR(sourceFile, 1, LASTPOS(".", sourceFile)) || SUBSTR(extension, 1, 2)
  3083.  
  3084.    DO i = generations - 1 TO 0 BY -1
  3085.       sourceFile = STREAM(tmp || i, "C", "QUERY EXISTS")
  3086.       IF sourceFile <> "" THEN LEAVE            /* found ! */
  3087.    END
  3088.  
  3089.    RETURN sourceFile
  3090.  
  3091.  
  3092.  
  3093.  
  3094. /*
  3095.    restores/updates from an INI-file
  3096.    if restore, then delete TopLevels and Keys in original which are not found in the backup
  3097. */
  3098. INI_RESTORE_FROM_TXT: PROCEDURE EXPOSE global. 
  3099.     bkpIniFile  = ARG(1)
  3100.     switch      = ARG(2)
  3101.     done        = 0
  3102.     line        = ""
  3103.  
  3104.     /* get target filename */
  3105.     DO WHILE LINES(bkpIniFile) > 0
  3106.        IF line = "" & done THEN LEAVE
  3107.        line = LINEIN(bkpIniFile)
  3108.        PARSE VAR line key "[" value "]" rest
  3109.  
  3110.        IF SUBSTR(STRIP(key), 1, 1) = ";" THEN ITERATE   /* ignore comments */
  3111.  
  3112.        key = TRANSLATE(key)             /* translate key into uppercase */
  3113.  
  3114.        IF key = "FILE" THEN
  3115.        DO
  3116.           done = 1
  3117.           IF rest <> "" THEN            /* right square bracket part of filename ? */
  3118.           DO
  3119.              right_bracket = LASTPOS("]", rest)
  3120.              IF right_bracket > 0 THEN
  3121.              DO
  3122.                 value = value || "]"
  3123.                 IF right_bracket > 1 THEN
  3124.                    value = value || SUBSTR(rest, 1, right_bracket-1)
  3125.              END
  3126.  
  3127.           END
  3128.  
  3129.           targetIniFile = value
  3130.  
  3131.           /* now check, whether file spans multiple lines */
  3132.           DO WHILE LINES(bkpIniFile) > 0
  3133.              line = LINEIN(bkpIniFile)
  3134.                             /* comment-line ? */
  3135.              IF line = "" | SUBSTR(STRIP(line), 1, 1) = ";" THEN LEAVE          /* finished reading filename */
  3136.  
  3137.              PARSE VAR line key "[" value
  3138.              IF key <> "" THEN LEAVE            /* TopL# - key comes along */
  3139.  
  3140.              targetIniFile = targetIniFile || SUBSTR(value, 1, LASTPOS("]", value) - 1)
  3141.           END
  3142.           LEAVE
  3143.        END
  3144.     END 
  3145.  
  3146.  
  3147.     IF \done THEN
  3148.     DO
  3149.        IF global.config.eLog THEN
  3150.           CALL log2file "***Error: ASCII-file does not contain a valid backup ! ***", "no-date"
  3151.  
  3152.        IF global.eMultipleFiles THEN
  3153.        DO
  3154.           SAY global.iRedWhite || "***Error: ASCII-file [" || bkpIniFile || "] does not contain a valid Backup !" global.iNormal
  3155.           RETURN
  3156.        END
  3157.  
  3158.        CALL BEEP 2500, 250
  3159.        SAY global.iCyan || "ASCII-file" global.iYellow || bkpIniFile global.iRedWhite ||   "does not contain a valid INI-backup !" || global.iNormal
  3160.        EXIT -3
  3161.     END
  3162.  
  3163.  
  3164.     /******************************/
  3165.     /* check validity of INI-file */
  3166.     century = SUBSTR(DATE("S"), 1, 2)    /* get century information */
  3167.  
  3168.     /* file to restore must be in the same directory as backup */
  3169.     tmpFile = global.iBackupMode.iDrive ||,
  3170.               global.iBackupMode.iPath ||,
  3171.               FILESPEC("name", targetIniFile)
  3172.  
  3173.     tmpResult = STREAM(tmpFile, "C", "QUERY EXISTS")
  3174.  
  3175.     IF tmpResult = "" THEN              /* does not exist in backup directory */
  3176.     DO
  3177.        IF global.eMultipleFiles THEN
  3178.        DO
  3179.           origIniFile = tmpFile
  3180.        END
  3181.        ELSE
  3182.        DO
  3183.           CALL BEEP 2000, 250
  3184.           SAY global.iCyan || "Target-INI-file:" global.iYellow || tmpFile global.iCyan || "not found!" || global.iNormal
  3185.           SAY
  3186.    
  3187.           tmp = global.iCyan || "Create it ?" global.yesHint
  3188.           global.eAnswer = 1                              /* default to yes */
  3189.    
  3190.           IF \get_yes_no(tmp, "global.eAnswer") | \global.eAnswer THEN
  3191.           DO
  3192.              IF global.config.eLog THEN
  3193.                 CALL log2file "Message: original INI-file does not exist, user aborted.", "no-date"
  3194.              CALL CHAROUT , global.iNormal
  3195.              EXIT -1    /* user aborted backup */
  3196.           END
  3197.  
  3198.           CALL CHAROUT , global.iNormal
  3199.  
  3200.           origIniFile = tmpFile
  3201.        END
  3202.  
  3203.        IF global.config.eLog THEN
  3204.           CALL log2file "Message: original INI-file does not exist, will be created.", "no-date"
  3205.     END
  3206.     ELSE
  3207.        origIniFile = tmpResult
  3208.    
  3209.     /* get date/time of target, i.e. INI-file */
  3210.     tmpDateTime = STREAM(origIniFile, "C", "QUERY DATETIME")
  3211.    
  3212.     IF tmpDateTime <> '' THEN
  3213.     DO
  3214.         /* make a sorted day from the American bound date-format  ! */
  3215.         PARSE VAR tmpDateTime month"-"day"-"year time
  3216.         tmpOrigDateTime = century || year || month || day time
  3217.     END
  3218.     ELSE
  3219.        tmpOrigDateTime = RIGHT("-", 8) || "  " || RIGHT("-", 8)
  3220.    
  3221.     /* get date/time of backup */
  3222.     tmpDateTime = STREAM(bkpIniFile, "C", "QUERY DATETIME")
  3223.    
  3224.     IF tmpDateTime <> '' THEN
  3225.     DO
  3226.         /* make a sorted day from the American bound date-format  ! */
  3227.         PARSE VAR tmpDateTime month"-"day"-"year time
  3228.         tmpBkpDateTime = century || year || month || day time
  3229.     END
  3230.    
  3231.     IF global.config.eLog THEN
  3232.        CALL log2file "TARGET:" origIniFile, "no-date"
  3233.  
  3234.     SAY global.iCyan || "Backup:   (" || global.iYellow || tmpBkpDateTime  || global.iCyan || ")" global.iYellow || bkpIniFile
  3235.     SAY global.iCyan || "Original: (" || global.iYellow || tmpOrigDateTime || global.iCyan || ")" global.iYellow || origIniFile || global.iNormal
  3236.  
  3237.  
  3238.     /***********/
  3239.     /* restore */
  3240.  
  3241.     DROP stemTopLevel.
  3242.     stemTopLevel. = ""
  3243.     stemTopLevel.0 = 0          /* no entries into this array */
  3244.  
  3245.     DROP stemKey.
  3246.     stemKey. = ""
  3247.     stemKey.0 = 0               /* no entries into this array */
  3248.  
  3249.     DROP tmp.
  3250.     tmp. = ""
  3251.  
  3252.     iTop = 0
  3253.     iKey = 0
  3254.     iVal = 0
  3255.  
  3256.     global.eTmp = 0
  3257.  
  3258.     DO WHILE LINES(bkpIniFile) > 0
  3259.        line = LINEIN(bkpIniFile)                /* read next line */
  3260.  
  3261.                       /* comment ? */
  3262.        IF line = "" | SUBSTR(STRIP(line), 1, 1) = ";" THEN ITERATE      /* iterate on empty line */
  3263.  
  3264.        PARSE VAR line key "[" type "]" "[" value "]" rest
  3265.  
  3266.        type = TRANSLATE(type)                   /* translate types into uppercase */
  3267.        IF type = "F" THEN ITERATE               /* hexadecimal representation with filtered ASCII-strings is ignored */
  3268.  
  3269.        key = TRANSLATE(key)                     /* translate keys into uppercase */
  3270.  
  3271.        IF SUBSTR(type, 1, 1) = "A" THEN         /* is value an ASCII-string ? */
  3272.           IF rest <> "" THEN                    /* right square bracket is part of ASCII-string ! */
  3273.           DO
  3274.              right_bracket = LASTPOS("]", rest)
  3275.              IF right_bracket > 0 THEN
  3276.              DO
  3277.                 value = value || "]"
  3278.                 IF right_bracket > 1 THEN
  3279.                    value = value || SUBSTR(rest, 1, right_bracket-1)
  3280.              END
  3281.  
  3282.           END
  3283.  
  3284.  
  3285.        SELECT
  3286.           WHEN key <> ""  THEN                  /* new Key */
  3287.                DO
  3288.                   SELECT
  3289.                      WHEN key = "TOP" THEN
  3290.                           DO
  3291.                              IF iVal THEN 
  3292.                              DO
  3293.                                 IF iTop THEN    /* same as previous TopLevel */
  3294.                                    CALL update_ini origIniFile   /* set value in hand */
  3295.                                 ELSE
  3296.                                    CALL update_ini origIniFile, "new Top"   /* set value in hand */
  3297.                              END
  3298. /*
  3299.                              SAY global.iCyan || "Toplevel #" global.iYellow || stemTopLevel.0 + 1 || global.iNormal
  3300. */
  3301.  
  3302.                              IF stemKey.0 > 0 & switch = "RT" THEN      /* restore mode, delete all keys not in backup of present TopLevel */
  3303.                                    CALL delete_non_backedup_keys origIniFile, VALUE("stemTopLevel." || stemTopLevel.0)
  3304.  
  3305.                              DROP stemKey.
  3306.                              stemKey. = ""
  3307.                              stemKey.0 = 0               /* no entries into this array */
  3308.  
  3309.                              tmp.toppy               = value
  3310.                              tmp.toppy.typeIndicator = type
  3311.                              iTop = 0
  3312.                              iKey = 0
  3313.                              iVal = 0
  3314.                           END
  3315.  
  3316.                      WHEN key = "KEY" THEN
  3317.                           DO
  3318.                              IF iVal THEN 
  3319.                              DO
  3320.                                 IF iTop THEN    /* same as previous TopLevel */
  3321.                                    CALL update_ini origIniFile   /* set value in hand */
  3322.                                 ELSE
  3323.                                 DO
  3324.                                    CALL update_ini origIniFile, "new Top"   /* set value in hand */
  3325.                                    iTop = 1
  3326.                                 END
  3327.                              END
  3328. /*
  3329.                              SAY global.iCyan || "             Key #" global.iYellow || stemKey.0 + 1 || global.iNormal
  3330. */
  3331.  
  3332.                              tmp.keyiy               = value
  3333.                              tmp.keyiy.typeIndicator = type
  3334.                              iKey = 1
  3335.                              iVal = 0
  3336.                           END
  3337.  
  3338.                      WHEN key = "VAL" THEN
  3339.                           DO
  3340.                              tmp.valuly               = value
  3341.                              tmp.valuly.typeIndicator = type
  3342.                              iVal = 1
  3343.                           END
  3344.                      OTHERWISE NOP
  3345.                   END 
  3346.                END
  3347.  
  3348.           OTHERWISE     /* add the next piece to the value */
  3349.                DO
  3350.                   SELECT
  3351.                      WHEN iVal THEN
  3352.                           tmp.valuly = tmp.valuly || value
  3353.  
  3354.                      WHEN iKey THEN
  3355.                           tmp.keyiy = tmp.keyiy || value
  3356.  
  3357.                      OTHERWISE 
  3358.                           tmp.toppy = tmp.toppy || value
  3359.                   END  
  3360.                END
  3361.        END  
  3362.     END
  3363.  
  3364.  
  3365.     IF iVal THEN 
  3366.     DO
  3367.        IF iTop THEN    /* same as previous TopLevel */
  3368.           CALL update_ini origIniFile   /* set value in hand */
  3369.        ELSE
  3370.           CALL update_ini origIniFile, "new Top"   /* set value in hand */
  3371.     END
  3372.  
  3373.     IF switch = "RT" THEN               /* restore mode, delete all keys not in backup */
  3374.     DO
  3375.        /* TopLevel in hand */
  3376.        CALL delete_non_backedup_keys origIniFile, VALUE("stemTopLevel." || stemTopLevel.0)
  3377.        CALL delete_non_backedup_toplevels origIniFile
  3378.     END
  3379.  
  3380.     IF switch = "RT" THEN tmp = "Restoring"
  3381.                      ELSE tmp = "Updating"
  3382.  
  3383.     SAY
  3384.     SAY global.iCyan || tmp "finished." global.iNormal
  3385.  
  3386.     RETURN
  3387.  
  3388.  
  3389.  
  3390. /*
  3391.    transform values, update stemTopLevel., stemKey., update target INI-file with new key
  3392.    if a second argument is given, then we have a new TopLevel being built, if there is an
  3393.    old one it has to be stored in the array
  3394. */
  3395. UPDATE_INI: PROCEDURE EXPOSE global. stemTopLevel. stemKey. tmp.
  3396.     origIniFile = ARG(1)
  3397.     iTop = stemTopLevel.0
  3398.  
  3399.     IF ARG() = 2 | iTop = 0 THEN        /* build a new TopLevel ? */
  3400.     DO
  3401.        iTop = iTop + 1
  3402.        SELECT
  3403.           WHEN tmp.toppy.typeIndicator = "H" THEN  /* hexadecimal value */
  3404.                stemTopLevel.iTop = X2C(tmp.toppy)
  3405.           WHEN tmp.toppy.typeIndicator = "A" THEN
  3406.                stemTopLevel.iTop = tmp.toppy
  3407.           WHEN tmp.toppy.typeIndicator = "A0" THEN
  3408.                stemTopLevel.iTop = tmp.toppy || "00"x
  3409.           OTHERWISE             /* illegal data-type */
  3410.                   RETURN
  3411.        END
  3412.        stemTopLevel.0 = iTop
  3413.     END
  3414.  
  3415.     iKey = stemKey.0
  3416.     iKey = iKey + 1
  3417.     stemKey.0 = iKey
  3418.  
  3419.     SELECT
  3420.        WHEN tmp.keyiy.typeIndicator = "H" THEN  /* hexadecimal value */
  3421.             stemKey.iKey = X2C(tmp.keyiy)
  3422.        WHEN tmp.keyiy.typeIndicator = "A" THEN
  3423.             stemKey.iKey = tmp.keyiy
  3424.        WHEN tmp.keyiy.typeIndicator = "A0" THEN
  3425.             stemKey.iKey = tmp.keyiy || "00"x
  3426.        OTHERWISE             /* illegal data-type */
  3427.                RETURN
  3428.     END
  3429.  
  3430.     SELECT
  3431.        WHEN tmp.valuly.typeIndicator = "H" THEN  /* hexadecimal value */
  3432.             value = X2C(tmp.valuly)
  3433.        WHEN tmp.valuly.typeIndicator = "A" THEN
  3434.             value = tmp.valuly
  3435.        WHEN tmp.valuly.typeIndicator = "A0" THEN
  3436.             value = tmp.valuly || "00"x
  3437.        OTHERWISE             /* illegal data-type */
  3438.                RETURN
  3439.     END
  3440.  
  3441.  
  3442.  
  3443.     CALL SysIni origIniFile, stemTopLevel.iTop, stemKey.iKey, value     /* set new value */
  3444.  
  3445.     IF global.eTmp <> iTop THEN         /* was this Toplevel printed already ? */
  3446.     DO
  3447.        CALL check_value stemTopLevel.iTop
  3448.        SAY
  3449.        SAY global.iCyan || "Toplevel #" global.iYellow || iTop global.iCyan || "[" || strings.type || "] [" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iNormal
  3450.        global.eTmp = iTop
  3451.     END
  3452.  
  3453.     CALL check_value stemKey.iKey
  3454.     SAY global.iCyan || "        Key #" global.iYellow || iKey global.iCyan || "[" || strings.type || "] [" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iNormal
  3455.  
  3456.  
  3457.     RETURN
  3458.  
  3459.  
  3460.  
  3461.  
  3462. /*
  3463.    restores/updates from an INI-file
  3464.    if restore, then delete TopLevels and Keys in original which are not found in the backup
  3465. */
  3466. INI_RESTORE_FROM_INI: PROCEDURE EXPOSE global.
  3467.     sourceFile = ARG(1)         /* backup-INI-file */
  3468.     targetFile = ARG(2)         
  3469.     switch     = ARG(3)         /* if "R", then delete superfluous TopLevels and Keys in the original */
  3470.  
  3471.     CALL SysIni sourceFile, 'ALL:', 'stemTopLevel'
  3472.  
  3473.     DO i = 1 TO stemTopLevel.0          /* cycle thru all TopLevels */
  3474.        DROP stemKey.
  3475.        stemKey. = ""
  3476.  
  3477.        CALL check_value stemTopLevel.i
  3478.  
  3479.        SAY
  3480.        SAY global.iCyan || "Toplevel #" global.iYellow || i global.iCyan || "of" global.iYellow || stemTopLevel.0,
  3481.            global.iCyan ||"[" || strings.type || "] [" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iNormal
  3482.  
  3483.        CALL SysIni sourceFile, stemTopLevel.i, 'ALL:', 'stemKey'
  3484.  
  3485.        DO j = 1 TO stemKey.0            /* cycle thru all Keys */
  3486.           CALL check_value stemKey.j
  3487.  
  3488.           SAY global.iCyan || "             Key #" global.iYellow || j global.iCyan || "of" global.iYellow || stemKey.0,
  3489.               global.iCyan ||"[" || strings.type || "] [" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iNormal
  3490.  
  3491.           value = SysIni(sourceFile, stemTopLevel.i, stemKey.j)         /* get value */
  3492.           tmp =   SysIni(targetFile, stemTopLevel.i, stemKey.j, value)  /* set value */
  3493.        END 
  3494.  
  3495.        IF switch = "R" THEN             /* restore mode, delete all keys not in backup */
  3496.           CALL delete_non_backedup_keys targetFile, stemTopLevel.i
  3497.     END
  3498.  
  3499.     IF switch = "R" THEN             /* restore mode, delete all targets not in backup */
  3500.        CALL delete_non_backedup_toplevels targetFile
  3501.  
  3502.     RETURN
  3503.  
  3504.  
  3505.  
  3506.  
  3507.  
  3508.  
  3509. /*
  3510.    for restore-mode only: delete keys in target which are not in backup
  3511. */
  3512. DELETE_NON_BACKEDUP_KEYS: PROCEDURE EXPOSE stemKey. stemTargetKey. global.
  3513.    targetFile = ARG(1)
  3514.    TopLevel   = ARG(2)
  3515.  
  3516.    stemTargetKey. = ""
  3517.  
  3518.    CALL SysIni targetFile, TopLevel, "ALL:", "stemTargetKey"
  3519.    CALL sort_generic "stemKey", "EXACT"          /* sort backup-keys */
  3520.    CALL sort_generic "stemTargetKey", "EXACT"    /* sort target-keys */
  3521.  
  3522.    i = 1
  3523.    j = 1
  3524.    DO FOREVER                    /* delete keys, which cannot be found in the backup */
  3525.       SELECT
  3526.          WHEN stemKey.i << stemTargetKey.j THEN          /* delete superfluous keys */
  3527.               DO
  3528.                  IF i > stemKey.0 THEN
  3529.                  DO
  3530.                     DO k = j to stemTargetKey.0
  3531.                        SAY global.iCyan || "deleting key:" global.iYellow || stemTargetKey.k global.iNormal || global.iNormal
  3532.                        CALL SysIni targetFile, TopLevel, stemTargetKey.k, "DELETE:"
  3533.                        CALL log_deletions TopLevel, stemTargetKey.k
  3534.                     END
  3535.                     LEAVE
  3536.                  END
  3537.                  ELSE                   /* duplicate in bkp-keys, if so it comes from TXT-file */
  3538.                    i = i + 1
  3539.               END
  3540.  
  3541.          WHEN stemKey.i == stemTargetKey.j THEN          /* identical, o.k. */
  3542.               DO
  3543.                  i = i + 1
  3544.                  j = j + 1
  3545.                  IF j > stemTargetKey.0 THEN LEAVE       /* finished */
  3546.               END
  3547.  
  3548.          OTHERWISE /* stemKey.i >> stemTargetKey.j THEN  ... delete superfluous key */
  3549.               DO
  3550.                  SAY global.iCyan || "deleting key:" global.iYellow || stemTargetKey.j global.iNormal || global.iNormal
  3551.  
  3552.                  CALL SysIni targetFile, TopLevel, stemTargetKey.j, "DELETE:"
  3553.                  CALL log_deletions TopLevel, stemTargetKey.k
  3554.                  j = j + 1
  3555.               END
  3556.       END
  3557.    END
  3558.    RETURN
  3559.  
  3560.  
  3561. /*
  3562.    for restore-mode only: delete TopLevels in target which are not in backup
  3563. */
  3564. DELETE_NON_BACKEDUP_TOPLEVELS: PROCEDURE EXPOSE stemTopLevel. global.
  3565.    targetFile = ARG(1)
  3566.  
  3567.    stemTargetTopLevel. = ""
  3568.  
  3569.    CALL SysIni targetFile, "ALL:", "stemTargetTopLevel"
  3570.    CALL sort_generic "stemTopLevel", "EXACT"          /* sort backup-TopLevels */
  3571.    CALL sort_generic "stemTargetTopLevel", "EXACT"    /* sort target-TopLevels */
  3572.  
  3573.    i = 1
  3574.    j = 1
  3575.    DO FOREVER                    /* delete TopLevels, which cannot be found in the backup */
  3576.       SELECT
  3577.          WHEN stemTopLevel.i << stemTargetTopLevel.j THEN          /* delete superfluos TopLevels */
  3578.               DO
  3579.                  IF i > stemTopLevel.0 THEN
  3580.                  DO
  3581.                     DO k = j TO stemTargetTopLevel.0
  3582.                        SAY global.iCyan || "deleting topLevel:" global.iYellow || stemTargetTopLevel.k || global.iNormal || global.iNormal
  3583.                        CALL SysIni targetFile, stemTargetTopLevel.k, "DELETE:"
  3584.  
  3585.                        CALL log_deletions TopLevel
  3586.                     END
  3587.                     LEAVE
  3588.                  END
  3589.                  ELSE                   /* duplicate in bkp-toplevels, if so it comes from TXT-file */
  3590.                    i = i + 1
  3591.               END
  3592.  
  3593.          WHEN stemTopLevel.i == stemTargetTopLevel.j THEN          /* identical, o.k. */
  3594.               DO
  3595.                  i = i + 1
  3596.                  j = j + 1
  3597.                  IF j > stemTargetTopLevel.0 THEN LEAVE       /* finished */
  3598.               END
  3599.  
  3600.          OTHERWISE /* stemTopLevel.i >> stemTargetTopLevel.j THEN  ... delete superfluous key */
  3601.               DO
  3602.                  SAY global.iCyan || "deleting topLevel:" global.iYellow || stemTargetTopLevel.j global.iNormal || global.iNormal
  3603.                  CALL SysIni targetFile, stemTargetTopLevel.j, "DELETE:"
  3604.                  CALL log_deletions TopLevel
  3605.                  j = j + 1
  3606.               END
  3607.       END
  3608.    END
  3609.    RETURN
  3610.  
  3611.  
  3612.  
  3613.  
  3614.  
  3615.  
  3616. /*
  3617.    check whether backup-file is a valid OS/2-INI-file, containing entries
  3618. */
  3619. GETBKPDATA_INI: PROCEDURE EXPOSE global. bkpTopLevel.
  3620.    bkpIniFile = ARG(1)                  /* INI-file containing the backup-data */
  3621.  
  3622.    IF check_if_ini(bkpIniFile) < 0 THEN /* check whether INI-file */
  3623.    DO
  3624.       SAY global.iRedWhite || "***Error: Backup file [" || bkpIniFile || "] is not an OS/2-INI-file !" global.iNormal
  3625.  
  3626.       IF global.config.eLog THEN
  3627.          CALL log2file "***Error: not an OS/2-INI-file !***", "no-date"
  3628.  
  3629.       RETURN ""
  3630.    END
  3631.  
  3632.    century = SUBSTR(DATE("S"), 1, 2)    /* get century information */
  3633.    tmpDateTime = STREAM(bkpIniFile, "C", "QUERY DATETIME")
  3634.  
  3635.    /* make a sorted day from the American bound date-format  ! */
  3636.    PARSE VAR tmpDateTime month"-"day"-"year time
  3637.    tmpBkpDateTime = century || year || month || day time
  3638.  
  3639.    origIniFile = SysIni(bkpIniFile, global.config.showTopLevel, global.bkp.iOName)
  3640.  
  3641.    IF origIniFile <> "ERROR:" THEN      /* target must be in same directory as backup */
  3642.    DO
  3643.       origIniFile = global.iBackupMode.iDrive ||,
  3644.                     global.iBackupMode.iPath ||,
  3645.                     FILESPEC("name", origIniFile)
  3646.    END
  3647.    ELSE         /* backup was not created by this program, maybe a true copy */
  3648.    DO
  3649.       tmpFile  = FILESPEC("name", bkpIniFile)
  3650.       
  3651.       position = LASTPOS(".", tmpFile)                  /* get last dot in string */
  3652.       IF position < 1 THEN tmpTargetFile = tmpFile || ".INI"
  3653.                       ELSE tmpTargetFile = SUBSTR(tmpFile, 1, position) || "INI"
  3654.  
  3655.       origIniFile = global.iBackupMode.iDrive ||,
  3656.                     global.iBackupMode.iPath ||,
  3657.                     tmpTargetFile        /* put target INI in backup-directory */
  3658.    END
  3659.  
  3660.  
  3661.    tmpResult = STREAM(origIniFile, "C", "QUERY EXISTS") /* check whether INI-file exists */
  3662.  
  3663.    IF tmpResult = "" THEN                               /* not found */
  3664.    DO
  3665.  
  3666.       IF \global.eMultipleFiles THEN
  3667.       DO
  3668.          CALL BEEP 2000, 250
  3669.          SAY global.iCyan  "Target-INI-file:" global.iYellow || origIniFile global.iCyan || "not found!" || global.iNormal
  3670.          SAY
  3671.    
  3672.          tmp = global.iCyan || "Create it ?" global.yesHint
  3673.          global.eAnswer = 1                              /* default to yes */
  3674.          CALL CHAROUT , global.iNormal
  3675.    
  3676.          IF \get_yes_no(tmp, "global.eAnswer") | \global.eAnswer THEN 
  3677.          DO
  3678.             IF global.config.eLog THEN
  3679.                CALL log2file "Message: original INI-file does not exist, user aborted.", "no-date"
  3680.  
  3681.             CALL CHAROUT , global.iNormal
  3682.             EXIT -1      /* user aborted backup */
  3683.          END
  3684.       END
  3685.  
  3686.       IF global.config.eLog THEN
  3687.          CALL log2file "Message: original INI-file does not exist, will be created.", "no-date"
  3688.    END
  3689.    ELSE
  3690.       origIniFile = tmpResult
  3691.  
  3692.  
  3693.    tmpDateTime = STREAM(origIniFile, "C", "QUERY DATETIME")
  3694.  
  3695.    IF tmpDateTime <> '' THEN
  3696.    DO
  3697.        /* make a sorted day from the American bound date-format  ! */
  3698.        PARSE VAR tmpDateTime month"-"day"-"year time
  3699.        tmpOrigDateTime = century || year || month || day time
  3700.    END
  3701.    ELSE
  3702.       tmpOrigDateTime = RIGHT("-", 8) || "  " || RIGHT("-", 8)
  3703.  
  3704.    SAY global.iCyan || "Backup:   (" || global.iYellow || tmpBkpDateTime  || global.iCyan || ")" global.iYellow || bkpIniFile
  3705.    SAY global.iCyan || "Original: (" || global.iYellow || tmpOrigDateTime || global.iCyan || ")" global.iYellow || origIniFile || global.iNormal
  3706.  
  3707.    RETURN origIniFile
  3708.  
  3709.  
  3710. /*
  3711.         check whether file in hand is an INI-file
  3712. */
  3713. CHECK_IF_INI: PROCEDURE EXPOSE bkpTopLevel. global.
  3714.     iniFile = ARG(1)
  3715.     /* not an OS/2-INI-file or empty ? */
  3716.     val = 0
  3717.  
  3718.     IF SysIni(iniFile, "ALL:", "bkpTopLevel") = "ERROR:" THEN val = -3
  3719.     ELSE
  3720.        IF bkpTopLevel.0 = 0 THEN val = -3
  3721.  
  3722.     IF val < 0 & \global.eMultipleFiles THEN    /* not an OS/2 INI-file */
  3723.     DO
  3724.        IF global.config.eLog THEN
  3725.            CALL log2file "***Error: not an OS/2-INI-file !***", "no-date"
  3726.  
  3727.        CALL BEEP 2000, 250
  3728.        SAY global.iYellow || iniFile || global.iCyan || ":" global.iRedWhite || "not an OS/2-INI-file !" || global.iNormal
  3729.        EXIT val
  3730.     END
  3731.  
  3732.     RETURN val
  3733.  
  3734.  
  3735. /*
  3736.         produce an OS/2-INI-backup-file
  3737. */
  3738. MAKE_BACKUP: PROCEDURE EXPOSE global. bkpTopLevel.
  3739.    origIniFile = ARG(1)                 /* INI-file to be backed up */
  3740.    bkpIniFile = ARG(2)                  /* name of backup-INI-file */
  3741.  
  3742.    SAY
  3743.    SAY global.iCyan || "Source (OS/2-INI-file):" global.iYellow || origIniFile
  3744.    SAY global.iCyan || "Target (backup-file):  " global.iYellow || bkpIniFile || global.iNormal
  3745.  
  3746.    /* write information about backup-process and original file-name into INI-file */
  3747.    CALL SysIni bkpIniFile, global.config.showTopLevel, global.bkp.iOName, origIniFile
  3748.    CALL SysIni bkpIniFile, global.config.showTopLevel, global.bkp.iBName, bkpIniFile
  3749.    CALL SysIni bkpIniFile, global.config.showTopLevel, global.bkp.iDTimeStart, DATE("S") TIME()
  3750.    CALL SysIni bkpIniFile, global.config.showTopLevel, global.bkp.iTLentries, bkpTopLevel.0
  3751.  
  3752.    /* cycle thru TopLevel-entries */
  3753.    DO i = 1 TO bkpTopLevel.0               
  3754.       IF SysIni(origIniFile, bkpTopLevel.i, "ALL:", "keys") <> "ERROR:"        /* TopLevel not available anymore */
  3755.       THEN
  3756.       DO
  3757.           CALL check_value bkpTopLevel.i
  3758.    
  3759.           SAY
  3760.           SAY global.iCyan || "Toplevel #" global.iYellow || i global.iCyan || "of" global.iYellow || bkpTopLevel.0,
  3761.               global.iCyan ||"[" || strings.type || "] [" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iNormal
  3762.  
  3763.           DO j = 1 TO keys.0            /* cycle thru Key-entries */
  3764.              val = SysIni(origIniFile, bkpTopLevel.i, keys.j)
  3765.              IF val <> "ERROR:" THEN                                            /* key not available anymore */
  3766.              DO
  3767.                 CALL check_value keys.j
  3768.  
  3769.                 SAY global.iCyan || "             Key #" global.iYellow || j global.iCyan || "of" global.iYellow || Keys.0,
  3770.                     global.iCyan ||"[" || strings.type || "] [" || global.iYellow || strings.displayValue || global.iCyan || "]" global.iNormal
  3771.  
  3772.                 CALL SysIni bkpIniFile, bkpTopLevel.i, keys.j, val
  3773.              END
  3774.              ELSE
  3775.                 SAY global.iYellow || " ** error reading TopLevel #" global.iCyan || i || ", Key #" j || global.iNormal
  3776.           END
  3777.        END
  3778.        ELSE
  3779.           SAY global.iYellow || "*** error reading TopLevel #" global.iCyan || i || global.iNormal
  3780.    END
  3781.  
  3782.    CALL SysIni bkpIniFile, global.config.showTopLevel, global.bkp.iDTimeEnd, DATE("S") TIME()
  3783.  
  3784.    SAY global.iCyan || "finished backup." || global.iNormal
  3785.    RETURN
  3786.    
  3787.  
  3788.  
  3789.  
  3790.  
  3791.  
  3792. /*
  3793.         Backup-files contain at the last character position a number ranging
  3794.         from 0 (oldest) to 9 (youngest backup) == total of 10 generations
  3795.  
  3796.         ARG(1): filename with full path, e.g. "D:\OS2\OS2.INI" or "D:\OS2\OS2.TXT"
  3797.         ARG(2): number of maximum generations (1-10, expressed in filetype as 0-9)
  3798.  
  3799. */
  3800. GET_NEXT_FILE: PROCEDURE EXPOSE global.
  3801.      file        = ARG(1)
  3802.      generations = ARG(2)
  3803.  
  3804.      testname    = SUBSTR(file, 1, LENGTH(file)-1)
  3805.  
  3806.      /* find number of backups already present */
  3807.      DO last_backup = 9 TO 0 BY -1
  3808.         new_name = testname || last_backup
  3809.         IF STREAM(new_name, "C", "QUERY EXISTS") <> "" THEN LEAVE       /* last backup found ! */
  3810.      END
  3811.  
  3812.      to_delete = (last_backup + 1) - generations 
  3813.      IF to_delete >= 0 THEN                                             /* erase superfluos backups */
  3814.      DO
  3815.         DO i = 0 TO to_delete
  3816.            ADDRESS CMD "@erase" '"' || testname || i || '" 2>nul'       /* erase oldest file */
  3817.  
  3818.            IF global.config.eLog THEN
  3819.               CALL log2file "Message:" generations "backup generation(s) only, therefore erasing superfluos [" || testname || i || "]", "no-date"
  3820.         END
  3821.  
  3822.         /* move youngest backups down */
  3823.         j = 0
  3824.         DO i = i TO last_backup
  3825.            old_name = testname || i
  3826.            new_name = testname || j
  3827.            ADDRESS CMD "@ren" '"' || old_name || '" "' || FILESPEC("Name",new_name) || '"'
  3828.            j = j + 1
  3829.         END
  3830.         new_name = testname || (generations - 1)
  3831.      END
  3832.      ELSE new_name = testname || (last_backup + 1)
  3833.  
  3834.      RETURN new_name
  3835.  
  3836.  
  3837. /*
  3838.         write log-entry to file
  3839.         ARG(1) ... text to write
  3840.         ARG(2) ... if present, supply date & time
  3841. */
  3842. LOG2FILE: PROCEDURE EXPOSE global.
  3843.    IF ARG() = 1 THEN tmp = DATE("S") TIME() ARG(1)
  3844.                 ELSE IF ARG(2) = "no-indention" THEN tmp = ARG(1)
  3845.                 ELSE tmp = RIGHT("", 17) ARG(1)         /* indent */
  3846.  
  3847.    CALL LINEOUT global.eLogFile, tmp
  3848.    RETURN
  3849.  
  3850. /*
  3851.         log deletions of keys or of toplevels
  3852. */
  3853. LOG_DELETIONS: PROCEDURE EXPOSE global.
  3854.    IF \global.config.eLog THEN RETURN
  3855.  
  3856.    TopLevel = ARG(1)
  3857.    KeyName  = ARG(2)
  3858.  
  3859.    CALL check_value TopLevel
  3860.    CALL log2file "Message: Deleting Toplevel [" || strings.type || "] [" || strings.displayValue || "],", "no-date"
  3861.  
  3862.    tmp = "                  "
  3863.  
  3864.    IF KeyName = "" THEN         /* all keys were deleted */
  3865.    DO
  3866.       tmp = tmp || "all KEYS of this Toplevel were deleted !"
  3867.    END
  3868.    ELSE
  3869.    DO
  3870.       CALL check_value KeyName
  3871.       tmp = tmp || "     Key [" || strings.type || "] [" || strings.displayValue || "]."
  3872.    END
  3873.  
  3874.    CALL log2file tmp, "no-date"
  3875.  
  3876.    RETURN
  3877.  
  3878.  
  3879. USAGE:
  3880.    SAY "SHOWINI: allow to view, edit, print, backup, restore OS/2-INI-files"
  3881.    SAY
  3882.    SAY "showini        ... allow to work interactively"
  3883.    SAY
  3884.    SAY "showini /switch[generations]  {filename | /modifier}  ... batch-mode execution"
  3885.    SAY '    switch:    B[T]   ... make a BACKUP of an OS/2-INI-file'
  3886.    SAY '               U[T]   ... UPDATE original OS/2-INI using a backup'
  3887.    SAY '               R[T]   ... RESTORE original OS/2-INI using a backup, i.e. delete'
  3888.    SAY '                          keys not found in backup'
  3889.    SAY '                 T    ... backup is a text-file (i.e. ASCII-file)'
  3890.    SAY '    generations: a number between 1-10, indicating how many backup-files you'
  3891.    SAY '                 want, respectively, which backup you wish to use'
  3892.    SAY '    filename: filename of OS/2-INI-file or the filename of the backup'
  3893.    SAY '    modifier: look for all OS/2-INI-files on the filesystem[s]:'
  3894.    SAY '              L[OCAL]  ... only LOCAL filesystems are scanned'
  3895.    SAY '              R[EMOTE] ... only REMOTE filesystems are scanned'
  3896.    SAY '              A[LL] ...... both, LOCAL and REMOTE filesystems are scanned'
  3897.    SAY '              D[RIVES]:letters ... only the given driveletters are scanned,'
  3898.    SAY '                                   where letters is e.g. ACDEH'
  3899.    SAY '              process OS/2-system INI-files:'
  3900.    SAY '              S[YSTEM] ... "OS2SYS.INI" only'
  3901.    SAY '              U[SER] ..... "OS2.INI" only'
  3902.    CALL CHAROUT , '              B[OTH] ..... both, "OS2SYS.INI" and "OS2.INI"'
  3903.  
  3904.  
  3905.    EXIT 0
  3906.  
  3907. ERROR:        
  3908.    myrc = RC
  3909.    SAY 'SHOWINI.CMD: error occurred !'
  3910.    SAY
  3911.    SAY 'REXX error' myrc 'in line' SIGL':' ERRORTEXT(myrc)
  3912.    SAY Substr('     ',1,6-Length(SIGL))(SIGL)' *-*   'Sourceline(sigl)
  3913.    SAY
  3914.    SAY 'Please contact the author with a description of the error.'
  3915.  
  3916.    EXIT -99
  3917.