/*
*         OpenPBS (Portable Batch System) v2.3 Software License
*
* Copyright (c) 1999-2000 Veridian Information Solutions, Inc.
* All rights reserved.
*
* ---------------------------------------------------------------------------
* For a license to use or redistribute the OpenPBS software under conditions
* other than those described below, or to purchase support for this software,
* please contact Veridian Systems, PBS Products Department ("Licensor") at:
*
*    www.OpenPBS.org  +1 650 967-4675                  sales@OpenPBS.org
*                        877 902-4PBS (US toll-free)
* ---------------------------------------------------------------------------
*
* This license covers use of the OpenPBS v2.3 software (the "Software") at
* your site or location, and, for certain users, redistribution of the
* Software to other sites and locations.  Use and redistribution of
* OpenPBS v2.3 in source and binary forms, with or without modification,
* are permitted provided that all of the following conditions are met.
* After December 31, 2001, only conditions 3-6 must be met:
*
* 1. Commercial and/or non-commercial use of the Software is permitted
*    provided a current software registration is on file at www.OpenPBS.org.
*    If use of this software contributes to a publication, product, or
*    service, proper attribution must be given; see www.OpenPBS.org/credit.html
*
* 2. Redistribution in any form is only permitted for non-commercial,
*    non-profit purposes.  There can be no charge for the Software or any
*    software incorporating the Software.  Further, there can be no
*    expectation of revenue generated as a consequence of redistributing
*    the Software.
*
* 3. Any Redistribution of source code must retain the above copyright notice
*    and the acknowledgment contained in paragraph 6, this list of conditions
*    and the disclaimer contained in paragraph 7.
*
* 4. Any Redistribution in binary form must reproduce the above copyright
*    notice and the acknowledgment contained in paragraph 6, this list of
*    conditions and the disclaimer contained in paragraph 7 in the
*    documentation and/or other materials provided with the distribution.
*
* 5. Redistributions in any form must be accompanied by information on how to
*    obtain complete source code for the OpenPBS software and any
*    modifications and/or additions to the OpenPBS software.  The source code
*    must either be included in the distribution or be available for no more
*    than the cost of distribution plus a nominal fee, and all modifications
*    and additions to the Software must be freely redistributable by any party
*    (including Licensor) without restriction.
*
* 6. All advertising materials mentioning features or use of the Software must
*    display the following acknowledgment:
*
*     "This product includes software developed by NASA Ames Research Center,
*     Lawrence Livermore National Laboratory, and Veridian Information
*     Solutions, Inc.
*     Visit www.OpenPBS.org for OpenPBS software support,
*     products, and information."
*
* 7. DISCLAIMER OF WARRANTY
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT
* ARE EXPRESSLY DISCLAIMED.
*
* IN NO EVENT SHALL VERIDIAN CORPORATION, ITS AFFILIATED COMPANIES, OR THE
* U.S. GOVERNMENT OR ANY OF ITS AGENCIES BE LIABLE FOR ANY DIRECT OR INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This license will be governed by the laws of the Commonwealth of Virginia,
* without reference to its choice of law rules.
*/

#include <pbs_config.h>   /* the master config generated by configure */

#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE 1
#endif

/* System headers */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* local headers */
#include "portability.h"
#include "af_job.h"

/* Macros */
/* File Scope Variables */
static char ident[] = "@(#) $RCSfile$ $Revision$";

struct JobSortArgs
  {

  struct SetJobElement **array;
  union
    {
    int (*ifunc)();
    double(*ffunc)();
    char    *(*sfunc)();
    DateTime(*dfunc)();
    Size(*szfunc)();
    } key;
  enum { INTKEY, FLOATKEY, STRINGKEY, DATETIMEKEY, SIZEKEY } keytype;
  int  order;     /* ASC or DESC */
  };

/* External Variables */
/* External Functions */
/* Structures and Unions */
/* Signal catching functions */
/* External Functions */
/* Functions */

char *JobIdGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->jobId;
  }

char *JobNameGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->jobName;
  }

char *JobOwnerNameGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->ownerName;
  }

char *JobEffectiveUserNameGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->effectiveUserName;
  }


char *JobEffectiveGroupNameGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->effectiveGroupName;
  }

