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