/** quicksort.c
*
*   Originally, a test program to demonstrate the direct variable interface
*   to ARexx, by William Hawes. From the original docs:
*   "Opens a public port called "VarTest" and then waits for REXX messages.
*    The port stays open until a "CLOSE" command is received.
*    Usage:  run vartest
*    Then send commands from within ARexx by "address 'VarTest' command" "
*
*   Converted to Manx by WGL
*   Converted to a QuickSort command host by Marvin Weinstein.
*   Converted back to SAS by WGL
*
*   Added a List Requester, and the QuickSort now really is an insertion
*   sort (easier with Lists), to be used with Spellfix.ttx and ISpell. WGL.
*
*   Recent change: changed the "QUIT" command to QS_QUIT, to make it not
*   interfere with QUIT commands for other hosts. CLOSE is now QS_CLOSE.
*
**/
/*
*   Standard includes for SAS C.
*/
#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <graphics/gfxbase.h>
#include <graphics/text.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <proto/rexxsys.h>
#include <proto/all.h>

/*
*   Prototypes for functions defined in RexxVars.o
*/
long __stdargs SetRexxVar(struct RexxMsg *, unsigned char *, unsigned char *,
                          long);
long __stdargs GetRexxVar(struct RexxMsg *, unsigned char *, STRPTR *, long);

long __stdargs CheckRexxMsg(struct RexxMsg *);

static long Sort(struct List *);
static void ReturnMsg(struct RexxMsg *, long, long);
static struct List *AddList(struct List *, char *, long);
static struct List *NukeList(struct List *);
static struct List *GetRexxList(struct RexxMsg *, long);
static struct List *PutRexxList(struct RexxMsg *, struct List *);

struct Node *ListRequest(struct Screen *, struct List *, long, long, long, long);

struct RexxLib *RexxSysBase         = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase             = NULL;
struct Library *GadToolsBase        = NULL;

#define DO_SORT 1
#define NO_SORT 0

void main(int argc, char **argv)
{
   struct MsgPort *MyPort = NULL;
   struct RexxMsg *rmptr;
   long           quitflag = 0, sortflag;
   long           res1, res2, x = 0, y = 0, w = 0, h = 0, n;
   struct List    *list = NULL;
   struct Node    *result;
   char           *screenname = NULL, *flags = NULL, *ptr;
   struct Screen  *screen = NULL;

   RexxSysBase = (struct RexxLib *) OpenLibrary("rexxsyslib.library",0);
   if (RexxSysBase == 0L) goto cleanup;

   IntuitionBase = (struct RexxLib *) OpenLibrary("intuition.library",0);
   if (IntuitionBase == 0L) goto cleanup;

   GfxBase = (struct RexxLib *) OpenLibrary("graphics.library",0);
   if (GfxBase == 0L) goto cleanup;

   GadToolsBase = (struct RexxLib *) OpenLibrary("gadtools.library",0);
   if (GadToolsBase == 0L) goto cleanup;
/*
*   Really should have used all upper case here, but too late now. WGL
*/
   MyPort = CreatePort("QuickSortPort", NULL);
   if (MyPort == NULL) goto cleanup;

   while (quitflag == 0) {
      Wait(1L << MyPort->mp_SigBit);

      rmptr = (struct RexxMsg *) GetMsg(MyPort);

      res1 = res2 = 0L;
      if (CheckRexxMsg(rmptr)) {
         if (stricmp(rmptr->rm_Args[0], "QSORT") == 0) {
            if ((rmptr->rm_Action & 0xFF) == 3) {
/*
*   Get the strings array using insertion sort
*/
               list = GetRexxList(rmptr, DO_SORT);
/*
*   Now put them back.
*/
               if (list) list = PutRexxList(rmptr, list);

               if (list == NULL) {
                  res1 = 10L;
                  res2 = 12L;
               }

               list = NukeList(list);
            }
            else {
               res1 = 10;
               res2 = 17;
            }
         }
         else if (stricmp(rmptr->rm_Args[0], "LISTREQUEST") == 0) {
            if ((n = (rmptr->rm_Action & 0xFF)) < 4) {
               res1 = 10;
               res2 = 17;
            }
            else {
               if (n >= 5)  x = atol(rmptr->rm_Args[5]);
               if (n >= 6)  y = atol(rmptr->rm_Args[6]);
               if (n >= 7)  w = atol(rmptr->rm_Args[7]);
               if (n >= 8)  h = atol(rmptr->rm_Args[8]);
               if (n >= 9)  screenname = rmptr->rm_Args[9];
               if (n >= 10) flags      = rmptr->rm_Args[10];

               sortflag = NO_SORT;
               if (flags && (stricmp(flags, "SORT") == 0)) sortflag = DO_SORT;

               list = GetRexxList(rmptr, sortflag);

               if (list) {
                  if (screenname) screen = LockPubScreen(screenname);

                  result = ListRequest(screen, list, x, y, w, h);

                  if (screen) UnlockPubScreen(NULL, screen);

                  if (result) ptr = result->ln_Name;
                  else        ptr = "";

                  SetRexxVar(rmptr, rmptr->rm_Args[4], ptr, strlen(ptr));

                  list = NukeList(list);
               }
               else {
                  res1 = 10L;
                  res2 = 12L;
               }
            }
         }
/*
*   See whether it's the close command
*/
         else if (strcmp(rmptr->rm_Args[0], "QS_CLOSE") == 0) {
            quitflag = 1;
         }
         else if (strcmp(rmptr->rm_Args[0], "QS_QUIT") == 0) {
            quitflag = 1;
         }
         else {
            res1 = 5;
            res2 = 1;
         }
      }
      else {
         res1 = 10;
         res2 = 10;
      }
/*
*   If we get here, we handled the command, but didn't return yet.
*/
      ReturnMsg(rmptr, res1, res2);
   }

cleanup:
   if (MyPort)        DeletePort(MyPort);
   if (RexxSysBase)   CloseLibrary(RexxSysBase);
   if (GadToolsBase)  CloseLibrary(GadToolsBase);
   if (GfxBase)       CloseLibrary(GfxBase);
   if (IntuitionBase) CloseLibrary(IntuitionBase);

   exit(0);
}