int JobStateGet(job)
Job  *job;
  {
  if (job == NOJOB)
    return(-1); /* unknown state */

  return job->state;
  }

int JobPriorityGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(-1);

  return job->priority;
  }

int JobRerunFlagGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(-1);

  return job->rerunFlag;
  }

int JobInteractiveFlagGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(-1);

  return job->interactiveFlag;
  }

DateTime  JobDateTimeCreatedGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(strToDateTime("(0|0|0)"));

  return job->dateTimeCreated;
  }

char *JobEmailAddrGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->emailAddr;
  }

void *JobServerGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULL);

  return job->server;
  }

void *JobQueueGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULL);

  return job->queue;
  }

int JobRefCntGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(-1);

  return job->refCnt;
  }

char *JobStageinFilesGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->stageinFiles;
  }

char *JobStageoutFilesGet(job)
Job  *job;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return job->stageoutFiles;
  }

int JobIntResReqGet(job, name)
Job *job;
char    *name;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(-1);

  return(IntResValueGet(job->intResReq, name));
  }

Size JobSizeResReqGet(job, name)
Job *job;
char    *name;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(strToSize("-1b"));

  return(SizeResValueGet(job->sizeResReq, name));
  }

char *JobStringResReqGet(job, name)
Job *job;
char    *name;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return(StringResValueGet(job->stringResReq, name));
  }

int JobIntResUseGet(job, name)
Job *job;
char    *name;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(-1);

  return(IntResValueGet(job->intResUse, name));
  }

Size JobSizeResUseGet(job, name)
Job *job;
char    *name;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(strToSize("-1b"));

  return(SizeResValueGet(job->sizeResUse, name));
  }

char *JobStringResUseGet(job, name)
Job *job;
char    *name;
  {
  if (job == NOJOB || JobStateGet(job) == DELETED)
    return(NULLSTR);

  return(StringResValueGet(job->stringResUse, name));
  }

void JobIdPut(job, jobId)
Job    *job;
char   *jobId;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->jobId, jobId);
  varstrModPptr(job->jobId, job);
  }

void JobNamePut(job, jobName)
Job    *job;
char   *jobName;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->jobName, jobName);
  varstrModPptr(job->jobName, job);
  }

void JobOwnerNamePut(job, ownerName)
Job    *job;
char   *ownerName;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->ownerName, ownerName);
  varstrModPptr(job->ownerName, job);
  }

void JobEffectiveUserNamePut(job, euser)
Job    *job;
char   *euser;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->effectiveUserName, euser);
  varstrModPptr(job->effectiveUserName, job);
  }

void JobEffectiveGroupNamePut(job, groupName)
Job    *job;
char   *groupName;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->effectiveGroupName, groupName);
  varstrModPptr(job->effectiveGroupName, job);
  }

void JobStatePut(job, state)
Job    *job;
int    state;
  {
  assert(job != NOJOB);

  job->state = state;
  }

void JobPriorityPut(job, priority)
Job    *job;
int    priority;
  {
  assert(job != NOJOB);

  job->priority = priority;
  }

void JobRerunFlagPut(job, rerunFlag)
Job    *job;
int    rerunFlag;
  {
  assert(job != NOJOB);

  job->rerunFlag = rerunFlag;
  }

void JobInteractiveFlagPut(job, interactiveFlag)
Job    *job;
int    interactiveFlag;
  {
  assert(job != NOJOB);

  job->interactiveFlag = interactiveFlag;
  }

void JobDateTimeCreatedPut(job, cdate)
Job    *job;
DateTime  cdate;
  {
  assert(job != NOJOB);

  job->dateTimeCreated.d.m = cdate.d.m;
  job->dateTimeCreated.d.d = cdate.d.d;
  job->dateTimeCreated.d.y = cdate.d.y;

  job->dateTimeCreated.t.h = cdate.t.h;
  job->dateTimeCreated.t.m = cdate.t.m;
  job->dateTimeCreated.t.s = cdate.t.s;
  }

void JobEmailAddrPut(job, emailAddr)
Job    *job;
char   *emailAddr;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->emailAddr, emailAddr);
  varstrModPptr(job->emailAddr, job);
  }

void JobServerPut(job, server)
Job *job;
void *server;
  {
  assert(job != NOJOB);

  job->server = server;
  }

