/*
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|          Program Name: WinLib                                                                                         |
|                Author: Michael Krämer                                                                                 |
|                        Michael@dk8pp.de               (private)                                                       |
|                        Michael.Kraemer@eu.necel.com   (business)                                                      |
|                  Date: 30-Nov-2007                                                                                    |
|              Language: Open Watcom C 1.7                                                                              |
|               Version: 0.2                                                                                            |
|        Program status: Version with stdint.h and stdbool.h, still testing                                             |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|  Purpose: Library with some often used functions for Windows programming                                              |
|                                                                                                                       |
|           Define WIN_CHARACTERMODE for character mode programs                                                        |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Includes                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#define __STDC_WANT_LIB_EXT1__ 1 /* for OpenWatcom Tools */
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#include <stdlib.h>
#include <malloc.h>
#include <errno.h>
#include <tchar.h>
#include "WinLib.h"
#include "stdint.h"
#include "stdbool.h"

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Defines                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#define STRINGSIZE 256                  /* default size for strings */
#define DYN_BUFFERSIZE (64*1024)        /* Size of dynamically allocated buffers */
#define INIFILENAMESIZE 256             /* maximum size of .ini filename */
#define SNAP 20                         /* SNAP when moving window */
#define SKN_LENGTH 100                  /* length of sub key name (take care! allocted on stack) */
#define ESCAPE_CHAR '%'                 /* for conversion of multi size strings */
#define MULTI_SZ_CHAR '_'               /* for conversion of multi size strings */

#define EMAILADDR "Michael@dk8pp.de"
#define WEBSITE "http://downloads.dk8pp.de/"
#ifdef WIN_CHARACTERMODE
#define PROGRAMMER "Michael Kr„mer"
#define NEWLINE "\n"
#else
#define PROGRAMMER "Michael Krämer"
#define NEWLINE "\r\r\n"
#endif

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       structs and enums                                                                                               |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       global variables                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
*/
extern HWND      MainWindow ;
extern HINSTANCE MyInstance ;

extern CLO clo[] ;
extern uint32_t clo_size ;
extern char purpose[] ;
static int process_prio[] = {IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS} ;
static int thread_prio[] = {THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL,
                            THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST,
                            THREAD_PRIORITY_TIME_CRITICAL} ;
static uint32_t helpdlgitem ;
static char inifilename[INIFILENAMESIZE] ;
static char local_strng1[STRINGSIZE] ;
static char HelpWindowName[STRINGSIZE] = "" ;
static uint32_t GetHelp_pos ;
#ifndef WIN_CHARACTERMODE
/* The following externs are only defined in windowed mode */
extern COORDS coordinates[] ;
extern COORDS *sorted_coord[] ;
extern uint32_t coord_size ;
static HWND HelpWindow ;
static int32_t min_size_x ;    /* Minimum horizontal dialog size */
static int32_t min_size_y ;    /* Minimum vertical dialog size */
#endif

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Function prototypes                                                                                             |
+-----------------------------------------------------------------------------------------------------------------------+
*/
BOOL CALLBACK DlgHelp(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam) ;
static uint32_t wl_get_token(char *in, char *out) ;
static uint32_t str2len(char *str) ;
static char *GetHelp(uint32_t clpnr, char *strng) ;
static void decode_arraystring(char *strng, int32_t *value, uint32_t size) ;
static void generate_arraystring(char *strng, INIVALUE *inival) ;