static struct List *GetRexxList(rmptr, sortflag)
struct RexxMsg *rmptr;
long sortflag;
{
   long left, right, i, error;
   char buffer[256];
   STRPTR value;
   struct List *list = NULL;
/*
*   Get left and right bounds of string array to be sorted
*/
   left  = atoi(rmptr->rm_Args[1]);
   right = atoi(rmptr->rm_Args[2]);
/*
*   Copy RexxVariable array into strings
*/
   for (i = left; i <= right; i++) {
/*
*   Put name of stem in buffer
*/
      sprintf(buffer, "%s.%d", rmptr->rm_Args[3], i);
/*
*   Now get the string from the program
*/
      error = GetRexxVar(rmptr, buffer, &value, 256);
      if (error) {
         list = NukeList(list);
         break;
      }
      list = AddList(list, (char *) value, sortflag);
   }
   return(list);
}


static struct List *PutRexxList(rmptr, list)
struct RexxMsg *rmptr;
struct List *list;
{
   long left, right, i, error;
   char buffer[256], *ptr;
   struct Node *n;
/*
*   Get left and right bounds of string array to be sorted
*/
   left  = atoi(rmptr->rm_Args[1]);
   right = atoi(rmptr->rm_Args[2]);
/*
*   Put sort ed array back in old stem variable
*/
   for (i = left, n = list->lh_Head; i <= right; i++, n = n->ln_Succ) {
/*
*   Put name of stem in buffer
*/
      sprintf(buffer, "%s.%d", rmptr->rm_Args[3], i);
      ptr = n->ln_Name;
/*
*   Now get the string from the program
*/
      error = SetRexxVar(rmptr, buffer, ptr, strlen(ptr));
      if (error) {
         list = NukeList(list);
         break;
      }
   }
   return(list);
}


static void ReturnMsg(rmptr, res1, res2)
struct RexxMsg *rmptr;
long res1, res2;
{
   rmptr->rm_Result1 = res1;
   rmptr->rm_Result2 = res2;
   ReplyMsg(rmptr);
   return;
}

/**
*
*   Some list utility functions.
*   AddList adds a string to an Exec list. If the list doesn't exist
*   yet, it is first created. If sortflag is true, the list is sorted
*   using insertion sort.
*   NukeList deletes an entire list created by AddList.
*
**/
#define LSIZE ((long) sizeof(struct List))
#define NSIZE ((long) sizeof(struct Node))

static struct List *AddList(list, s, sortflag)
struct List *list;
char *s;
long sortflag;
{
   struct Node *n, *nt;
   char *ptr;

   if (list == NULL) {
      list = AllocMem(LSIZE, MEMF_CLEAR);
      if (list == NULL) return(NULL);
      NewList(list);
   }

   if (s) {
      n = AllocMem(NSIZE, MEMF_CLEAR);
      if (n == NULL) return(NukeList(list));

      ptr = AllocMem(strlen(s) + 1, 0L);
      if (ptr == NULL) return(NukeList(list));

      strcpy(ptr, s);
      n->ln_Name = ptr;

      if (sortflag) {
         nt = list->lh_Head;
         while (nt->ln_Succ) {
            if (stricmp(nt->ln_Name, ptr) >= 0) break;
            nt = nt->ln_Succ;
         }
         Insert(list, n, nt->ln_Pred);
      }
      else {
         AddTail(list, n);
      }
   }

   return(list);
}

static struct List *NukeList(list)
struct List *list;
{
   struct Node *n, *nn;
   char *ptr;

   if (list) {
      if (list != (struct List *) list->lh_TailPred) {
         n = list->lh_Head;
         while (nn = n->ln_Succ) {
            Remove(n);
            if (ptr = n->ln_Name) FreeMem(ptr, strlen(ptr) + 1);
            FreeMem(n, NSIZE);
            n = nn;
         }
      }
      FreeMem(list, LSIZE);
   }
   return(NULL);
}