void JobQueuePut(job, queue)
Job *job;
void *queue;
  {
  assert(job != NOJOB);

  job->queue = queue;
  }

void JobRefCntPut(job, refCnt)
Job *job;
int refCnt;
  {
  assert(job != NOJOB);

  job->refCnt = refCnt;
  }

void JobStageinFilesPut(job, stagein)
Job *job;
char *stagein;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->stageinFiles, stagein);
  varstrModPptr(job->stageinFiles, job);
  }

void JobStageoutFilesPut(job, stageout)
Job *job;
char *stageout;
  {
  assert(job != NOJOB);

  dynamic_strcpy(&job->stageoutFiles, stageout);
  varstrModPptr(job->stageoutFiles, job);
  }

void JobIntResReqPut(job, name, value)
Job *job;
char    *name;
int     value;
  {

  struct   IntRes *m;

  assert(job != NOJOB && name != NULLSTR);

  m = IntResValuePut(job->intResReq, name, value, job);

  if (m != NULL)
    job->intResReq = m;
  }

void JobSizeResReqPut(job, name, value)
Job *job;
char    *name;
Size    value;
  {

  struct   SizeRes *m;

  assert(job != NOJOB && name != NULLSTR);

  m = SizeResValuePut(job->sizeResReq, name, value, job);

  if (m != NULL)
    job->sizeResReq = m;
  }

void JobStringResReqPut(job, name, value)
Job *job;
char    *name;
char    *value;
  {

  struct   StringRes *m;

  assert(job != NOJOB && name != NULLSTR);

  m = StringResValuePut(job->stringResReq, name, value, job);

  if (m != NULL)
    job->stringResReq = m;
  }

void JobIntResUsePut(job, name, value)
Job *job;
char    *name;
int     value;
  {

  struct   IntRes *m;

  assert(job != NOJOB && name != NULLSTR);

  m = IntResValuePut(job->intResUse, name, value, job);

  if (m != NULL)
    job->intResUse = m;
  }

void JobSizeResUsePut(job, name, value)
Job *job;
char    *name;
Size    value;
  {

  struct   SizeRes *m;

  assert(job != NOJOB && name != NULLSTR);

  m = SizeResValuePut(job->sizeResUse, name, value, job);

  if (m != NULL)
    job->sizeResUse = m;
  }

void JobStringResUsePut(job, name, value)
Job *job;
char    *name;
char    *value;
  {

  struct   StringRes *m;

  assert(job != NOJOB && name != NULLSTR);

  m = StringResValuePut(job->stringResUse, name, value, job);

  if (m != NULL)
    job->stringResUse = m;
  }

void  JobInit(job)
Job   *job;
  {

  assert(job != NOJOB);

  job->jobId = NULLSTR;
  job->jobName = NULLSTR;
  job->ownerName = NULLSTR;
  job->effectiveUserName = NULLSTR;
  job->effectiveGroupName = NULLSTR;
  JobStatePut(job, -1);
  JobPriorityPut(job, 0);
  JobRerunFlagPut(job, -1);
  JobInteractiveFlagPut(job, FALSE);
  JobDateTimeCreatedPut(job, strToDateTime("(0|0|0@-1:-1:-1)"));
  job->emailAddr = NULLSTR;
  JobServerPut(job, NULL);
  JobRefCntPut(job, 0);
  job->stageinFiles = NULLSTR;
  job->stageoutFiles = NULLSTR;
  job->intResReq = NULL;
  job->sizeResReq = NULL;
  job->stringResReq = NULL;

  job->intResUse = NULL;
  job->sizeResUse = NULL;
  job->stringResUse = NULL;
  }

