]> git.saurik.com Git - wxWidgets.git/blobdiff - utils/Install/sfxzip/rexxapi.c
merged 2.2 branch
[wxWidgets.git] / utils / Install / sfxzip / rexxapi.c
diff --git a/utils/Install/sfxzip/rexxapi.c b/utils/Install/sfxzip/rexxapi.c
new file mode 100644 (file)
index 0000000..43b4ddd
--- /dev/null
@@ -0,0 +1,886 @@
+/**********************************************************************
+*   REXXAPI.C                                                         *
+*                                                                     *
+*   This program adds a ZIP engine directly to the REXX language.     *
+*   The functions are:                                                *
+*       UZDropFuncs         -- Makes all functions in this package    *
+*                              unknown to REXX.                       *
+*       UZLoadFuncs         -- Makes all functions in this package    *
+*                              known to REXX so REXX programs may     *
+*                              call them.                             *
+*       UZFileTree          -- Searches for files matching a given    *
+*                              filespec, including files in           *
+*                              subdirectories.                        *
+*       UZUnZip             -- Unzip command-line entry point.        *
+*                              This is functionally equivalent to     *
+*                              using Unzip as an external program.    *
+*       UZUnZipToVar            -- Unzip one file to a variable       *
+*       UZUnZipToStem       -- Unzip files to a variable array        *
+*       UZVer               -- Returns the Unzip version number       *
+*                                                                     *
+**********************************************************************/
+/* Include files */
+
+#ifdef OS2DLL
+
+#define  INCL_DOS
+#define  INCL_DOSMEMMGR
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#define UNZIP_INTERNAL
+#include "../unzip.h"
+#include "../version.h"
+
+
+/*********************************************************************/
+/*  Various definitions used by various functions.                   */
+/*********************************************************************/
+
+RexxFunctionHandler UZDropFuncs;
+RexxFunctionHandler UZLoadFuncs;
+RexxFunctionHandler UZFileTree;
+RexxFunctionHandler UZUnZip;
+RexxFunctionHandler UZUnZipToVar;
+RexxFunctionHandler UZUnZipToStem;
+RexxFunctionHandler UZVer;
+RexxFunctionHandler UZAPIVer;
+
+
+int SetOutputVar(__GPRO__ const char *name);
+int SetOutputVarStem(__GPRO__ const char *name);
+int SetOutputVarLength(__GPRO);
+int WriteToVariable(__GPRO__ const char *name, char *buffer, int len);
+int PrintToSubVariable(__GPRO__ int idx, const char *format,...);
+int PrintToVariable(__GPRO__ const char *name, const char *format,...);
+int _PrintToVariable(__GPRO__ const char *name, const char *format, va_list arg_ptr);
+int TextSetNext(__GPRO__ char *format, int len, int all);
+
+#define EZRXSTRING(r,p)  {(r).strptr=(PCH)p;(r).strlength=(ULONG)strlen((r).strptr);}
+
+
+/*********************************************************************/
+/* RxFncTable                                                        */
+/*   Array of names of the UNZIPAPI functions.                       */
+/*   This list is used for registration and deregistration.          */
+/*********************************************************************/
+
+static PSZ  RxFncTable[] =
+   {
+      "UZDropFuncs",
+      "UZLoadFuncs",
+      "UZFileSearch",
+      "UZFileTree",
+      "UZUnZip",
+      "UZUnZipToVar",
+      "UZUnZipToStem",
+      "UZVer",
+   };
+
+/*********************************************************************/
+/* Numeric Error Return Strings                                      */
+/*********************************************************************/
+
+#define  NO_UTIL_ERROR    "0"          /* No error whatsoever        */
+#define  ERROR_NOMEM      "2"          /* Insufficient memory        */
+
+/*********************************************************************/
+/* Numeric Return calls                                              */
+/*********************************************************************/
+
+#define  INVALID_ROUTINE 40            /* Raise Rexx error           */
+#define  VALID_ROUTINE    0            /* Successful completion      */
+
+/*********************************************************************/
+/* Some useful macros                                                */
+/*********************************************************************/
+
+#define BUILDRXSTRING(t, s) { \
+  strcpy((t)->strptr,(s));\
+  (t)->strlength = strlen((s)); \
+}
+
+
+/*********************************************************************/
+/****************  UNZIPAPI Supporting Functions  ********************/
+/****************  UNZIPAPI Supporting Functions  ********************/
+/****************  UNZIPAPI Supporting Functions  ********************/
+/*********************************************************************/
+
+
+int RexxReturn(__GPRO__ int nodefault, RXSTRING *retstr)
+{
+  int ret = G.os2.rexx_error;
+  if (G.filenotfound)
+    G.os2.rexx_mes = "file not found";
+  if (*G.os2.rexx_mes != '0') {
+    if (retstr->strlength > 255) {
+      DosFreeMem(retstr->strptr);
+      retstr->strptr = NULL;
+    }
+  } else if (nodefault)
+    goto noBuild;
+  BUILDRXSTRING(retstr, G.os2.rexx_mes);
+ noBuild:
+  DESTROYGLOBALS();
+  return ret;
+}
+
+/* Get a variable from REXX, return 0 if OK */
+int GetVariable(__GPRO__ const char *name)
+{
+  G.os2.request.shvnext = NULL;
+  EZRXSTRING(G.os2.request.shvname, name);
+  G.os2.request.shvnamelen = G.os2.request.shvname.strlength;
+  G.os2.request.shvvalue.strptr = G.os2.buffer;
+  G.os2.request.shvvalue.strlength = IBUF_LEN;
+  G.os2.request.shvvaluelen = IBUF_LEN;
+  G.os2.request.shvcode = RXSHV_SYFET;
+  G.os2.request.shvret = 0;
+  switch (RexxVariablePool(&G.os2.request)) {
+  case RXSHV_MEMFL:
+    G.os2.rexx_mes = ERROR_NOMEM;
+    break;
+  case RXSHV_BADN:
+  case RXSHV_NEWV:
+    G.os2.request.shvvaluelen = 0;
+  case RXSHV_OK:
+    *(G.os2.buffer+G.os2.request.shvvaluelen) = 0;
+    return G.os2.request.shvvaluelen;
+  }
+  return 0;
+}
+
+
+/* Get REXX compound variable */
+/* Stem must exist in G.os2.getvar_buf w/ length in G.os2.getvar_len */
+int GetVariableIndex(__GPRO__ int index)
+{
+  sprintf(G.os2.getvar_buf+G.os2.getvar_len,"%d",index);
+  return GetVariable(__G__ G.os2.getvar_buf);
+}
+
+
+/* Transfer REXX array to standard C string array */
+/* Returns number of elements */
+/* User is responsible for calling KillStringArray */
+
+int CompoundToStringArray(__GPRO__ char ***pointer, const char *name)
+{
+  int count;
+  int total;
+  char **trav;
+
+  G.os2.getvar_len = strlen(name);
+  memcpy(G.os2.getvar_buf,name,G.os2.getvar_len+1);
+  if (*(G.os2.getvar_buf+G.os2.getvar_len-1) != '.')
+    *(G.os2.getvar_buf+G.os2.getvar_len++) = '.', *(G.os2.getvar_buf+G.os2.getvar_len) = 0;
+
+  if (GetVariableIndex(__G__ 0))
+    return 0;
+
+  total = atoi(G.os2.buffer);
+  *pointer = (char **)malloc((total+1)<<2);
+  trav = *pointer;
+  for (count = 1; count <= total; count++) {
+    GetVariableIndex(__G__ count);
+    trav[count-1] = (char *)malloc(strlen(G.os2.buffer)+1);
+    strcpy(trav[count-1],G.os2.buffer);
+  }
+  trav[count-1] = NULL;
+  return total;
+}
+
+
+/* Kill string array created by CompoundToStringArray */
+
+void KillStringArray(char **pointer)
+{
+  char **trav=pointer;
+  while (*trav != NULL) {
+    free(*trav);
+    trav++;
+  }
+  free(pointer);
+}
+
+
+/*************************************************************************
+* Function:  UZDropFuncs                                                 *
+*                                                                        *
+* Syntax:    call UZDropFuncs                                            *
+*                                                                        *
+* Return:    NO_UTIL_ERROR - Successful.                                 *
+*************************************************************************/
+
+ULONG UZDropFuncs(CHAR *name, ULONG numargs, RXSTRING args[],
+                  CHAR *queuename, RXSTRING *retstr)
+{
+  INT     entries;                     /* Num of entries             */
+  INT     j;                           /* Counter                    */
+
+  if (numargs != 0)                    /* no arguments for this      */
+    return INVALID_ROUTINE;            /* raise an error             */
+
+  retstr->strlength = 0;               /* return a null string result*/
+
+  entries = sizeof(RxFncTable)/sizeof(PSZ);
+
+  for (j = 0; j < entries; j++)
+    RexxDeregisterFunction(RxFncTable[j]);
+
+  return VALID_ROUTINE;                /* no error on call           */
+}
+
+
+/*************************************************************************
+* Function:  UZFileTree                                                  *
+*                                                                        *
+* Syntax:    call UZFileTree zipfile, stem[, include-filespec]           *
+*                                [, exclude-filespec][, options]         *
+*                                                                        *
+* Params:    zipfile  - Name of zip file to search.                      *
+*            stem     - Name of stem var to store results in.            *
+*            include  - Filespec to search for (may include * and ?).    *
+*            exclude  - Filespec to exclude (may include * and ?).       *
+*            options  - Either of the following:                         *
+*                       'F' - Give file statistics.                      *
+*                          Length Date Time Name                         *
+*                       'Z' - Give zip statistics, too.                  *
+*                          Length Method Size Ratio Date Time CRC-32 Name*
+*                       Default is to return only filenames              *
+*                                                                        *
+* Return:    NO_UTIL_ERROR   - Successful.                               *
+*            ERROR_NOMEM     - Out of memory.                            *
+*************************************************************************/
+
+ULONG UZFileTree(CHAR *name, ULONG numargs, RXSTRING args[],
+                 CHAR *queuename, RXSTRING *retstr)
+{
+                                       /* validate arguments         */
+  char *incname[2];
+  char *excname[2];
+  CONSTRUCTGLOBALS();
+
+  if (numargs < 2 || numargs > 5 ||
+      !RXVALIDSTRING(args[0]) ||
+      !RXVALIDSTRING(args[1]) ||
+      args[0].strlength > 255) {
+    DESTROYGLOBALS();
+    return INVALID_ROUTINE;            /* Invalid call to routine    */
+  }
+                                       /* initialize data area       */
+  SetOutputVarStem(__G__ args[1].strptr);
+  G.wildzipfn = args[0].strptr;
+  G.process_all_files = TRUE;
+
+  uO.lflag = 1;
+  uO.zipinfo_mode = TRUE;
+  uO.C_flag = 1;
+  G.extract_flag = FALSE;
+  uO.qflag = 2;
+
+  if (numargs >= 3 &&                  /* check third option         */
+      !RXNULLSTRING(args[2]) &&
+      args[2].strlength > 0) {            /* a zero length string isn't */
+    if (!(G.filespecs = CompoundToStringArray(__G__ &G.pfnames,args[2].strptr))) {
+      G.pfnames = incname;
+      incname[0] = args[2].strptr;
+      incname[1] = NULL;
+      G.filespecs = 1;
+    }
+    G.process_all_files = FALSE;
+  }
+
+  if (numargs >= 4 &&                  /* check third option         */
+      !RXNULLSTRING(args[3]) &&
+      args[3].strlength > 0) {            /* a zero length string isn't */
+    if (!(G.xfilespecs = CompoundToStringArray(__G__ &G.pxnames,args[3].strptr))) {
+      G.pxnames = excname;
+      excname[0] = args[3].strptr;
+      excname[1] = NULL;
+      G.xfilespecs = 1;
+    }
+    G.process_all_files = FALSE;
+  }
+
+  if (numargs == 5 &&                  /* check third option         */
+      !RXNULLSTRING(args[4]) &&
+      args[4].strlength > 0) {            /* a zero length string isn't */
+    int first = *args[4].strptr & 0x5f;
+
+    if (first == 'Z')
+      uO.vflag = 2, uO.lflag = 0, uO.zipinfo_mode = FALSE;
+    else if (first == 'F')
+      uO.vflag = 1, uO.lflag = 0, uO.zipinfo_mode = FALSE;
+  }
+
+  process_zipfiles(__G);
+  SetOutputVarLength(__G);
+  if (G.filespecs > 0 && G.pfnames != incname)
+    KillStringArray(G.pfnames);
+  if (G.xfilespecs > 0 && G.pxnames != excname)
+    KillStringArray(G.pxnames);
+  return RexxReturn(__G__ 0,retstr);        /* no error on call           */
+}
+
+
+/*************************************************************************
+* Function:  UZUnZipToVar                                                *
+*                                                                        *
+* Syntax:    call UZUnZipToVar zipfile, filespec [, stem]                *
+*                                                                        *
+* Params:    zipfile  - Name of zip file to search.                      *
+*            filespec - File to extract                                  *
+*            stem     - If you specify a stem variable, the file will be *
+*                       extracted to the variable, one line per index    *
+*                       In this case, 0 will be returned                 *
+*                                                                        *
+* Return:    Extracted file                                              *
+*            ERROR_NOMEM     - Out of memory.                            *
+*************************************************************************/
+
+ULONG UZUnZipToVar(CHAR *name, ULONG numargs, RXSTRING args[],
+                          CHAR *queuename, RXSTRING *retstr)
+{
+  CONSTRUCTGLOBALS();
+  UzpBuffer *ub = (UzpBuffer *)retstr;
+                                       /* validate arguments         */
+  if (numargs < 2 || numargs > 3 ||
+      !RXVALIDSTRING(args[0]) ||
+      !RXVALIDSTRING(args[1]) ||
+      args[0].strlength == 0 ||
+      args[1].strlength == 0) {
+    DESTROYGLOBALS();
+    return INVALID_ROUTINE;            /* Invalid call to routine    */
+  }
+
+  uO.C_flag = 1;
+  G.redirect_data=1;
+  if (numargs == 3) {
+    if (!RXVALIDSTRING(args[2]) ||
+        RXNULLSTRING(args[1]) ||
+        args[2].strlength == 0) {
+      DESTROYGLOBALS();
+      return INVALID_ROUTINE;            /* Invalid call to routine    */
+    }
+    SetOutputVarStem(__G__ args[2].strptr);
+    G.redirect_text = 0;
+    G.redirect_data++;
+  }
+  unzipToMemory(__G__ args[0].strptr, args[1].strptr,
+                G.redirect_data==1 ? ub : NULL);
+  return RexxReturn(__G__ G.redirect_data==1,retstr);
+}
+
+
+/*************************************************************************
+* Function:  UZUnZipToStem                                               *
+*                                                                        *
+* Syntax:    call UZUnZipToStem zipfile, stem[, include-filespec]        *
+*                                [, exclude-filespec][, mode]            *
+*                                                                        *
+* Params:    zipfile  - Name of zip file to search.                      *
+*            stem     - Name of stem var to store files in.              *
+*            include  - Filespec to search for (may include * and ?).    *
+*            exclude  - Filespec to exclude (may include * and ?).       *
+*            mode     - Specifies 'F'lat or 'T'ree mode.  Umm, this is   *
+*                        hard to explain so I'll give an example, too.   *
+*                       Assuming a file unzip.zip containing:            *
+*                               unzip.c                                  *
+*                               unshrink.c                               *
+*                               extract.c                                *
+*                               os2/makefile.os2                         *
+*                               os2/os2.c                                *
+*                               os2/dll/dll.def                          *
+*                               os2/dll/unzipapi.c                       *
+*                                                                        *
+*                       -- In flat mode, each file is stored in          *
+*                          stem.fullname i.e. stem."os2/dll/unzipapi.c"  *
+*                          A list of files is created in stem.<index>    *
+*                                                                        *
+*                       Flat mode returns:                               *
+*                               stem.0 = 7                               *
+*                               stem.1 = unzip.c                         *
+*                               stem.2 = unshrink.c                      *
+*                               stem.3 = extract.c                       *
+*                               stem.4 = os2/makefile.os2                *
+*                               stem.5 = os2/os2.c                       *
+*                               stem.6 = os2/dll/dll.def                 *
+*                               stem.7 = os2/dll/unzipapi.c              *
+*                                                                        *
+*                       And the following contain the contents of the    *
+*                       various programs:                                *
+*                               stem.unzip.c                             *
+*                               stem.unshrink.c                          *
+*                               stem.extract.c                           *
+*                               stem.os2/makefile.os2                    *
+*                               stem.os2/os2.c                           *
+*                               stem.os2/dll/dll.def                     *
+*                               stem.os2/dll/unzipapi.c                  *
+*                                                                        *
+*                       -- In tree mode, slashes are converted to periods*
+*                          in the pathname thus the above file would have*
+*                          been stored in stem.os2.dll.unzipapi.c        *
+*                          The index would then be stored in stem.OS2.   *
+*                          DLL.<index>.                                  *
+*                                                                        *
+*                       NOTE: All path names are converted to uppercase  *
+*                                                                        *
+*                       Tree mode returns:                               *
+*                               stem.0 = 4                               *
+*                               stem.1 = unzip.c                         *
+*                               stem.2 = unshrink.c                      *
+*                               stem.3 = extract.c                       *
+*                               stem.4 = OS2/                            *
+*                                                                        *
+*                               stem.OS2.0 = 3                           *
+*                               stem.OS2.1 = makefile.os2                *
+*                               stem.OS2.2 = os2.c                       *
+*                               stem.OS2.3 = DLL/                        *
+*                                                                        *
+*                               stem.OS2.DLL.0 = 2                       *
+*                               stem.OS2.DLL.1 = def                     *
+*                               stem.OS2.DLL.2 = unzipapi.c              *
+*                                                                        *
+*                       And the following contain the contents of the    *
+*                       various programs:                                *
+*                               stem.unzip.c                             *
+*                               stem.unshrink.c                          *
+*                               stem.extract.c                           *
+*                               stem.OS2.makefile.os2                    *
+*                               stem.OS2.os2.c                           *
+*                               stem.OS2.DLL.dll.def                     *
+*                               stem.OS2.DLL.unzipapi.c                  *
+*                                                                        *
+*                                                                        *
+* Return:    NO_UTIL_ERROR   - Successful.                               *
+*            ERROR_NOMEM     - Out of memory.                            *
+*************************************************************************/
+
+ULONG UZUnZipToStem(CHAR *name, ULONG numargs, RXSTRING args[],
+                          CHAR *queuename, RXSTRING *retstr)
+{
+  char *incname[2];
+  char *excname[2];
+  CONSTRUCTGLOBALS();
+                                       /* validate arguments         */
+  if (numargs < 2 || numargs > 5 ||
+      !RXVALIDSTRING(args[0]) ||
+      !RXVALIDSTRING(args[1]) ||
+      args[0].strlength > 255) {
+    DESTROYGLOBALS();
+    return INVALID_ROUTINE;            /* Invalid call to routine    */
+  }
+                                       /* initialize data area       */
+  G.wildzipfn = args[0].strptr;
+  G.process_all_files = TRUE;
+
+  uO.C_flag = 1;
+  G.extract_flag = TRUE;
+  SetOutputVarStem(__G__ args[1].strptr);
+  G.redirect_data = 3;
+  G.redirect_text = 0;
+
+  if (numargs >= 3 &&                  /* check third option         */
+      !RXNULLSTRING(args[2]) &&
+      args[2].strlength > 0) {            /* a zero length string isn't */
+    if (!(G.filespecs = CompoundToStringArray(__G__ &G.pfnames,args[2].strptr))) {
+      G.pfnames = incname;
+      incname[0] = args[2].strptr;
+      incname[1] = NULL;
+      G.filespecs = 1;
+    }
+    G.process_all_files = FALSE;
+  }
+
+  if (numargs >= 4 &&                  /* check third option         */
+      !RXNULLSTRING(args[3]) &&
+      args[3].strlength > 0) {            /* a zero length string isn't */
+    if (!(G.xfilespecs = CompoundToStringArray(__G__ &G.pxnames,args[3].strptr))) {
+      G.pxnames = excname;
+      excname[0] = args[3].strptr;
+      excname[1] = NULL;
+      G.xfilespecs = 1;
+    }
+    G.process_all_files = FALSE;
+  }
+
+  if (numargs == 5 &&                  /* check third option         */
+      !RXNULLSTRING(args[4]) &&
+      (*args[4].strptr & 0x5f) == 'T') {
+    G.redirect_data++;
+    G.os2.request.shvnext = NULL;
+    EZRXSTRING(G.os2.request.shvname, args[4].strptr);
+    G.os2.request.shvnamelen = G.os2.request.shvname.strlength;
+    G.os2.request.shvcode = RXSHV_SYDRO;
+    G.os2.request.shvret = 0;
+    RexxVariablePool(&G.os2.request);
+  }
+
+
+  uO.qflag = 2;
+
+  process_zipfiles(__G);
+  if (G.filespecs > 0 && G.pfnames != incname)
+    KillStringArray(G.pfnames);
+  if (G.xfilespecs > 0 && G.pxnames != excname)
+    KillStringArray(G.pxnames);
+  if (G.redirect_data == 3)
+    SetOutputVarLength(__G);
+  return RexxReturn(__G__ 0,retstr);            /* no error on call           */
+}
+
+
+/*************************************************************************
+* Function:  UZLoadFuncs                                                 *
+*                                                                        *
+* Syntax:    call UZLoadFuncs [option]                                   *
+*                                                                        *
+* Params:    none                                                        *
+*                                                                        *
+* Return:    null string                                                 *
+*************************************************************************/
+
+ULONG UZLoadFuncs(CHAR *name, ULONG numargs, RXSTRING args[],
+                           CHAR *queuename, RXSTRING *retstr)
+{
+  INT    entries;                      /* Num of entries             */
+  INT    j;                            /* Counter                    */
+
+  retstr->strlength = 0;               /* set return value           */
+                                       /* check arguments            */
+  if (numargs > 0)
+    return INVALID_ROUTINE;
+
+  entries = sizeof(RxFncTable)/sizeof(PSZ);
+
+  for (j = 0; j < entries; j++) {
+    RexxRegisterFunctionDll(RxFncTable[j],
+          "UNZIP32", RxFncTable[j]);
+  }
+  return VALID_ROUTINE;
+}
+
+
+
+/*************************************************************************
+* Function:  UZVer                                                       *
+*                                                                        *
+* Syntax:    call UZVer                                                  *
+*                                                                        *
+* Return:    Version of Unzip                                            *
+*************************************************************************/
+
+ULONG UZVer(CHAR *name, ULONG numargs, RXSTRING args[],
+                        CHAR *queuename, RXSTRING *retstr)
+{
+  if (numargs > 1)                    /* validate arg count         */
+    return INVALID_ROUTINE;
+
+  if (numargs == 0 || (*args[0].strptr & 0x5f) != 'L')
+    /* strcpy( retstr->strptr, UZ_VERNUM );    "5.13a BETA" */
+    sprintf( retstr->strptr, "%d.%d%d%s", UZ_MAJORVER, UZ_MINORVER,
+      PATCHLEVEL, BETALEVEL );
+  else
+    /* strcpy( retstr->strptr, UZ_VERSION );   UZ_VERNUM" of 26 Sep 94" */
+    sprintf( retstr->strptr, "%d.%d%d%s of %s", UZ_MAJORVER, UZ_MINORVER,
+      PATCHLEVEL, BETALEVEL, VERSION_DATE );
+  retstr->strlength = strlen(retstr->strptr);
+  return VALID_ROUTINE;
+}
+
+
+/*************************************************************************
+* Function:  UZUnZip                                                     *
+*                                                                        *
+* Syntax:    call UZUnZip                                                *
+*                                                                        *
+* Return:    Unzip return code                                           *
+*************************************************************************/
+
+ULONG UZUnZip(CHAR *name, ULONG numargs, RXSTRING args[],
+                        CHAR *queuename, RXSTRING *retstr)
+{
+  char *argv[30];
+  char *scan;
+  int argc=0;
+  int idx;
+  CONSTRUCTGLOBALS();
+
+  if (numargs < 1 || numargs > 2 ||
+      args[0].strlength > 255) {
+    DESTROYGLOBALS();
+    return INVALID_ROUTINE;            /* Invalid call to routine    */
+  }
+                                       /* initialize data area       */
+  if (numargs == 2)
+    SetOutputVarStem(__G__ args[1].strptr);
+
+  scan = args[0].strptr;
+  argv[argc++] = "";         /* D:\\SOURCECODE\\UNZIP51S\\UNZIP.COM"; */
+  while (*scan == ' ')
+    scan++;
+  argv[argc++] = scan;
+  while ( (scan = strchr(scan,' ')) != NULL) {
+    *scan++ = 0;
+    while (*scan == ' ')
+      scan++;
+    argv[argc++] = scan;
+  }
+  if (*argv[argc-1] == 0)
+    argc--;
+  argv[argc] = 0;
+
+         /* GRR:  should resetMainFlags() be called in here somewhere? */
+
+  sprintf(retstr->strptr, "%d", unzip(__G__ argc, argv));   /* a.k.a. MAIN() */
+  if (numargs == 2)
+    SetOutputVarLength(__G);
+  retstr->strlength = strlen(retstr->strptr);
+  return RexxReturn(__G__ 1,retstr);
+}
+
+int varmessage(__GPRO__ uch *buf, ulg size)
+{
+  if (size > 0)
+    memcpy(G.os2.buffer+G.os2.putchar_idx,buf,size);
+    G.os2.putchar_idx = TextSetNext(__G__ G.os2.buffer, size+G.os2.putchar_idx,0);
+  return 0;
+}
+
+int varputchar(__GPRO__ int c)
+{
+  G.os2.buffer[G.os2.putchar_idx++] = c;
+  if (c == '\n') {
+    G.os2.buffer[G.os2.putchar_idx] = 0;
+    if (G.os2.output_var[0])
+      G.os2.putchar_idx = TextSetNext(__G__ G.os2.buffer, G.os2.putchar_idx,0);
+    else {
+      G.os2.buffer[--G.os2.putchar_idx] = 0;
+      puts(G.os2.buffer);
+      G.os2.putchar_idx = 0;
+    }
+  }
+  return 1;
+}
+
+
+
+int SetOutputVarStem(__GPRO__ const char *name)
+{
+  int len=strlen(name);
+  G.redirect_text=1;
+  G.os2.output_idx = 0;
+  strcpy(G.os2.output_var, name);
+  if (len) {
+    strupr(G.os2.output_var);                 /* uppercase the name         */
+    if (*(G.os2.output_var+len-1) != '.') {
+      *(G.os2.output_var+len) = '.';
+      len++;
+      *(G.os2.output_var+len) = 0;
+    }
+    WriteToVariable(__G__ G.os2.output_var,"",0);
+  }
+  G.os2.stem_len = len;
+  return G.os2.stem_len;
+}
+
+int SetOutputVar(__GPRO__ const char *name)
+{
+  int len=strlen(name);
+  G.redirect_text=1;
+  G.os2.output_idx = 0;
+  strcpy(G.os2.output_var, name);
+  strupr(G.os2.output_var);                 /* uppercase the name         */
+  if (*(name+len-1) == '.')
+    G.os2.stem_len = len;
+  else
+    G.os2.stem_len = 0;
+  return G.os2.stem_len;
+}
+
+int SetOutputVarLength(__GPRO)
+{
+  if (G.os2.stem_len > 0) {
+    if (G.os2.putchar_idx)
+      TextSetNext(__G__ G.os2.buffer,G.os2.putchar_idx,1);
+    return PrintToSubVariable(__G__ 0,"%d",G.os2.output_idx);
+  }
+  return 0;
+}
+
+int PrintToVariable(__GPRO__ const char *name, const char *format,...)
+{
+  va_list arg_ptr;
+  int ret;
+
+  va_start(arg_ptr, format);
+  ret = _PrintToVariable(__G__ name, format, arg_ptr);
+  va_end(arg_ptr);
+  return ret;
+}
+
+int WriteToVariable(__GPRO__ const char *name, char *buffer, int len)
+{
+  G.os2.request.shvnext = NULL;
+  EZRXSTRING(G.os2.request.shvname, name);
+  G.os2.request.shvnamelen = G.os2.request.shvname.strlength;
+  G.os2.request.shvvalue.strptr = buffer;
+  G.os2.request.shvvalue.strlength = len;
+  G.os2.request.shvvaluelen = len;
+  G.os2.request.shvcode = RXSHV_SET;
+  G.os2.request.shvret = 0;
+  switch (RexxVariablePool(&G.os2.request)) {
+  case RXSHV_BADN:
+    G.os2.rexx_error = INVALID_ROUTINE;
+    break;
+  case RXSHV_MEMFL:
+    G.os2.rexx_mes = ERROR_NOMEM;
+    break;
+  case RXSHV_OK:
+    return 0;
+  }
+  return INVALID_ROUTINE;      /* error on non-zero          */
+}
+
+int _PrintToVariable(__GPRO__ const char *name, const char *format, va_list arg_ptr)
+{
+  int ret = vsprintf(G.os2.buffer, format, arg_ptr);
+  WriteToVariable(__G__ name, G.os2.buffer, strlen(G.os2.buffer));
+  return ret;
+}
+
+int PrintToSubVariable(__GPRO__ int idx, const char *format, ...)
+{
+  va_list arg_ptr;
+  int ret;
+
+  if (G.os2.stem_len == 0)
+    return INVALID_ROUTINE;      /* error on non-zero          */
+  sprintf(G.os2.output_var+G.os2.stem_len,"%d",idx);
+
+  va_start(arg_ptr, format);
+  ret = _PrintToVariable(__G__ G.os2.output_var, format, arg_ptr);
+  va_end(arg_ptr);
+  return ret;
+}
+
+
+int WriteToNextVariable(__GPRO__ char *buffer, int len)
+{
+  if (G.os2.stem_len > 0) {
+    G.os2.output_idx++;
+    sprintf(G.os2.output_var+G.os2.stem_len,"%d",G.os2.output_idx);
+  }
+  return WriteToVariable(__G__ G.os2.output_var, buffer, len);
+}
+
+
+int TextSetNext(__GPRO__ char *buffer, int len, int all)
+{
+  char *scan = buffer, *next, *base=buffer;
+  int remaining=len;
+  int ret;
+
+  while ((next = strchr(scan,'\n')) != NULL && remaining > 0) {
+    if (next > scan && *(next-1) == 0xd)
+      *(next-1) = 0;
+    else
+      *next = 0;
+    if (WriteToNextVariable(__G__ scan,strlen(scan)))
+      return 0;
+    next++;
+    remaining -= (next-scan);
+    scan = next;
+  }
+  if (remaining > 0)
+    if (all) {
+      *(scan+remaining) = 0;
+      WriteToNextVariable(__G__ scan,remaining);
+    } else {
+      memcpy(buffer,scan,remaining);
+      return remaining;
+    }
+
+  return 0;
+}
+
+int finish_REXX_redirect(__GPRO)
+{
+  char *scan, *ptr;
+  int idx=0, first=1, offset;
+
+  if (!G.redirect_size)
+    return 0;
+  switch(G.redirect_data) {
+  case 1:
+    break;
+  case 2:
+    TextSetNext(__G__ G.redirect_buffer, G.redirect_size, 1);
+    SetOutputVarLength(__G);
+    DosFreeMem(G.redirect_buffer);
+    G.redirect_buffer = NULL;
+    G.redirect_size = 0;
+    break;
+  case 3:
+    WriteToNextVariable(__G__ G.filename,strlen(G.filename));
+    sprintf(G.os2.output_var+G.os2.stem_len,G.filename);
+    WriteToVariable(__G__ G.os2.output_var, G.redirect_buffer, G.redirect_size);
+    DosFreeMem(G.redirect_buffer);
+    G.redirect_buffer = NULL;
+    G.redirect_size = 0;
+    break;
+  case 4:
+    if ((scan = strrchr(G.filename,'/')) != NULL) {
+      idx = *scan;
+      *scan = 0;
+      strupr(G.filename);
+      *scan = idx;
+    }
+    scan = G.os2.output_var+G.os2.stem_len;
+    strcpy(scan,G.filename);
+    while ((scan = strchr(scan,'/')) != NULL)
+      *scan = '.';
+    WriteToVariable(__G__ G.os2.output_var, G.redirect_buffer, G.redirect_size);
+    DosFreeMem(G.redirect_buffer);
+    G.redirect_buffer = NULL;
+    G.redirect_size = 0;
+    strcpy(G.os2.getvar_buf, G.os2.output_var);
+    do {
+      if ((scan = strrchr(G.filename,'/')) == NULL)
+        offset = 0;
+      else
+        offset = scan-G.filename+1;
+      if (first || !GetVariable(__G__ G.os2.output_var)) {
+        ptr = G.os2.getvar_buf+offset+G.os2.stem_len;
+        *ptr = '0';
+        *(ptr+1) = 0;
+        if (!GetVariable(__G__ G.os2.getvar_buf))
+          idx = 1;
+        else
+          idx = atoi(G.os2.buffer)+1;
+        PrintToVariable(__G__ G.os2.getvar_buf,"%d",idx);
+        sprintf(ptr,"%d",idx);
+        if (!first) {
+          PrintToVariable(__G__ G.os2.output_var,"%d",idx);
+          idx = strlen(G.filename);
+          *(G.filename+idx)   = '/';
+          *(G.filename+idx+1) = 0;
+        }
+        WriteToVariable(__G__ G.os2.getvar_buf,G.filename+offset,strlen(G.filename+offset));
+        first=0;
+      }
+      if (offset) {
+        *(G.os2.output_var+G.os2.stem_len+offset-1)   = 0;
+        *scan = 0;
+      }
+    } while (offset);
+    break;
+  }
+  return 0;
+}
+
+#endif /* OS2DLL */