/*
  int last_error = 0;
 *
 * qstat - (PBS) show stats of batch jobs, queues, or servers
 *
 * Authors:
 *      Terry Heidelberg
 *      Livermore Computing
 *
 *      Bruce Kelly
 *      National Energy Research Supercomputer Center
 *
 *      Lawrence Livermore National Laboratory
 *      University of California
 */

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

#include <pwd.h>
#include <limits.h>
#include <string.h>
#include <vector>

#if TCL_QSTAT
#include <sys/stat.h>
#include <tcl.h>
#if TCLX
#include <tclExtend.h>
#endif
#if ((TCL_MAJOR_VERSION < 8) || ((TCL_MAJOR_VERSION == 8) && \
    (TCL_MINOR_VERSION < 6))) && (!defined(Tcl_GetErrorLine))
#define Tcl_GetErrorLine(interp) (interp->errorLine)
#endif
#endif

#include "cmds.h"
#include "mcom.h"
#include "utils.h"
#include "libcmds.h" /* TShowAbout_exit */
#include "net_cache.h"
#include "utils.h"
#include "allocation.hpp"
#include "lib_ifl.h"

using namespace std;

bool    do_not_display_complete = false;

static void states(

  char *string, /* I */
  char *queued,      /* O */
  char *running,      /* O */
  char *held,      /* O */
  char *waiting,      /* O */
  char *transit,      /* O */
  char *exiting,      /* O */
  char *complete,      /* O */
  int   len);    /* I */


#if !defined(PBS_NO_POSIX_VIOLATION)
/* defines for alternative display formats */
#define ALT_DISPLAY_a 1 /* -a option - show all jobs */
#define ALT_DISPLAY_i 2 /* -i option - show not running */
#define ALT_DISPLAY_r 4 /* -r option - show only running */
#define ALT_DISPLAY_u 8 /* -u option - list user's jobs */
#define ALT_DISPLAY_n 0x10 /* -n option - add node list */
#define ALT_DISPLAY_s 0x20 /* -s option - add scheduler comment */
#define ALT_DISPLAY_R 0x40 /* -R option - add SRFS info - unsupported */

#define ALT_DISPLAY_q 0x80 /* -q option - alt queue display */

#define ALT_DISPLAY_Mb 0x100 /* show sizes in MB */
#define ALT_DISPLAY_Mw 0x200 /* -M option - show sizes in MW */
#define ALT_DISPLAY_G 0x400 /* -G option - show sizes in GB */
#define ALT_DISPLAY_o   0x800   /* -1 option - add node list on same line */
#endif /* not PBS_NO_POSIX_VIOLATION */

#define MAX_RETRIES  3

/* globals */
extern char         *optarg;

enum qstat_mode { JOBS, QUEUES, SERVERS } mode;

bool  DisplayXML = false;
#define maxlinesize 65536
int   linesize = 77;

int                  alt_opt;
bool                 f_opt;
int                  B_opt;
int                  Q_opt;
int                  t_opt;
int                  E_opt;

std::string          ExtendOpt;
bool                 condensed = false;
struct attropl      *p_atropl = 0;
struct attrl        *attrib = NULL;
char                 user[MAXPATHLEN];
#if (TCL_QSTAT == 0)
  char                *pc;
#else
  char                 option[3];
#endif

int                  exec_only = 0;
const char          *conflict_msg = "qstat: conflicting options.\n";

static const char *summarize_arrays_extend_opt = "summarize_arrays";

static bool print_header = true;
/* END globals */


#define DEFTASKSIZE 6

int   tasksize = DEFTASKSIZE;
int                  alias_opt = FALSE;

string get_err_msg(
  int   any_failed,
  const char *mode,
  int   connect,
  char *id)
  {
  char* errmsg = pbs_geterrmsg(connect);
  string msg;
  char any_failed_str[16];
  if (errmsg != NULL)
    {
    msg = string("qstat: ") + string(errmsg) + " ";
    free(errmsg);
    }
  else
    {
    sprintf(any_failed_str, "%d", any_failed);
    msg = string("qstat: Error (") + string(any_failed_str) + string(" - ")
      + string(pbs_strerror(any_failed)) + string(") ");
    if (mode)
      {
      msg.append(string("getting status of ") + string(mode) + string(" "));
      }
    }
  if (id != NULL)
    {
    msg.append(id);
    }
  return msg;
  }

bool isjobid(

  const char *string)

  {
  int i;
  int result;

  i = strspn(string, " "); /* locate first non-blank */

  if (isdigit(string[i]))
    result = true;  /* job_id */
  else if (isalpha(string[i]))
    result = false; /* not a job_id */
  else
    result = false;  /* who knows - probably a syntax error */

  return(result);
  }  /* END isjobid() */





bool istrue(

  const char *string)  /* I */

  {
  if (!strcasecmp(string, "TRUE"))
    {
    return(true);
    }

  if (!strcmp(string, "1"))
    {
    return(true);
    }

  return(false);
  }  /* END istrue() */


int time_to_string(

  char *time_string,
  int   time_to_convert)

  {
  int seconds = 0;
  int total_minutes = 0;
  int minutes = 0;
  int hours = 0;
  int time_in_seconds = 0;

  time_in_seconds = time_to_convert;
  /* Get the number of seconds in the job */
  seconds = time_in_seconds % 60;

  /* Calculate the total minutes and then get the minutes less than an hour */
  total_minutes = time_in_seconds / 60;

  /* Calculate hours */
  hours = total_minutes / 60;

  /* calculate minutes */
  minutes = total_minutes % 60;

  sprintf(time_string, "%.2d:%.2d:%.2d", hours, minutes, seconds);

  return(PBSE_NONE);
  }

int timestring_to_int(

  const char *timestring,
  int        *req_walltime)

  {
  char *ptr_string;
  char *ptr;
  char *number;
  int   hours;
  int   minutes;
  int   seconds;

  ptr_string = strdup(timestring);
  if (ptr_string == NULL)
    return(PBSE_MEM_MALLOC);

  number = ptr_string;
  ptr = strstr(ptr_string, ":");
  if (ptr == NULL)
    {
    free(ptr_string);
    return (PBSE_BAD_PARAMETER);
    }

  *ptr = 0;
  ptr++;
  hours = atoi(number);
  hours = hours * 3600;
  number = ptr;

  ptr = strstr(ptr, ":");
  if (ptr == NULL)
    {
    free(ptr_string);
    return (PBSE_BAD_PARAMETER);
    }

  *ptr = 0;
  ptr++;
  minutes = atoi(number);
  minutes = minutes * 60;
  number = ptr;

  seconds = atoi(number);

  *req_walltime = hours + minutes + seconds;

  free(ptr_string);
  return(PBSE_NONE);
  } /* END timestring_to_int() */



static void states(

  char *string, /* I */
  char *queued,      /* O */
  char *running,      /* O */
  char *held,      /* O */
  char *waiting,      /* O */
  char *transit,      /* O */
  char *exiting,      /* O */
  char *complete,      /* O */
  int   len)    /* I */

  {
  char *c, *d, *f, *s, l;

  /* FORMAT:  Queued:X Running:Y... */

  c = string;

  while (isspace(*c) && *c != '\0')
    c++;

  while (*c != '\0')
    {
    s = c;

    while ((*c != ':')&&(*c != '\0'))
      c++;

    if(*c != '\0')
      {
      *c = '\0';
      c++;
      }

    d = NULL;

    if (strcmp(s, "Queued") == 0)
      d = queued;
    else if (strcmp(s, "Running") == 0)
      d = running;
    else if (strcmp(s, "Held")    == 0)
      d = held;
    else if (strcmp(s, "Waiting") == 0)
      d = waiting;
    else if (strcmp(s, "Transit") == 0)
      d = transit;
    else if (strcmp(s, "Exiting") == 0)
      d = exiting;
    else if (strcmp(s, "Complete") == 0)
      d = complete;


    if (d != NULL)
      {
      s = c;

      while ((*c != ' ') && (*c != '\0'))
        c++;

      l = *c;

      *c = '\0';

      if (strlen(s) > (size_t)len)
        {
        f = s + len;
        *f = '\0';
        }

      strcpy(d, s);

      if (l != '\0')
        c++;
      }
    }

  return;
  }  /* END states() */





/*
 * print an attribute value string, formating to break a comma if possible
 */

void prt_attr(

  const char *n,  /* I name */
  const char *r,  /* I resource (optional) */
  const char *v)  /* I value */

  {
  char *c;
  const char *comma = ",";
  int   first = 1;
  int   l;
  int   start;
  char *work_val = strdup(v);

  start = strlen(n) + 7; /* 4 spaces + ' = ' is 7 */

  printf("    %s", n);

  if (r != NULL)
    {
    start += strlen(r) + 1;

    printf(".%s", r);
    }

  printf(" = ");

  c = strtok(work_val, comma);

  while (c != NULL)
    {
    if ((l = strlen(c)) + start < linesize)
      {
      printf("%s",
             c);

      start += l;
      }
    else
      {
      if (!first)
        {
        printf("\n\t");

        start = 9;
        }

      while (*c)
        {
        putchar(*c++);

        if (++start > linesize)
          {
          start = 8;

          printf("\n\t");
          }
        }
      }

    if ((c = strtok(NULL, comma)) != NULL)
      {
      first = 0;

      putchar(',');
      }
    }

  free(work_val);

  return;
  }  /* END prt_attr() */





/*
 * locate an attribute (attrl) by name (and resource) and return value
 * returns null if not found
 */

static char *findattrl(

  struct attrl *pattrl,
  char        *name,
  char         *resc)

  {
  while (pattrl != NULL)
    {
    if (!strcmp(name, pattrl->name))
      {
      if (resc != NULL)
        {
        if (!strcmp(resc, pattrl->resource))
          {
          return(pattrl->value);
          }
        }
      else
        {
        return(pattrl->value);
        }
      }

    pattrl = pattrl->next;
    }

  return(NULL);
  }

#ifndef PBS_MINNAMELEN
#define PBS_MINNAMELEN  16 /* min size for printf job jobs, queues, and servers */
#endif /* PBS_MINNAMELEN */