void JobPrint(job)
Job   *job;
  {

  int  state, flag;

  if (job == NOJOB || JobStateGet(job) == DELETED)
    return;  /* ignore */

  (void)printf("\t\tJob Id: %s\n",
               JobIdGet(job) ? JobIdGet(job) : "null");

  (void)printf("\t\t\tJob Name = %s\n",
               JobNameGet(job) ? JobNameGet(job) : "null");

  (void)printf("\t\t\tJob Owner = %s\n",
               JobOwnerNameGet(job) ? JobOwnerNameGet(job) : "null");

  (void)printf("\t\t\tEffective User = %s\n",
               JobEffectiveUserNameGet(job) ? JobEffectiveUserNameGet(job) : "null");

  (void)printf("\t\t\tEffective Group = %s\n",
               JobEffectiveGroupNameGet(job) ? JobEffectiveGroupNameGet(job) : "null");

  state = JobStateGet(job);

  (void)printf("\t\t\tState = %d ", state);

  switch (state)
    {

    case TRANSIT:
      (void)printf("(TRANSIT)");
      break;

    case QUEUED:
      (void)printf("(QUEUED)");
      break;

    case HELD:
      (void)printf("(HELD)");
      break;

    case WAITING:
      (void)printf("(WAITING)");
      break;

    case RUNNING:
      (void)printf("(RUNNING)");
      break;

    case EXITING:
      (void)printf("(EXITING)");
      break;

    case DELETED:
      (void)printf("(DELETED)");
      break;

    default:
      (void)printf("%d", state);
    }

  (void)printf("\n");
  (void)printf("\t\t\tPriority = %d\n", JobPriorityGet(job));
  flag = JobRerunFlagGet(job);
  (void)printf("\t\t\tRerun Flag =  %d ", flag);

  switch (flag)
    {

    case FALSE:
      (void)printf("(False)");
      break;

    case TRUE:
      (void)printf("(True)");
      break;

    default:
      (void)printf("%d", flag);
    }

  (void)printf("\n");

  flag = JobInteractiveFlagGet(job);
  (void)printf("\t\t\tInteractive Flag = %d ", flag);

  switch (flag)
    {

    case FALSE:
      (void)printf("(False)");
      break;

    case TRUE:
      (void)printf("(True)");
      break;

    default:
      (void)printf("%d", flag);
    }

  (void)printf("\n");

  (void)printf("\t\t\tDate/Time Created = ");
  datetimePrint(JobDateTimeCreatedGet(job));
  printf("\n");

  (void)printf("\t\t\tEmail Address to Notify = %s\n",
               JobEmailAddrGet(job) ? JobEmailAddrGet(job) : "null");
  (void)printf("\t\t\tStagein Files = %s\n",
               JobStageinFilesGet(job) ? JobStageinFilesGet(job) : "null");
  (void)printf("\t\t\tStageout Files = %s\n",
               JobStageoutFilesGet(job) ? JobStageoutFilesGet(job) : "null");

  IntResListPrint(job->intResReq, "\t\t\tResources_required");
  SizeResListPrint(job->sizeResReq, "\t\t\tResources_required");
  StringResListPrint(job->stringResReq, "\t\t\tResources_required");

  IntResListPrint(job->intResUse, "\t\t\tResources_used");
  SizeResListPrint(job->sizeResUse, "\t\t\tResources_used");
  StringResListPrint(job->stringResUse, "\t\t\tResources_used");

  (void)printf("\t\t\t# of Queues Referencing this job = %d\n",
               JobRefCntGet(job));

  }

/* Frees up malloc-ed elements of a Job */
void JobFree(job)
Job *job;
  {

  assert(job != NOJOB);

  /* deallocate all dynamic strings */
  varstrFreeByPptr(job);
  /* deallocate all storages related to Job */
  mallocTableFreeByPptr(job);
  /* force job to have consistent values for dynamic stuff in case of */
  /* of reuse */
  job->jobId = NULLSTR;
  job->jobName = NULLSTR;
  job->ownerName = NULLSTR;
  job->effectiveUserName = NULLSTR;
  job->effectiveGroupName = NULLSTR;
  job->emailAddr = NULLSTR;
  JobServerPut(job, NULL);
  job->stageinFiles = NULLSTR;
  job->stageoutFiles = NULLSTR;
  }

/* Set of Jobs abstraction */
void SetJobInit(sjob)
SetJob *sjob;
  {

  struct SetJobElement *jobptr;

  jobptr = (struct SetJobElement *)malloc(sizeof(struct SetJobElement));
  mallocTableAdd(jobptr, sjob, 0);

#ifdef DEBUG
  printf("Added NULL job %x to SetJob %x\n", jobptr, sjob);
#endif

  jobptr->job = NOJOB; /* end of list record */
  jobptr->first = jobptr;
  jobptr->nextptr = EMPTYSETJOB;
  sjob->head = jobptr;
  sjob->tail = jobptr;
  }