#ifndef WIN_CHARACTERMODE
/* These functions make sense only in windowed mode */
/*
+-----------------------------------------------------------------------------------------------------------------------+
|       WinLibMessage                                                                                                   |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| This function processes some of the dialog messages.                                                                  |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
uint32_t WinLibMessage(unsigned msg, WPARAM wparam, LPARAM lparam)
{
uint32_t retcd ;
WPARAM fwSide ;
LPRECT lprc ;
RECT rect ;
int32_t xPos ;
int32_t yPos ;
int32_t xSize ;
int32_t ySize ;
bool movewin ;

   retcd = 0 ; /* return 0 if we don't process the message */

   switch(msg)
   {
   case WM_SIZE:
      retcd = 0 ;
      resize_controls(&coordinates[0], coord_size) ;
   break ;

   case WM_SIZING:
      retcd = true ;
      fwSide = wparam;         // edge of window being sized
      lprc = (LPRECT) lparam;  // screen coordinates of drag rectangle
      if ((fwSide == WMSZ_RIGHT) || (fwSide == WMSZ_BOTTOMRIGHT) || (fwSide == WMSZ_TOPRIGHT))
         if ((lprc->right-lprc->left) < min_size_x) lprc->right = lprc->left + min_size_x ;
      if ((fwSide == WMSZ_LEFT) || (fwSide == WMSZ_BOTTOMLEFT) || (fwSide == WMSZ_TOPLEFT))
         if ((lprc->right-lprc->left) < min_size_x) lprc->left = lprc->right - min_size_x ;
      if ((fwSide == WMSZ_BOTTOM) || (fwSide == WMSZ_BOTTOMRIGHT) || (fwSide == WMSZ_BOTTOMLEFT))
         if ((lprc->bottom-lprc->top) < min_size_y) lprc->bottom = lprc->top + min_size_y ;
      if ((fwSide == WMSZ_TOP) || (fwSide == WMSZ_TOPRIGHT) || (fwSide == WMSZ_TOPLEFT))
         if ((lprc->bottom-lprc->top) < min_size_y) lprc->top = lprc->bottom - min_size_y ;
   break ;

   case WM_MOVE:
      retcd = true ;
      movewin = false ;
      GetWindowRect(MainWindow, &rect) ;
      xPos = rect.left ;    // horizontal position
      yPos = rect.top ;    // vertical position
      if (abs(xPos) < SNAP) {xPos = 0 ; movewin = true ;}
      if (abs(yPos) < SNAP) {yPos = 0 ; movewin = true ;}
      xSize = GetSystemMetrics(SM_CXFULLSCREEN) ;
      ySize = GetSystemMetrics(SM_CYFULLSCREEN) ;
      if (abs(xSize - rect.right) < SNAP) {xPos = xSize - (rect.right - rect.left) ; movewin = true ;}
      if (abs(ySize - rect.bottom) < SNAP) {yPos = ySize - (rect.bottom - rect.top) ; movewin = true ;}
      if (movewin) MoveWindow(
                   MainWindow,            // handle of window
                   xPos,            // horizontal position
                   yPos,            // vertical position
                   rect.right - rect.left,        // width
                   rect.bottom - rect.top,       // height
                   true);                 // repaint flag
   break ;

   }
   return(retcd) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       SaveCoordinates                                                                                                 |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Save the coordinates of the dialog and of the controls.                                                               |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void SaveCoordinates(void)
{
RECT rect ;
// save original coordinates of dialog
   GetWindowRect(MainWindow, &rect) ;
   coordinates[0].top = rect.top ;
   coordinates[0].right = rect.right ;
   coordinates[0].bottom = rect.bottom ;
   coordinates[0].left = rect.left ;
   min_size_x = rect.right - rect.left ; /* the original dialog size is the minimum size */
   min_size_y = rect.bottom - rect.top ; /* the original dialog size is the minimum size */

// save original coordinates of controls
   get_coords(&coordinates[0], coord_size) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       resize_controls                                                                                                 |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| This function is called as a response to the WM_SIZE message. It resizes the controls of a dialog.                    |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void resize_controls(COORDS *coordinates, uint32_t size)
{
RECT rect ;
uint32_t i, hsize, vsize, hsize_org, vsize_org, addx, addy, movx, movy ;
uint32_t static hsize_prev = 0 ;
uint32_t static vsize_prev = 0 ;
// get current size of dialog
   GetWindowRect(MainWindow, &rect) ;
   hsize = rect.right - rect.left ;
   vsize = rect.bottom - rect.top ;
   hsize_org = coordinates[0].right-coordinates[0].left ;
   vsize_org = coordinates[0].bottom-coordinates[0].top ;

   if (hsize_prev == 0) hsize_prev = hsize_org ;
   if (vsize_prev == 0) vsize_prev = vsize_org ;

/* horizontal sizing */
   if (hsize_prev != hsize)
   {
/* Controls must be sorted so that leftmost controls are moved first, when dialog gets smaller and rightmost
   controls are moved first when dialog gets larger */
      for (i=1; i<size; i++) sorted_coord[i-1] = &coordinates[i] ;
      if (hsize < hsize_prev)
      { /* dialog gets smaller */
         qsort(&sorted_coord[0], i-1, sizeof(COORDS *), compare_hup) ;
      }
      else
      { /* dialog gets larger */
         qsort(&sorted_coord[0], i-1, sizeof(COORDS *), compare_hdown) ;
      }

      for (i=1; i<size; i++)
      {
         if (((sorted_coord[i-1]->flags)&RESIZE_X)!=0) addx = hsize-hsize_org ; else addx = 0 ;
         if (((sorted_coord[i-1]->flags)&RESIZE_Y)!=0) addy = vsize_prev-vsize_org ; else addy = 0 ;
         if (((sorted_coord[i-1]->flags)&MOVE_X)!=0) movx = hsize-hsize_org ; else movx = 0 ;
         if (((sorted_coord[i-1]->flags)&MOVE_Y)!=0) movy = vsize_prev-vsize_org ; else movy = 0 ;
         MoveWindow(
         GetDlgItem(MainWindow, sorted_coord[i-1]->control),    // handle of window
         sorted_coord[i-1]->left+movx,                          // horizontal position
         sorted_coord[i-1]->top+movy,                           // vertical position
         sorted_coord[i-1]->right-sorted_coord[i-1]->left+addx, // width
         sorted_coord[i-1]->bottom-sorted_coord[i-1]->top+addy, // height
         true);                                                 // repaint flag
      }
   }
   hsize_prev = hsize ;

/* vertical sizing */
   if (vsize_prev != vsize)
   {
/* Controls must be sorted so that topmost controls are moved first, when dialog gets smaller and bottommost
   controls are moved first when dialog gets larger */
      for (i=1; i<size; i++) sorted_coord[i-1] = &coordinates[i] ;
      if (vsize < vsize_prev)
      { /* dialog gets smaller */
         qsort(&sorted_coord[0], i-1, sizeof(COORDS *), compare_vup) ;
      }
      else
      { /* dialog gets larger */
         qsort(&sorted_coord[0], i-1, sizeof(COORDS *), compare_vdown) ;
      }
      for (i=1; i<size; i++)
      {
         if (((sorted_coord[i-1]->flags)&RESIZE_X)!=0) addx = hsize_prev-hsize_org ; else addx = 0 ;
         if (((sorted_coord[i-1]->flags)&RESIZE_Y)!=0) addy = vsize-vsize_org ; else addy = 0 ;
         if (((sorted_coord[i-1]->flags)&MOVE_X)!=0) movx = hsize_prev-hsize_org ; else movx = 0 ;
         if (((sorted_coord[i-1]->flags)&MOVE_Y)!=0) movy = vsize-vsize_org ; else movy = 0 ;
         MoveWindow(
         GetDlgItem(MainWindow, sorted_coord[i-1]->control),    // handle of window
         sorted_coord[i-1]->left+movx,                          // horizontal position
         sorted_coord[i-1]->top+movy,                           // vertical position
         sorted_coord[i-1]->right-sorted_coord[i-1]->left+addx, // width
         sorted_coord[i-1]->bottom-sorted_coord[i-1]->top+addy, // height
         true);                                                 // repaint flag
      }
   }
   vsize_prev = vsize ;

}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       int compare_hup(const void *op1, const void *op2 )                                                              |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Compare for qsort                                                                                                     |
| For horizontal moves to the left                                                                                      |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int32_t compare_hup(const void *op1, const void *op2)
{
COORDS *p1, *p2 ;
   p1 = * (COORDS **) op1 ;
   p2 = * (COORDS **) op2 ;
   return (p1->left - p2->left) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       int compare_hdown(const void *op1, const void *op2 )                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Compare for qsort                                                                                                     |
| For horizontal moves to the right                                                                                     |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int32_t compare_hdown(const void *op1, const void *op2)
{
COORDS *p1, *p2 ;
   p1 = * (COORDS **) op1 ;
   p2 = * (COORDS **) op2 ;
   return (p2->left - p1->left) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       int compare_vup(const void *op1, const void *op2 )                                                              |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Compare for qsort                                                                                                     |
| For vertical moves to the top                                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int32_t compare_vup(const void *op1, const void *op2)
{
COORDS *p1, *p2 ;
   p1 = * (COORDS **) op1 ;
   p2 = * (COORDS **) op2 ;
   return (p1->top - p2->top) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       int compare_vdown(const void *op1, const void *op2 )                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Compare for qsort                                                                                                     |
| For vertical moves to the bottom                                                                                      |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int32_t compare_vdown(const void *op1, const void *op2)
{
COORDS *p1, *p2 ;
   p1 = * (COORDS **) op1 ;
   p2 = * (COORDS **) op2 ;
   return (p2->top - p1->top) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       get_coords                                                                                                      |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Get the coordinates of the controls, which are defined in the coordinates structure.                                  |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void get_coords(COORDS *coordinates, uint32_t size)
{
RECT rect ;
POINT pntlo ;
POINT pntru ;
uint32_t i ;
   for (i=1; i<size; i++) /* skip first entry; reserved for main dialog */
   {
      GetWindowRect(GetDlgItem(MainWindow, coordinates[i].control), &rect) ;
      pntlo.x = rect.left ;
      pntlo.y = rect.top ;
      pntru.x = rect.right ;
      pntru.y = rect.bottom ;
      ScreenToClient(MainWindow, &pntlo) ;
      ScreenToClient(MainWindow, &pntru) ;
      coordinates[i].left = pntlo.x ;
      coordinates[i].top = pntlo.y ;
      coordinates[i].right = pntru.x ;
      coordinates[i].bottom = pntru.y ;
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       DlgHelp                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Help Dialog                                                                                                           |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
BOOL CALLBACK DlgHelp(HWND window_handle, unsigned msg, WPARAM wparam, LPARAM lparam)
{
uint32_t retcd ; /* return nonzero, if the message is processed here and zero, if it is not */
HWND msg_hwnd ;
uint32_t i ;

   retcd = 0 ; /* return 0 if we don't process the message */
   switch(msg)
   {
   case WM_INITDIALOG:
      retcd = 1 ;
      HelpWindow = window_handle ;
      msg_hwnd = GetDlgItem(HelpWindow, helpdlgitem) ;
      if (HelpWindowName != NULL) SendMessage(HelpWindow, WM_SETTEXT, 0, (LPARAM) HelpWindowName);
      SendMessage(msg_hwnd, EM_SETSEL, 0, -1); /* clear window */
      SendMessage(msg_hwnd, EM_REPLACESEL, (BOOL) false, (WPARAM) local_strng1) ;
      SendMessage(msg_hwnd, EM_REPLACESEL, (BOOL) false, (WPARAM) purpose) ;
      SendMessage(msg_hwnd, EM_REPLACESEL, (BOOL) false, (WPARAM) "The following command line parameters are supported:" NEWLINE) ;
      GetHelp_pos = 1 ;
      for (i=0; i<clo_size; i++) SendMessage(msg_hwnd, EM_REPLACESEL, (BOOL) false, (WPARAM) GetHelp(i, local_strng1)) ;
   break ;

   case WM_COMMAND:
      retcd = 1 ;
      switch(LOWORD(wparam))
      {
      case IDOK:
      case IDABORT:
      case IDCANCEL:
         EndDialog(HelpWindow, true);      /* exits the dialog box */
      break ;

      default:
         retcd = 0 ; /* we have not processed this command, so return zero */
      break ;
      }
   break ;
   }

   return(retcd) ;
} /* DlgHelp */
#endif

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       GetHelp                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Get Help for a specific command line parameter                                                                        |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
char *GetHelp(uint32_t clpnr, char *strng)
{
char *prefix ;
char *parmname ;
char *parmval ;
char parm[16] ;

   switch (clo[clpnr].parmtype)
   {
      case PARM_SIMPLE: prefix = "-" ; parmname = clo[clpnr].parmname ; parmval = "" ; break ;
      case PARM_INT: prefix = "-" ; parmname = clo[clpnr].parmname ; parmval = "=n" ; break ;
      case PARM_STRING: prefix = "-" ; parmname = clo[clpnr].parmname ; parmval = "=\"string\"" ; break ;
      case PARM_POS: prefix = "" ; sprintf_s(parm, 16, "pos%u", GetHelp_pos) ; parmname = parm ; GetHelp_pos++; parmval = "" ; break ;
      default: prefix = "" ; parmname = "" ; parmval = "" ; break ;
   }
   sprintf_s(strng, STRINGSIZE, "%s%s%s: %s" NEWLINE, prefix, parmname, parmval, clo[clpnr].help) ;

   return(strng) ;
} /* GetHelp */

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       PrintProgramInfo                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Open a message box and print some information about the program                                                       |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void PrintProgramInfo(uint32_t dlgitem, char *programname, char *date, char *time)
{
#ifdef WIN_CHARACTERMODE
uint32_t i ;
#endif
   helpdlgitem = dlgitem ;
   sprintf_s(local_strng1, STRINGSIZE, "%s" NEWLINE
                 "Written by: " PROGRAMMER ", compiled on %s, %s" NEWLINE
                 "for updates check: " WEBSITE NEWLINE
                 "or email to: " EMAILADDR NEWLINE NEWLINE,
                 programname, date, time) ;
   sprintf_s(HelpWindowName, STRINGSIZE, "%s - Program Information", programname) ;
#ifdef WIN_CHARACTERMODE
   printf("%s", local_strng1) ;
   printf("Purpose: %s\n", purpose) ;
   printf("The following command line parameters are supported:\n") ;
   for (i=0; i<clo_size; i++) printf("%s", GetHelp(i, local_strng1)) ;
#else
   DialogBox(MyInstance, _T("DLG_HELP"), MainWindow, (DLGPROC)DlgHelp) ;
#endif
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       wl_get_token                                                                                                    |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Get the next token from the input string                                                                              |
|                                                                                                                       |
| Returns: Number of bytes taken from input string.                                                                     |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
uint32_t wl_get_token(char *in, char *out)
{
uint32_t i, j ;
bool terminate, hyphen ;
   i = 0 ;
   j = 0 ;
   terminate = false ;
   hyphen = false ;
   while (!terminate)
   {
      out[j] = '\0' ;
      if (in[i] == '\0') terminate = true ;
      if ((in[i] == ' ') && !hyphen)
      {
         out[j] = '\0' ;
         i++ ;
         terminate = true ;
      }
      if ((in[i] == '"') && hyphen)
      {
         out[j] = '\0' ;
         i++ ;
         terminate = true ;
      }
      if (!terminate)
      {
         out[j] = in[i] ;
         hyphen = hyphen || (in[i] == '"') ;
         if (in[i] != '"') j++ ;
         i++ ;
      }
   }
   return(i) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       getparms                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Get the command line parameters                                                                                       |
|                                                                                                                       |
| Standard ANSI, no Unicode yet!                                                                                        |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void getparms(LPSTR cmdline, char *errorparm)
{
bool cmd_done, found_eq ;
char static token[MAXPARMLENGTH] ;
char *parmvalue ;
uint32_t i, j, k ;
int32_t intval ;
bool found ;
uint32_t increment ;
   cmd_done = false ;
   errorparm[0] = '\0' ;
/* check parameters if cmdline points to a string */
   if ((cmdline != NULL) && (cmdline[0] != '\0'))
   {
      i = 0 ;
      while (!cmd_done)
      {
         while (cmdline[i] == ' ') i++ ; /* skip any possibly leading blanks */
         if (cmdline[i] == '\0') cmd_done = true ;
         else
         {
            if ((cmdline[i] == '-') || (cmdline[i] == '/'))
            {/* this is a parameter */
               i++ ;
               if ((cmdline[i] == ' ') || (cmdline[i] == '\0')) /* we found a "-" without a parameter */
               {
                  token[0] = '\0' ;
               }
               else
               {
                  if ((increment = wl_get_token(&cmdline[i], &token[0])) == 0) cmd_done = true ;
               }
               if (!cmd_done)
               { /* we isolated a parameter. Check if it is an assignment. */
                  found_eq = false ;
                  k = 0 ;
                  while ((token[k] != '\0') && (token[k] != '=')) k++ ;
                  if (token[k] == '=')
                  {
                     found_eq = true ;
                     token[k] = '\0' ; /* terminator for parameter name */
                     parmvalue = &token[k+1] ;
                  }
                  /* Now we search the parameter name in the list. */
                  found = false ;
                  for (j=0; (j<clo_size) && (!found); j++)
                  {
                     if ((clo[j].parmtype == PARM_SIMPLE)
                      || (clo[j].parmtype == PARM_INT)
                      || (clo[j].parmtype == PARM_STRING))
                     found = strcmp(token, clo[j].parmname) == 0 ;
                  }
                  if (found_eq) token[k] = '=' ; /* restore = */
                  if (found)
                  {
                     j-- ; /* fix increment from previous for loop */
                     if (clo[j].parmtype == PARM_SIMPLE) clo[j].found = true ;
                     if (clo[j].parmtype == PARM_INT)
                     { /* convert parmvalue to int */
                        if (clo[j].found == true)
                        {
                           sprintf_s(&errorparm[strlen(errorparm)], MAXPARMLENGTH, "%c%s (duplicate parameter) ", cmdline[i-1], token) ;
                           clo[j].found = false ;
                        }
                        if (sscanf_s(parmvalue, "%i", &intval) == 1)
                        {
                           clo[j].value = intval ;
                           clo[j].found = true ;
                        }
                        else
                        { /* we support some special values: off=0, on=1 */
                           if (_stricmp(parmvalue, "off") == 0)
                           {
                              clo[j].value = 0 ;
                              clo[j].found = true ;
                           }
                           if (_stricmp(parmvalue, "on") == 0)
                           {
                              clo[j].value = 1 ;
                              clo[j].found = true ;
                           }
                        }
                        if (!(clo[j].found))
                           sprintf_s(&errorparm[strlen(errorparm)], MAXPARMLENGTH, "%c%s (invalid value)", cmdline[i-1], token) ;
                     }
                     if (clo[j].parmtype == PARM_STRING)
                     { /* copy parmvalue to destination */
                        if (clo[j].value != 0)
                        {
                           strncpy_s(clo[j].dest, clo[j].value, parmvalue, clo[j].value) ;
                           ((char *) clo[j].dest)[clo[j].value-1] = '\0' ; /* force string termination */
                           clo[j].found = true ;
                        }
                     }
                  }
                  else
                  { /* unexpected parameter */
                     sprintf_s(&errorparm[strlen(errorparm)], MAXPARMLENGTH, "%c%s ", cmdline[i-1], token) ;
                  }
                  i += increment ;
               }
            }
            else
            { /* this is a positional parameter (filename e.t.c.) */
               if ((increment = wl_get_token(&cmdline[i], &token[0])) == 0) cmd_done = true ;
               /* Now we search the next unassigned positional parameter in the list. */
               found = false ;
               for (j=0; (j<clo_size) && (!found); j++)
               {
                  if ((clo[j].parmtype == PARM_POS) && (clo[j].found == false) && (clo[j].dest != NULL))
                  {
                     strncpy_s(clo[j].dest, clo[j].value, token, clo[j].value) ;
                     clo[j].found = true ;
                     found = true ;
                  }
               }
               i += increment ;
            }
         }
      }
   }
   i = strlen(errorparm) ;
   if (i > 0) i-- ;
   if (errorparm[i] == ' ') errorparm[i] = '\0' ; /* remove trailing blank for the beauty of the error message */
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       get_registry                                                                                                    |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Get the required data from registry. Return true if all keys were successfully read.                                  |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
bool get_registry(MYINI *myini)
{
HKEY keyhandle ;
LONG retv ;
bool rc ;
uint_least32_t csize ;
uint32_t i, is, id ;
int32_t sd1, sd2 ;
char skn[SKN_LENGTH] ; /* sub key name */
char *gr_buffer ;
char *destination ;
bool terminate ;

   rc = false ;
   gr_buffer = (char *) malloc(DYN_BUFFERSIZE) ;
   if (gr_buffer != NULL)
   { /* Execute function only if malloc succeeds. Otherwise something is seriously wrong */
      if ((myini->initype & INI_REGREAD) != 0)
      {
         rc = true ;
         for (i=0; i<myini->size ; i++)
         {
// Open Registry key
// assemble sub key name
            strcpy_s(skn, SKN_LENGTH, myini->inikeyname) ;
            if ((myini->inivalues[i].sectionname != NULL) && (strlen(myini->inivalues[i].sectionname) != 0))
            {
               strcat_s(skn, SKN_LENGTH, "\\") ;
               strcat_s(skn, SKN_LENGTH, myini->inivalues[i].sectionname) ;
            }
            retv = RegOpenKeyEx(myini->hkey,
                   skn,
                   0,
                   KEY_READ,
                   &keyhandle) ;
            if (retv != ERROR_SUCCESS) rc = false ;
            else
            {
               switch (myini->inivalues[i].valuetype)
               {
                  case REG_DWORD :
                  if (((myini->inivalues[i].flags) & REGFLAG_ARRAY) == 0)
                  { /* This is a normal DWORD */
                     csize = 4 ;
                     retv = RegQueryValueEx(
                            keyhandle,                         // handle of key to query
                            myini->inivalues[i].valuename,     // address of name of value to query
                            0,                                 // reserved
                            NULL,                              // address of buffer for value type
                            (LPBYTE)myini->inivalues[i].value, // address of data buffer
                            &csize                             // address of data buffer size
                            ) ;
                     if (retv != ERROR_SUCCESS) rc = false ;
                  }
                  else
                  { /* This is an array of DWORDs stored as a character string */
                     csize = DYN_BUFFERSIZE ;
                     retv = RegQueryValueEx(
                            keyhandle,                         // handle of key to query
                            myini->inivalues[i].valuename,     // address of name of value to query
                            0,                                 // reserved
                            NULL,                              // address of buffer for value type
                            (LPBYTE) gr_buffer,                // address of data buffer
                            &csize                             // address of data buffer size
                            ) ;
                     if (retv != ERROR_SUCCESS) rc = false ;
                     else
                     {
                        decode_arraystring(gr_buffer, myini->inivalues[i].value, myini->inivalues[i].valuesize) ;
                     }
                  }
                  break ;

                  case REG_SZ :
                  case REG_MULTI_SZ :
                  csize = myini->inivalues[i].valuesize ;
                  retv = RegQueryValueEx(
                         keyhandle,                         // handle of key to query
                         myini->inivalues[i].valuename,     // address of name of value to query
                         0,                                 // reserved
                         NULL,                              // address of buffer for value type
                         (LPBYTE)myini->inivalues[i].value, // address of data buffer
                         &csize                             // address of data buffer size
                         ) ;
                  if (retv != ERROR_SUCCESS) rc = false ;
                  break ;
               }

               RegCloseKey(keyhandle) ;
            }
         }
      }

      if ((myini->initype & INI_FILEREAD) != 0)
      {
         rc = true ;
         strcpy_s(inifilename, INIFILENAMESIZE, myini->inifilename) ;
         for (i=0; i<myini->size ; i++)
         {
            switch (myini->inivalues[i].valuetype)
            {
               case REG_DWORD :
               /* This may be a single DWORD or an array of DWORDs stored as a character string */
                  if (((myini->inivalues[i].flags) & REGFLAG_ARRAY) == 0)
                  { /* This is a single DWORD. Earlier versions did not include the size, so we set it to 1 */
                     myini->inivalues[i].valuesize = 1 ;
                  }
                  csize = DYN_BUFFERSIZE ;
                  sd1 = GetPrivateProfileString(
                         myini->inivalues[i].sectionname,  // pointer to section name
                         myini->inivalues[i].valuename,    // pointer to key name
                         "~~",                             // points to default string
                         gr_buffer,                        // points to destination buffer
                         csize,                            // size of destination buffer
                         inifilename                       // pointer to initialization filename
                         ) ;
                  csize = DYN_BUFFERSIZE ;
                  sd2 = GetPrivateProfileString(
                         myini->inivalues[i].sectionname,  // pointer to section name
                         myini->inivalues[i].valuename,    // pointer to key name
                         "####",                           // points to default string
                         gr_buffer,                        // points to destination buffer
                         csize,                            // size of destination buffer
                         inifilename                       // pointer to initialization filename
                         ) ;
                  if (sd1 == sd2)
                  { /* string was found */
                     decode_arraystring(gr_buffer, myini->inivalues[i].value, myini->inivalues[i].valuesize) ;
                  }
                  else
                     rc = false ; /* key was not found */
               break ;

               case REG_SZ :
                  csize = DYN_BUFFERSIZE ;
                  sd1 = GetPrivateProfileString(
                         myini->inivalues[i].sectionname,  // pointer to section name
                         myini->inivalues[i].valuename,    // pointer to key name
                         "~~",                             // points to default string
                         gr_buffer,                        // points to destination buffer
                         csize,                            // size of destination buffer
                         inifilename                       // pointer to initialization filename
                         ) ;
                  csize = DYN_BUFFERSIZE ;
                  sd2 = GetPrivateProfileString(
                         myini->inivalues[i].sectionname,  // pointer to section name
                         myini->inivalues[i].valuename,    // pointer to key name
                         "####",                           // points to default string
                         gr_buffer,                        // points to destination buffer
                         csize,                            // size of destination buffer
                         inifilename                       // pointer to initialization filename
                         ) ;
                  csize = myini->inivalues[i].valuesize ;
                  if (sd1 == sd2)
                  { /* string was found */
                     if (csize > DYN_BUFFERSIZE) csize = DYN_BUFFERSIZE ; /* truncate string if too long */
                     strncpy_s(myini->inivalues[i].value, myini->inivalues[i].valuesize, gr_buffer, csize) ; /* key was found */
                     *(((char *) myini->inivalues[i].value) + csize - 1) = '\0' ;
                  }
                  else
                     rc = false ; /* key was not found */
               break ;

               case REG_MULTI_SZ :
                  sd1 = GetPrivateProfileString(
                         myini->inivalues[i].sectionname,  // pointer to section name
                         myini->inivalues[i].valuename,    // pointer to key name
                         "~~",                             // points to default string
                         gr_buffer,                        // points to destination buffer
                         DYN_BUFFERSIZE,                   // size of destination buffer
                         inifilename                       // pointer to initialization filename
                         ) ;
                  sd2 = GetPrivateProfileString(
                         myini->inivalues[i].sectionname,  // pointer to section name
                         myini->inivalues[i].valuename,    // pointer to key name
                         "####",                           // points to default string
                         gr_buffer,                        // points to destination buffer
                         DYN_BUFFERSIZE,                   // size of destination buffer
                         inifilename                       // pointer to initialization filename
                         ) ;
                  if (sd1 == sd2)
                  { /* string was found. */
                     destination = myini->inivalues[i].value ;
//                     strcpy_s(gr_buffer, DYN_BUFFERSIZE, destination) ; /* get the original data to buffer */
                     is = 0 ;
                     id = 0 ;
                     terminate = false ;
                     do
                     {
                        if (gr_buffer[is] == '\0')
                        {
                           destination[id+0] = '\0' ;
                           destination[id+1] = '\0' ;
                           terminate = true ;
                        }
                        else
                        {
                           if (gr_buffer[is] == ESCAPE_CHAR)
                           {
                              if (gr_buffer[is+1] == ESCAPE_CHAR)
                              {
                                 destination[id] = ESCAPE_CHAR ;
                              }
                              if (gr_buffer[is+1] == MULTI_SZ_CHAR)
                              {
                                 destination[id] = '\0' ;
                              }
                              id++ ;
                              is += 2 ;
                           }
                           else
                           { /* copy character */
                              destination[id] = gr_buffer[is] ;
                              id++ ;
                              is++ ;
                           }
                        }
                     } while (!terminate) ;
                  }
                  else
                     rc = false ; /* key was not found */
               break ;
            }
         }
      }
      free(gr_buffer) ;
   }
   return(rc) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       put_registry                                                                                                    |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Put data into registry                                                                                                |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void put_registry(MYINI *myini)
{
HKEY keyhandle ;
LONG retv ;
uint32_t csize ;
uint_least32_t disposition ;
uint32_t i, is, id ;
bool value_exists, terminate ;
char skn[SKN_LENGTH] ; /* sub key name */
char *source ;
char *pr_buffer ;
int32_t sd1, sd2 ;

   pr_buffer = (char *) malloc(DYN_BUFFERSIZE) ;
   if (pr_buffer != NULL)
   { /* Execute function only if malloc succeeds. Otherwise something is seriously wrong */
      if ((myini->initype & INI_REGWRITE) != 0)
      {
         for (i=0; i<myini->size; i++)
         {
//    assemble sub key name
            strcpy_s(skn, SKN_LENGTH, myini->inikeyname) ;
            if ((myini->inivalues[i].sectionname != NULL) && (strlen(myini->inivalues[i].sectionname) != 0))
            {
               strcat_s(skn, SKN_LENGTH, "\\") ;
               strcat_s(skn, SKN_LENGTH, myini->inivalues[i].sectionname) ;
            }
//    Open or Create the key
            retv = RegCreateKeyEx(myini->hkey,   // handle of an open key
                   skn,                          // address of subkey name
                   0,                            // reserved
                   NULL,                         // address of class string
                   REG_OPTION_NON_VOLATILE,      // special options flag
                   KEY_ALL_ACCESS,               // desired security access
                   REG_OPTION_NON_VOLATILE,      // address of key security structure
                   &keyhandle,                   // address of buffer for opened handle
                   &disposition                  // address of disposition value buffer
               ) ;
            if (retv == ERROR_SUCCESS)
            { // write data unless read only value and not newly created
               value_exists = (RegQueryValueEx(keyhandle, myini->inivalues[i].valuename, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) ;
               if ((((myini->inivalues[i].flags) & REGFLAG_WRITEBACK) != 0) || (!value_exists))
               {
                  switch (myini->inivalues[i].valuetype)
                  {
                     case REG_DWORD :
                        if (((myini->inivalues[i].flags) & REGFLAG_ARRAY) == 0)
                        { /* This is a normal DWORD */
                           retv = RegSetValueEx(
                                  keyhandle,                               // handle of key to set value for
                                  myini->inivalues[i].valuename,           // address of name of value to set
                                  0,                                       // reserved
                                  REG_DWORD,                               // flag for value type
                                  (CONST BYTE *)myini->inivalues[i].value, // address of data
                                  4                                        // size of value data
                                  ) ;
                        }
                        else
                        { /* This is an array of DWORDs to be stored as a character string */
//                           generate_arraystring(pr_buffer, myini->inivalues[i].value, myini->inivalues[i].valuesize) ;
                           generate_arraystring(pr_buffer, &myini->inivalues[i]) ;
                           csize = strlen(pr_buffer) + 1 ;
                           retv = RegSetValueEx(
                                  keyhandle,                        // handle of key to set value for
                                  myini->inivalues[i].valuename,    // address of name of value to set
                                  0,                                // reserved
                                  REG_SZ,                           // flag for value type
                                  pr_buffer,                        // address of value data
                                  csize                             // size of value data
                                  ) ;
                        }
                     break ;

                     case REG_SZ :
                        csize = strlen(myini->inivalues[i].value) + 1 ;
                        retv = RegSetValueEx(
                               keyhandle,                        // handle of key to set value for
                               myini->inivalues[i].valuename,    // address of name of value to set
                               0,                                // reserved
                               REG_SZ,                           // flag for value type
                               myini->inivalues[i].value,        // address of value data
                               csize                             // size of value data
                               ) ;
                     break ;

                     case REG_MULTI_SZ :
                        csize = str2len(myini->inivalues[i].value) + 2 ;
                        retv = RegSetValueEx(
                               keyhandle,                        // handle of key to set value for
                               myini->inivalues[i].valuename,    // address of name of value to set
                               0,                                // reserved
                               REG_MULTI_SZ,                     // flag for value type
                               myini->inivalues[i].value,        // address of value data
                               csize                             // size of value data
                               ) ;
                     break ;
                  }
               }
               RegCloseKey(keyhandle) ;
            }
         }
      }

      if ((myini->initype & INI_FILEWRITE) != 0)
      {
         strcpy_s(inifilename, INIFILENAMESIZE, myini->inifilename) ;
         for (i=0; i<myini->size ; i++)
         { // write data unless read only value and not newly created
            csize = DYN_BUFFERSIZE ;
            sd1 = GetPrivateProfileString(
                   myini->inivalues[i].sectionname,  // pointer to section name
                   myini->inivalues[i].valuename,    // pointer to key name
                   "~~",                             // points to default string
                   pr_buffer,                        // points to destination buffer
                   csize,                            // size of destination buffer
                   inifilename                       // pointer to initialization filename
                   ) ;
            csize = DYN_BUFFERSIZE ;
            sd2 = GetPrivateProfileString(
                   myini->inivalues[i].sectionname,  // pointer to section name
                   myini->inivalues[i].valuename,    // pointer to key name
                   "####",                           // points to default string
                   pr_buffer,                        // points to destination buffer
                   csize,                            // size of destination buffer
                   inifilename                       // pointer to initialization filename
                   ) ;
            value_exists = (sd1 == sd2) ; /* string was found */
            if ((((myini->inivalues[i].flags) & REGFLAG_WRITEBACK) != 0) || (!value_exists))
            {
               switch (myini->inivalues[i].valuetype)
               {
                  case REG_DWORD :
                     csize = 1 ;
                     if (((myini->inivalues[i].flags) & REGFLAG_ARRAY) == 0)
                     { /* This is a single DWORD. Earlier versions dis not include the size, so we set it to 1 */
                        myini->inivalues[i].valuesize = 1 ;
                     }
                     generate_arraystring(pr_buffer, &myini->inivalues[i]) ;
                     WritePrivateProfileString(
                            myini->inivalues[i].sectionname,  // pointer to section name
                            myini->inivalues[i].valuename,    // pointer to key name
                            pr_buffer,                        // pointer to string to add
                            inifilename                       // pointer to initialization filename
                            ) ;
                  break ;

                  case REG_SZ :
                     WritePrivateProfileString(
                            myini->inivalues[i].sectionname,  // pointer to section name
                            myini->inivalues[i].valuename,    // pointer to key name
                            myini->inivalues[i].value,        // pointer to string to add
                            inifilename                       // pointer to initialization filename
                            ) ;
                  break ;

                  case REG_MULTI_SZ :
                    is = 0 ;
                    id = 0 ;
                    source = myini->inivalues[i].value ;
                    terminate = false ;
                    do
                    {
                       if (source[is] != '\0')
                       {
                          if  (source[is] == ESCAPE_CHAR)
                          { /* double the escape character */
                             pr_buffer[id] = ESCAPE_CHAR ;
                             id++ ;
                          }
                          pr_buffer[id] = source[is] ;
                          id++ ;
                          is++ ;
                       }
                       else
                       {
                          if (source[is+1] != '\0')
                          {
                             pr_buffer[id+0] = ESCAPE_CHAR ;
                             pr_buffer[id+1] = MULTI_SZ_CHAR ;
                             id += 2 ;
                             is++ ;
                          }
                          else
                          {
                             terminate = true ;
                             pr_buffer[id] = '\0' ;
                          }
                       }
                    } while (!terminate) ;
                    WritePrivateProfileString(
                           myini->inivalues[i].sectionname,  // pointer to section name
                           myini->inivalues[i].valuename,    // pointer to key name
                           pr_buffer,                        // pointer to string to add
                           inifilename                       // pointer to initialization filename
                           ) ;
                  break ;
               }
            }
         }
      }
      free(pr_buffer) ;
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       decode_arraystring                                                                                              |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Convert a character string with multiple WORDs into an array of WORDs                                                 |
| String may contain hex words, which must be preceeded by a "0x".                                                      |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void decode_arraystring(char *strng, int32_t *value, uint32_t size)
{
uint32_t i ;
char *da_buff ;
char *next_token = NULL;
#ifdef __WATCOMC__
size_t buff ;
   buff = DYN_BUFFERSIZE ;
   da_buff = strtok_s(strng, &buff, ",.; ", &next_token) ;
#else
   da_buff = strtok_s(strng, ",.; ", &next_token) ;
#endif
   for (i=0; i<size; i++)
   {
      if (da_buff != NULL)
      {
         if ((da_buff[0] == '0') && ((da_buff[1] == 'x') || (da_buff[1] == 'X')))
            sscanf_s(da_buff+2, "%x", &value[i]) ; /* it is a hex word */
         else
            sscanf_s(da_buff, "%u", &value[i]) ;
#ifdef __WATCOMC__
         da_buff = strtok_s(NULL, &buff, ",.; ", &next_token) ;
#else
         da_buff = strtok_s(NULL, ",.; ", &next_token) ;
#endif
      }
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       generate_arraystring                                                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Generate a character string from an array of WORDs                                                                    |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
//void generate_arraystring(char *strng, int32_t *value, uint32_t size)
void generate_arraystring(char *strng, INIVALUE *inival)
{
uint32_t i ;
char *format ;
   if ((inival->flags & REGFLAG_HEX) != 0) format = "0x%x" ; else format = "%u" ;
   for (i=0; i<inival->valuesize; i++)
   {
      if (i > 0)
      {
         strng[0] = ',' ;
         strng++ ;
      }
      strng += sprintf_s(strng, DYN_BUFFERSIZE, format, ((uint32_t *) inival->value)[i]) ;
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       str2len                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Calculate the length of an array of null-terminated strings, terminated by two null characters (REG_MULTI_SZ).        |
| As with strlen, the size of the two terminating 0's is not counted.                                                   |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
uint32_t str2len(char *str)
{
uint32_t i ;
   i = 0 ;
   while ((str[i] != '\0') || (str[i+1] != '\0')) i++ ;
   return(i) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       clear_registry                                                                                                  |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Clear all registry keys which this program has created.                                                               |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void clear_registry(MYINI *myini)
{
HKEY keyhandle ;
LONG retv ;
   retv = RegOpenKeyEx(myini->hkey,
          myini->inikeyname,
          0,
          KEY_WRITE,
          &keyhandle) ;
   if (retv == ERROR_SUCCESS)
   {
      RegDeleteKey(myini->hkey, myini->inikeyname) ;
      RegCloseKey(keyhandle) ;
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void ShowError(int dialog)                                                                                      |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Display last error message                                                                                            |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void ShowError(int dialog)
{
char *lpMsgBuf ;

FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL,
    GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL) ;
#ifdef WIN_CHARACTERMODE
    printf("%s", lpMsgBuf) ;
#else
    SetDlgItemText(MainWindow, dialog, lpMsgBuf) ;
#endif
// Free the buffer.
    LocalFree(lpMsgBuf) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void ShowSystemError(int errnr, int dialog)                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Display system error message                                                                                          |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void ShowSystemError(int errnr, int dialog)
{
char *lpMsgBuf ;

FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL,
    errnr,
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL) ;
#ifdef WIN_CHARACTERMODE
    printf("%s", lpMsgBuf) ;
#else
    SetDlgItemText(MainWindow, dialog, lpMsgBuf) ;
#endif
// Free the buffer.
    LocalFree(lpMsgBuf) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       set_process_prio                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Set priority of current process.                                                                                      |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void set_process_prio(int32_t prio)
{
int32_t last = (sizeof(process_prio)/sizeof(process_prio[0])) - 1 ;
   if (prio < 0) prio = 0 ;
   if (prio > last) prio = last ;
   SetPriorityClass(GetCurrentProcess(), process_prio[prio]) ; /* set priority */
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       set_thread_prio                                                                                                 |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Set priority of a thread.                                                                                             |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void set_thread_prio(HANDLE hthread, int32_t prio)
{
int32_t last = (sizeof(thread_prio)/sizeof(thread_prio[0])) - 1 ;
   if (prio < 0) prio = 0 ;
   if (prio > last) prio = last ;
   SetThreadPriority(hthread, thread_prio[prio]) ; /* set priority */
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       void display_info(char *msg)                                                                                   |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Display messages on the DLG_INFO control. DLG_INFO is a multi line edit control.                                      |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
//void display_info(char *msg)
//{
//HWND msg_hwnd ;
//uint32_t iLen ;
//   msg_hwnd = GetDlgItem(MainWindow, DLG_INFO) ;
//   iLen = GetWindowTextLength(msg_hwnd) ;
//   SendMessage(msg_hwnd, EM_SETSEL, iLen, iLen);
//   SendMessage(msg_hwnd, EM_REPLACESEL, (BOOL) false, (WPARAM) msg) ;
//   iLen = GetWindowTextLength(msg_hwnd) ;
//   SendMessage(msg_hwnd, EM_SETSEL, iLen, iLen);
//   SendMessage(msg_hwnd, EM_REPLACESEL, (BOOL) false, (WPARAM) NEWLINE) ;
//}