#ifndef PBS_NAMELEN
#define PBS_NAMELEN   16  /* printf of jobs, queues, and servers */
#endif  /* PBS_NAMELEN */

#define PBS_JOB_ID_LEN 22

#define OWNERL  15  /* printf of jobs */
#define TIMEUL  8   /* printf of jobs */
#define STATEL  1   /* printf of jobs */
#define LOCL    15  /* printf of jobs */
#define SIZEL 6   /* length of "SIZE" fields in printf */




/*
 * Format and display string of assigned nodes, (1) strip off domain name
 * if present and (2) break line at '+' sign.
 */

static void prt_nodes(

  const char *nodes)   /* I */

  {
  int   i;
  const char *stp;

  char  linebuf[65536];

  if ((nodes == NULL) || (*nodes == '\0'))
    {
    /* FAILURE - node is invalid */

    return;
    }

  i = 0;

  stp = nodes;

  while (*nodes != '\0')
    {
    if ((*stp == '.') || (*stp == '+') || (*stp == '\0'))
      {
      /* does node fit into line? */

      if (i + stp - nodes < linesize)
        {
        while (nodes < stp)
          linebuf[i++] = *nodes++;
        }
      else
        {
        /* flush line and start next */

        linebuf[i] = '\0';

        printf("   %s\n",
               linebuf);

        i = 0;

        while (nodes < stp)
          linebuf[i++] = *nodes++;
        }

      /* strip off domain name to keep string short */

      while ((*stp != '+') && (*stp != '\0'))
        stp++;

      nodes = stp++;
      }
    else
      {
      stp++;
      }
    }

  if (i != 0)
    {
    linebuf[i] = '\0';

    printf("   %s\n",
           linebuf);
    }

  return;
  }  /* END prt_nodes() */





/*
 * convert size from suffix string (nnnn[ kmgt][ bw]) to string of
 * k[bw] for neither -M or -G
 * mw    for   -M
 * gb    for   -G
 */

static char *cnv_size(

  char *value,
  int   opt)

  {
  static int sift_factor[3][5] =
    {
      { -20, -10, 0, 10, 20 }, /* mb conversion */
    { -23, -13, -3, 7, 17 }, /* mw conversion */
    { -30, -20, -10, 0, 10 }
    }; /* gb conversion */

  int in;
  int out;
  int sft;
  unsigned long nval;
  char *pc;
  const char *suffix;
  static char outbuf[25];

  if (opt & ALT_DISPLAY_Mb)
    {
    out = 0;

    suffix = "mb";
    }
  else if (opt & ALT_DISPLAY_Mw)
    {
    out = 1;

    suffix = "mw";
    }
  else if (opt & ALT_DISPLAY_G)
    {
    out = 2;

    suffix = "gb";
    }
  else
    {
    return (value);  /* return unmodified */
    }

  nval = strtol(value, &pc, 10);

  if (*pc == 'k')
    in = 1;
  else if (*pc == 'm')
    in = 2;
  else if (*pc == 'g')
    in = 3;
  else if (*pc == 't')
    in = 4;
  else
    in = 0;

  sft = sift_factor[out][in];

  if ((*pc == 'w') || (*(pc + 1) == 'w'))
    nval = nval << 3; /* convert to bytes */

  if (sft < 0)
    {
    nval = nval + ((1 << -sft) - 1);  /* round up (ceiling) */
    nval = nval >> -sft;
    }
  else
    {
    nval = nval << sft;
    }

  sprintf(outbuf, "%lu%s",

          nval,
          suffix);

  return(outbuf);
  }



int read_int_token(
  const char **ptr) /* (M) a pointer to the beginning of a token */
  {
  long cnt;
  char *tmp;
  /* Try to decode a decimal integer */
  cnt = strtol(*ptr, &tmp, 10);
  if (tmp == *ptr                                   /* no digit found */
    || (*tmp != ':' && *tmp != '+' && *tmp != '\0') /* the token isn't a decimal integer number */
    || cnt >= (long)INT_MAX || cnt <= 0)            /* huge or negative value */
    {
    cnt = 1; /* use default */
    }
  *ptr = strpbrk(tmp, "+:"); /* move to the next token */

  return (int)cnt;
  }



int read_int_prop(
  const char **prop,        /* (M) a pointer to a node property specification */
  const char *prefix) /* (I) property prefix ("<prop_name>=") */
  {
  size_t len = strlen(prefix);
  if (strncmp(*prop, prefix, len) == 0) /* equal */
    {
    *prop += len;
    return read_int_token(prop);
    }
  *prop = strpbrk(*prop, "+:"); /* move to the next token */
  return 0;
  }



int read_node_spec(
  const char **nodespec) /* (M) a pointer to the beginning of a node specification */
  {
  int nodes;
  int procs = 0;
  long long res;
  nodes = read_int_token(nodespec);
  while (*nodespec && **nodespec == ':' && procs == 0)
    {
    ++*nodespec;
    procs = read_int_prop(nodespec, "ppn=");
    }
  if (procs == 0)
    {
    procs = 1;
    }
  if (*nodespec)
    {
    *nodespec = strchr(*nodespec, '+');
    }
  res = (long long) nodes * procs;
  if (res > INT_MAX)
    {
    return 1;
    }
  return (int)res;
  }



int get_tasks_from_nodes_resc(const char * nodes)
  {
  int ret = 0;
  while (nodes && *nodes)
    {
    int sum;
    sum = read_node_spec(&nodes);
    ret += sum;
    if (ret < sum)
      {
      return 0;
      }
    if (nodes && *nodes)
      {
      ++nodes;
      }
    }
  return ret;
  }



/*
 * Format and display status of job in alternate form (not POSIX standard)
 */