void SetJobAdd(sjob, job)
SetJob *sjob;
Job   *job;
  {


  struct SetJobElement *jobptr;

  assert(job != NOJOB && sjob->head != EMPTYSETJOB && sjob->tail != EMPTYSETJOB);

  jobptr = (struct SetJobElement *)malloc(sizeof(struct SetJobElement));
  assert(jobptr != EMPTYSETJOB);
  mallocTableAdd(jobptr, sjob, 0);
  jobptr->job = job;

#ifdef DEBUG
  printf("Added job %x to SetJob %x\n", jobptr, sjob);
  JobPrint(jobptr->job);
#endif

  JobRefCntPut(job, JobRefCntGet(job) + 1);

  if (sjob->head->job == NOJOB && sjob->tail->job == NOJOB)    /* first */
    {
    sjob->head->first = jobptr;
    jobptr->first = jobptr;

    jobptr->nextptr = sjob->head;

    sjob->head = jobptr;
    sjob->tail = jobptr;
    }
  else
    {
    jobptr->first = sjob->head;
    jobptr->nextptr = sjob->tail->nextptr;

    sjob->tail->nextptr = jobptr;
    sjob->tail = jobptr;
    }

  }

void
nextJobPtr(struct SetJobElement **sjeptr)
  {
  *sjeptr = (*sjeptr)->nextptr;

  while ((*sjeptr)->job != NULL && JobStateGet((*sjeptr)->job) == DELETED)
    *sjeptr = (*sjeptr)->nextptr;
  }

void
firstJobPtr(struct SetJobElement **sjeptr, struct SetJobElement *first)
  {
  *sjeptr = first;

  while ((*sjeptr)->job != NULL && JobStateGet((*sjeptr)->job) == DELETED)
    *sjeptr = (*sjeptr)->nextptr;
  }

static void SetJobUpdateFirst(sjob, first)
SetJob   *sjob;

struct SetJobElement  *first;
  {

  struct SetJobElement *j;

  for (j = sjob->head; j; j = j->nextptr)
    {
    j->first = first;
    }
  }

void SetJobRemove(sjob, job)
SetJob  *sjob;
Job  *job;
  {

  struct SetJobElement *j, *prev, *tmp;

  assert(sjob->head != EMPTYSETJOB && sjob->tail != EMPTYSETJOB);

  prev = EMPTYSETJOB;
  tmp  = EMPTYSETJOB;

  for (j = sjob->head; j->job; j = tmp)
    {
    tmp = j->nextptr;

    if (j->job == job)
      {
      if (prev != EMPTYSETJOB)
        {
        prev->nextptr = j->nextptr;

        if (sjob->tail == j)     /* lost our tail */
          {
          sjob->tail = prev;  /* reconnect tail */
          }
        }
      else   /* lost our head */
        {
        sjob->head = j->nextptr; /* reconnect head */

        if (sjob->tail == j)     /* lost our tail */
          {
          sjob->tail = j->nextptr; /* reconnect tail */
          }

        SetJobUpdateFirst(sjob, sjob->head);
        }

      JobRefCntPut(j->job, JobRefCntGet(j->job) - 1);

      if (JobRefCntGet(j->job) == 0)
        {

        /* free up all dynamic strings and cNodeReqs of job */
        JobFree(j->job);
        /* free up the job structure itself */
        mallocTableFree(j->job);
        }

      /* free up the special JobElement structure */
      mallocTableFree(j);

      return;  /* only free one at a time */
      }

    prev = j;
    }
  }

/* Free up an entire set of jobs */
void SetJobFree(sjob)
SetJob *sjob;
  {

  struct SetJobElement *j, *tmp;


  tmp = EMPTYSETJOB;

  for (j = sjob->head; j->job; j = tmp)
    {
    tmp = j->nextptr;

    JobRefCntPut(j->job, JobRefCntGet(j->job) - 1);

    if (JobRefCntGet(j->job) == 0)
      {
      /* free all dynamic strings, cNode req attached to job */
      JobFree(j->job);
      /* Free up the job structure itself */
      mallocTableFree(j->job);
      }
    }

  /* Finally, free up the special JobElement structures */
  mallocTableFreeByPptr(sjob);
  }

