Index: trunk/src/helpers/comctl.c
===================================================================
--- trunk/src/helpers/comctl.c	(revision 114)
+++ trunk/src/helpers/comctl.c	(revision 116)
@@ -133,4 +133,6 @@
  *
  *@@added V0.9.13 (2001-06-21) [umoeller]
+ *@@changed V0.9.16 (2001-10-24) [umoeller]: fixed wrong hatch color and paint offset
+ *@@changed V0.9.16 (2001-10-28) [umoeller]: added bitmap support, fixed bad clip rectangle
  */
 
@@ -172,6 +174,7 @@
     {
         // button border:
-
         // now paint button frame
+
+        // make rcl inclusive
         rclWin.xRight--;
         rclWin.yTop--;
@@ -203,6 +206,6 @@
         cy = rclWin.yTop - rclWin.yBottom;
 
-        ptl.x = rclWin.xLeft + ((cx - pxbd->cxMiniIcon) / 2);
-        ptl.y = rclWin.yBottom + ((cy - pxbd->cxMiniIcon) / 2);
+        ptl.x = rclWin.xLeft + ((cx - pxbd->cxIconOrBitmap) / 2);
+        ptl.y = rclWin.yBottom + ((cy - pxbd->cyIconOrBitmap) / 2);
 
         if (fl & XBF_INUSE)
@@ -214,8 +217,10 @@
             ptl2.y = ptl.y - 2;
             GpiMove(hps,
-                    &ptl);
+                    &ptl2);     // &ptl
+                                // duh, typo V0.9.16 (2001-10-24) [umoeller]
             GpiSetPattern(hps, PATSYM_DIAG1);
-            ptl2.x = ptl.x + pxbd->cxMiniIcon + 1; // inclusive!
-            ptl2.y = ptl.y + pxbd->cxMiniIcon + 1; // inclusive!
+            GpiSetColor(hps, RGBCOL_BLACK);     // V0.9.16 (2001-10-24) [umoeller]
+            ptl2.x = ptl.x + pxbd->cxIconOrBitmap + 1; // inclusive!
+            ptl2.y = ptl.y + pxbd->cyIconOrBitmap + 1; // inclusive!
             GpiBox(hps,
                    DRO_FILL,
@@ -226,11 +231,30 @@
 
         // now paint icon
-        GpiIntersectClipRectangle(hps, &rclWin);    // exclusive!
-        WinDrawPointer(hps,
-                       // center this in remaining rectl
-                       ptl.x + ulOfs,
-                       ptl.y - ulOfs,
-                       pxbd->hptr,
-                       DP_MINI);
+
+        // make rcl inclusive           // V0.9.16 (2001-10-28) [umoeller]
+        rclWin.xRight--;
+        rclWin.yTop--;
+        GpiIntersectClipRectangle(hps,
+                                  &rclWin);    // inclusive!
+
+        // center this in remaining rectl
+        ptl.x += ulOfs;
+        ptl.y -= ulOfs;
+        if (fl & XBF_BITMAP)
+            // V0.9.16 (2001-10-28) [umoeller]
+            WinDrawBitmap(hps,
+                          pxbd->hptr,           // a bitmap really
+                          NULL,                 // entire bitmap
+                          &ptl,
+                          0,
+                          0,
+                          DBM_NORMAL);
+        else
+            WinDrawPointer(hps,
+                           // center this in remaining rectl
+                           ptl.x + ulOfs,
+                           ptl.y - ulOfs,
+                           pxbd->hptr,
+                           DP_MINI);
     }
 }
Index: trunk/src/helpers/dosh.c
===================================================================
--- trunk/src/helpers/dosh.c	(revision 114)
+++ trunk/src/helpers/dosh.c	(revision 116)
@@ -64,4 +64,5 @@
 #include <stdio.h>
 #include <stdarg.h>
+#include <ctype.h>
 
 #include "setup.h"                      // code generation and debugging options
@@ -357,5 +358,5 @@
  */
 