static void altdsp_statjob(

  struct batch_status *pstat,     /* I */
  struct batch_status *prtheader, /* I */
  int                  alt_opt)   /* I */

  {
  const char *comment;
  char *pc;

  struct attrl *pat;
  const char *exechost;
  char *usern = NULL;
  char *queuen = NULL;
  char *jobn = NULL;
  const char *sess;
  const char *tasks;
  char  calcTasks[64];
  const char *nodect;
  const char *rqtimecpu;
  const char *rqtimewal;
  const char *jstate;
  const char *eltimecpu;
  const char *walltime_remaining = "0";
  int         rem_walltime = 0;
  int         req_walltime = 0;
  int         elap_time = 0;
  char        elap_time_string[100];
  char        format_string[MAX_LINE_LEN];

  const char *width;
  const char *ppn;

  int   usecput;
  static char  pfs[SIZEL];
  static char  rqmem[SIZEL + 10];
  static char  srfsbig[SIZEL];
  static char  srfsfast[SIZEL];
  static char  tmpNodeCt[SIZEL];
  static const char *blank = " -- ";

  if (prtheader)
    {
    printf("\n%s: ",
           prtheader->name);

    if ((pc = findattrl(prtheader->attribs, (char *)ATTR_comment, NULL)) != NULL)
      {
      printf("%s",
             pc);
      }

    if (alt_opt & ALT_DISPLAY_R)
      {
      printf("\n                                                          Req'd     Req'd       Elap");
      printf("\nJob ID                  Username    Queue    NDS   TSK    Memory    Time      S Time       BIG  FAST   PFS");
      printf("\n----   ---------------- ----------- -------- ----- ------ --------- --------- - --------- ----- ----- -----\n");
      }
    else
      {
      printf("\n                                                                                  Req'd       Req'd       Elap");
      printf("\nJob ID                  Username    Queue    Jobname          SessID  NDS   TSK   Memory      Time    S   Time");
      printf("\n----------------------- ----------- -------- ---------------- ------ ----- ------ --------- --------- - ---------\n");
      }
    }

  while (pstat != NULL)
    {
    exechost  = blank;
    sess      = blank;
    nodect    = blank;
    tasks     = blank;
    rqtimecpu = blank;
    rqtimewal = blank;
    eltimecpu = blank;
    jstate    = blank;
    comment   = blank;
    width     = blank;
    ppn       = blank;

    snprintf(elap_time_string, sizeof(elap_time_string), "%s", blank);
    snprintf(pfs, sizeof(pfs), "%s", blank);
    snprintf(rqmem, sizeof(rqmem), "%s", blank);
    snprintf(srfsbig, sizeof(srfsbig), "%s", blank);
    snprintf(srfsfast, sizeof(srfsfast), "%s", blank);
    usecput = 0;
    elap_time = 0;
    req_walltime = 0;
    rem_walltime = 0;
    bool dummyProcVal = false;

    // Make the memory take the alphabetically last specification. There is no reason it has
    // to be this way or should be this way, but I'm making this change so that the behavior
    // stays the same.
    char mem_char = 'a';

    pat = pstat->attribs;

    while (pat != NULL)
      {
      if (!strcmp(pat->name, ATTR_N))
        {
        jobn = pat->value;
        }
      else if (!strcmp(pat->name, ATTR_owner))
        {
        usern = pat->value;

        if ((pc = strchr(usern, (int)'@')) != NULL)
          * pc = '\0';
        }
      else if (!strcmp(pat->name, ATTR_state))
        {
        jstate = pat->value;
        }
      else if (!strcmp(pat->name, ATTR_queue))
        {
        queuen = pat->value;
        }
      else if (!strcmp(pat->name, ATTR_session))
        {
        sess = pat->value;
        }
      else if (!strcmp(pat->name, ATTR_l))
        {
        if (!strcmp(pat->resource, "nodect"))
          {
          nodect = pat->value;
          }
        else if (!strcmp(pat->resource, "nodes"))
          {
          int tsk = get_tasks_from_nodes_resc(pat->value);

          if (tsk != 0)
            {
            sprintf(calcTasks,"%d",tsk);
            tasks = calcTasks;
            }
          }
        else if (!strcmp(pat->resource, "procs"))
          {
          if ((strcmp(pat->value, "0"))&&((!strcmp(tasks,blank))||dummyProcVal))
            tasks = pat->value;
          }
        else if (!strcmp(pat->resource, "mppwidth"))
          {
          if ((strcmp(pat->value, "0"))&&(!strcmp(width,blank)))
            width = pat->value;
          }
        else if (!strcmp(pat->resource, "mppnppn"))
          {
          if ((strcmp(pat->value, "0"))&&(!strcmp(ppn,blank)))
            ppn = pat->value;
          }
        else if (!strcmp(pat->resource, "ncpus"))
          {
          if ((!strcmp(tasks,blank)) && (strcmp(pat->value, "0")))
            tasks = pat->value;
          }
        else if (!strcmp(pat->resource, "mppe"))
          {
          if (strcmp(pat->value, "0"))
            tasks = pat->value;
          }
        else if (!strcmp(pat->resource, "size"))
          {
          if (strcmp(pat->value, "0"))
            tasks = pat->value;
          }
        else if (!strcmp(pat->resource, "mem"))
          {
          if (mem_char < 'm')
            {
            mem_char = 'm';

            snprintf(rqmem, sizeof(rqmem), "%s", cnv_size(pat->value, alt_opt));
            }
          }
        else if (!strcmp(pat->resource, "pmem"))
          {
          if (mem_char < 'p')
            {
            mem_char = 'p';

            snprintf(rqmem, sizeof(rqmem), "%s", cnv_size(pat->value, alt_opt));
            }
          }
        else if (!strcmp(pat->resource, "dmem"))
          {
          if (mem_char < 'd')
            {
            mem_char = 'd';
  
            snprintf(rqmem, sizeof(rqmem), "%s", cnv_size(pat->value, alt_opt));
            }
          }
        else if (!strcmp(pat->resource, "vmem"))
          {
          if (mem_char < 'v')
            {
            mem_char = 'v';
  
            snprintf(rqmem, sizeof(rqmem), "%s", cnv_size(pat->value, alt_opt));
            }
          }
        else if (!strcmp(pat->resource, "pvmem"))
          {
          if (mem_char <= 'p')
            {
            mem_char = 'p';

            snprintf(rqmem, sizeof(rqmem), "%s", cnv_size(pat->value, alt_opt));
            }
          }
        else if (!strcmp(pat->resource, "walltime"))
          {
          rqtimewal = pat->value;
          timestring_to_int(pat->value, &req_walltime);
          }
        else if (!strcmp(pat->resource, "cput"))
          {
          rqtimecpu = pat->value;
          usecput = 1;
          }
        else if (!strcmp(pat->resource, "srfs_big"))
          {
          snprintf(srfsbig, sizeof(srfsbig), "%s", cnv_size(pat->value, alt_opt));
          }
        else if (!strcmp(pat->resource, "srfs_fast"))
          {
          snprintf(srfsfast, sizeof(srfsfast), "%s", cnv_size(pat->value, alt_opt));
          }
        else if (!strcmp(pat->resource, "piofs"))
          {
          snprintf(pfs, sizeof(pfs), "%s", cnv_size(pat->value, alt_opt));
          }
        }
      else if (!strcmp(pat->name, ATTR_exechost))
        {
        exechost = pat->value;
        }
      else if (!strcmp(pat->name, ATTR_used))
        {
        if (!strcmp(pat->resource, "cput"))
          {
          eltimecpu = pat->value;
          }
        //else if (!strcmp(pat->resource, "walltime"))
        }
      else if (!strcmp(pat->name, ATTR_comment))
        {
        comment = pat->value;
        }
      else if (!strcmp(pat->name, "Walltime"))
        {
        walltime_remaining = pat->value;
        rem_walltime = atoi(walltime_remaining);
        }

      pat = pat->next;
      }

    if(strcmp(width,blank))
      {
      tasks = width;
      if(strcmp(ppn,blank))
        {
        int w = atoi(width);
        int p = atoi(ppn);
        int c;
        if((p != 0)&&(w != 0))
          {
          c = w/p;
          if(w%p != 0)
            {
            c++;
            }
          sprintf(tmpNodeCt,"%d",c);
          nodect = tmpNodeCt;
          }
        }
      }

    if ((*jstate != 'Q') && (*jstate != 'C') && (*jstate != 'H'))
      {
      elap_time = req_walltime - rem_walltime;
      time_to_string(elap_time_string, elap_time);
      }

    /* inject precision into the format string */
    snprintf(format_string, sizeof(format_string), "%%-23.%ds %%-11.11s %%-8.8s ",
             PBS_JOB_ID_LEN);

    printf(format_string,
           pstat->name,
           usern,
           queuen);

    if (alt_opt & ALT_DISPLAY_R)
      {
      printf("%5.5s %*.*s %9.9s %9.9s %1.1s %9.9s %5.5s %5.5s %5.5s",
             nodect,
             tasksize,
             tasksize,
             tasks,
             rqmem,
             usecput ? rqtimecpu : rqtimewal,
             jstate,
             usecput ? eltimecpu : elap_time_string,
             srfsbig,
             srfsfast,
             pfs);
      }
    else
      {
      /* inject precision into the format string */
      snprintf(format_string, sizeof(format_string), "%%-%d.%ds %%6.6s %%5.5s %%*.*s %%9.9s %%9.9s %%1.1s %%9.9s",
               PBS_NAMELEN, PBS_NAMELEN);

      printf(format_string,
             jobn,
             sess,
             nodect,
             tasksize,
             tasksize,
             tasks,
             rqmem,
             usecput ? rqtimecpu : rqtimewal,
             jstate,
             usecput ? eltimecpu : elap_time_string);
      }

    if (linesize < maxlinesize)
      printf("\n");

    if (alt_opt & ALT_DISPLAY_n)
      {
      /* print assigned nodes */

      prt_nodes(exechost);
      }

    if (alt_opt & ALT_DISPLAY_s)
      {
      /* print (scheduler) comment */

      if (*comment != '\0')
        printf("   %s\n",
               comment);
      }

    pstat = pstat->next;
    }
  }  /* END altdsp_statjob() */




/*
 * get_ct - get count of jobs in queue/run state
 * support function for altdsp_statque()
 */

void get_ct(

  const char *str,
  int  *jque,
  int  *jrun)

  {
  char *ps;
  int   colon = (int)':';

  ps = strchr((char *)str, colon); /* Transit - skip */

  ps = strchr(ps + 1, colon); /* Queued  - add to jque */

  *jque += atoi(ps + 1);

  ps = strchr(ps + 1, colon); /* Held    - add to jque  */

  *jque += atoi(ps + 1);

  ps = strchr(ps + 1, colon); /* Waiting - add to jque  */

  *jque += atoi(ps + 1);

  ps = strchr(ps + 1, colon); /* Running - add to jrun  */

  *jrun += atoi(ps + 1);

  return;
  }  /* END get_ct() */





/*
 * altdsp_statque - alternative display for queue information, -q option
 */

static void altdsp_statque(

  char                *serv,
  struct batch_status *pstat,
  int                  opt)

  {
  char  rmem[SIZEL];
  const char *cput;
  const char *wallt;
  const char *jmax;
  const char *nodect;
  const char *blank = "--   ";
  int   jrun;
  int   jque;
  char  qenabled = '\0';
  char  qstarted = '\0';
  int   tot_jrun = 0;
  int   tot_jque = 0;

  struct attrl *pat;

  printf("\nserver: %s\n\n", serv);
  printf("Queue            Memory CPU Time Walltime Node  Run Que Lm  State\n");
  printf("---------------- ------ -------- -------- ----  --- --- --  -----\n");

  while (pstat != NULL)
    {
    snprintf(rmem, sizeof(rmem), "--  ");

    cput  = blank;
    wallt = blank;
    nodect = "-- ";
    jrun  = 0;
    jque  = 0;
    jmax  = blank;

    pat = pstat->attribs;

    while (pat != NULL)
      {
      if (strcmp(pat->name, ATTR_maxrun) == 0)
        {
        jmax = pat->value;
        }
      else if (strcmp(pat->name, ATTR_enable) == 0)
        {
        if (*pat->value == 'T')
          qenabled = 'E';
        else
          qenabled = 'D';
        }
      else if (strcmp(pat->name, ATTR_start) == 0)
        {
        if (*pat->value == 'T')
          qstarted = 'R';
        else
          qstarted = 'S';
        }
      else if (strcmp(pat->name, ATTR_count) == 0)
        {
        get_ct(pat->value, &jque, &jrun);

        tot_jque += jque;
        tot_jrun += jrun;
        }
      else if (strcmp(pat->name, ATTR_rescmax) == 0)
        {
        if (strcmp(pat->resource, "mem") == 0)
          {
          snprintf(rmem, sizeof(rmem), "%s", cnv_size(pat->value, opt));
          }
        else if (strcmp(pat->resource, "cput") == 0)
          {
          cput = pat->value;
          }
        else if (strcmp(pat->resource, "walltime") == 0)
          {
          wallt = pat->value;
          }
        else if (strcmp(pat->resource, "nodect") == 0)
          {
          nodect = pat->value;
          }
        }

      pat = pat->next;
      }

    printf("%-16.16s %6.6s %8.8s %8.8s %5.5s ",

           pstat->name,
           rmem,
           cput,
           wallt,
           nodect);

    printf("%3d %3d %2.2s   %c %c\n",
           jrun,
           jque,
           jmax,
           qenabled,
           qstarted);

    pstat = pstat->next;
    }  /* END while (pstat != NULL) */

  printf("                                               ----- -----\n");

  printf("                                               %5d %5d\n",
         tot_jrun,
         tot_jque);

  return;
  }  /* END altdsp_statque() */





/* build and add an attropl struct to the list */

static void add_atropl(

  struct attropl **list,
  char            *name,
  char            *resc,
  char            *value,
  enum batch_op    op)

  {

  struct attropl *patro;

  patro = (struct attropl *)calloc(1, sizeof(struct attropl));

  if (patro == NULL)
    {
    fprintf(stderr, "cannot calloc space\n");

    exit(1);
    }

  patro->next     = *list;

  patro->name     = name;
  patro->resource = resc;
  patro->value    = value;
  patro->op       = op;

  *list = patro;

  return;
  }  /* END add_atropl() */




/*
 * is_the_user
 * determine whether or not this job is owned by the specified user
 *
 * @param user - the user to check against
 * @param a - the attribute list for this job
 */