static int
JobPartition(struct JobSortArgs *A, int p, int r)
  {

  Job         *jtemptr;
  int             i, j;
  int   k;

  assert(A->keytype == INTKEY || A->keytype == STRINGKEY || \
         A->keytype == DATETIMEKEY || A->keytype == SIZEKEY || \
         A->keytype == FLOATKEY);

  i = p - 1;
  j = r + 1;

  while (TRUE)
    {

    switch (A->keytype)
      {

      case INTKEY:

        do
          {
          j--;
          }
        while ((A->order == ASC  && A->key.ifunc(A->array[j]->job) > \
                A->key.ifunc(A->array[p]->job)) ||  \
               (A->order == DESC && A->key.ifunc(A->array[j]->job) < \
                A->key.ifunc(A->array[p]->job)));

        do
          {
          i++;

          if (i >= dynamicArraySize(A->array))
            {
            printf("i: %d is out of bounds!\n", i);
            }
          }
        while ((A->order == ASC  && A->key.ifunc(A->array[i]->job) < \
                A->key.ifunc(A->array[p]->job)) ||  \
               (A->order == DESC && A->key.ifunc(A->array[i]->job) > \
                A->key.ifunc(A->array[p]->job)));

        break;

      case FLOATKEY:
        do
          {
          j--;
          }
        while ((A->order == ASC  && A->key.ffunc(A->array[j]) > \
                A->key.ffunc(A->array[p])) ||  \
               (A->order == DESC && A->key.ffunc(A->array[j]) < \
                A->key.ffunc(A->array[p])));

        do
          {
          i++;
          }
        while ((A->order == ASC  && A->key.ffunc(A->array[i]) < \
                A->key.ffunc(A->array[p])) ||  \
               (A->order == DESC && A->key.ffunc(A->array[i]) > \
                A->key.ffunc(A->array[p])));

        break;

      case STRINGKEY:
        do
          {
          j--;
          }
        while ((A->order == ASC && \
                STRCMP(A->key.sfunc(A->array[j]->job),
                       > , A->key.sfunc(A->array[p]->job))) || \
               (A->order == DESC && \
                STRCMP(A->key.sfunc(A->array[j]->job),
                       < , A->key.sfunc(A->array[p]->job))));

        do
          {
          i++;
          }
        while ((A->order == ASC && \
                STRCMP(A->key.sfunc(A->array[i]->job),
                       < , A->key.sfunc(A->array[p]->job))) || \
               (A->order == DESC && \
                STRCMP(A->key.sfunc(A->array[i]->job),
                       > , A->key.sfunc(A->array[p]->job))));

        break;

      case DATETIMEKEY:
        do
          {
          j--;
          }
        while ((A->order == ASC && \
                DATETIMECMP(A->key.dfunc(A->array[j]->job),
                            > , A->key.dfunc(A->array[p]->job))) || \
               (A->order == DESC && \
                DATETIMECMP(A->key.dfunc(A->array[j]->job),
                            < , A->key.dfunc(A->array[p]->job))));

        do
          {
          i++;
          }
        while ((A->order == ASC && \
                DATETIMECMP(A->key.dfunc(A->array[i]->job),
                            < , A->key.dfunc(A->array[p]->job))) || \
               (A->order == DESC && \
                DATETIMECMP(A->key.dfunc(A->array[i]->job),
                            > , A->key.dfunc(A->array[p]->job))));

        break;

      case SIZEKEY:
        do
          {
          j--;
          }
        while ((A->order == ASC && \
                SIZECMP(A->key.szfunc(A->array[j]->job),
                        > , A->key.szfunc(A->array[p]->job))) || \
               (A->order == DESC && \
                SIZECMP(A->key.szfunc(A->array[j]->job),
                        < , A->key.szfunc(A->array[p]->job))));

        do
          {
          i++;
          }
        while ((A->order == ASC && \
                SIZECMP(A->key.szfunc(A->array[i]->job),
                        < , A->key.szfunc(A->array[p]->job))) || \
               (A->order == DESC && \
                SIZECMP(A->key.szfunc(A->array[i]->job),
                        > , A->key.szfunc(A->array[p]->job))));

        break;
      }

    if (i < j)
      {
      jtemptr = A->array[j]->job;
      A->array[j]->job = A->array[i]->job;
      A->array[i]->job = jtemptr;

      }
    else
      {
      return(j);
      }

    } /* while */
  }

