Sync up with XEmacs 21.4.17.
[chise/xemacs-chise.git.1] / lib-src / winclient.c
index 0399d99..7715882 100644 (file)
-/* DDE client for XEmacs.\r
-   Copyright (C) 2002 Alastair J. Houghton\r
-\r
-   This file is part of XEmacs.\r
-\r
-   XEmacs is free software; you can redistribute it and/or modify it\r
-   under the terms of the GNU General Public License as published by the\r
-   Free Software Foundation; either version 2, or (at your option) any\r
-   later version.\r
-\r
-   XEmacs is distributed in the hope that it will be useful, but WITHOUT\r
-   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
-   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
-   for more details.\r
-\r
-   You should have received a copy of the GNU General Public License\r
-   along with XEmacs; see the file COPYING.  If not, write to\r
-   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
-   Boston, MA 02111-1307, USA.  */\r
-\r
-/* Synched up with: Not in FSF. */\r
-\r
-/* -- Pre-Include Defines --------------------------------------------------- */\r
-\r
-#define STRICT\r
-\r
-/* -- Includes -------------------------------------------------------------- */\r
-\r
-#include <windows.h>\r
-#ifdef HAVE_CONFIG_H\r
-# include <config.h>\r
-#endif\r
-#include <stdlib.h>\r
-#include <stdio.h>\r
-#include <ctype.h>\r
-#include <errno.h>\r
-\r
-static void error (const char* s1, const char* s2);\r
-static void fatal (const char *s1, const char *s2);\r
-static void * xmalloc (size_t size);\r
-static char * getNextArg (const char **ptr, unsigned *len);\r
-\r
-/* -- Post-Include Defines -------------------------------------------------- */\r
-\r
-/* Timeouts & delays */\r
-#define CONNECT_DELAY          500             /* ms */\r
-#define TRANSACTION_TIMEOUT    5000            /* ms */\r
-#define MAX_INPUT_IDLE_WAIT     INFINITE       /* ms */\r
-\r
-/* DDE Strings */\r
-#define SERVICE_NAME   "XEmacs"\r
-#define TOPIC_NAME     "System"\r
-#define COMMAND_FORMAT "[open(\"%s%s\")]"\r
-\r
-/* XEmacs program name */\r
-#define PROGRAM_TO_RUN "xemacs.exe"\r
-\r
-/* -- Constants ------------------------------------------------------------- */\r
-\r
-/* -- Global Variables ------------------------------------------------------ */\r
-\r
-HINSTANCE hInstance;\r
-DWORD     idInst = 0;\r
-\r
-/* -- Function Declarations ------------------------------------------------- */\r
-\r
-HDDEDATA CALLBACK ddeCallback (UINT uType, UINT uFmt, HCONV hconv,\r
-                              HSZ hsz1, HSZ hsz2, HDDEDATA hdata,\r
-                              DWORD dwData1, DWORD dwData2);\r
-\r
-int WINAPI WinMain (HINSTANCE hInst,\r
-                   HINSTANCE hPrev,\r
-                   LPSTR     lpCmdLine,\r
-                   int       nCmdShow);\r
-\r
-static HCONV openConversation (void);\r
-static void closeConversation (HCONV hConv);\r
-static int doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2);\r
-static int parseCommandLine (HCONV hConv, LPSTR lpszCommandLine);\r
-\r
-/* -- Function Definitions -------------------------------------------------- */\r
-\r
-/*\r
- * Name    : ddeCallback\r
- * Function: Gets called by DDEML.\r
- *\r
- */\r
-\r
-HDDEDATA CALLBACK\r
-ddeCallback (UINT uType, UINT uFmt, HCONV hconv,\r
-            HSZ hsz1, HSZ hsz2, HDDEDATA hdata,\r
-            DWORD dwData1, DWORD dwData2)\r
-{\r
-  return (HDDEDATA) NULL;\r
-}\r
-\r
-/*\r
- * Name    : WinMain\r
- * Function: The program's entry point function.\r
- *\r
- */\r
-\r
-int WINAPI\r
-WinMain (HINSTANCE hInst,\r
-        HINSTANCE hPrev,\r
-        LPSTR     lpCmdLine,\r
-        int       nCmdShow)\r
-{\r
-  HCONV hConv;\r
-  int   ret = 0;\r
-  UINT  uiRet;\r
-  \r
-  /* Initialise the DDEML library */\r
-  uiRet = DdeInitialize (&idInst,\r
-                        (PFNCALLBACK) ddeCallback,\r
-                        APPCMD_CLIENTONLY\r
-                        |CBF_FAIL_ALLSVRXACTIONS,\r
-                        0);\r
-\r
-  if (uiRet != DMLERR_NO_ERROR)\r
-    {\r
-      MessageBox (NULL, "Could not initialise DDE management library.",\r
-                 "winclient", MB_ICONEXCLAMATION | MB_OK);\r
-\r
-      return 1;\r
-    }\r
-\r
-  /* Open a conversation */\r
-  hConv = openConversation ();\r
-\r
-  if (hConv)\r
-    {\r
-      /* OK. Next, we need to parse the command line. */\r
-      ret = parseCommandLine (hConv, lpCmdLine);\r
-\r
-      /* Close the conversation */\r
-      closeConversation (hConv);\r
-    }\r
-  \r
-  DdeUninitialize (idInst);\r
-\r
-  return ret;\r
-}\r
-\r
-/*\r
- * Name    : openConversation\r
- * Function: Start a conversation.\r
- *\r
- */\r
-\r
-static HCONV\r
-openConversation (void)\r
-{\r
-  HSZ             hszService = NULL, hszTopic = NULL;\r
-  HCONV           hConv = NULL;\r
-\r
-  /* Get the application (service) name */\r
-  hszService = DdeCreateStringHandle (idInst,\r
-                                     SERVICE_NAME,\r
-                                     CP_WINANSI);\r
-\r
-  if (!hszService)\r
-    {\r
-      MessageBox (NULL, "Could not create string handle for service.",\r
-                 "winclient", MB_ICONEXCLAMATION | MB_OK);\r
-\r
-      goto error;\r
-    }\r
-  \r
-  /* Get the topic name */\r
-  hszTopic = DdeCreateStringHandle (idInst,\r
-                                   TOPIC_NAME,\r
-                                   CP_WINANSI);\r
-\r
-  if (!hszTopic)\r
-    {\r
-      MessageBox (NULL, "Could not create string handle for topic.",\r
-                 "winclient", MB_ICONEXCLAMATION | MB_OK);\r
-\r
-      goto error;\r
-    }\r
-\r
-  /* Try to connect */\r
-  hConv = DdeConnect (idInst, hszService, hszTopic, NULL);\r
-\r
-  if (!hConv)\r
-    {\r
-      STARTUPINFO         sti;\r
-      PROCESS_INFORMATION pi;\r
-      int                 n;\r
-      \r
-      /* Try to start the program */\r
-      ZeroMemory (&sti, sizeof (sti));\r
-      sti.cb = sizeof (sti);\r
-      if (!CreateProcess (NULL, PROGRAM_TO_RUN, NULL, NULL, FALSE, 0,\r
-                         NULL, NULL, &sti, &pi))\r
-       {\r
-         MessageBox (NULL, "Could not start process.",\r
-                     "winclient", MB_ICONEXCLAMATION | MB_OK);\r
-\r
-         goto error;\r
-       }\r
-\r
-      /* Wait for the process to enter an idle state */\r
-      WaitForInputIdle (pi.hProcess, MAX_INPUT_IDLE_WAIT);\r
-\r
-      /* Close the handles */\r
-      CloseHandle (pi.hThread);\r
-      CloseHandle (pi.hProcess);\r
-      \r
-      /* Try to connect */\r
-      for (n = 0; n < 5; n++)\r
-       {\r
-         Sleep (CONNECT_DELAY);\r
-         \r
-         hConv = DdeConnect (idInst, hszService, hszTopic, NULL);\r
-\r
-         if (hConv)\r
-           break;\r
-       }\r
-\r
-      if (!hConv)\r
-       {\r
-         /* Still couldn't connect. */\r
-         MessageBox (NULL, "Could not connect to DDE server.",\r
-                     "winclient", MB_ICONEXCLAMATION | MB_OK);\r
-\r
-         goto error;\r
-       }\r
-    }\r
-\r
-  /* Release the string handles */\r
-  DdeFreeStringHandle (idInst, hszService);\r
-  DdeFreeStringHandle (idInst, hszTopic);\r
-\r
-  return hConv;\r
-  \r
- error:\r
-  if (hConv)\r
-    DdeDisconnect (hConv);\r
-  if (hszService)\r
-    DdeFreeStringHandle (idInst, hszService);\r
-  if (hszTopic)\r
-    DdeFreeStringHandle (idInst, hszTopic);\r
-\r
-  return NULL;\r
-}\r
-\r
-/*\r
- * Name    : closeConversation\r
- * Function: Close a conversation.\r
- *\r
- */\r
-\r
-static void\r
-closeConversation (HCONV hConv)\r
-{\r
-  /* Shut down */\r
-  DdeDisconnect (hConv);\r
-}\r
-\r
-/*\r
- * Name    : doFile\r
- * Function: Process a file.\r
- *\r
- */\r
-\r
-int\r
-doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2)\r
-{\r
-  char            *buf = NULL;\r
-  unsigned        len;\r
-  \r
-  /* Calculate the buffer length */\r
-  len = strlen (lpszFileName1) + strlen (lpszFileName2)\r
-    + strlen (COMMAND_FORMAT);\r
-  \r
-  /* Allocate a buffer */\r
-  buf = (char *) xmalloc (len);\r
-\r
-  if (!buf)\r
-    {\r
-      MessageBox (NULL, "Not enough memory.",\r
-                 "winclient", MB_ICONEXCLAMATION | MB_OK);\r
-\r
-      return 1;\r
-    }\r
-\r
-  /* Build the command */\r
-  len = wsprintf (buf, COMMAND_FORMAT, lpszFileName1, lpszFileName2);\r
-\r
-  len++;\r
-  \r
-  /* OK. We're connected. Send the message. */\r
-  DdeClientTransaction (buf, len, hConv, NULL,\r
-                       0, XTYP_EXECUTE, TRANSACTION_TIMEOUT, NULL);\r
-\r
-  free (buf);\r
-  \r
-  return 0;\r
-}\r
-\r
-/*\r
- * Name    : getNextArg\r
- * Function: Retrieve the next command line argument.\r
- *\r
- */\r
-\r
-static char *\r
-getNextArg (const char **ptr, unsigned *len)\r
-{\r
-  int        in_quotes = 0, quit = 0, all_in_quotes = 0;\r
-  const char *p = *ptr, *start;\r
-  char       *buf = NULL;\r
-  unsigned   length = 0;\r
-\r
-  /* Skip whitespace */\r
-  while (*p && isspace (*p))\r
-    p++;\r
-\r
-  /* If this is the end, return NULL */\r
-  if (!*p)\r
-    return NULL;\r
-  \r
-  /* Remember where we are */\r
-  start = p;\r
-  \r
-  /* Find the next whitespace character outside quotes */\r
-  if (*p == '"')\r
-    all_in_quotes = 1;\r
-  \r
-  while (*p && !quit)\r
-    {\r
-      switch (*p)\r
-       {\r
-       case '"':\r
-         in_quotes = 1 - in_quotes;\r
-         p++;\r
-         break;\r
-\r
-       case '\\':\r
-         if (!in_quotes)\r
-           all_in_quotes = 0;\r
-         \r
-         p++;\r
-\r
-         if (!*p)\r
-           break;\r
-\r
-         p++;\r
-         break;\r
-\r
-       default:\r
-         if (isspace (*p) && !in_quotes)\r
-           quit = 1;\r
-         else if (!in_quotes)\r
-           all_in_quotes = 0;\r
-\r
-         if (!quit)\r
-           p++;\r
-       }\r
-    }\r
-\r
-  /* Work out the length */\r
-  length = p - start;\r
-\r
-  /* Strip quotes if the argument is completely quoted */\r
-  if (all_in_quotes)\r
-    {\r
-      start++;\r
-      length -= 2;\r
-    }\r
-  \r
-  /* Copy */\r
-  buf = (char *) xmalloc (length + 1);\r
-\r
-  if (!buf)\r
-    return NULL;\r
-  \r
-  strncpy (buf, start, length);\r
-  buf[length] = '\0';\r
-\r
-  /* Return the pointer and length */\r
-  *ptr = p;\r
-  *len = length;\r
-\r
-  return buf;\r
-}\r
-\r
-/*\r
- * Name    : parseCommandLine\r
- * Function: Process the command line. This program accepts a list of strings\r
- *         : (which may contain wildcards) representing filenames.\r
- *\r
- */\r
-\r
-int\r
-parseCommandLine (HCONV hConv, LPSTR lpszCommandLine)\r
-{\r
-  char            *fullpath, *filepart;\r
-  char            *arg;\r
-  unsigned        len, pathlen;\r
-  int             ret = 0;\r
-  HANDLE          hFindFile = NULL;\r
-  WIN32_FIND_DATA wfd;\r
-\r
-  /* Retrieve arguments */\r
-  while ((arg = getNextArg ((const char**)&lpszCommandLine, &len)) != NULL)\r
-    {\r
-      /* First find the canonical path name */\r
-      fullpath = filepart = NULL;\r
-      pathlen = GetFullPathName (arg, 0, fullpath, &filepart);\r
-\r
-      fullpath = (char *) xmalloc (pathlen);\r
-\r
-      if (!fullpath)\r
-       {\r
-         MessageBox (NULL, "Not enough memory.", "winclient",\r
-                     MB_ICONEXCLAMATION | MB_OK);\r
-         \r
-         ret = 1;\r
-         free (arg);\r
-         \r
-         break;\r
-       }\r
-\r
-      GetFullPathName (arg, pathlen, fullpath, &filepart);\r
-\r
-      /* Find the first matching file */\r
-      hFindFile = FindFirstFile (arg, &wfd);\r
-\r
-      if (hFindFile == INVALID_HANDLE_VALUE)\r
-       ret = doFile (hConv, fullpath, "");\r
-      else\r
-       {\r
-         /* Chop off the file part from the full path name */\r
-         if (filepart)\r
-           *filepart = '\0';\r
-\r
-         /* For each matching file */\r
-         do\r
-           {\r
-             /* Process it */\r
-             ret = doFile (hConv, fullpath, wfd.cFileName);\r
-\r
-             if (ret)\r
-               break;\r
-           }\r
-         while (FindNextFile (hFindFile, &wfd));\r
-\r
-         FindClose (hFindFile);\r
-       }\r
-\r
-      /* Release the path name buffers */\r
-      free (fullpath);\r
-      free (arg);\r
-\r
-      if (ret)\r
-       break;\r
-    }\r
-\r
-  return ret;\r
-}\r
-\r
-static void\r
-fatal (const char *s1, const char *s2)\r
-{\r
-  error (s1, s2);\r
-  exit (1);\r
-}\r
-\r
-/* Print error message.  `s1' is printf control string, `s2' is arg for it. */\r
-static void\r
-error (const char* s1, const char* s2)\r
-{\r
-  fprintf (stderr, "winclient: ");\r
-  fprintf (stderr, s1, s2);\r
-  fprintf (stderr, "\n");\r
-}\r
-\r
-/* Like malloc but get fatal error if memory is exhausted.  */\r
-\r
-static void *\r
-xmalloc (size_t size)\r
-{\r
-  void *result = malloc (size);\r
-  if (result == NULL)\r
-    fatal ("virtual memory exhausted", (char *) 0);\r
-  return result;\r
-}\r
+/* DDE client for XEmacs.
+   Copyright (C) 2002 Alastair J. Houghton
+
+   This file is part of XEmacs.
+
+   XEmacs is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   XEmacs is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with XEmacs; see the file COPYING.  If not, write to
+   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* -- Pre-Include Defines --------------------------------------------------- */
+
+#define STRICT
+
+/* -- Includes -------------------------------------------------------------- */
+
+#include <windows.h>
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+static void error (const char* s1, const char* s2);
+static void fatal (const char *s1, const char *s2);
+static void * xmalloc (size_t size);
+static char * getNextArg (const char **ptr, unsigned *len);
+
+/* -- Post-Include Defines -------------------------------------------------- */
+
+/* Timeouts & delays */
+#define CONNECT_DELAY          500             /* ms */
+#define TRANSACTION_TIMEOUT    5000            /* ms */
+#define MAX_INPUT_IDLE_WAIT     INFINITE       /* ms */
+
+/* DDE Strings */
+#define SERVICE_NAME   "XEmacs"
+#define TOPIC_NAME     "System"
+#define COMMAND_FORMAT "[open(\"%s%s\")]"
+
+/* XEmacs program name */
+#define PROGRAM_TO_RUN "xemacs.exe"
+
+/* -- Constants ------------------------------------------------------------- */
+
+/* -- Global Variables ------------------------------------------------------ */
+
+HINSTANCE hInstance;
+DWORD     idInst = 0;
+
+/* -- Function Declarations ------------------------------------------------- */
+
+HDDEDATA CALLBACK ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
+                              HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+                              DWORD dwData1, DWORD dwData2);
+
+int WINAPI WinMain (HINSTANCE hInst,
+                   HINSTANCE hPrev,
+                   LPSTR     lpCmdLine,
+                   int       nCmdShow);
+
+static HCONV openConversation (void);
+static void closeConversation (HCONV hConv);
+static int doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2);
+static int parseCommandLine (HCONV hConv, LPSTR lpszCommandLine);
+
+/* -- Function Definitions -------------------------------------------------- */
+
+/*
+ * Name    : ddeCallback
+ * Function: Gets called by DDEML.
+ *
+ */
+
+HDDEDATA CALLBACK
+ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
+            HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+            DWORD dwData1, DWORD dwData2)
+{
+  return (HDDEDATA) NULL;
+}
+
+/*
+ * Name    : WinMain
+ * Function: The program's entry point function.
+ *
+ */
+
+int WINAPI
+WinMain (HINSTANCE hInst,
+        HINSTANCE hPrev,
+        LPSTR     lpCmdLine,
+        int       nCmdShow)
+{
+  HCONV hConv;
+  int   ret = 0;
+  UINT  uiRet;
+  
+  /* Initialise the DDEML library */
+  uiRet = DdeInitialize (&idInst,
+                        (PFNCALLBACK) ddeCallback,
+                        APPCMD_CLIENTONLY
+                        |CBF_FAIL_ALLSVRXACTIONS,
+                        0);
+
+  if (uiRet != DMLERR_NO_ERROR)
+    {
+      MessageBox (NULL, "Could not initialise DDE management library.",
+                 "winclient", MB_ICONEXCLAMATION | MB_OK);
+
+      return 1;
+    }
+
+  /* Open a conversation */
+  hConv = openConversation ();
+
+  if (hConv)
+    {
+      /* OK. Next, we need to parse the command line. */
+      ret = parseCommandLine (hConv, lpCmdLine);
+
+      /* Close the conversation */
+      closeConversation (hConv);
+    }
+  
+  DdeUninitialize (idInst);
+
+  return ret;
+}
+
+/*
+ * Name    : openConversation
+ * Function: Start a conversation.
+ *
+ */
+
+static HCONV
+openConversation (void)
+{
+  HSZ             hszService = NULL, hszTopic = NULL;
+  HCONV           hConv = NULL;
+
+  /* Get the application (service) name */
+  hszService = DdeCreateStringHandle (idInst,
+                                     SERVICE_NAME,
+                                     CP_WINANSI);
+
+  if (!hszService)
+    {
+      MessageBox (NULL, "Could not create string handle for service.",
+                 "winclient", MB_ICONEXCLAMATION | MB_OK);
+
+      goto error;
+    }
+  
+  /* Get the topic name */
+  hszTopic = DdeCreateStringHandle (idInst,
+                                   TOPIC_NAME,
+                                   CP_WINANSI);
+
+  if (!hszTopic)
+    {
+      MessageBox (NULL, "Could not create string handle for topic.",
+                 "winclient", MB_ICONEXCLAMATION | MB_OK);
+
+      goto error;
+    }
+
+  /* Try to connect */
+  hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
+
+  if (!hConv)
+    {
+      STARTUPINFO         sti;
+      PROCESS_INFORMATION pi;
+      int                 n;
+      
+      /* Try to start the program */
+      ZeroMemory (&sti, sizeof (sti));
+      sti.cb = sizeof (sti);
+      if (!CreateProcess (NULL, PROGRAM_TO_RUN, NULL, NULL, FALSE, 0,
+                         NULL, NULL, &sti, &pi))
+       {
+         MessageBox (NULL, "Could not start process.",
+                     "winclient", MB_ICONEXCLAMATION | MB_OK);
+
+         goto error;
+       }
+
+      /* Wait for the process to enter an idle state */
+      WaitForInputIdle (pi.hProcess, MAX_INPUT_IDLE_WAIT);
+
+      /* Close the handles */
+      CloseHandle (pi.hThread);
+      CloseHandle (pi.hProcess);
+      
+      /* Try to connect */
+      for (n = 0; n < 5; n++)
+       {
+         Sleep (CONNECT_DELAY);
+         
+         hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
+
+         if (hConv)
+           break;
+       }
+
+      if (!hConv)
+       {
+         /* Still couldn't connect. */
+         MessageBox (NULL, "Could not connect to DDE server.",
+                     "winclient", MB_ICONEXCLAMATION | MB_OK);
+
+         goto error;
+       }
+    }
+
+  /* Release the string handles */
+  DdeFreeStringHandle (idInst, hszService);
+  DdeFreeStringHandle (idInst, hszTopic);
+
+  return hConv;
+  
+ error:
+  if (hConv)
+    DdeDisconnect (hConv);
+  if (hszService)
+    DdeFreeStringHandle (idInst, hszService);
+  if (hszTopic)
+    DdeFreeStringHandle (idInst, hszTopic);
+
+  return NULL;
+}
+
+/*
+ * Name    : closeConversation
+ * Function: Close a conversation.
+ *
+ */
+
+static void
+closeConversation (HCONV hConv)
+{
+  /* Shut down */
+  DdeDisconnect (hConv);
+}
+
+/*
+ * Name    : doFile
+ * Function: Process a file.
+ *
+ */
+
+int
+doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2)
+{
+  char            *buf = NULL;
+  unsigned        len;
+  
+  /* Calculate the buffer length */
+  len = strlen (lpszFileName1) + strlen (lpszFileName2)
+    + strlen (COMMAND_FORMAT);
+  
+  /* Allocate a buffer */
+  buf = (char *) xmalloc (len);
+
+  if (!buf)
+    {
+      MessageBox (NULL, "Not enough memory.",
+                 "winclient", MB_ICONEXCLAMATION | MB_OK);
+
+      return 1;
+    }
+
+  /* Build the command */
+  len = wsprintf (buf, COMMAND_FORMAT, lpszFileName1, lpszFileName2);
+
+  len++;
+  
+  /* OK. We're connected. Send the message. */
+  DdeClientTransaction (buf, len, hConv, NULL,
+                       0, XTYP_EXECUTE, TRANSACTION_TIMEOUT, NULL);
+
+  free (buf);
+  
+  return 0;
+}
+
+/*
+ * Name    : getNextArg
+ * Function: Retrieve the next command line argument.
+ *
+ */
+
+static char *
+getNextArg (const char **ptr, unsigned *len)
+{
+  int        in_quotes = 0, quit = 0, all_in_quotes = 0;
+  const char *p = *ptr, *start;
+  char       *buf = NULL;
+  unsigned   length = 0;
+
+  /* Skip whitespace */
+  while (*p && isspace (*p))
+    p++;
+
+  /* If this is the end, return NULL */
+  if (!*p)
+    return NULL;
+  
+  /* Remember where we are */
+  start = p;
+  
+  /* Find the next whitespace character outside quotes */
+  if (*p == '"')
+    all_in_quotes = 1;
+  
+  while (*p && !quit)
+    {
+      switch (*p)
+       {
+       case '"':
+         in_quotes = 1 - in_quotes;
+         p++;
+         break;
+
+       case '\\':
+         if (!in_quotes)
+           all_in_quotes = 0;
+         
+         p++;
+
+         if (!*p)
+           break;
+
+         p++;
+         break;
+
+       default:
+         if (isspace (*p) && !in_quotes)
+           quit = 1;
+         else if (!in_quotes)
+           all_in_quotes = 0;
+
+         if (!quit)
+           p++;
+       }
+    }
+
+  /* Work out the length */
+  length = p - start;
+
+  /* Strip quotes if the argument is completely quoted */
+  if (all_in_quotes)
+    {
+      start++;
+      length -= 2;
+    }
+  
+  /* Copy */
+  buf = (char *) xmalloc (length + 1);
+
+  if (!buf)
+    return NULL;
+  
+  strncpy (buf, start, length);
+  buf[length] = '\0';
+
+  /* Return the pointer and length */
+  *ptr = p;
+  *len = length;
+
+  return buf;
+}
+
+/*
+ * Name    : parseCommandLine
+ * Function: Process the command line. This program accepts a list of strings
+ *         : (which may contain wildcards) representing filenames.
+ *
+ */
+
+int
+parseCommandLine (HCONV hConv, LPSTR lpszCommandLine)
+{
+  char            *fullpath, *filepart;
+  char            *arg;
+  unsigned        len, pathlen;
+  int             ret = 0;
+  HANDLE          hFindFile = NULL;
+  WIN32_FIND_DATA wfd;
+
+  /* Retrieve arguments */
+  while ((arg = getNextArg ((const char**)&lpszCommandLine, &len)) != NULL)
+    {
+      /* First find the canonical path name */
+      fullpath = filepart = NULL;
+      pathlen = GetFullPathName (arg, 0, fullpath, &filepart);
+
+      fullpath = (char *) xmalloc (pathlen);
+
+      if (!fullpath)
+       {
+         MessageBox (NULL, "Not enough memory.", "winclient",
+                     MB_ICONEXCLAMATION | MB_OK);
+         
+         ret = 1;
+         free (arg);
+         
+         break;
+       }
+
+      GetFullPathName (arg, pathlen, fullpath, &filepart);
+
+      /* Find the first matching file */
+      hFindFile = FindFirstFile (arg, &wfd);
+
+      if (hFindFile == INVALID_HANDLE_VALUE)
+       ret = doFile (hConv, fullpath, "");
+      else
+       {
+         /* Chop off the file part from the full path name */
+         if (filepart)
+           *filepart = '\0';
+
+         /* For each matching file */
+         do
+           {
+             /* Process it */
+             ret = doFile (hConv, fullpath, wfd.cFileName);
+
+             if (ret)
+               break;
+           }
+         while (FindNextFile (hFindFile, &wfd));
+
+         FindClose (hFindFile);
+       }
+
+      /* Release the path name buffers */
+      free (fullpath);
+      free (arg);
+
+      if (ret)
+       break;
+    }
+
+  return ret;
+}
+
+static void
+fatal (const char *s1, const char *s2)
+{
+  error (s1, s2);
+  exit (1);
+}
+
+/* Print error message.  `s1' is printf control string, `s2' is arg for it. */
+static void
+error (const char* s1, const char* s2)
+{
+  fprintf (stderr, "winclient: ");
+  fprintf (stderr, s1, s2);
+  fprintf (stderr, "\n");
+}
+
+/* Like malloc but get fatal error if memory is exhausted.  */
+
+static void *
+xmalloc (size_t size)
+{
+  void *result = malloc (size);
+  if (result == NULL)
+    fatal ("virtual memory exhausted", (char *) 0);
+  return result;
+}