bool is_the_user(

  char         *user,
  struct attrl *a)

  {
  char *at_user;
  char *at_owner;
  bool   is_the_user = false;

  if ((user == NULL) ||
      (user[0] == '\0'))
    return(TRUE);

  at_user = strchr(user, '@');

  for (; a != NULL; a = a->next)
    {
    if (!strcmp(a->name, ATTR_owner))
      {
      if (at_user == NULL)
        {
        if ((at_owner = strchr(a->value, '@')) != NULL)
          {
          *at_owner = '\0';

          if (!strcmp(a->value, user))
            is_the_user = true;

          *at_owner = '@';

          return(is_the_user);
          }
        else
          {
          if (!strcmp(a->value, user))
            return(true);
          else
            return(false);
          }
        }
      else if (!strcmp(a->value, user))
        return(true);
      else
        return(false);
      }
    }

  return(false);
  } /* END is_the_user() */



/*
 * get_summary_attributes()
 *
 * Iterates over the attributes and stores the ones we're interested in for the summary
 * @param attribute - a linked list of the job's attributes
 * @param name - O to store the name
 * @param owner - O to store the owner
 * @param timeu - O to store the time
 * @param state - O to store the state
 * @param location - O to store the queue
 */

void get_summary_attributes(

  struct attrl *attribute,
  std::string  &name,
  std::string  &owner,
  std::string  &timeu,
  std::string  &state,
  std::string  &location)

  {
  char         *c;
  unsigned int  len;
  char          long_name[17];

  for (; attribute != NULL; attribute = attribute->next)
    {
    if (attribute->name != NULL)
      {
      if (strcmp(attribute->name, ATTR_name) == 0)
        {
        len = strlen(attribute->value);

        /* truncate AName */

        if (len > PBS_NAMELEN)
          {
          len = len - PBS_NAMELEN + 3;

          c = attribute->value + len;

          while ((*c != '/') && (*c != '\0'))
            c++;

          if (*c == '\0')
            c = attribute->value + len;

          memset(long_name, 0, sizeof(long_name));
          snprintf(long_name, sizeof(long_name), "...%s", c);

          c = long_name;
          }
        else
          {
          c = attribute->value;
          }

        name = c;
        }
      else if (!strcmp(attribute->name, ATTR_owner))
        {
        c = attribute->value;

        while ((*c != '@') && (*c != '\0'))
          c++;

        *c = '\0';

        len = strlen(attribute->value);

        if (len > OWNERL)
          {
          c = attribute->value + OWNERL;

          *c = '\0';
          }

        owner = attribute->value;
        }
      else if (!strcmp(attribute->name, ATTR_used))
        {
        if (!strcmp(attribute->resource, "cput"))
          {
          len = strlen(attribute->value);

          if (len > TIMEUL)
            {
            c = attribute->value + TIMEUL;

            *c = '\0';
            }

          timeu = attribute->value;
          }
        }
      else if (!strcmp(attribute->name, ATTR_state))
        {
        len = strlen(attribute->value);

        if (len > STATEL)
          {
          c = attribute->value + STATEL;

          *c = '\0';
          }

        state = attribute->value;
        }
      else if (!strcmp(attribute->name, ATTR_queue))
        {
        c = attribute->value;

        while ((*c != '@') && (*c != '\0'))
          c++;

        *c = '\0';

        len = strlen(attribute->value);

        if (len > LOCL)
          {
          c = attribute->value + LOCL;

          *c = '\0';
          }

        location = attribute->value;
        }
      }
    }

  if (timeu.size() == 0)
    timeu = "0";
  } // END get_summary_attributes()



/*
 * display_job_summary()
 *
 * Displays a job for a default qstat command.
 * @param p - the source of information about the job
 * @param format - the format in which the job should be displayed.
 */

void display_job_summary(

  struct batch_status *p,
  const char          *format)

  {
  unsigned int  len;
  char         *c = NULL;
  char         *jid = NULL;
  std::string   name;
  std::string   owner;
  std::string   state;
  std::string   location;
  std::string   timeu;

  if (p->name != NULL)
    {
    len = strlen(p->name);

    if (len > (PBS_MAXSEQNUM + PBS_MAXJOBARRAYLEN + 8))
      {
      /* truncate job name */

      c = p->name + PBS_MAXSEQNUM + PBS_MAXJOBARRAYLEN + 14;

      *c = '\0';
      }

    jid = p->name;
    }

  get_summary_attributes(p->attribs, name, owner, timeu, state, location);

  /* display summary data */
  printf(format,
         jid,
         name.c_str(),
         owner.c_str(),
         timeu.c_str(),
         state.c_str(),
         location.c_str());
  } // END display_job_summary()



/*
 * job_is_complete()
 *
 * Checks if a job is complete or not
 * @param a - a list of the job's attributes
 * @return true if the job is complete, false otherwise
 */

bool job_is_complete(

  struct attrl *a)

  {
  while (a != NULL)
    {
    if (!strcmp(a->name, ATTR_state))
      {
      if (a->value[0] == 'C')
        {
        return(true);
        }
      }

    a = a->next;
    }

  return(false);
  } // END job_is_complete()


void add_xml_resource(

  mxml_t     *RE,
  const char *resc_name,
  const char *value)

  {
  mxml_t      *AE;
    
  MXMLCreateE(&AE, resc_name);
  MXMLSetVal(AE, (void *)value, mdfString);
  MXMLAddE(RE, AE);
  } // END add_xml_resource()



void print_req_information(

  struct attrl *req_information_attr,
  mxml_t       *JE)

  {
  mxml_t      *RE;
  std::string  out;
  char         buf[100];
  char         name[1024];
  char        *left_dot;
  char        *right_dot;
  int          req_index = 0;
  int          task_index = 0;
  allocation   a;
  a.initialize_from_string(req_information_attr->value);

  if ((left_dot = strchr(req_information_attr->resource, '.')) != NULL)
    req_index = strtol(left_dot + 1, NULL, 10);

  if ((right_dot = strrchr(req_information_attr->resource, '.')) != NULL)
    task_index = strtol(right_dot + 1, NULL, 10);

  if (JE == NULL)
    sprintf(name, "%s.task_usage.%d.task.%d", ATTR_req_information, req_index, task_index);
  else
    sprintf(name, "task_usage.%d.task.%d", req_index, task_index);
        
  translate_vector_to_range_string(out, a.cpu_indices);
  if (JE == NULL)
    {
    prt_attr(name, "cpu_list", out.c_str());
    printf("\n");
    }
  else
    {
    MXMLCreateE(&RE, name);
    MXMLAddE(JE, RE);
    add_xml_resource(RE, "cpu_list", out.c_str());
    }
    
  translate_vector_to_range_string(out, a.mem_indices);
  if (JE == NULL)
    {
    prt_attr(name, "mem_list", out.c_str());
    printf("\n");
    }
  else
    add_xml_resource(RE, "mem_list", out.c_str());
    
  if (a.task_cput_used != 0)
    {
    sprintf(buf, "%lu", a.task_cput_used);
    
    if (JE == NULL)
      {
      prt_attr(name, "cpu_time_used", buf);
      printf("\n");
      }
    else
      add_xml_resource(RE, "cpu_time_used", buf);
    }
    
  if (a.task_memory_used != 0)
    {
    unsigned long long mem_used;

    mem_used = a.task_memory_used/1024;

    sprintf(buf, "%llukb", mem_used);

    if (JE == NULL)
      {
      prt_attr(name, "memory_used", buf);
      printf("\n");
      }
    else
      add_xml_resource(RE, "memory_used", buf);
    }
    
  sprintf(buf, "%d", a.cores);
  if (JE == NULL)
    {
    prt_attr(name, "cores", buf);
    printf("\n");
    }
  else
    add_xml_resource(RE, "cores", buf);
    
  sprintf(buf, "%d", a.threads);
  if (JE == NULL)
    {
    prt_attr(name, "threads", buf);
    printf("\n");
    }
  else
    add_xml_resource(RE, "threads", buf);

  if (JE == NULL)
    {
    prt_attr(name, "host", a.hostname.c_str());
    printf("\n");
    }
  else
    add_xml_resource(RE, "host", a.hostname.c_str());

  } // END print_req_information()



/*
 * create_full_job_xml()
 *
 * Builds the xml for the job's full ouput
 * @param p - the source for the job's information
 * @param DE - the document element we're adding to
 */

void create_full_job_xml(

  struct batch_status *p,
  mxml_t              *DE)

  {
  mxml_t       *JE = NULL;
  mxml_t       *AE;
  mxml_t       *RE1 = NULL;
  mxml_t       *JI = NULL;
  struct attrl *attribute;
  
  MXMLCreateE(&JE, "Job");

  MXMLAddE(DE, JE);

  MXMLCreateE(&JI, "Job_Id");

  MXMLSetVal(JI, p->name,mdfString);

  MXMLAddE(JE, JI);

  for (attribute = p->attribs; attribute != NULL; attribute = attribute->next)
    {
    if (attribute->name != NULL)
      {
      if (!strcmp(attribute->name, ATTR_execport))
        continue;

      /* lookup attribute->name -> XML attr name */
      if ((attribute->resource != NULL) &&
          (!strncmp("task_usage", attribute->resource, strlen("task_usage"))))
        {
        print_req_information(attribute, RE1);
        }
      else
        {

        AE = NULL;

        if (attribute->resource != NULL)
          {
          if (RE1 == NULL)
            {
            MXMLCreateE(&RE1, attribute->name);
            MXMLAddE(JE, RE1);
            }

          MXMLCreateE(&AE, attribute->resource);

          MXMLSetVal(AE, attribute->value, mdfString);
          MXMLAddE(RE1, AE);
          }
        else
          {
          RE1 = NULL;
          MXMLCreateE(&AE, attribute->name);
          MXMLSetVal(AE, attribute->value, mdfString);
          MXMLAddE(JE, AE);
          }
        }
      }
    }
  } // END create_full_job_xml()



/*
 * display_full_job()
 *
 * Prints the full ouput for a job, including each attribute
 * @param p - the source for information about the job
 */