static void
JobQuickSort(struct JobSortArgs *A, int p, int r)
  {
  int     q;

  if (p < r)
    {

    q = JobPartition(A, p, r);
    JobQuickSort(A, p, q);
    JobQuickSort(A, q + 1, r);
    }
  }

int
SetJobSortInt(struct SetJobElement *sje, int (*key)(void), int order)
  {

  struct SetJobElement **sje_ptrs = NULL;
  int    beforecnt, aftercnt;

  struct JobSortArgs A;
  Job    *job;

  if (order != ASC && order != DESC)
    {
    fprintf(stderr,
            "SetJobSortInt: order != ASC and order !=DESC\n");
    return(FAIL);
    }

  beforecnt = 0;

  for (sje = sje->first; (job = sje->job); sje = sje->nextptr)
    {
    sje_ptrs = (struct SetJobElement **)extendDynamicArray(sje_ptrs,
               beforecnt + 1, sizeof(struct SetJobElement *));

    aftercnt = dynamicArraySize(sje_ptrs);

    if (beforecnt == aftercnt)
      {
      fprintf(stderr,
              "SetJobSortInt: Unable to realloc sje_ptrs");

      if (aftercnt > 0)
        {
        freeDynamicArray(sje_ptrs);
        }

      return(FAIL);
      }

    sje_ptrs[beforecnt] = sje;

    beforecnt++;
    }

  A.array = sje_ptrs;

  A.key.ifunc = key;
  A.keytype = INTKEY;
  A.order   = order;

  JobQuickSort(&A, 0, beforecnt - 1);
  freeDynamicArray(sje_ptrs);
  return(SUCCESS);
  }

int
SetJobSortStr(struct SetJobElement *sje, char *(*key)(void), int order)
  {

  struct SetJobElement **sje_ptrs = NULL;
  int    beforecnt, aftercnt;

  struct JobSortArgs A;
  Job    *job;

  if (order != ASC && order != DESC)
    {
    fprintf(stderr,
            "SetJobSortStr: order != ASC and order !=DESC\n");
    return(FAIL);
    }

  beforecnt = 0;

  for (sje = sje->first; (job = sje->job); sje = sje->nextptr)
    {
    sje_ptrs = (struct SetJobElement **)extendDynamicArray(sje_ptrs,
               beforecnt + 1, sizeof(struct SetJobElement *));

    aftercnt = dynamicArraySize(sje_ptrs);

    if (beforecnt == aftercnt)
      {
      fprintf(stderr, "Unable to realloc sje_ptrs");

      if (aftercnt > 0)
        {
        freeDynamicArray(sje_ptrs);
        }

      return(FAIL);
      }

    sje_ptrs[beforecnt] = sje;

    beforecnt++;
    }

  A.array = sje_ptrs;

  A.key.sfunc = key;
  A.keytype = STRINGKEY;
  A.order   = order;

  JobQuickSort(&A, 0, beforecnt - 1);
  freeDynamicArray(sje_ptrs);
  return(SUCCESS);
  }

int SetJobSortDateTime(sje, key, order)

struct SetJobElement *sje;
DateTime(*key)();
int       order;
  {

  struct SetJobElement **sje_ptrs = NULL;
  int    beforecnt, aftercnt;

  struct JobSortArgs A;
  Job    *job;

  if (order != ASC && order != DESC)
    {
    fprintf(stderr,
            "SetJobSortDateTime: order != ASC and order !=DESC\n");
    return(FAIL);
    }

  beforecnt = 0;

  for (sje = sje->first; (job = sje->job); sje = sje->nextptr)
    {
    sje_ptrs = (struct SetJobElement **)extendDynamicArray(sje_ptrs,
               beforecnt + 1, sizeof(struct SetJobElement *));

    aftercnt = dynamicArraySize(sje_ptrs);

    if (beforecnt == aftercnt)
      {
      fprintf(stderr, "Unable to realloc sje_ptrs");

      if (aftercnt > 0)
        {
        freeDynamicArray(sje_ptrs);
        }

      return(FAIL);
      }

    sje_ptrs[beforecnt] = sje;

    beforecnt++;
    }

  A.array = sje_ptrs;

  A.key.dfunc = key;
  A.keytype = DATETIMEKEY;
  A.order   = order;

  JobQuickSort(&A, 0, beforecnt - 1);
  freeDynamicArray(sje_ptrs);
  return(SUCCESS);
  }

