Index: trunk/configure.cmd
===================================================================
--- trunk/configure.cmd	(revision 9)
+++ trunk/configure.cmd	(revision 9)
@@ -0,0 +1,973 @@
+/* REXX */
+/*
+ * Configures to build the Qt library
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * Contact: Qt Software Information (qt-info@nokia.com)
+ *
+ * Copyright (C) 2009 netlabs.org. OS/2 parts.
+ *
+ * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+signal on syntax
+signal on halt
+signal on novalue
+trace off
+numeric digits 12
+'@echo off'
+
+
+/* globals
+ ******************************************************************************/
+
+G.TAB           = '09'x
+G.EOL           = '0D0A'x
+
+G.ScreenWidth   = -1
+G.ScreenHeight  = -1
+
+
+G.QMAKE_VARS_FILE   = ".qmake.vars"
+
+/* Qt for OS/2 is always the open source edition */
+G.Licensee      = "Open Source"
+G.Edition       = "OpenSource"
+G.EditionString = "Open Source"
+G.QT_EDITION    = "QT_EDITION_OPENSOURCE"
+
+/* So far, GCC only */
+G.PLATFORM      = "os2-g++"
+
+/* all globals to be exposed in procedures */
+Globals = 'G. Opt. Static.'
+
+
+/* init rexx lib
+ ******************************************************************************/
+
+if (RxFuncQuery('SysLoadFuncs')) then do
+    call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
+    call SysLoadFuncs
+end
+
+
+/* startup + main + termination
+ ******************************************************************************/
+
+parse value SysTextScreenSize() with G.!ScreenHeight G.!ScreenWidth
+if (G.!ScreenHeight < 25 | G.!ScreenWidth < 80) then do
+    address 'cmd' 'mode co80,25'
+    parse value SysTextScreenSize() with G.!ScreenHeight G.!ScreenWidth
+    if (G.!ScreenHeight < 25 | G.!ScreenWidth < 80) then do
+        call SayErr 'WARNING: Cannot set screen size to 80 x 25!'
+        call SayErr 'Some messages can be unreadable.'
+        say
+        call WaitForAnyKey
+    end
+end
+
+call MagicCmdHook arg(1)
+call MagicLogHook arg(1)
+
+call Main
+
+call Done 0
+
+
+/* functions
+ ******************************************************************************/
+
+/**
+ *  Just do the job.
+ */
+Main: procedure expose (Globals)
+
+    /* reset the vars file */
+    call DeleteFile G.QMAKE_VARS_FILE
+
+    call QMakeVar "add", "styles", "cde mac motif plastique cleanlooks windows"
+    call QMakeVar "add", "decorations", "default windows styled"
+/*
+    @todo
+    QMakeVar add gfx-drivers "linuxfb"
+    QMakeVar add kbd-drivers "tty"
+    QMakeVar add mouse-drivers "pc linuxtp"
+*/
+    signal Nonsense
+    return
+
+/**
+ * Adds a new qmake variable to the cache.
+ *
+ * @param  aMode        One of: set, add, del.
+ * @param  aVarname     Variable name.
+ * @param  aContents    Variable value.
+ */
+QMakeVar: procedure expose (Globals)
+
+    parse arg aMode, aVarname, aContents
+
+    select
+        when aMode == "set" then eq = "="
+        when aMode == "add" then eq = "+="
+        when aMode == "del" then eq = "-="
+        otherwise signal Nonsence
+    end
+
+    call lineout G.QMAKE_VARS_FILE, aVarname eq aContents
+
+    return
+
+
+/* utility functions
+ ******************************************************************************/
+
+CompareFileToVar: procedure expose (Globals)
+    parse arg aFile, aVar
+    rc = stream(aFile, 'C', 'OPEN READ')
+    if (rc \= 'READY:') then do
+        call SayErr 'FATAL: Could not open '''aFile'''!'
+        call SayErr 'stream returned 'rc
+        call Done 1
+    end
+    contents = charin(aFile, 1, chars(aFile))
+    call stream aFile, 'C', 'CLOSE'
+    if (contents < aVar) then return -1
+    if (contents > aVar) then return 1
+    return 0
+
+CopyFile: procedure expose (Globals)
+    parse arg fileFrom, fileTo
+    address 'cmd' 'copy' fileFrom fileTo '1>nul 2>nul'
+    if (rc \== 0) then do
+        call SayErr 'FATAL: Could not copy '''fileFrom''' to '''fileTo'''!'
+        call SayErr 'copy returned 'rc
+        call Done 1
+    end
+    return
+
+DeleteFile: procedure expose (Globals)
+    parse arg file
+    rc = SysFileDelete(file)
+    if (rc \= 0 & rc \= 2) then do
+        call SayErr 'FATAL: Could not delete file '''file'''!'
+        call SayErr 'SysFileDelete returned 'rc
+        call Done 1
+    end
+    return
+
+MakeDir: procedure expose (Globals)
+    parse arg path
+    rc = SysMkDir(path)
+    if (rc \= 0) then do
+        call SayErr 'FATAL: Could not create directory '''path'''!'
+        call SayErr 'SysMkDir returned 'rc
+        call Done 1
+    end
+    return
+
+SaySay: procedure expose (Globals)
+    parse arg str, noeol
+    noeol = (noeol == 1)
+    if (noeol) then call charout, '    'str
+    else say '    'str
+    return
+
+SayErr: procedure expose (Globals)
+    parse arg str, noeol
+    noeol = (noeol == 1)
+    if (noeol) then call charout, str
+    else say str
+    return
+
+SayPrompt: procedure expose (Globals)
+    parse arg str, noeol
+    noeol = (noeol == 1)
+    if (noeol) then call charout, '>>> 'str
+    else say '>>> 'str
+    return
+
+/**
+ *  Waits for any key.
+ */
+WaitForAnyKey: procedure expose (Globals)
+    call SayPrompt 'Press any key to continue...'
+    call SysGetKey 'NOECHO'
+    say
+    return
+
+SayNoMove: procedure expose (Globals)
+    parse arg str
+    parse value SysCurPos() with row col
+    call SysCurState 'OFF'
+    call charout, str
+    call SysCurPos row, col
+    call SysCurState 'ON'
+    return
+
+MovePosBy: procedure expose (Globals)
+    parse arg delta
+    parse value SysCurPos() with row col
+    col = col + delta
+    row = row + (col % G.!ScreenWidth)
+    if (col < 0) then
+        row = row - 1
+    col = col // G.!ScreenWidth
+    if (col < 0) then
+        col = col + G.!ScreenWidth
+    call SysCurPos row, col
+    return
+
+/**
+ *  Displays a prompt to input a text line and returns the line entered by the
+ *  user. Letters in the mode argument have the following meanings:
+ *
+ *      N -- empty lines are not allowed (e.g. '' can never be returned)
+ *      C -- ESC can be pressed to cancel input ('1B'x is returned)
+ *
+ *  @param  prompt  prompt to display
+ *  @param  default initial line contents
+ *  @param  mode    input mode string consisting of letters as described above
+ *  @return         entered line
+ */
+InputLine: procedure expose (Globals)
+
+    parse arg prompt, default, mode
+    call SaySay prompt
+    say
+
+    mode = translate(mode)
+    allow_empty = pos('N', mode) == 0
+    allow_cancel = pos('C', mode) > 0
+
+    line = default
+    len = length(line)
+    n = len
+
+    call SayPrompt line, 1
+    extended = 0
+
+    do forever
+
+        key = SysGetKey('NOECHO')
+        if (key == 'E0'x) then do
+            extended = 1
+            iterate
+        end
+
+        select
+            when (\extended & key == '08'x) then do
+                /* Backspace */
+                if (n > 0) then do
+                    call MovePosBy -1
+                    line = delstr(line, n, 1)
+                    call SayNoMove substr(line, n)||' '
+                    len = len - 1
+                    n = n - 1
+                end
+                iterate
+            end
+            when (key == '0D'x) then do
+                /* Enter */
+                if (line == '' & \allow_empty) then iterate
+                say
+                leave
+            end
+            when (key == '1B'x) then do
+                /* ESC */
+                line = key
+                leave
+            end
+            when (extended) then do
+                select
+                    when (key == '53'x) then do
+                        /* Delete */
+                        if (n < len) then do
+                            line = delstr(line, n + 1, 1)
+                            call SayNoMove substr(line, n + 1)||' '
+                        end
+                        len = len - 1
+                    end
+                    when (key == '4B'x) then do
+                        /* Left arrow */
+                        if (n > 0) then do
+                            call MovePosBy -1
+                            n = n - 1
+                        end
+                    end
+                    when (key == '4D'x) then do
+                        /* Right arrow */
+                        if (n < len) then do
+                            call MovePosBy 1
+                            n = n + 1
+                        end
+                    end
+                    when (key == '47'x) then do
+                        /* Home */
+                        if (n > 0) then do
+                            call MovePosBy -n
+                            n = 0
+                        end
+                    end
+                    when (key == '4F'x) then do
+                        /* End */
+                        if (n < len) then do
+                            call MovePosBy len - n
+                            n = len
+                        end
+                    end
+                    otherwise nop
+                end
+                extended = 0
+                iterate
+            end
+            otherwise do
+                call charout, key
+                if (n == len) then do
+                    line = line||key
+                end
+                else do
+                    line = insert(key, line, n)
+                    call SayNoMove substr(line, n + 2)
+                end
+                len = len + 1
+                n = n + 1
+            end
+        end
+    end
+
+    say
+    return line
+
+/**
+ *  Shows the prompt to input a directory path and returns the path entered by
+ *  the user. The procedure does not return until a valid existing path is
+ *  entered or until ESC is pressed in which case it returns ''.
+ *
+ *  @param  prompt  prompt to show
+ *  @param  default initial directory
+ *  @return         selected directory
+ */
+InputDir: procedure expose (Globals)
+    parse arg prompt, default
+    dir = default
+    do forever
+        dir = InputLine(prompt, dir, 'NC')
+        if (dir == '1B'x) then do
+            say
+            return '' /* canceled */
+        end
+        if (DirExists(dir)) then leave
+        call SayErr 'The entered directory does not exist.'
+        say
+    end
+    return dir
+
+/**
+ *  Shows a Yes/No choice.
+ *
+ *  @param  prompt  prompt to show (specify '' to suppress the prompt)
+ *  @param  default default choice:
+ *      ''          - no default choice
+ *      'Y' or 1    - default is yes
+ *      other       - default is no
+ *  @return
+ *      1 if Yes is selected, otherwise 0
+ */
+GetYesNo: procedure expose (Globals)
+    parse arg prompt, default
+    default = translate(default)
+    if (default == 1) then default = 'Y'
+    else if (default \== '' & default \== 'Y') then default = 'N'
+    if (prompt \= '') then call SaySay prompt
+    say
+    call SayPrompt '[YN] ', 1
+    yn = ReadChoice('YN',, default, 'I')
+    say
+    say
+    return (yn == 'Y')
+
+/**
+ *  Shows a menu of choices and returns the menu item number selected by the
+ *  user. Letters in the mode argument have the following meanings:
+ *
+ *      E -- allow to press Enter w/o a choice (will return '')
+ *      C -- ESC can be pressed to cancel selection (will return -1)
+ *
+ *  @param  prompt  prompt to display
+ *  @param  stem    stem containing a list of choices
+ *  @param  default default choice
+ *  @param  mode    input mode string consisting of letters as described above
+ *  @return
+ *      selected menu item number
+ */
+GetChoice: procedure expose (Globals)
+    parse arg prompt, stem, default, mode
+    mode = translate(mode)
+    allowEnter = pos('E', mode) > 0
+    allowESC = pos('C', mode) > 0
+    count = value(stem'.0')
+    if (count == 0) then return
+    call SaySay prompt
+    say
+    first = 1
+    do forever
+        extChoices = ''
+        last = first + 9
+        if (last > count) then last = count
+        choices = substr('1234567890', 1, last - first + 1)
+        prompt = choices
+        if (allowEnter) then prompt = prompt'/Enter'
+        if (allowESC) then do
+            prompt = prompt'/Esc'
+            choices = choices||'1B'x
+        end
+        if (first > 1) then do
+            extChoices = extChoices||'49'x
+            prompt = prompt'/PgUp'
+            call SaySay '^^'
+        end
+        do i = first to last
+            ii = i - first + 1
+            if (ii == 10) then ii = 0
+            call SaySay ii')' value(stem'.'i)
+        end
+        if (last < count) then do
+            extChoices = extChoices||'51'x
+            prompt = prompt'/PgDn'
+            call SaySay 'vv'
+        end
+        say
+        def = ''
+        if (default \== '') then do
+            def = default - first + 1
+            if (def < 1 | def > 10) then def = ''
+            else if (def == 10) then def = 0
+        end
+        call SayPrompt '['prompt'] ', 1
+        n = ReadChoice(choices, extChoices, def, mode)
+        say
+        say
+        if (n == '1B'x) then do
+            return -1
+        end
+        if (n == '0E49'x) then do
+            first = first - 10
+            iterate
+        end
+        if (n == '0E51'x) then do
+            first = first + 10
+            iterate
+        end
+        if (n \== '') then do
+            if (n == 0) then n = 10
+            n = n + first - 1
+        end
+        leave
+    end
+    return n
+
+/**
+ *  Reads a one-key choice from the keyboard.
+ *  user. Letters in the mode argument have the following meanings:
+ *
+ *      E -- allow to press Enter w/o a choice (will return '')
+ *      C -- ESC can be pressed to cancel selection (will return '1B'x)
+ *      I -- ignore case of pressed letters
+ *
+ *  @param  choices     string of allowed one-key choices
+ *  @param  extChoices  string of allowed one-extended-key choices
+ *  @param  default     default choice (can be a key from choices)
+ *  @param  mode        input mode string consisting of letters as described above
+ *  @return
+ *      entered key (prefixed with 'E0'x if from extChoices)
+ */
+ReadChoice: procedure expose (Globals)
+    parse arg choices, extChoices, default, mode
+    mode = translate(mode)
+    ignoreCase = pos('I', mode) > 0
+    allowEnter = pos('E', mode) > 0
+    allowCancel = pos('C', mode) > 0
+    choice = default
+    call charout, choice
+    if (ignoreCase) then choice = translate(choice)
+    extended = 0
+    do forever
+        key = SysGetKey('NOECHO')
+        if (key == 'E0'x) then do
+            extended = 1
+            iterate
+        end
+        if (\extended & ignoreCase) then key = translate(key)
+        select
+            when (allowCancel & \extended & key == '1B'x) then do
+                choice = key
+                leave
+            end
+            when (choice == '' & \extended & verify(key, choices) == 0) then do
+                choice = key
+            end
+            when (extended & verify(key, extChoices) == 0) then do
+                choice = '0E'x||key
+                leave
+            end
+            when (\extended & key == '08'x & choice \== '') then do
+                /* backspace pressed */
+                call charout, key' '
+                choice = ''
+            end
+            when (\extended & key == '0D'x & (choice \== '' | allowEnter)) then do
+                leave
+            end
+            otherwise do
+                extended = 0
+                iterate
+            end
+        end
+        call charout, key
+        extended = 0
+    end
+    return choice
+
+/**
+ *  Shows a menu to select a path from the list of entries.
+ *
+ *  A special menu entry is automatically added as the last choice that allows
+ *  to enter a new location to search for valid paths (this operation will
+ *  completely overwrite all the menu entries passed to this function in the
+ *  stem).
+ *
+ *  Letters in the mode argument have the following meanings:
+ *
+ *      C -- ESC can be pressed to cancel selection (will return '')
+ *
+ *  @param  stem
+ *      stem containing entries to choose from (must be prefixed by a name from
+ *      Globals), stem'.!choice' must contain a default choice (or '')
+ *  @param  prompt
+ *      prompt to display
+ *  @param  searchPattern
+ *      pattern to search for when selecting suitable paths in the new location
+ *      (can include the directory prefix, but wildcards are supported in the
+ *      filename part only)
+ *  @param  checkPath
+ *      name of the funciton to check the path. The first argument is the path
+ *      to check. If the second argument is 0, it means a preliminary check (i.e.
+ *      the path is the result of the SysFileThree procedure); checkPath must
+ *      return a validated (and probably modified) path on success or '' if the
+ *       path is invalid. If the second argument is 1, it's a final check
+ *      (path is what returned by the preliminary check call); checkPath must
+ *      return either 1 on success or 0 on failure. The final check is done
+ *      right before calling checkVer, so it can set some global variables in
+ *      order to pass necessary data to checkVer and to the global level for
+ *      further configuration.
+ *  @param  checkVer
+ *      name of the funciton to check the version. The argument is a path to
+ *      check the version for (as returned by checkPath after the preliminary
+ *      check call).
+ *  @param  errPath
+ *      error message to display when a path check fails (%1 will be replaced
+ *      with the failed path name)
+ *  @param  mode
+ *      input mode string consisting of letters as described above
+ *  @return
+ *      the selected path or '' if the selection was canceled.
+ *      If a non-empty path is returned, stem'.!choice' will contain
+ *      the selected item number, otherwise it will be empty.
+ */
+MenuSelectPath: procedure expose (Globals)
+
+    parse arg stem, prompt, searchPattern, checkPath, checkVer, errPath, mode
+
+    mode = translate(mode)
+    if (pos('C', mode) > 0) then mode = 'C'
+    else mode = ''
+
+    if (symbol('Static.!MenuSelectPath.!Recent') \= 'VAR') then
+        Static.!MenuSelectPath.!Recent = ''
+
+    do forever
+
+        n = value(stem'.0') + 1
+        call SysStemInsert stem, n, '[type a location...]'
+        if (n == 1) then default = 1
+        else default = value(stem'.!choice')
+
+        choice = GetChoice(prompt, stem, default, mode)
+        call SysStemDelete stem, n
+
+        if (choice == -1) then return '' /* canceled */
+
+        if (choice == n) then do
+            call value stem'.!choice', ''
+            path = InputDir('Enter a location where to start searching from ',
+                            '(or Esc to cancel):',,
+                            Static.!MenuSelectPath.!Recent)
+            if (path == '') then iterate /* canceled */
+            Static.!MenuSelectPath.!Recent = path
+            call SaySay 'Please wait...'
+            say
+            patternPath = translate(filespec('D', searchPattern) || filespec('P', searchPattern))
+            patternName = filespec('N', searchPattern)
+            call SysFileTree FixDirNoSlash(path)'\'patternName, 'found', 'FSO'
+            found2.0 = 0
+            if (found.0 > 0) then do
+                do i = 1 to found.0
+                    dir = filespec('D', found.i) || filespec('P', found.i)
+                    /* check that the found path ends with the pattern path */
+                    if (translate(right(dir, length(patternPath))) \== patternPath) then
+                        iterate
+                    dir = left(dir, length(dir) - length(patternPath))
+                    /* check path validity  */
+                    interpret 'dir = 'checkPath'("'dir'")'
+                    if (dir \== '') then
+                        call SysStemInsert 'found2', 1, FixDir(dir)
+                end
+            end
+            if (found2.0 > 0) then do
+                call SysStemCopy 'found2', stem
+                /* SysStemCopy is bogus and doesn't copy the count field... */
+                call value stem'.0', found2.0
+                call value stem'.!choice', ''
+                call value stem'.!changed', 1
+                iterate
+            end
+        end
+        else do
+            path = value(stem'.'choice)
+            /* check path validity and tell the version check will be done next */
+            interpret 'ok = 'checkPath'("'path'", 1)'
+            if (ok) then do
+                if (value(stem'.!choice') \== choice) then
+                    call value stem'.!changed', 1
+                interpret 'ok = 'checkVer'("'path'")'
+                if (ok) then do
+                    call value stem'.!choice', choice
+                    return path
+                end
+                call value stem'.!choice', ''
+                iterate
+            end
+        end
+
+        call SayErr Replace(errPath, '%1', path)
+        say
+        call value stem'.!choice', ''
+
+    end
+
+/**
+ *  Encloses the given path with quotes if it contains
+ *  space characters, otherwise returns it w/o changes.
+ */
+QuotePath: procedure expose (Globals)
+    parse arg path
+    if (verify(path, ' +', 'M') > 0) then path = '"'path'"'
+    return path
+
+/**
+ *  Doubles all back slash characters to make the path ready to be put
+ *  to a C/C++ source.
+ */
+CPPPath: procedure expose (Globals)
+    parse arg path
+    return Replace(path, '\', '\\')
+
+/**
+ *  Fixes the directory path by a) converting all slashes to back
+ *  slashes and b) ensuring that the trailing slash is present if
+ *  the directory is the root directory, and absent otherwise.
+ *
+ *  @param dir      the directory path
+ *  @param noslash
+ *      optional argument. If 1, the path returned will not have a
+ *      trailing slash anyway. Useful for concatenating it with a
+ *      file name.
+ */
+FixDir: procedure expose (Globals)
+    parse arg dir, noslash
+    noslash = (noslash = 1)
+    dir = translate(dir, '\', '/')
+    if (right(dir, 1) == '\' &,
+        (noslash | \(length(dir) == 3 & (substr(dir, 2, 1) == ':')))) then
+        dir = substr(dir, 1, length(dir) - 1)
+    return dir
+
+/**
+ *  Shortcut to FixDir(dir, 1)
+ */
+FixDirNoSlash: procedure expose (Globals)
+    parse arg dir
+    return FixDir(dir, 1)
+
+/**
+ *  Returns 1 if the specified dir exists and 0 otherwise.
+ */
+DirExists: procedure expose (Globals)
+    parse arg dir
+    return (GetAbsDirPath(dir) \== '')
+
+/**
+ *  Returns the absolute path to the given directory
+ *  or an empty string if no directory exists.
+ */
+GetAbsDirPath: procedure expose (Globals)
+    parse arg dir
+    if (dir \== '') then do
+        curdir = directory()
+        dir = directory(FixDir(dir))
+        call directory curdir
+    end
+    return dir
+
+/**
+ *  Returns 1 if the specified file exists and 0 otherwise.
+ */
+FileExists: procedure expose (Globals)
+    parse arg file
+    return (GetAbsFilePath(file) \= '')
+
+/**
+ *  Returns the absolute path to the given file (including the filename)
+ *  or an empty string if no file exists.
+ */
+GetAbsFilePath: procedure expose (Globals)
+    parse arg file
+    if (file \= '') then do
+        file = stream(FixDir(file), 'C', 'QUERY EXISTS')
+    end
+    return file
+
+/**
+ *  Returns the name of the temporary directory.
+ *  The returned value doesn't end with a slash.
+ */
+GetTempDir: procedure expose (Globals)
+    dir = value('TEMP',,'OS2ENVIRONMENT')
+    if (dir == '') then dir = value('TMP',,'OS2ENVIRONMENT')
+    if (dir == '') then dir = SysBootDrive()
+    return dir
+
+/**
+ *  Magic cmd handler.
+ *  Executes the commad passed as --magic-cmd <cmd> and writes the result code
+ *  to the standard output in the form of 'RC:<rc>:CR'
+ *  (to be used by MagicLogHook).
+ */
+MagicCmdHook: procedure
+    parse arg magic' 'cmd
+    if (magic \== '--magic-cmd') then return
+    cmd = strip(cmd)
+    signal on halt name MagicCmdHalt
+    address 'cmd' cmd
+    say 'RC:'rc':CR'
+    exit 0
+
+MagicCmdHalt:
+    say 'RC:255:CR'
+    exit 255
+
+/**
+ *  Magic log handler.
+ */
+MagicLogHook: procedure
+    parse arg magic' 'file
+    if (magic \== '--magic-log') then return
+    file = strip(file)
+    signal on halt name MagicLogHalt
+    rc = 0
+    do while (stream('STDIN', 'S') == 'READY')
+        line = linein()
+        if (left(line, 3) == 'RC:') then do
+            if (right(line,3) == ':CR') then do
+                rc = substr(line, 4, length(line) - 6)
+                iterate
+            end
+        end
+        call lineout, line
+        call lineout file, line
+    end
+    exit rc
+
+MagicLogHalt:
+    exit 255
+
+/**
+ *  Adds the given path to the contents of the given variable
+ *  containing a list of paths separated by semicolons.
+ *  This function guarantees that if the given path is already contained in the
+ *  variable, the number of occurences will not increase (but the order may be
+ *  rearranged depending on the mode argument).
+ *
+ *  @param  name        variable name
+ *  @param  path        path to add
+ *  @param  mode        'P' (prepend): remove the old occurence of path (if any)
+ *                          and put it to the beginning of the variable's contents
+ *                      'A' (append): remove the old occurence of path (if any)
+ *                          and put it to the end of the variable's contents
+ *                      otherwise: append path to the variable only if it's
+ *                          not already contained there
+ *  @param  environment either '' to act on REXX variables or 'OS2ENVIRONMENT'
+ *                      to act on OS/2 environment variables
+ *
+ *  @version 1.1
+ */
+/* <<< export to qt.cmd starts */
+AddPathVar: procedure expose (Globals)
+    parse arg name, path, mode, environment
+    if (path == '') then return
+    if (verify(path, ' +', 'M') > 0) then path = '"'path'"'
+    mode = translate(mode)
+    prepend = (mode == 'P') /* strictly prepend */
+    append = (mode == 'A') /* strictly append */
+    os2Env = (translate(environment) == 'OS2ENVIRONMENT')
+    if (os2Env) then do
+        extLibPath = (translate(name) == 'BEGINLIBPATH' | translate(name) == 'ENDLIBPATH')
+        if (extLibPath) then cur = SysQueryExtLibPath(left(name, 1))
+        else cur = value(name,, environment)
+    end
+    else cur = value(name)
+    /* locate the previous occurence of path */
+    l = length(path)
+    found = 0; p = 1
+    pathUpper = translate(path)
+    curUpper = translate(cur)
+    do while (\found)
+        p = pos(pathUpper, curUpper, p)
+        if (p == 0) then leave
+        cb = ''; ca = ''
+        if (p > 1) then cb = substr(cur, p - 1, 1)
+        if (p + l <= length(cur)) then ca = substr(cur, p + l, 1)
+        found = (cb == '' | cb == ';') & (ca == '' | ca == ';')
+        if (\found) then p = p + 1
+    end
+    if (found) then do
+        /* remove the old occurence when in strict P or A mode */
+        if (prepend | append) then cur = delstr(cur, p, l)
+    end
+    /* add path when necessary */
+    if (prepend) then cur = path';'cur
+    else if (append | \found) then cur = cur';'path
+    /* remove excessive semicolons */
+    cur = strip(cur, 'B', ';')
+    p = 1
+    do forever
+        p = pos(';;', cur, p)
+        if (p == 0) then leave
+        cur = delstr(cur, p, 1)
+    end
+    if (os2Env) then do
+        if (extLibPath) then call SysSetExtLibPath cur, left(name, 1)
+        else call value name, cur, environment
+    end
+    else call value name, cur
+    return
+/* >>> export to qt.cmd ends */
+
+/**
+ *  Shortcut to AddPathVar(name, path, prepend, 'OS2ENVIRONMENT')
+ */
+/* <<< export to qt.cmd starts */
+AddPathEnv: procedure expose (Globals)
+    parse arg name, path, prepend
+    call AddPathVar name, path, prepend, 'OS2ENVIRONMENT'
+    return
+/* >>> export to qt.cmd ends */
+
+/**
+ *  Replaces all occurences of a given substring in a string with another
+ *  substring.
+ *
+ *  @param  str the string where to replace
+ *  @param  s1  the substring which to replace
+ *  @param  s2  the substring to replace with
+ *  @return     the processed string
+ *
+ *  @version 1.1
+ */
+Replace: procedure expose (Globals)
+    parse arg str, s1, s2
+    l1  = length(s1)
+    l2  = length(s2)
+    i   = 1
+    do while (i > 0)
+        i = pos(s1, str, i)
+        if (i > 0) then do
+            str = delstr(str, i, l1)
+            str = insert(s2, str, i-1)
+            i = i + l2
+        end
+    end
+    return str
+
+/**
+ *  NoValue signal handler.
+ */
+NoValue:
+    errl    = sigl
+    say
+    say
+    say 'EXPRESSION HAS NO VALUE at line #'errl'!'
+    say
+    say 'This is usually a result of a misnamed variable.'
+    say 'Please contact the author.'
+    call Done 252
+
+/**
+ *  Nonsense handler.
+ */
+Nonsense:
+    errl    = sigl
+    say
+    say
+    say 'NONSENSE at line #'errl'!'
+    say
+    say 'The author decided that this point of code should'
+    say 'have never been reached, but it accidentally had.'
+    say 'Please contact the author.'
+    call Done 253
+
+/**
+ *  Syntax signal handler.
+ */
+Syntax:
+    errn    = rc
+    errl    = sigl
+    say
+    say
+    say 'UNEXPECTED PROGRAM TERMINATION!'
+    say
+    say 'REX'right(errn , 4, '0')': 'ErrorText(rc)' at line #'errl
+    say
+    say 'Possible reasons:'
+    say
+    say '  1. Some of REXX libraries are not found but required.'
+    say '  2. You have changed this script and made a syntax error.'
+    say '  3. Author made a mistake.'
+    say '  4. Something else...'
+    call Done 254
+
+/**
+ *  Halt signal handler.
+ */
+Halt:
+    say
+    say 'CTRL-BREAK pressed, exiting.'
+    call Done 255
+
+/**
+ *  Always called at the end. Should not be called directly.
+ *  @param the exit code
+ */
+Done: procedure expose (Globals)
+    parse arg code
+    /* protect against recursive calls */
+    if (value('G.!Done_done') == 1) then exit code
+    call value 'G.!Done_done', 1
+    /* cleanup stuff goes there */
+    /* ... */
+    /* finally, exit */
+    exit code
+
+