void display_full_job(

  struct batch_status *p)

  {
  struct attrl *attribute;
  time_t        epoch;

  printf("Job Id: %s\n", p->name);
  
  for (attribute = p->attribs; attribute != NULL; attribute = attribute->next)
    {
    if (attribute->name != NULL)
      {
      if (!strcmp(attribute->name, ATTR_execport))
        continue;

      if ((attribute->resource != NULL) &&
          (!strncmp("task_usage", attribute->resource, strlen("task_usage"))))
        {
        print_req_information(attribute, NULL);
        }
      else if (!strcmp(attribute->name, ATTR_ctime) ||
          !strcmp(attribute->name, ATTR_etime) ||
          !strcmp(attribute->name, ATTR_mtime) ||
          !strcmp(attribute->name, ATTR_qtime) ||
          !strcmp(attribute->name, ATTR_start_time) ||
          !strcmp(attribute->name, ATTR_comp_time) ||
          !strcmp(attribute->name, ATTR_checkpoint_time) ||
          !strcmp(attribute->name, ATTR_a))
        {
        epoch = (time_t)strtol(attribute->value, NULL, 10);

        prt_attr(attribute->name, attribute->resource, ctime(&epoch));
        }
      else
        {
        if ((!strcmp(attribute->name, "Walltime")) && (attribute->value[0] == '-'))
          prt_attr(attribute->name, (char *)"Exceeded", attribute->value);
        else
          prt_attr(attribute->name, attribute->resource, attribute->value);
        printf("\n");
        }
      }
    }
  } // END display_full_job()



/*
 * display_single_job()
 *
 * Displays a single job for qstat. For qstat -x, builds the xml but doesn't output it yet.
 * @param p - the source for all of our job information
 * @param user - the user's name
 * @param format - the format string
 * @param DE - the document element for the XML. Used conditionally.
 * @param full - true if the output should display the full job
 */

void display_single_job(
  
  struct batch_status *p,
  char                *user,
  const char          *format,
  mxml_t              *DE,
  bool                 full)

  {
  if (is_the_user(user, p->attribs) == false)
    {
    return;
    }

  if ((do_not_display_complete == true) &&
      (job_is_complete(p->attribs) == true))
    return;

  if (full)
    {
    if (DisplayXML == true)
      create_full_job_xml(p, DE);
    else
      display_full_job(p);
    }   /* END if (full) */
  else
    {
    display_job_summary(p, format);
    }  /* END else (full) */

  if ((DisplayXML != true) &&
      (full == true))
    printf("\n");

  } // END display_single_job()



/* display when a normal "qstat" is executed */

void display_statjob(

  struct batch_status *status,    /* I (data) */
  bool                 prtheader, /* I (boolean) */
  bool                 full,      /* I (boolean) */
  char                *user)

  {
  struct batch_status *p;

  char                 format[80];

  mxml_t              *DE;

  /* XML only support for full output */

  if (DisplayXML == true)
    {
    printf("<?xml version=\"1.0\"?>\n");
    full = true;
    }

  if (!full)
    {
    sprintf(format, "%%-%ds %%-%ds %%-%ds %%%ds %%%ds %%-%ds\n",
            PBS_MAXSEQNUM + PBS_MAXJOBARRAYLEN + 11,
            PBS_MINNAMELEN,
            OWNERL,
            TIMEUL,
            STATEL,
            LOCL);

    if (prtheader == true)
      {
      /* display summary header TODO - the sizes of these fields should be determined from
         #defines in pbs_ifl.h */
      printf("Job ID                    Name             User            Time Use S Queue\n");
      printf("------------------------- ---------------- --------------- -------- - -----\n");
      }
    }    /* END if (!full) */

  if (DisplayXML == true)
    {
    /* create parent */

    DE = NULL;

    MXMLCreateE(&DE, "Data");
    }

  for (p = status; p != NULL; p = p->next)
    {
    display_single_job(p, user, format, DE, full);
    }  /* END for (p = status) */

  if (DisplayXML == true)
    {
    char *tmpBuf = NULL, *tail = NULL;
    int  bufsize;

    MXMLToXString(DE, &tmpBuf, &bufsize, INT_MAX, &tail, TRUE);

    MXMLDestroyE(&DE);

    fprintf(stdout, "%s\n",
            tmpBuf);
    }

  return;
  }  /* END display_statjob() */



#define MINNUML    3
#define MAXNUML    5
#define TYPEL      1

void display_statque(

  struct batch_status *status,
  bool                 prtheader,
  int                  full)

  {

  struct batch_status *p;

  struct attrl        *a = NULL;
  int                  l;
  char                *c;
  char                *name;
  const char         *max;
  const char         *tot;
  char                 ena[MAXNUML + 1];
  char                 str[MAXNUML + 1];
  char                 que[MAXNUML + 1];
  char                 run[MAXNUML + 1];
  char                 hld[MAXNUML + 1];
  char                 wat[MAXNUML + 1];
  char                 trn[MAXNUML + 1];
  char                 ext[MAXNUML + 1];
  char                 dne[MAXNUML + 1];
  const char         *type;
  char                 format[80];

  int                  NUML = 5;


  sprintf(format, "%%-%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%-%ds %%%ds\n",
          PBS_MINNAMELEN,
          NUML,
          NUML+1,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          TYPEL,
          NUML);

  if (!full && (prtheader == true))
    {
    printf(format, "Queue", "Max", "Tot", "Ena", "Str", "Que", "Run", "Hld", "Wat", "Trn", "Ext", "T", "Cpt");
    printf(format, "----------------", "---", "----", "--", "--", "---", "---", "---", "---", "---", "---", "-", "---");
    }

  p = status;

  while (p != NULL)
    {
    name = NULL;
    max = "0";
    tot = "0";
    snprintf(ena, sizeof(ena), "no");
    snprintf(str, sizeof(str), "no");
    snprintf(que, sizeof(que), "0");
    snprintf(run, sizeof(run), "0");
    snprintf(hld, sizeof(hld), "0");
    snprintf(wat, sizeof(wat), "0");
    snprintf(trn, sizeof(trn), "0");
    snprintf(ext, sizeof(ext), "0");
    snprintf(dne, sizeof(dne), "0");
    type = "not defined";

    if (full)
      {
      printf("Queue: %s\n",
             p->name);

      a = p->attribs;

      while (a != NULL)
        {
        if (a->name != NULL)
          {
          prt_attr(a->name, a->resource, a->value);

          printf("\n");
          }

        a = a->next;
        }
      }
    else
      {
      a = p->attribs;

      if (p->name != NULL)
        {
        l = strlen(p->name);

        if ((l > PBS_NAMELEN) &&
            (a != NULL))
          {
          c = a->name + PBS_NAMELEN;

          *c = '\0';
          }

        name = p->name;
        }

      while (a != NULL)
        {
        if (a->name != NULL)
          {
          if (strcmp(a->name, ATTR_maxrun) == 0)
            {
            l = strlen(a->value);

            if (l > NUML)
              {
              c = a->value + NUML;

              *c = '\0';
              }

            max = a->value;
            }
          else if (strcmp(a->name, ATTR_total) == 0)
            {
            l = strlen(a->value);

            if (l > (NUML+1))
              {
              c = a->value + (NUML+1);

              *c = '\0';
              }

            tot = a->value;
            }
          else if (strcmp(a->name, ATTR_enable) == 0)
            {
            if (istrue(a->value) == true)
              snprintf(ena, sizeof(ena), "yes");
            else
              snprintf(ena, sizeof(ena), "no");
            }
          else if (strcmp(a->name, ATTR_start) == 0)
            {
            if (istrue(a->value) == true)
              snprintf(str, sizeof(str), "yes");
            else
              snprintf(str, sizeof(str), "no");
            }
          else if (strcmp(a->name, ATTR_count) == 0)
            {
            states(a->value, que, run, hld, wat, trn, ext, dne, NUML);
            }
          else if (strcmp(a->name, ATTR_qtype) == 0)
            {
            type = a->value;

            *((char *)type + 1) = '\0';
            }
          }

        a = a->next;
        }

      printf(format,
             name,
             max,
             tot,
             ena,
             str,
             que,
             run,
             hld,
             wat,
             trn,
             ext,
             type,
             dne);
      }

    if (full)
      printf("\n");

    p = p->next;
    }

  return;
  }



#define STATUSL 10

void display_statserver(

  struct batch_status *status,
  bool                 prtheader,
  int                  full)

  {

  struct batch_status *p;

  struct attrl *a;
  int l;
  char *c;
  char *name;
  const char *max;
  const char *tot;
  char que[MAXNUML + 1];
  char run[MAXNUML + 1];
  char hld[MAXNUML + 1];
  char wat[MAXNUML + 1];
  char trn[MAXNUML + 1];
  char ext[MAXNUML + 1];
  char dne[MAXNUML + 1];
  const char *stats;
  char format[80];

  int  NUML;

  NUML = MAXNUML;

  sprintf(format, "%%-%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%-%ds\n",
          PBS_MINNAMELEN,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          NUML,
          STATUSL);

  if (!full && (prtheader == true))
    {
    printf(format, "Server", "Max", "Tot", "Que", "Run", "Hld", "Wat", "Trn", "Ext", "Com", "Status");
    printf(format, "----------------", "---", "---", "---", "---", "---", "---", "---", "---", "---", "----------");
    }

  p = status;

  while (p != NULL)
    {
    name = NULL;
    max = "0";
    tot = "0";
    snprintf(que, sizeof(que), "0");
    snprintf(run, sizeof(run), "0");
    snprintf(hld, sizeof(hld), "0");
    snprintf(wat, sizeof(wat), "0");
    snprintf(trn, sizeof(trn), "0");
    snprintf(ext, sizeof(ext), "0");
    snprintf(dne, sizeof(dne), "0");
    stats = "";

    if (full)
      {
      printf("Server: %s\n", p->name);
      a = p->attribs;

      while (a != NULL)
        {
        if (a->name != NULL)
          {
          prt_attr(a->name, a->resource, a->value);
          printf("\n");
          }

        a = a->next;
        }
      }
    else
      {
      if (p->name != NULL)
        {
        l = strlen(p->name);

        if (l > PBS_NAMELEN)
          {
          c = p->name + PBS_NAMELEN;
          *c = '\0';
          }

        name = p->name;
        }

      a = p->attribs;

      while (a != NULL)
        {
        if (a->name != NULL)
          {
          if (strcmp(a->name, ATTR_maxrun) == 0)
            {
            l = strlen(a->value);

            if (l > NUML)
              {
              c = a->value + NUML;
              *c = '\0';
              }

            max = a->value;
            }
          else if (strcmp(a->name, ATTR_total) == 0)
            {
            l = strlen(a->value);

            if (l > NUML)
              {
              c = a->value + NUML;
              *c = '\0';
              }

            tot = a->value;
            }
          else if (strcmp(a->name, ATTR_count) == 0)
            {
            states(a->value, que, run, hld, wat, trn, ext, dne, NUML);
            }
          else if (strcmp(a->name, ATTR_status) == 0)
            {
            l = strlen(a->value);

            if (l > STATUSL)
              {
              c = a->value + STATUSL;
              *c = '\0';
              }

            stats = a->value;
            }
          }

        a = a->next;
        }

      printf(format, name, max, tot, que, run, hld, wat, trn, ext, dne, stats);
      }

    if (full) printf("\n");

    p = p->next;
    }

  return;
  }  /* END display_statserver() */