int SetJobSortSize(sje, key, order)

struct SetJobElement *sje;
Size(*key)();
int       order;
  {

  struct SetJobElement **sje_ptrs = NULL;
  int    beforecnt, aftercnt;

  struct JobSortArgs A;
  Job    *job;

  if (order != ASC && order != DESC)
    {
    fprintf(stderr,
            "SetJobSortSize: order != ASC and order !=DESC\n");
    return(FAIL);
    }

  beforecnt = 0;

  for (sje = sje->first; (job = sje->job); sje = sje->nextptr)
    {
    sje_ptrs = (struct SetJobElement **)extendDynamicArray(sje_ptrs,
               beforecnt + 1, sizeof(struct SetJobElement *));

    aftercnt = dynamicArraySize(sje_ptrs);

    if (beforecnt == aftercnt)
      {
      fprintf(stderr, "Unable to realloc sje_ptrs");

      if (aftercnt > 0)
        {
        freeDynamicArray(sje_ptrs);
        }

      return(FAIL);
      }

    sje_ptrs[beforecnt] = sje;

    beforecnt++;
    }

  A.array = sje_ptrs;

  A.key.szfunc = key;
  A.keytype = SIZEKEY;
  A.order   = order;

  JobQuickSort(&A, 0, beforecnt - 1);
  freeDynamicArray(sje_ptrs);
  return(SUCCESS);
  }

int
SetJobSortFloat(struct SetJobElement *sje, double(*key)(void), int order)
  {

  struct SetJobElement **sje_ptrs = NULL;
  int    beforecnt, aftercnt;

  struct JobSortArgs A;
  Job    *job;

  if (order != ASC && order != DESC)
    {
    fprintf(stderr,
            "SetJobSortSize: order != ASC and order !=DESC\n");
    return(FAIL);
    }

  beforecnt = 0;

  for (sje = sje->first; (job = sje->job); sje = sje->nextptr)
    {
    sje_ptrs = (struct SetJobElement **)extendDynamicArray(sje_ptrs,
               beforecnt + 1, sizeof(struct SetJobElement *));

    aftercnt = dynamicArraySize(sje_ptrs);

    if (beforecnt == aftercnt)
      {
      fprintf(stderr, "Unable to realloc sje_ptrs");

      if (aftercnt > 0)
        {
        freeDynamicArray(sje_ptrs);
        }

      return(FAIL);
      }

    sje_ptrs[beforecnt] = sje;

    beforecnt++;
    }

  A.array = sje_ptrs;

  A.key.ffunc = key;
  A.keytype = FLOATKEY;
  A.order   = order;

  JobQuickSort(&A, 0, beforecnt - 1);
  freeDynamicArray(sje_ptrs);
  return(SUCCESS);
  }

void
SetJobPrint(struct SetJobElement *sje)
  {

  Job *job;
  int i;

  i = 1;

  for (firstJobPtr(&sje, sje->first); (job = sje->job); nextJobPtr(&sje))
    {
    printf("\n\t\tJob %3d: ===========================================\n", i);
    JobPrint(job);
    i++;
    }
  }

/* is job in 'sjob'? 1 = true, 0 = false */
int inSetJob(job, sje)
Job *job;

struct SetJobElement *sje;
  {
  Job *j;

  /*  every element of sje has a ptr to first */

  if (sje == EMPTYSETJOB)
    return(0);

  for (firstJobPtr(&sje, sje->first); (j = sje->job); nextJobPtr(&sje))
    {
    if (job == j)
      return(1);
    }

  return(0);
  }

int
strToJobState(char *val)
  {
  if (val == NULLSTR)
    {
    return(-1);
    }

  if (STRCMP(val, == , "Q"))
    return(QUEUED);
  else if (STRCMP(val, == , "R"))
    return(RUNNING);
  else if (STRCMP(val, == , "T"))
    return(TRANSIT);
  else if (STRCMP(val, == , "H"))
    return(HELD);
  else if (STRCMP(val, == , "E"))
    return(EXITING);
  else if (STRCMP(val, == , "W"))
    return(WAITING);
  else if (STRCMP(val, == , "D"))
    return(DELETED);
  else
    return(-1);
  }