-PVOID doshRequestSharedMem(const char *pcszName)
+PVOID doshRequestSharedMem(PCSZ pcszName)
 {
     PVOID pvrc = NULL;
@@ -682,5 +683,5 @@
 
 VOID doshEnumDrives(PSZ pszBuffer,      // out: drive letters
-                    const char *pcszFileSystem,  // in: FS's to match or NULL
+                    PCSZ pcszFileSystem,  // in: FS's to match or NULL
                     BOOL fSkipRemoveables) // in: if TRUE, only non-removeable disks will be returned
 {
@@ -1191,12 +1192,132 @@
 
 /*
- *@@category: Helpers\Control program helpers\File management
+ *@@category: Helpers\Control program helpers\File name parsing
  */
 
 /* ******************************************************************
  *
- *   File helpers
+ *   File name parsing
  *
  ********************************************************************/
+
+/*
+ *@@ doshGetDriveSpec:
+ *      returns the drive specification in pcszFullFile,
+ *      if any is present. This is useful for UNC support.
+ *
+ *      This returns:
+ *
+ *      --  NO_ERROR: drive spec was given, and the output
+ *          fields have been set.
+ *
+ *      --  ERROR_INVALID_NAME: incorrect UNC syntax.
+ *
+ *      --  ERROR_INVALID_DRIVE: second char is ':', but
+ *          drive letter is not in the range [A-Z].
+ *
+ *      --  ERROR_INVALID_PARAMETER: no drive spec given
+ *          at all; apparently pcszFullFile is not fully
+ *          qualified in the first place, or it is NULL,
+ *          or its length is <= 2.
+ *
+ *@@added V0.9.16 (2001-10-25) [umoeller]
+ */
+
+APIRET doshGetDriveSpec(PCSZ pcszFullFile,      // in: fully q'fied file spec
+                        PSZ pszDrive,           // out: drive spec ("C:" or "\\SERVER\RESOURCE"; ptr can be NULL)
+                        PULONG pulDriveLen,     // out: length of drive spec (2 if local drive; ptr can be NULL)
+                        PBOOL pfIsUNC)          // out: set to TRUE if UNC name, FALSE otherwise (ptr can be NULL)
+{
+    APIRET  arc = NO_ERROR;
+    ULONG   ulFileSpecLength;
+
+    if (    (pcszFullFile)
+         && (ulFileSpecLength = strlen(pcszFullFile))
+         && (ulFileSpecLength >= 2)
+       )
+    {
+        // upper-case the drive letter
+        if (pcszFullFile[1] == ':')
+        {
+            CHAR cDrive = toupper(*pcszFullFile);
+            // local drive specified:
+            if (    (cDrive >= 'A')
+                 && (cDrive <= 'Z')
+               )
+            {
+                if (pszDrive)
+                {
+                    pszDrive[0] = cDrive;
+                    pszDrive[1] = ':';
+                    pszDrive[2] = '\0';
+                }
+
+                if (pulDriveLen)
+                    *pulDriveLen = 2;
+                if (pfIsUNC)
+                    *pfIsUNC = FALSE;
+            }
+            else
+                // this is not a valid drive:
+                arc = ERROR_INVALID_DRIVE;
+        }
+        else if (    (pcszFullFile[0] == '\\')
+                  && (pcszFullFile[1] == '\\')
+                )
+        {
+            // UNC drive specified:
+            // this better be a full \\SERVER\RESOURCE string
+            PCSZ pResource;
+            if (pResource = strchr(pcszFullFile + 3, '\\'))
+            {
+                // we got at least \\SERVER\:
+                ULONG ulLength;
+                PCSZ p;
+
+                // check if more stuff is coming
+                if (p = strchr(pResource + 1, '\\'))
+                {
+                    // yes: copy server and resource excluding that backslash
+                    if (p == pResource + 1)
+                        // "\\SERVER\\" is invalid
+                        arc = ERROR_INVALID_NAME;
+                    else
+                        // we got "\\SERVER\something\":
+                        // drop the last backslash
+                        ulLength = p - pcszFullFile;
+                }
+                else
+                    // "\\SERVER\something" only:
+                    ulLength = ulFileSpecLength;
+
+                if (!arc)
+                {
+                    if (pszDrive)
+                    {
+                        memcpy(pszDrive,
+                               pcszFullFile,
+                               ulLength);
+                        pszDrive[ulLength] = '\0';
+                    }
+
+                    if (pulDriveLen)
+                        *pulDriveLen = ulLength;
+                    if (pfIsUNC)
+                        *pfIsUNC = TRUE;
+                }
+            }
+            else
+                // invalid UNC name:
+                arc = ERROR_INVALID_NAME;
+        }
+        else
+            // neither local, nor UNC:
+            arc = ERROR_INVALID_PARAMETER;
+    }
+    else
+        arc = ERROR_INVALID_PARAMETER;
+
+    return (arc);
+}
 
 /*
@@ -1216,5 +1337,5 @@
  */
 
-PSZ doshGetExtension(const char *pcszFilename)
+PSZ doshGetExtension(PCSZ pcszFilename)
 {
     PSZ pReturn = NULL;
@@ -1223,8 +1344,8 @@
     {
         // find filename
-        const char *p2 = strrchr(pcszFilename + 2, '\\'),
+        PCSZ    p2 = strrchr(pcszFilename + 2, '\\'),
                             // works on "C:\blah" or "\\unc\blah"
-                   *pStartOfName = NULL,
-                   *pExtension = NULL;
+                pStartOfName = NULL,
+                pExtension = NULL;
 
         if (p2)
@@ -1234,5 +1355,5 @@
             // no backslash found:
             // maybe only a drive letter was specified:
-            if (*(pcszFilename + 1) == ':')
+            if (pcszFilename[1] == ':')
                 // yes:
                 pStartOfName = pcszFilename + 2;
@@ -1251,4 +1372,14 @@
     return (pReturn);
 }
+
+/*
+ *@@category: Helpers\Control program helpers\File management
+ */
+
+/* ******************************************************************
+ *
+ *   File helpers
+ *
+ ********************************************************************/
 
 /*
@@ -1445,5 +1576,5 @@
  */
 
-APIRET doshOpenExisting(const char *pcszFilename,   // in: file name
+APIRET doshOpenExisting(PCSZ pcszFilename,   // in: file name
                         ULONG ulOpenFlags,          // in: open flags
                         HFILE *phf)                 // out: OS/2 file handle
@@ -1552,5 +1683,5 @@
  */
 
-APIRET doshOpen(const char *pcszFilename,   // in: filename to open
+APIRET doshOpen(PCSZ pcszFilename,   // in: filename to open
                 ULONG ulOpenMode,       // in: XOPEN_* mode
                 PULONG pcbFile,         // in: new file size (if new file is created)
@@ -1756,5 +1887,5 @@
 
             arc = doshWrite(pFile,
-                            (PVOID)szTemp,
+                            (PCSZ)szTemp,
                             ulLength);
         }
@@ -1823,5 +1954,5 @@
  */
 
-APIRET doshLoadTextFile(const char *pcszFile,  // in: file name to read
+APIRET doshLoadTextFile(PCSZ pcszFile,  // in: file name to read
                         PSZ* ppszContent)      // out: newly allocated buffer with file's content
 {
@@ -1960,7 +2091,7 @@
 
 APIRET doshCreateTempFileName(PSZ pszTempFileName,        // out: fully q'fied temp file name
-                              const char *pcszDir,        // in: dir or NULL for %TEMP%
-                              const char *pcszPrefix,     // in: prefix for temp file or NULL
-                              const char *pcszExt)        // in: extension (without dot) or NULL
+                              PCSZ pcszDir,        // in: dir or NULL for %TEMP%
+                              PCSZ pcszPrefix,     // in: prefix for temp file or NULL
+                              PCSZ pcszExt)        // in: extension (without dot) or NULL
 {
     APIRET      arc = NO_ERROR;
@@ -2172,5 +2303,5 @@
  */
 
-BOOL doshQueryDirExist(const char *pcszDir)
+BOOL doshQueryDirExist(PCSZ pcszDir)
 {
     FILESTATUS3 fs3;
@@ -2195,5 +2326,5 @@
  */
 
-APIRET doshCreatePath(const char *pcszPath,
+APIRET doshCreatePath(PCSZ pcszPath,
                       BOOL fHidden) // in: if TRUE, the new directories will get FILE_HIDDEN
 {
@@ -2304,5 +2435,5 @@
  */
 
-APIRET doshDeleteDir(const char *pcszDir,
+APIRET doshDeleteDir(PCSZ pcszDir,
                      ULONG flFlags,
                      PULONG pulDirs,        // out: directories found
@@ -2844,5 +2975,5 @@
  */
 
-APIRET doshExecVIO(const char *pcszExecWithArgs,
+APIRET doshExecVIO(PCSZ pcszExecWithArgs,
                    PLONG plExitCode)            // out: exit code (ptr can be NULL)
 {
@@ -2926,6 +3057,6 @@
  */
 
-APIRET doshQuickStartSession(const char *pcszPath,       // in: program to start
-                             const char *pcszParams,     // in: parameters for program
+APIRET doshQuickStartSession(PCSZ pcszPath,       // in: program to start
+                             PCSZ pcszParams,     // in: parameters for program
                              BOOL fForeground,  // in: if TRUE, session will be in foreground
                              USHORT usPgmCtl,   // in: STARTDATA.PgmControl
Index: trunk/src/helpers/lan.c
===================================================================
--- trunk/src/helpers/lan.c	(revision 114)
+++ trunk/src/helpers/lan.c	(revision 116)
@@ -145,10 +145,10 @@
         PSERVER pBuf;
         ULONG cb = 4096;            // set this fixed, can't get it to work otherwise
-        if (pBuf = doshMalloc(cb,
-                              &arc))
+        if (pBuf = (PSERVER)doshMalloc(cb,
+                                       &arc))
         {
             if (!(arc = pNet32ServerEnum2(NULL,
                                           1,          // ulLevel
-                                          (PBYTE)pBuf,       // pbBuffer
+                                          (PUCHAR)pBuf,       // pbBuffer
                                           cb,          // cbBuffer,
                                           &ulEntriesRead, // *pcEntriesRead,
Index: trunk/src/helpers/nls.c
===================================================================
--- trunk/src/helpers/nls.c	(revision 114)
+++ trunk/src/helpers/nls.c	(revision 116)
@@ -39,4 +39,6 @@
     // as unsigned char
 
+#define INCL_DOSNLS
+#define INCL_DOSERRORS
 #define INCL_WINSHELLDATA
 #include <os2.h>
@@ -455,2 +457,35 @@
 }
 
+
+/*
+ *@@ nlsUpper:
+ *      quick hack for upper-casing a string.
+ *
+ *      This uses DosMapCase with the default system country
+ *      code and the process's codepage.
+ *
+ *@@added V0.9.16 (2001-10-25) [umoeller]
+ */
+
+APIRET nlsUpper(PSZ psz,            // in/out: string
+                ULONG ulLength)     // in: string length; if 0, we run strlen(psz)
+{
+    COUNTRYCODE cc;
+
+    if (psz)
+    {
+        if (!ulLength)
+            ulLength = strlen(psz);
+
+        if (ulLength)
+        {
+            cc.country = 0;         // use system country code
+            cc.codepage = 0;        // use process default codepage
+            return (DosMapCase(ulLength,
+                               &cc,
+                               psz));
+        }
+    }
+
+    return (ERROR_INVALID_PARAMETER);
+}
Index: trunk/src/helpers/stringh.c
===================================================================
--- trunk/src/helpers/stringh.c	(revision 114)
+++ trunk/src/helpers/stringh.c	(revision 116)
@@ -95,19 +95,27 @@
 
 PSZ strhdupDebug(const char *pszSource,
+                 unsigned long *pulLength,
                  const char *pcszSourceFile,
                  unsigned long ulLine,
                  const char *pcszFunction)
 {
-    if (pszSource)
-    {
-        PSZ p = (PSZ)memdMalloc(strlen(pszSource) + 1,
-                                pcszSourceFile,
-                                ulLine,
-                                pcszFunction);
-        strcpy(p,  pszSource);
-        return (p);
-    }
-    else
-        return (0);
+    PSZ     pszReturn = NULL;
+    ULONG   ulLength = 0;
+
+    if (    (pcszSource)
+         && (ulLength = strlen(pcszSource))
+       )
+    {
+        if (pszReturn = (PSZ)memdMalloc(ulLength + 1,
+                                        pcszSourceFile,
+                                        ulLine,
+                                        pcszFunction))
+            memcpy(pszReturn, pcszSource, ulLength + 1);
+    }
+
+    if (pulLength)
+        *pulLength = ulLength;
+
+    return (pszReturn);
 }
 
@@ -116,16 +124,30 @@
 /*
  *@@ strhdup:
- *      like strdup, but this one doesn't crash if pszSource is NULL,
- *      but returns NULL also.
+ *      like strdup, but this one doesn't crash if pszSource
+ *      is NULL, but returns NULL also. In addition, this
+ *      can report the length of the string (V0.9.16).
  *
  *@@added V0.9.0 [umoeller]
- */
-
-PSZ strhdup(const char *pszSource)
-{
-    if (pszSource)
-        return (strdup(pszSource));
-    else
-        return (0);
+ *@@changed V0.9.16 (2001-10-25) [umoeller]: added pulLength
+ */
+
+PSZ strhdup(const char *pcszSource,
+            unsigned long *pulLength)       // out: length of string excl. null terminator (ptr can be NULL)
+{
+    PSZ     pszReturn = NULL;
+    ULONG   ulLength = 0;
+
+    if (    (pcszSource)
+         && (ulLength = strlen(pcszSource))
+       )
+    {
+        if (pszReturn = (PSZ)malloc(ulLength + 1))
+            memcpy(pszReturn, pcszSource, ulLength + 1);
+    }
+
+    if (pulLength)
+        *pulLength = ulLength;
+
+    return (pszReturn);
 }
 
Index: trunk/src/helpers/threads.c
===================================================================
--- trunk/src/helpers/threads.c	(revision 114)
+++ trunk/src/helpers/threads.c	(revision 116)
@@ -146,7 +146,5 @@
                     // run thread func
                     ((PTHREADFUNC)pti->pThreadFunc)(pti);
-
                     WinDestroyMsgQueue(pti->hmq);
-
                 }
                 WinTerminate(pti->hab);
@@ -247,4 +245,12 @@
  *         the HMQ in its THREADINFO. These are automatically
  *         destroyed when the thread terminates.
+ *
+ *         WARNING: Be careful with this setting for short-lived
+ *         threads which are started synchronously while some
+ *         other thread is locking PM, e.g. in a WinSendMsg
+ *         call. While creation of the thread will still
+ *         succeed, the thread will _not_ terminate properly
+ *         while PM is locked, so do not wait for such a thread
+ *         to terminate!! (V0.9.16)
  *
  *      -- THRF_WAIT: if this is set, thrCreate does not
@@ -288,9 +294,11 @@
  *@@changed V0.9.9 (2001-03-14) [umoeller]: added THRF_WAIT_EXPLICIT
  *@@changed V0.9.12 (2001-05-20) [umoeller]: changed pfRunning to ptidRunning
+ *@@changed V0.9.16 (2001-10-28) [umoeller]: made ptidRunning volatile to prohibit compiler optimizations
  */
 
 ULONG thrCreate(PTHREADINFO pti,     // out: THREADINFO data
                 PTHREADFUNC pfn,     // in: _Optlink thread function
-                PULONG ptidRunning,  // out: variable set to TID while thread is running;
+                volatile unsigned long *ptidRunning,
+                                     // out: variable set to TID while thread is running;
                                      // ptr can be NULL
                 const char *pcszThreadName, // in: thread name (for identification)
Index: trunk/src/helpers/tmsgfile.c
===================================================================
--- trunk/src/helpers/tmsgfile.c	(revision 114)
+++ trunk/src/helpers/tmsgfile.c	(revision 116)
@@ -292,4 +292,10 @@
                         pNew->cbText = strlen(pStartOfText);
 
+                    // remove trailing newlines
+                    while (    (pNew->cbText)
+                            && (pStartOfText[pNew->cbText-1] == '\n')
+                          )
+                        (pNew->cbText)--;
+
                     // store this thing
                     if (!treeInsert(&pFile->IDsTreeRoot,
Index: trunk/src/helpers/tree.c
===================================================================
--- trunk/src/helpers/tree.c	(revision 114)
+++ trunk/src/helpers/tree.c	(revision 116)
@@ -285,4 +285,9 @@
  *      initializes the root of a tree.
  *
+ *      If (plCount != NULL), *plCount is set to null also.
+ *      This same plCount pointer can then be passed to
+ *      treeInsert and treeDelete also to automatically
+ *      maintain a tree item count.
+ *
  *@@changed V0.9.16 (2001-10-19) [umoeller]: added plCount
  */
@@ -509,7 +514,11 @@
  *      "x" specifies the new tree node which must
  *      have been allocated by the caller. x->ulKey
- *      must already contain the node's key (data).
+ *      must already contain the node's key (data)
+ *      which the sort function can understand.
+ *
  *      This function will then set the parent,
- *      left, right, and color members.
+ *      left, right, and color members. In addition,
+ *      if (plCount != NULL), *plCount is raised by
+ *      one.
  *
  *      Returns 0 if no error. Might return
@@ -521,5 +530,5 @@
 
 int treeInsert(TREE **root,                     // in: root of the tree
-               PLONG plCount,                 // in/out: item count (ptr can be NULL)
+               PLONG plCount,                   // in/out: item count (ptr can be NULL)
                TREE *x,                         // in: new node to insert
                FNTREE_COMPARE *pfnCompare)      // in: comparison func
@@ -750,4 +759,7 @@
  *      Does not free() the node though.
  *
+ *      In addition, if (plCount != NULL), *plCount is
+ *      decremented.
+ *
  *      Returns 0 if the node was deleted or
  *      STATUS_INVALID_NODE if not.
@@ -757,5 +769,5 @@
 
 int treeDelete(TREE **root,         // in: root of the tree
-               PLONG plCount,     // in/out: item count (ptr can be NULL)
+               PLONG plCount,       // in/out: item count (ptr can be NULL)
                TREE *tree)          // in: tree node to delete
 {
Index: trunk/src/helpers/wphandle.c
===================================================================
--- trunk/src/helpers/wphandle.c	(revision 114)
+++ trunk/src/helpers/wphandle.c	(revision 116)
@@ -996,5 +996,5 @@
                                          NULL);
 
-                    _Pmpf((__FUNCTION__ ": wphFindPartName returned %d", arc));
+                    // _Pmpf((__FUNCTION__ ": wphFindPartName returned %d", arc));
                 }
 