#if TCL_QSTAT
#define ARGNUM 1024

char *attrlist(

  struct attrl *ap)

  {
  char nameres[256];
  char *argv[ARGNUM];
  char *ret;
  int i, num = 0;

  while (ap)
    {
    char *twol[2];

    if (ap->resource != NULL)
      {
      sprintf(nameres, "%s%s%s",
              ap->name,
              TCL_ATRSEP,
              ap->resource);

      twol[0] = nameres;
      }
    else
      {
      twol[0] = ap->name;
      }

    twol[1] = ap->value;

    argv[num++] = Tcl_Merge(2, (const char **)twol);

    if (num == ARGNUM)
      break;

    ap = ap->next;
    }

  ret = Tcl_Merge(num, (const char **)argv);

  for (i = 0;i < num;i++)
    free(argv[i]);

  return(ret);
  }





Tcl_Interp *interp = NULL;
char  script[200];
char  flags[] = "flags";
char  ops[] = "operands";
char  error[] = "error";

void
tcl_init(void)
  {

  struct passwd *pw;
  uid_t  uid;

  struct  stat    sb;

  uid = getuid();
  pw = getpwuid(uid);

  if (pw == NULL)
    return;

  sprintf(script, "%s/.qstatrc", pw->pw_dir);

  if (stat(script, &sb) == -1)
    {
    snprintf(script, sizeof(script), "%s", QSTATRC_PATH);

    if (stat(script, &sb) == -1)
      return;
    }

  interp = Tcl_CreateInterp();

  if (Tcl_Init(interp) == TCL_ERROR)
    {
    fprintf(stderr, "Tcl_Init error: %s",
	    Tcl_GetStringResult(interp));
    }

#if TCLX
#if TCL_MINOR_VERSION < 5  && TCL_MAJOR_VERSION < 8
  if (TclX_Init(interp) == TCL_ERROR)
    {
#else

  if (Tclx_Init(interp) == TCL_ERROR)
    {
#endif
    fprintf(stderr, "Tclx_Init error: %s",
	    Tcl_GetStringResult(interp));
    }

#endif /* TCLX */
  return;
  }

void tcl_addarg(

  const char *name,
  const char *arg)

  {
  if (interp == NULL)
    return;

  if (arg == NULL || *arg == '\0')
    return;

  Tcl_SetVar(interp, name, arg,
             TCL_GLOBAL_ONLY |
             TCL_LIST_ELEMENT |
             TCL_APPEND_VALUE);
  } /* END tcl_addarg() */




int tcl_stat(

  const char          *type,
  struct batch_status *bs,
  bool                 f_opt)

  {

  struct batch_status *bp;
  const char          *twol[2];
  const char          *word_one;
  char                *word_two;
  char                *argv[ARGNUM];
  int                  i;
  int                  num = 0;
  char                *result;

  if (interp == NULL)
    {
    return(1);
    }

  if (f_opt == false)
    {
    return(1);
    }

  word_one = type;

  for (bp = bs;bp != NULL;bp = bp->next)
    {
    char *threel[3];

    threel[0] = bp->name;
    threel[1] = attrlist(bp->attribs);
    threel[2] = bp->text;

    argv[num++] = Tcl_Merge(3, (const char **)threel);

    free(threel[1]); /* calloc'ed in attrlist() */

    if (num == ARGNUM)
      break;
    }

  word_two = Tcl_Merge(num, (const char **)argv);

  for (i = 0;i < num;i++)
    free(argv[i]);

  twol[0] = word_one;
  twol[1] = word_two;

  result = Tcl_Merge(2, twol);

  Tcl_SetVar(
    interp,
    "objects",
    result,
    TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);

  free(word_two);

  free(result);

  return(0);
  }




void tcl_run(

  bool f_opt)

  {
  if (interp == NULL)
    {
    return;
    }

  if (f_opt && Tcl_EvalFile(interp, script) != TCL_OK)
    {
    char *trace;

    trace = (char *)Tcl_GetVar(interp, "errorInfo", 0);

    if (trace == NULL)
      trace = (char *)Tcl_GetStringResult(interp);

    fprintf(stderr, "%s: TCL error @ line %d: %s\n",
            script, Tcl_GetErrorLine(interp), trace); 
    }

  Tcl_DeleteInterp(interp);

  return;
  }  /* END tcl_run() */



#else
#define tcl_init()
#define tcl_addarg(name, arg)
#define tcl_stat(type, bs, f_opt) ;
#define tcl_run(f_opt)
#endif /* TCL_QSTAT */


int process_commandline_opts(

  int argc,
  char **argv,
  int *exec_only_flg,
  int *errflg_out)

  {
  int c;
  int exec_only = 0;
  int errflg = 0;
  int rc = PBSE_NONE;

#if !defined(PBS_NO_POSIX_VIOLATION)
#define GETOPT_ARGS "acCeE:filn1pqrstu:xGMQRBW:-:"
#else
#define GETOPT_ARGS "flpQBW:"
#endif /* PBS_NO_POSIX_VIOLATION */

  mode = JOBS;
  user[0] = '\0';

  alt_opt = 0;
  f_opt = false;
  B_opt = 0;
  Q_opt = 0;
  t_opt = 0;
  E_opt = 0;


  while ((c = getopt(argc, argv, GETOPT_ARGS)) != EOF)
    {
#ifdef TCL_QSTAT
    option[1] = (char)c;

    tcl_addarg(flags, option);
    tcl_addarg(flags, optarg);
#endif

    switch (c)
      {
#if !defined(PBS_NO_POSIX_VIOLATION)

      case '1':

        alt_opt |= ALT_DISPLAY_o;

        break;

      case 'a':

        alt_opt |= ALT_DISPLAY_a;

        break;

      case 'c':

        do_not_display_complete = true;
        break;

      case 'C':

        condensed = true;

        break;

      case 'e':

        exec_only = 1;

        break;

      case 'E':

        if (optarg != NULL)
          {
          ExtendOpt = optarg;
          E_opt = TRUE;
          }
        break;

      case 'i':

        alt_opt |= ALT_DISPLAY_i;

        add_atropl(&p_atropl, (char *)ATTR_state, NULL, (char *)"EHQTW", EQ);

        break;

      case 'n':

        alt_opt |= ALT_DISPLAY_n;

        if (attrib != NULL)
          set_attr(&attrib, ATTR_exechost, NULL);

        break;

      case 'q':

        alt_opt |= ALT_DISPLAY_q;

        if (mode == SERVERS)
          {
          /* we can only process one mode at a time */
          fprintf(stderr, "cannot combine -B with -q option\n");
          return(PBSE_IVALREQ);
          }

        mode = QUEUES;


        break;

      case 'r':

        alt_opt |= ALT_DISPLAY_r;

        add_atropl(&p_atropl, (char *)ATTR_state, NULL, (char *)"RS", EQ);

        break;

      case 's':

        alt_opt |= ALT_DISPLAY_s;

        if (attrib != NULL)
          set_attr(&attrib, ATTR_comment, NULL);

        break;

      case 't':

        t_opt = 1;

        break;

      case 'u':

        alt_opt |= ALT_DISPLAY_u;
        snprintf(user, sizeof(user), "%s", optarg);

        break;

      case 'R':

        alt_opt |= ALT_DISPLAY_R;

        break;

      case 'G':

        alt_opt |= ALT_DISPLAY_G;

        break;

      case 'M':

        alt_opt |= ALT_DISPLAY_Mw;

        break;

#endif /* PBS_NO_POSIX_VIOLATION */

      case 'f':

        if ((alt_opt != 0) &&
            (alt_opt != ALT_DISPLAY_u))
          {
          fprintf(stderr, "%s -f incompatible with one or more options. Read qstat man page\n", conflict_msg);

          errflg++;
          return(PBSE_IVALREQ);
          }

        f_opt = true;

        /* We want to return all attributes */
        attrib = NULL;

        break;

      case 'x':

        DisplayXML = true;

        /* We want to return all attributes */
        attrib = NULL;

        break;

      case 'B':

        B_opt = 1;

        mode = SERVERS;

        if (Q_opt || alt_opt)
          {
          fprintf(stderr, "%s -B incompatible with one or more options. Read qstat man page\n", conflict_msg);

          errflg++;
          return(PBSE_IVALREQ);
          }

        break;

      case 'l':

        alias_opt = TRUE;

        break;

      case 'Q':

        Q_opt = 1;

        mode = QUEUES;

        if (B_opt || alt_opt)
          {
          fprintf(stderr, "%s -Q incompatible with one or more options. Read qstat man page\n", conflict_msg);

          errflg++;
          return(PBSE_IVALREQ);
          }

        break;

      case '-':

        /* handle '--' options */

        if ((optarg != NULL) && !strcmp(optarg, "version"))
          {
          fprintf(stderr, "Version: %s\nCommit: %s\n",
            PACKAGE_VERSION, GIT_HASH);

          exit(0);
          }

        if ((optarg != NULL) && !strcmp(optarg, "about"))
          {
          TShowAbout_exit();
          }

        /* unexpected '--' option received */

        errflg = 1;

        break;

      case 'W':

#if (TCL_QSTAT == 0)
        pc = optarg;

        while ((pc != NULL)&&(*pc))
          {
          switch (*pc)
            {

            case 'a':

              alt_opt |= ALT_DISPLAY_a;

              break;

            case 'i':
              alt_opt |= ALT_DISPLAY_i;
              add_atropl(&p_atropl, (char *)ATTR_state, (char *)0, (char *)"EHQTW", EQ);
              break;

            case 'r':
              alt_opt |= ALT_DISPLAY_r;
              add_atropl(&p_atropl, (char *)ATTR_state, (char *)0, (char *)"RS", EQ);
              break;

            case 'u':
              /* note - u option is assumed to be last in  */
              /* string and all remaining is the name list */
              alt_opt |= ALT_DISPLAY_u;

              while (*++pc == ' ')
                /* NO-OP, moving pointer */;

              snprintf(user, sizeof(user), "%s", pc);

              pc = pc + strlen(pc) - 1; /* for the later incr */

              break;

            case 'n':
              alt_opt |= ALT_DISPLAY_n;

              break;

            case 's':
              alt_opt |= ALT_DISPLAY_s;

              break;

            case 'q':
              alt_opt |= ALT_DISPLAY_q;

              mode = QUEUES;

              break;

            case 'R':
              alt_opt |= ALT_DISPLAY_R;

              break;

            case 'G':
              alt_opt |= ALT_DISPLAY_G;

              break;

            case 'M':
              alt_opt |= ALT_DISPLAY_Mw;

              break;

            case ' ':
              break;  /* ignore blanks */

            default:
              errflg++;
            }

          ++pc;
          }

#endif /* (TCL_QSTAT == 0) */
        break;

      case '?':

      default:

        /* unexpected argument */

        errflg++;

        break;
      }  /* END switch (c) */
    }    /* END while (c = getopt()) */

#if !defined(PBS_NO_POSIX_VIOLATION)

  /* certain combinations are not allowed */

  c = alt_opt & (ALT_DISPLAY_a | ALT_DISPLAY_i | ALT_DISPLAY_r | ALT_DISPLAY_q);

  if ((c != 0) &&
      (c != ALT_DISPLAY_a) &&
      (c != ALT_DISPLAY_i) &&
      (c != ALT_DISPLAY_r) &&
      (c != ALT_DISPLAY_q))
    {
      fprintf(stderr, "%s", conflict_msg);

    errflg++;
    rc = PBSE_IVALREQ;
    }

  c = alt_opt & (ALT_DISPLAY_Mw | ALT_DISPLAY_G);

  if (c == (ALT_DISPLAY_Mw | ALT_DISPLAY_G))
    {
    fprintf(stderr, "%s", conflict_msg);

    errflg++;
    rc = PBSE_IVALREQ;
    }

  if ((alt_opt & ALT_DISPLAY_q) && (f_opt == true))
    {
    fprintf(stderr, "%s", conflict_msg);

    errflg++;
    rc = PBSE_IVALREQ;
    }

  if ((alt_opt & ALT_DISPLAY_o) && !((alt_opt & ALT_DISPLAY_n) || (f_opt)))
    {
    fprintf(stderr, "%s", conflict_msg);

    errflg++;
    rc = PBSE_IVALREQ;
    }

  if (alt_opt & ALT_DISPLAY_o)
    {
    linesize = maxlinesize;
    alt_opt &= ~ALT_DISPLAY_o;
    }

#endif /* PBS_NO_POSIX_VIOLATION */


  *errflg_out = errflg;
  *exec_only_flg = exec_only;
  return(rc);
  }


int run_job_mode(

    bool have_args,
    const char *operand,
    int  *located,
    char *server_out,
    char *server_old,
    char *queue_name_out,
    char *server_name_out,
    char *job_id_out,
    string &errmsg)

  {
  int    any_failed = PBSE_NONE;
  int    stat_single_job = 0;
  int    connect;

  int    job_id_out_size = PBS_MAXCLTJOBID;
  int    server_out_size = MAXSERVERNAME;
  int    server_old_size = MAXSERVERNAME;

  int    retry_count = 0;
  char   destination[PBS_MAXDEST + 1];
  char   job_id[PBS_MAXCLTJOBID];
  char   rmt_server[MAXSERVERNAME];

  struct batch_status *p_server;
  struct batch_status *p_status = NULL;
    
  std::string server_name;
  std::vector<std::string> id_list;

  if (have_args == true)
    {
    if (!strcmp(operand, "(null)"))
      {
      return(PBSE_NONE);
      }

    if (isjobid(operand) == true)
      {
      /* must be a job-id */

      stat_single_job = 1;

      snprintf(job_id, sizeof(job_id), "%s", operand);

      any_failed = get_server_and_job_ids(job_id, id_list, server_name);
      if (any_failed != PBSE_NONE)
        {
        fprintf(stderr, "qstat: illegally formed job identifier: %s\n",
                job_id);

        tcl_stat(error, NULL, f_opt);

        any_failed = 1;

        return(any_failed);
        }

      snprintf(server_out, server_out_size, "%s", server_name.c_str());
      }
    else
      {
      /* must be a destination-id */

      stat_single_job = 0;

      snprintf(destination, sizeof(destination), "%s", operand);

      if (parse_destination_id(
            destination,
            &queue_name_out,
            &server_name_out))
        {
        fprintf(stderr, "qstat: illegally formed destination: %s\n",
                destination);

        tcl_stat(error, NULL, f_opt);

        any_failed = PBSE_IVALREQ;

        return(any_failed);
        }

      if (notNULL(server_name_out))
        {
        snprintf(server_out, server_out_size, "%s", server_name_out);
        }
      else
        {
        server_out[0] = '\0';
        }

      snprintf(job_id_out, job_id_out_size, "%s", queue_name_out);

      if (*queue_name_out != '\0')
        {
        add_atropl(&p_atropl, (char *)ATTR_q, NULL, queue_name_out, EQ);
        }
      }    /* END else */
    }
  else
    id_list.push_back("");

  while (retry_count < MAX_RETRIES)
    {
    connect = cnt2server(server_out);

    any_failed = -1 * connect;
    if (connect <= 0 )
      {
      if ((++retry_count < MAX_RETRIES) && ((any_failed != PBSE_TIMEOUT) && (any_failed != PBSE_SOCKET_FAULT)))
        {
        continue;
        }


      if (server_out[0] != 0)
        fprintf(stderr, "qstat: cannot connect to server %s (errno=%d) %s\n",
            server_out,
            any_failed,
            pbs_strerror(any_failed));
      else
        fprintf(stderr, "qstat: cannot connect to server %s (errno=%d) %s\n",
              pbs_server,
              any_failed,
              pbs_strerror(any_failed));

      tcl_stat(error, NULL, f_opt);

      return(any_failed);
      }

    if ((alt_opt != 0) && (strcmp(pbs_server, server_old) != 0))
      {
      /* changing to a different server */

      p_server = pbs_statserver_err(connect, NULL, NULL, &any_failed);

      snprintf(server_old, server_old_size, "%s", pbs_server);
      }
    else
      {
      p_server = 0;
      }

    if ((stat_single_job == 1) || (p_atropl == 0))
      {
      if (id_list.size() == 0)
        id_list.push_back("");

      for (size_t i = 0; i < id_list.size(); i++)
        {
        snprintf(job_id_out, job_id_out_size, "%s", id_list[i].c_str());

        p_status = pbs_statjob_err(
                     connect,
                     job_id_out,
                     attrib,
                     exec_only ? (char *)EXECQUEONLY : (char *)ExtendOpt.c_str(),
                     &any_failed);

        if (any_failed != PBSE_UNKJOBID)
          break;
        }
      }
    else
      {
      if (t_opt)
        {
        p_status = pbs_selstatattr_err(connect, p_atropl, attrib, exec_only ? (char *)EXECQUEONLY : NULL, &any_failed);
        if (any_failed == PBSE_UNKREQ)
          p_status = pbs_selstat_err(connect, p_atropl, exec_only ? (char *)EXECQUEONLY : NULL, &any_failed);
        }
      else
        {
        p_status = pbs_selstatattr_err(connect, p_atropl, attrib, exec_only ? (char *)EXECQUEONLY : (char *)summarize_arrays_extend_opt, &any_failed);
        if (any_failed == PBSE_UNKREQ)
          p_status = pbs_selstat_err(connect, p_atropl, exec_only ? (char *)EXECQUEONLY : (char *)summarize_arrays_extend_opt, &any_failed);
        }
      }

    if (p_status == NULL)
      {
      if ((any_failed == PBSE_UNKJOBID) && !*located)
        {
        *located = TRUE;

        if (locate_job(job_id_out, server_out, rmt_server))
          {
          pbs_disconnect(connect);

          snprintf(server_out, server_out_size, "%s", rmt_server);

          ++retry_count;

          continue;
          }

        tcl_stat("job", NULL, f_opt);

        errmsg = get_err_msg(any_failed,"job", connect, job_id_out);
        break;
        }
      else
        {
        if ((any_failed != PBSE_NONE) && (++retry_count >= MAX_RETRIES))
          {
          errmsg = get_err_msg(any_failed,"job", connect, job_id_out);
          break;
          }

        if (any_failed && (retry_count < MAX_RETRIES))
          {
          pbs_disconnect(connect);
          continue;
          }
        
        // If it's XML output display an empty XML document
        if (DisplayXML)
          display_statjob(p_status, print_header, f_opt, user);

        tcl_stat("job", NULL, f_opt);

        }
      }
    else
      {
      int condition = TRUE;
#ifdef TCL_QSTAT
      condition = tcl_stat("job", p_status, f_opt);
#endif

      if (alt_opt != 0)
        {
        altdsp_statjob(p_status, p_server, alt_opt);
        }
      else if ((f_opt == 0) ||
               (condition))
        {
        display_statjob(p_status, print_header, f_opt, user);
        }

      print_header = false;

      pbs_statfree(p_status);
      }

    pbs_disconnect(connect);
    break;
    }

  return(any_failed);
  }


int run_queue_mode(

    bool have_args,
    const char *operand,
    char *server_out,
    char *queue_name_out,
    char *server_name_out,
    string &errmsg)

  {
  int    any_failed = PBSE_NONE;
  int    connect;
  int    server_out_size = MAXSERVERNAME;
  int    retry_count = 0;
  char   destination[PBS_MAXDEST + 1];

  struct batch_status *p_status;

  if (have_args == true)
    {
    if (!strcmp(operand, "(null)"))
      {
      return(any_failed);
      }

    snprintf(destination, sizeof(destination), "%s", operand);

    if (parse_destination_id(destination,
                             &queue_name_out,
                             &server_name_out))
      {
      fprintf(stderr, "qstat: illegal 'destination' value\n");
      tcl_stat(error, NULL, f_opt);
      return(PBSE_IVALREQ);
      }
    else
      {
      if (notNULL(server_name_out))
        {
        snprintf(server_out, server_out_size, "%s", server_name_out);
        }
      else
        server_out[0] = '\0';
      }
    }

  while(retry_count < MAX_RETRIES)
    {
    connect = cnt2server(server_out);

    if (connect <= 0)
      {
      if (++retry_count < MAX_RETRIES)
        {
        continue;
        }

      any_failed = -1 * connect;
      if (server_out[0] != 0)
        fprintf(stderr, "qstat: cannot connect to server %s (errno=%d) %s\n",
            server_out,
            any_failed,
            pbs_strerror(any_failed));
      else
        fprintf(stderr, "qstat: cannot connect to server %s (errno=%d) %s\n",
              pbs_server,
              any_failed,
              pbs_strerror(any_failed));

      tcl_stat(error, NULL, f_opt);
      return(any_failed);
      }

    p_status = pbs_statque_err(connect, queue_name_out, NULL, NULL, &any_failed);

    if (p_status == NULL)
      {
      if (any_failed && (++retry_count < MAX_RETRIES))
        {
        pbs_disconnect(connect);
        continue;
        }

      if (any_failed)
        {
        errmsg = get_err_msg(any_failed,"queue", connect, NULL);
        tcl_stat(error, NULL, f_opt);
        }
      }
    else
      {
      int condition = TRUE;
#ifdef TCL_QSTAT
      condition = tcl_stat("queue", p_status, f_opt);
#endif
      if (alt_opt & ALT_DISPLAY_q)
        {
        altdsp_statque(pbs_server, p_status, alt_opt);
        }
      else if (condition)
        {
        display_statque(p_status, print_header, f_opt);
        }

      print_header = false;

      pbs_statfree(p_status);
      }

    pbs_disconnect(connect);
    break;
    }

  return(any_failed);
  }


int run_server_mode(
    bool    have_args,
    const char *operand,
    char    *server_out,
    string &errmsg)

  {
  int    connect;
  int    any_failed = PBSE_NONE;
  int    server_out_size = MAXSERVERNAME;
  int    retry_count = 0;
  struct batch_status *p_status;

  if (have_args == true)
    {
    if (!strcmp(operand, "(null)"))
      {
      return(PBSE_NONE);
      }

    snprintf(server_out, server_out_size, "%s", operand);
    }

  while (retry_count < MAX_RETRIES)
    {
    connect = cnt2server(server_out);

    if (connect <= 0)
      {
      if (++retry_count < MAX_RETRIES)
        {
        continue;
        }

      any_failed = -1 * connect;
      if (server_out[0] != 0)
        fprintf(stderr, "qstat: cannot connect to server %s (errno=%d) %s\n",
            server_out,
            any_failed,
            pbs_strerror(any_failed));
      else
        fprintf(stderr, "qstat: cannot connect to server %s (errno=%d) %s\n",
              pbs_server,
              any_failed,
              pbs_strerror(any_failed));


      tcl_stat(error, NULL, f_opt);
      any_failed = connect;
      return(any_failed);
      }

    p_status = pbs_statserver_err(connect, NULL, NULL, &any_failed);

    if (p_status == NULL)
      {
      /* check to see if we will do a retry before we print any error messages */
      if (any_failed && (++retry_count < MAX_RETRIES))
        {
        pbs_disconnect(connect);
        continue;
        }

      if (any_failed)
        {
        errmsg = get_err_msg(any_failed,"server", connect, server_out);
        tcl_stat(error, NULL, f_opt);
        }
      }
    else
      {
      int condition = TRUE;
#ifdef TCL_QSTAT
      condition = tcl_stat("server", p_status, f_opt);
#endif
      if (condition)
        display_statserver(p_status, print_header, f_opt);

      print_header = false;

      pbs_statfree(p_status);
      }

    pbs_disconnect(connect);
    break;
    } /* end while */

  return(any_failed);
  }



void print_usage()
  {
  static char usage[] = "usage: \n\
                          qstat [-f [-1]] [-W site_specific] [-x] [ job_identifier... | destination... ]\n\
                          qstat [-a|-i|-r|-e] [-u user] [-n [-1]] [-s] [-t] [-G|-M] [-R] [job_id... | destination...]\n\
                          qstat -Q [-f [-1]] [-W site_specific] [ destination... ]\n\
                          qstat -q [-G|-M] [ destination... ]\n\
                          qstat -B [-f [-1]] [-W site_specific] [ server_name... ]\n\
                          qstat -t\n";

  fprintf(stderr,"%s", usage);
  }



/* connects to server side routine pbs_statjob() in lib/Libifl/pbs_statjob.c */
/*  routes to req_stat_job() in server/req_stat.c (PBS_BATCH_StatusJob) */

int main(

  int    argc,  /* I */
  char **argv)  /* I */

  {
  int                  rc;
  int                  errflg = 0;
  int                  exec_only = 0;
  int                  any_failed = 0;
  int                  ret_code = 0;
  int                  located = FALSE;

  char                 job_id_out[PBS_MAXCLTJOBID];
  char                 server_out[MAXSERVERNAME] = "";
  char                 server_old[MAXSERVERNAME] = "";
  const char          *def_server;
  char                *queue_name_out = NULL;
  char                *server_name_out = NULL;
  char                 operand[PBS_MAXCLTJOBID + 1];

  bool                 have_args = false;
  vector<string>       errmsg_list;
  string errmsg;
#ifndef mbool
#define mbool char
#endif /* !mbool */

#ifndef TRUE
#define TRUE 1
#endif /* !TRUE */

#ifndef FALSE
#define FALSE 0
#endif /* !FALSE */

  /* Attributes needed for default view */
  set_attr(&attrib, ATTR_name, NULL);
  set_attr(&attrib, ATTR_owner, NULL);
  set_attr(&attrib, ATTR_used, NULL);
  set_attr(&attrib, ATTR_state, NULL);
  set_attr(&attrib, ATTR_queue, NULL);

  tcl_init();
  tcl_addarg(flags, argv[0]);

#ifdef TCL_QSTAT
  option[0] = '-';
  option[2] = '\0';
#endif

  if (getenv("PBS_QSTAT_EXECONLY") != NULL)
    exec_only = 1;

  if (getenv("PBS_QSTAT_NO_COMPLETE") != NULL)
    do_not_display_complete = true;

  rc = process_commandline_opts(argc, argv, &exec_only, &errflg);
  if (rc != PBSE_NONE)
    {
    print_usage();
    exit(2);
    }


  if (errflg)
    {
    print_usage();
    exit(2);
    }

  if (!t_opt && !E_opt && !exec_only)
    {
    ExtendOpt = summarize_arrays_extend_opt;
    }

  if (condensed == true)
    ExtendOpt += "C";

  def_server = pbs_default();

  if (def_server == NULL)
    def_server = "";

  /* Alternate display requires a few extra attributes */
  if (alt_opt && (attrib != NULL))
    {
    set_attr(&attrib, ATTR_session, NULL);
    set_attr(&attrib, ATTR_used, NULL);
    set_attr(&attrib, ATTR_l, NULL);
    }

  if (alt_opt & ALT_DISPLAY_u)
    {
    if (f_opt == 0)
      add_atropl(&p_atropl, (char *)ATTR_u, NULL, user, EQ);
    else
      alt_opt &= ~ALT_DISPLAY_u;
    }


  if (optind >= argc)
    {
    /* If no arguments, then set defaults */
    snprintf(server_out, sizeof(server_out), "@%s", def_server);
    tcl_addarg(ops, server_out);

    job_id_out[0] = '\0';
    server_out[0] = '\0';

    queue_name_out = NULL;
    have_args = false;
    }    /* END if (optind >= argc) */
  else
    {
    have_args = true;
    }

  for (;optind <= argc;optind++)
    {
    located = FALSE;

    snprintf(operand, sizeof(operand), "%s", argv[optind]);

    tcl_addarg(ops, operand);

    switch (mode)
      {

      case JOBS:      /* get status of batch jobs */
        any_failed = run_job_mode(have_args, operand, &located, server_out, server_old,
         queue_name_out, server_name_out, job_id_out, errmsg);
        break;

      case QUEUES:        /* get status of batch queues */
        any_failed = run_queue_mode(have_args, operand, server_out, queue_name_out, server_name_out, errmsg);
        break;


      case SERVERS:           /* get status of batch servers */
        any_failed = run_server_mode(have_args, operand, server_out, errmsg);
       break;

      }    /* END switch (mode) */
    if (any_failed)
      {
      if (errmsg.size() == 0)
        {
        errmsg = get_err_msg(any_failed,NULL,-1,NULL);
        }
      errmsg_list.push_back(errmsg);
      }
    if ((ret_code == PBSE_NONE) && (any_failed != PBSE_NONE))
      {
        ret_code = any_failed;
      }
    }      /* END for () */

  tcl_run(f_opt);
  for(vector<string>::iterator it = errmsg_list.begin(); it != errmsg_list.end(); it++)
  {
    fprintf(stderr, "%s\n",it->c_str());
  }
  exit(ret_code);
  }  /* END main() */
