/*
 * Copyright (C) 1994-2021 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of both the OpenPBS software ("OpenPBS")
 * and the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * OpenPBS is free software. You can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * OpenPBS is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Commercial License Information:
 *
 * PBS Pro is commercially licensed software that shares a common core with
 * the OpenPBS software.  For a copy of the commercial license terms and
 * conditions, go to: (http://www.pbspro.com/agreement.html) or contact the
 * Altair Legal Department.
 *
 * Altair's dual-license business model allows companies, individuals, and
 * organizations to create proprietary derivative works of OpenPBS and
 * distribute them - whether embedded or bundled with other software -
 * under a commercial license agreement.
 *
 * Use of Altair's trademarks, including but not limited to "PBS™",
 * "OpenPBS®", "PBS Professional®", and "PBS Pro™" and Altair's logos is
 * subject to Altair's trademark licensing policies.
 */

/**
 * @file	qstat.c
 * @brief
 * 	qstat - (PBS) show stats of batch jobs, queues, or servers
 *
 * @author	Terry Heidelberg
 * 			Livermore Computing
 *
 * @author  Bruce Kelly
 * 			National Energy Research Supercomputer Center
 *
 * @author  Lawrence Livermore National Laboratory
 * 			University of California
 */

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

#include "pbs_ifl.h"
#include "cmds.h"
#include "pbs_share.h"
#include <pwd.h>
#include <stdlib.h>
#include "pbs_internal.h"
#include "libutil.h"
#include <arpa/inet.h>
#include "pbs_json.h"

#if TCL_QSTAT
#include <sys/stat.h>
#include <tcl.h>
#ifdef NAS /* localmod 071 */
extern char *tcl_atrsep;
#endif /* localmod 071 */
#endif

/* default server */
char *def_server;

static void states();
static char *cvtResvstate(char *);
static int cmp_est_time(struct batch_status *a, struct batch_status *b);
char *cnvt_est_start_time(char *start_time, int shortform);

#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_H 0x40	      /* -H option - show history(F/M) jobs */
#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_1l 0x800	      /* -n -s -f on line line */
#define ALT_DISPLAY_w 0x1000	      /* -w - wide output */
#define ALT_DISPLAY_T 0x2000	      /* -T option - estimated start times */
#define ALT_DISPLAY_p 0x4000	      /* -p option - percentage completed for the job */
#define ALT_DISPLAY_INCR_WIDTH 0x8000 /* increases qstat header width */

#endif /* not PBS_NO_POSIX_VIOLATION */
#define HPCBP_EXEC_TAG "jsdl-hpcpa:Executable"
#define CHAR_LINE_LIMIT 78 /* maximum number of characters which can be printed on a line */

#define DISPLAY_TRUNC_CHAR '*'

#define NUML 5

static struct attrl basic_attribs[] = {
	{&basic_attribs[1],
	 ATTR_N,
	 NULL,
	 "",
	 SET},
	{&basic_attribs[2],
	 ATTR_owner,
	 NULL,
	 "",
	 SET},
	{&basic_attribs[3],
	 ATTR_used,
	 NULL,
	 "",
	 SET},
	{&basic_attribs[4],
	 ATTR_state,
	 NULL,
	 "",
	 SET},
	{NULL,
	 ATTR_queue,
	 NULL,
	 "",
	 SET}};

static struct attrl alt_attribs[] = {
	{&alt_attribs[1],
	 ATTR_session,
	 NULL,
	 "",
	 SET},
	{&basic_attribs[0],
	 ATTR_l,
	 NULL,
	 "",
	 SET}};

enum output_format_enum {
	FORMAT_DEFAULT = 0,
	FORMAT_DSV,
	FORMAT_JSON,
	FORMAT_MAX
};

/* This array contains the names users may specify for output format. */
static char *output_format_names[] = {"default", "dsv", "json", NULL};

static int output_format = FORMAT_DEFAULT;
static char *dsv_delim = "|";
static char *delimiter = "\n";
static char *prev_resc_name = NULL;
static int first_stat = 1;
static int conn;
static json_data *json_root = NULL; /* root of json structure */
static json_data *json_prev_resc = NULL;

static struct attrl *display_attribs = &basic_attribs[0];

int
cmp_jobs(const void *j1, const void *j2)
{
	char *job1, *job2;
	char *seq_num1 = NULL;
	char *seq_num2 = NULL;
	char *pserver_one = NULL;
	char *pserver_two = NULL;
	int ret = 0;
	int ret_val = 0;
	char *eptr = NULL;
	long jid1, jid2;
	job1 = *(char **) j1;
	job2 = *(char **) j2;
	if (pbs_isjobid(job1) == 0) {
		ret_val = 1;
		goto return_here;
	} else if (pbs_isjobid(job2) == 0) {
		ret_val = 1;
		goto return_here;
	} else {
		parse_jobid(job1, &seq_num1, &pserver_one, NULL);
		/* default server */
		if (pserver_one == NULL)
			pserver_one = strdup(def_server);
		else if (pserver_one[0] == '\0')
			strcpy(pserver_one, def_server);
		parse_jobid(job2, &seq_num2, &pserver_two, NULL);
		/* default server */
		if (pserver_two == NULL)
			pserver_two = strdup(def_server);
		else if (pserver_two[0] == '\0')
			strcpy(pserver_two, def_server);

		ret = strcmp(pserver_one, pserver_two);
		if (ret < 0) {
			ret_val = -1;
			goto return_here;
		} else if (ret > 0) {
			ret_val = 1;
			goto return_here;
		}
	}
	/* Server name is same, Now sort on the job id */
	jid1 = strtoll(job1, &eptr, 10);
	jid2 = strtoll(job2, &eptr, 10);
	if (jid1 < jid2) {
		ret_val = -1;
		goto return_here;
	} else if (jid1 > jid2) {
		ret_val = 1;
		goto return_here;
	} else if (seq_num1 != NULL && seq_num2 != NULL) {
		/* Array sub jobs, sort on the basis of index */
		jid1 = strtoll(seq_num1, &eptr, 10);
		jid2 = strtoll(seq_num2, &eptr, 10);
		if (jid1 < jid2) {
			ret_val = -1;
			goto return_here;
		} else if (jid1 > jid2) {
			ret_val = 1;
			goto return_here;
		}
	}
	/* Same job id getting repeated */
	ret_val = 0;
return_here:
	free(seq_num1);
	free(seq_num2);
	free(pserver_one);
	free(pserver_two);
	return ret_val;
}

/**
 * @brief
 *	tests whether the string value is true or false
 *
 * @param[in] string - string to be tested
 *
 * @return Boolean value
 * @retval 0  Failre
 * @retval 1  Success
 *
 */
int
istrue(char *string)
{
	if (strcmp(string, "TRUE") == 0)
		return TRUE;
	if (strcmp(string, "True") == 0)
		return TRUE;
	if (strcmp(string, "true") == 0)
		return TRUE;
	if (strcmp(string, "1") == 0)
		return TRUE;
	return FALSE;
}

/**
 * @brief
 *	sets single character for each state of job
 *
 * @param[in] string - string holding state of job
 * @param[in] q      - string holding string for que
 * @param[in] r      - string holding string for run
 * @param[in] h      - string holding string for hld
 * @param[in] w      - string holding string for wait
 * @param[in] t      - string holding string for transit
 * @param[in] e      - string holding string for end
 * @param[in] len    - length of string
 *
 */
static void
states(char *string, char *q, char *r, char *h, char *w, char *t, char *e, int len)
{
	char *c, *d, *f, *s, l;

	c = string;
	while (isspace(*c) && *c != '\0')
		c++;
	while (*c != '\0') {
		s = c;
		while ((*c != ':') && (*c != '\0'))
			c++;
		if (*c == '\0')
			break;
		*c = '\0';
		d = NULL;
		if (strcmp(s, "Queued") == 0)
			d = q;
		else if (strcmp(s, "Running") == 0)
			d = r;
		else if (strcmp(s, "Held") == 0)
			d = h;
		else if (strcmp(s, "Waiting") == 0)
			d = w;
		else if (strcmp(s, "Transit") == 0)
			d = t;
		else if (strcmp(s, "Exiting") == 0)
			d = e;
		c++;
		if (d != NULL) {
			s = c;
			while (*c != ' ' && *c != '\0')
				c++;
			l = *c;
			*c = '\0';
			if (strlen(s) > (size_t) len) {
				f = s + len;
				if (f > s)
					*(f - 1) = DISPLAY_TRUNC_CHAR;
				*f = '\0';
			}
			pbs_strncpy(d, s, NUML + 1);
			if (l != '\0')
				c++;
		} else {
			while (*c != ' ' && *c != '\0')
				c++;
		}
	}
}

/**
 * @brief
 *	Convert special characters, line feed and form feed into '\\'+n and '\\'+f respectively.
 *
 * @param[in]   val - input string
 *
 * @return string
 * @retval NULL     - if memory allocation failed.
 * @retval string   - string with feed characters escaped.
 *
 * @note
 *	The string returned should be freed by the caller.
 */
char *
convert_feed_chars(char *val)
{
	int i = 0;
	int len = MAXBUFLEN;
	char *temp = NULL;
	char *buf = (char *) malloc(MAXBUFLEN);
	if (buf == NULL)
		return NULL;
	while (*val != '\0') {
		if (*val == '\n') {
			buf[i++] = '\\';
			buf[i++] = 'n';
			val++;
		} else if (*val == '\f') {
			buf[i++] = '\\';
			buf[i++] = 'f';
			val++;
		} else
			buf[i++] = *val++;
		if (i >= len - 2) {
			len *= BUFFER_GROWTH_RATE;
			temp = (char *) realloc(buf, len);
			if (temp == NULL) {
				free(buf);
				return NULL;
			}
			buf = temp;
		}
	}
	buf[i] = '\0';
	return buf;
}

/**
 * @brief
 *	print the exit message and die.
 */
void
exit_qstat(char *msg)
{
	fprintf(stderr, "qstat: %s\n", msg);
	exit(1);
}

/**
 * @brief
 *	print a attribute value string, formating to break at a comma if possible
 *
 * @param[in] name - attribute name
 * @param[in] resource - resource name
 * @param[in] value - value for the attribute
 * @param[in] one_line - one line per attribute
 *
 */
void
prt_attr(char *name, char *resource, char *value, int one_line, json_data * json_obj)
{
	int first = 1;
	int len = 0;
	int start = 0;
	char *comma = ",";
	char *key = NULL;
	char *val = NULL;
	char *buf = NULL;
	char *temp = NULL;
	json_data *json_attr = NULL;

	if (value == NULL)
		return;
	switch (output_format) {
		case FORMAT_JSON:
			if (strcmp(name, ATTR_v) == 0) {
				if ((json_attr = pbs_json_create_object()) == NULL)
					exit_qstat("json error");
				pbs_json_insert_item(json_obj, name, json_attr);
				buf = strdup(value);
				temp = buf;
				if (buf == NULL)
					exit_qstat("out of memory");
				while (*value) {
					/* value is split based on comma and each key-value is stored in keyvalpair.
				 * If the value contains an escaped comma, only the comma is copied to keyvalpair.
				 * Then separated into key and value based on the first '=' */
					key = buf;
					val = NULL;
					while (!(*value == *comma || *value == '\0')) {
						if (*value == ESC_CHAR && *(value + 1) == *comma)
							value++;
						if (!val && *value == '=') {
							*buf++ = '\0';
							val = buf;
							value++;
						}
						*buf++ = *value++;
					}
					*buf = '\0';
					if (pbs_json_insert_parsed(json_attr, key, val, 0))
						exit_qstat("json error");
					if (*value != '\0')
						value++;
				}
				free(temp);
			} else {
				if (resource) {
					if (prev_resc_name && strcmp(prev_resc_name, name) != 0) {
						prev_resc_name = NULL;
					}
					if (prev_resc_name == NULL || strcmp(prev_resc_name, name) != 0) {
						if ((json_prev_resc = pbs_json_create_object()) == NULL)
							exit_qstat("json error");
						pbs_json_insert_item(json_obj, name, json_prev_resc);
						prev_resc_name = name;
					}
					if (pbs_json_insert_parsed(json_prev_resc, resource, value, 0))
						exit_qstat("json error");
				} else {
					if (prev_resc_name) {
						json_prev_resc = NULL;
						prev_resc_name = NULL;
					}
					if (pbs_json_insert_parsed(json_obj, name, value, 0))
						exit_qstat("json error");
				}
			}
			break;

		case FORMAT_DSV:
			buf = escape_delimiter(value, delimiter, ESC_CHAR);
			if (buf == NULL)
				exit_qstat("out of memory");
			buf = convert_feed_chars(buf);
			if (buf == NULL)
				exit_qstat("out of memory");
			if (resource)
				printf("%s.%s=%s", name, resource, show_nonprint_chars(buf));
			else
				printf("%s=%s", name, show_nonprint_chars(buf));
			free(buf);
			break;

		default:
			if (one_line) {
				buf = convert_feed_chars(value);
				if (buf == NULL)
					exit_qstat("out of memory");
				if (resource)
					printf("    %s.%s = %s", name, resource, buf);
				else
					printf("    %s = %s", name, show_nonprint_chars(buf));
				free(buf);
			} else {
				start = strlen(name) + 7; /* 4 spaces + ' = ' is 7 */
				printf("    %s", name);
				if (resource) {
					start += strlen(resource) + 1; /* 1 for dot */
					printf(".%s", resource);
				}
				printf(" = ");
				if ((temp = strdup(value)) == NULL)
					exit_qstat("out of memory");
				buf = strtok(temp, comma);
				while (buf) {
					if ((len = strlen(buf)) + start < CHAR_LINE_LIMIT) {
						printf("%s", show_nonprint_chars(buf));
						start += len;
					} else {
						if (!first) {
							printf("\n\t");
							start = 9; /* tab + 1 */
						}
						while (*buf) {
							char *sbuf;
							int ch;
							char char_buf[2];

							ch = *buf++;
							sprintf(char_buf, "%c", ch);
							sbuf = show_nonprint_chars(char_buf);
							if (sbuf != NULL) {
								int c;
								for (c = 0; c < strlen(sbuf); c++)
									putchar(sbuf[c]);
							} else {
								putchar(ch);
							}
							if (++start > CHAR_LINE_LIMIT) {
								start = 8; /* tab */
								printf("\n\t");
							}
						}
					}
					if ((buf = strtok(NULL, comma)) != NULL) {
						first = 0;
						putchar(',');
					}
				}
				free(temp);
			}
	}
}

#define NAMEL 16  /* printf of jobs, queues, and servers */
#define OWNERL 16 /* printf of jobs */
#define TIMEUL 8  /* printf of jobs */
#define STATEL 1  /* printf of jobs */
#define LOCL 16	  /* printf of jobs */
#define SIZEL 6	  /* length of "SIZE" fields in printf */

/* format widths defined for display in normal format output */
#define SIZEJOBID 15	  /* length of JobId field in printf */
#define SIZEJOBID_INCR 20 /*length of jobId field in printf with incr_width */
#define SIZEJOBNAME 10	  /* length of JobName field in printf */
#define SIZEQUEUENAME 8	  /* length of Queue field in printf */
#define SIZESESSID 6	  /* length of session id in printf */
#define SIZENDS 3	  /* length of nds in printf */
#define SIZETSK 3	  /* length of tsk in printf */
#define SIZEUSER 8	  /* length of user name in printf */

/* format widths defined for display in wide format output */
#define SIZEJOBID_W 30	 /* length of JobId field in printf */
#define SIZEJOBNAME_W 15 /* length of JobName field in printf */
#define SIZESESSID_W 8	 /* length of session id in printf */
#define SIZENDS_W 4	 /* length of nds in printf */
#define SIZETSK_W 5	 /* length of tsk in printf */
#define SIZEUSER_W 15	 /* length of user name in printf */

/**
 * @brief
 *	Check if value is too long, truncate and append DISPLAY_TRUNC_CHAR if needed
 *
 * @param[in] value - value that may be truncated
 * @param[in] len - non-wide length that must be met
 * @param[in] wide_len - wide length that must be met
 * @param[in] wide - whether or not wide format is used
 *
 */

static void
trunc_value(char *value, int len, int wide_len, int wide)
{
	if (wide) {
		if (strlen(value) > wide_len && wide_len > 0) {
			*(value + wide_len - 1) = DISPLAY_TRUNC_CHAR;
		}
	} else {
		if (strlen(value) > len && len > 0) {
			*(value + len - 1) = DISPLAY_TRUNC_CHAR;
		}
	}
}

/**
 * @brief
 *	Format and display string of assigned nodes, (1) strip off domain name
 *	if present and (2) break line at '+' sign.
 *
 * @param[in] nodes - name of exechosts
 * @param[in] no_newl - int value to decide which comment to be set
 *
 */
static void
prt_nodes(char *nodes, int no_newl)
{
	int i, len;
	char linebuf[CHAR_LINE_LIMIT];
	char *rest = NULL;
	char *saveptr = NULL;
	char *token = NULL;
	char *token_cp = NULL;
	char *subtoken = NULL;
	char *node_name = NULL;
	char *node_name_bkp = NULL;
	char *chunk = NULL;
	struct sockaddr_in check_ip;
	int ret = 0;

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

	i = 0;
	rest = strdup(nodes);
	if (rest == NULL)
		exit_qstat("out of memory");
	/* The exec_host string has the format <host1>/<T1>*<P1>[+<host2>/<T2>*<P2>+... ].
	 * We are using '+' delimiter to find each <host1>/<T1>*<P1> string.
	 */
	token = strtok_r(rest, "+", &saveptr);
	while (token != NULL) {
		token_cp = strdup(token);
		if (token_cp == NULL)
			exit_qstat("out of memory");
		/* We are using '/' delimiter to extract the <host1> value
		 * from <host1>/<T1>*<P1> string. We use the <host1> to identify if
		 * the node is created using IP address.
		 */
		subtoken = strtok(token, "/");
		chunk = token_cp + strlen(subtoken);
		ret = inet_pton(AF_INET, subtoken, &(check_ip.sin_addr));
		if (ret == 1) {
			/* node name is an IP address */
			pbs_asprintf(&node_name, "%s%s", subtoken, chunk);
		} else {
			/* Node name is not an IP address */
			pbs_asprintf(&node_name, "%s%s", strtok(subtoken, "."), chunk);
		}
		/* Backing up node_name as we are modifying the pointer further in the code */
		node_name_bkp = node_name;
		len = strlen(node_name);
		if (i + len < (CHAR_LINE_LIMIT - 1)) {
			for (; len > 0; i++, len--) {
				linebuf[i] = *node_name++;
			}
			/* Appending a  '+' here because we want to maintain the
			 * exec_host format i.e. <host1>/<T1>*<P1>[+<host2>/<T2>*<P2>+.
			 */
			linebuf[i++] = '+';
		} else {
			/* flush line and start next */
			linebuf[i] = '\0';
			printf((no_newl ? "%s" : "   %s\n"), show_nonprint_chars(linebuf));
			for (i = 0; len > 0; i++, len--) {
				linebuf[i] = *node_name++;
			}
			linebuf[i++] = '+';
		}
		token = strtok_r(NULL, "+", &saveptr);
	}
	if (i > 0) {
		linebuf[--i] = '\0';
		printf((no_newl ? "%s\n" : "   %s\n"), show_nonprint_chars(linebuf));
	} else if (no_newl)
		printf("\n");
	free(token_cp);
	free(rest);
	free(node_name_bkp);
	token_cp = NULL;
	rest = NULL;
	node_name_bkp = NULL;
}

/**
 * @brief
 *	convert size from suffix string (nnnn[ kmgt][ bw]) to string of
 *	k[bw] for neither -M or -G (magnitude may be adjusted to fit print)
 *	mw    for	  -M
 *	gb    for	  -G
 * @param[in] value - string holding info about size which processed to get long int
 * @param[in] opt   - option indicating which conversion
 *
 * @return string
 * @retval string holding magnitude
 *
 */

static char *
cnv_size(char *value, int opt)
{
	static int sift_factor[5][5] = {
		{0, 10, 20, 30, 40},	/*  b conversion */
		{-10, 0, 10, 20, 30},	/* kb conversion */
		{-20, -10, 0, 10, 20},	/* mb conversion */
		{-30, -20, -10, 0, 10}, /* gb conversion */
		{-40, -30, -20, -10, 0} /* tb conversion */
	};

	static char suffixletter[] = " kmgtp?";
	int in;	 /* magnitude of value from server  */
	int out; /* magnitude of value when printed */
	int sft;
	unsigned long nval;
	char *pc;
	char suffix1;	    /* magnitude key letter [ kmgt] */
	char suffix2 = 'b'; /* suffix letter, 'b' or 'w'    */
	static char outbuf[25];

	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;

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

	if (opt & (ALT_DISPLAY_Mb | ALT_DISPLAY_Mw)) {
		out = 2;
		suffix1 = 'm';
		if (opt & ALT_DISPLAY_Mw)
			suffix2 = 'w';
		else
			suffix2 = 'b';
	} else if (opt & ALT_DISPLAY_G) {
		out = 3;
		suffix2 = 'b';
	} else {
		out = in;
	}

	sft = sift_factor[out][in];

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

	if (suffix2 == 'w')
		nval = (nval + 7) >> 3; /* round and convert (back) to words */

	/* if the value will overflow the field size, up the magnitude */
	while (nval > 9999) {
		nval = (nval + 1023) >> 10;
		out++;
	}
	suffix1 = suffixletter[out];

	(void) sprintf(outbuf, "%lu%c%c", nval, suffix1, suffix2);
	return outbuf;
}

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

static void
altdsp_statjob(struct batch_status *pstat, struct batch_status *prtheader, int alt_opt, int wide, int how_opt)
{
	char *comment;
	char *pc;
	struct attrl *pat;
	char *exechost;
	char *usern;
	char *queuen;
	char *jobn;
	char *sess;
	char *tasks;
	char *nodect;
	char *rqtimecpu;
	char *rqtimewal;
	char *jstate;
	char *eltimecpu;
	char *eltimewal;
	char *est_time;
	char *timeval;
	int usecput;
	static char pfs[SIZEL];
	static char rqmem[SIZEL];
	static char srfsbig[SIZEL];
	static char srfsfast[SIZEL];
	static char *blank = " -- ";
	char buf[COMMENT_BUF_SIZE] = {'\0'};
	int id_len;

	if (alt_opt & ALT_DISPLAY_T)
		pstat = bs_isort(pstat, cmp_est_time);

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

		pc = get_attr(prtheader->attribs, ATTR_comment, NULL);
		if (pc)
			printf("%s", show_nonprint_chars(pc));
		if (wide) {

			/* Used for for displaying spaces and dashes dynamically for wide formatted output */
#define STR_DASH "--------------------------------------------------------------------------------"
#define STR_SPACE "                                                                                "

			/* dynamic formatting to display spaces */
			printf("\n");
			if (alt_opt & ALT_DISPLAY_T) {
				printf("%*.*s %*.*s %*.*s %*.*s %*.*s %*.*s %*.*s ", SIZEJOBID_W, SIZEJOBID_W, STR_SPACE,
				       SIZEUSER_W, SIZEUSER_W, STR_SPACE,
				       PBS_MAXQUEUENAME, PBS_MAXQUEUENAME, STR_SPACE,
				       SIZEJOBNAME_W, SIZEJOBNAME_W, STR_SPACE,
				       SIZESESSID_W, SIZESESSID_W, STR_SPACE,
				       SIZENDS_W, SIZENDS_W, STR_SPACE,
				       SIZETSK_W, SIZETSK_W, STR_SPACE);
				printf("               Est\n");
			}

			printf("%*.*s %*.*s %*.*s %*.*s %*.*s %*.*s %*.*s ", SIZEJOBID_W, SIZEJOBID_W, STR_SPACE,
			       SIZEUSER_W, SIZEUSER_W, STR_SPACE,
			       PBS_MAXQUEUENAME, PBS_MAXQUEUENAME, STR_SPACE,
			       SIZEJOBNAME_W, SIZEJOBNAME_W, STR_SPACE,
			       SIZESESSID_W, SIZESESSID_W, STR_SPACE,
			       SIZENDS_W, SIZENDS_W, STR_SPACE,
			       SIZETSK_W, SIZETSK_W, STR_SPACE);
			if (alt_opt & ALT_DISPLAY_T)
				printf("Req'd  Req'd   Start\n");
			else
				printf("Req'd  Req'd   Elap\n");

			/* dynamic formatting to display header */
			printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s ", SIZEJOBID_W, SIZEJOBID_W, "Job ID",
			       SIZEUSER_W, SIZEUSER_W, "Username",
			       PBS_MAXQUEUENAME, PBS_MAXQUEUENAME, "Queue",
			       SIZEJOBNAME_W, SIZEJOBNAME_W, "Jobname",
			       SIZESESSID_W, SIZESESSID_W, "SessID",
			       SIZENDS_W, SIZENDS_W, "NDS",
			       SIZETSK_W, SIZETSK_W, "TSK");
			printf("Memory Time  S Time\n");

			/* dynamic formatting to display dashes */
			printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s ", SIZEJOBID_W, SIZEJOBID_W, STR_DASH,
			       SIZEUSER_W, SIZEUSER_W, STR_DASH,
			       PBS_MAXQUEUENAME, PBS_MAXQUEUENAME, STR_DASH,
			       SIZEJOBNAME_W, SIZEJOBNAME_W, STR_DASH,
			       SIZESESSID_W, SIZESESSID_W, STR_DASH,
			       SIZENDS_W, SIZENDS_W, STR_DASH,
			       SIZETSK_W, SIZETSK_W, STR_DASH);
			printf("------ ----- - -----\n");
		} else {
			if (alt_opt & ALT_DISPLAY_T) {
				if (how_opt & ALT_DISPLAY_INCR_WIDTH)
					printf("\n%80s%s\n%65s%-7s%-8s%s\n", " ", "Est", " ", "Req'd", "Req'd", "Start");
				else
					printf("\n%75s%s\n%60s%-7s%-8s%s\n", " ", "Est", " ", "Req'd", "Req'd", "Start");
			} else {
				if (how_opt & ALT_DISPLAY_INCR_WIDTH)
					printf("\n%65s%-7s%-8s%s\n", " ", "Req'd", "Req'd", "Elap");
				else
					printf("\n%60s%-7s%-8s%s\n", " ", "Req'd", "Req'd", "Elap");
			}
			if (how_opt & ALT_DISPLAY_INCR_WIDTH) {
				printf("Job ID               Username Queue    Jobname    SessID NDS TSK Memory Time  S Time\n");
				printf("-------------------- -------- -------- ---------- ------ --- --- ------ ----- - -----\n");
			} else {
				printf("Job ID          Username Queue    Jobname    SessID NDS TSK Memory Time  S Time\n");
				printf("--------------- -------- -------- ---------- ------ --- --- ------ ----- - -----\n");
			}
		}
	}
	while (pstat) {
		exechost = blank;
		sess = blank;
		nodect = blank;
		tasks = blank;
		rqtimecpu = blank;
		rqtimewal = blank;
		eltimecpu = blank;
		eltimewal = blank;
		jstate = blank;
		comment = blank;
		usern = blank;
		jobn = blank;
		queuen = blank;
		est_time = NULL;
		/*		*pfs      = *blank;  */
		strcpy(pfs, blank);
		/*		*rqmem    = *blank;  */
		strcpy(rqmem, blank);
		/*		*srfsbig  = *blank;  */
		strcpy(srfsbig, blank);
		/*		*srfsfast = *blank;  */
		strcpy(srfsfast, blank);
		usecput = 0;

		pat = pstat->attribs;

		while (pat) {
			if (strcmp(pat->name, ATTR_N) == 0) {
				jobn = pat->value;
				trunc_value(jobn, SIZEJOBNAME, SIZEJOBNAME_W, wide);
			} else if (strcmp(pat->name, ATTR_owner) == 0) {
				usern = pat->value;
				if ((pc = strchr(usern, (int) '@')) != NULL)
					*pc = '\0';
				trunc_value(usern, SIZEUSER, SIZEUSER_W, wide);
			} else if (strcmp(pat->name, ATTR_state) == 0) {
				jstate = pat->value;
			} else if (strcmp(pat->name, ATTR_queue) == 0) {
				queuen = pat->value;
				trunc_value(queuen, SIZEQUEUENAME, PBS_MAXQUEUENAME, wide);
			} else if (strcmp(pat->name, ATTR_session) == 0) {
				sess = pat->value;
				trunc_value(sess, SIZESESSID, SIZESESSID_W, wide);
			} else if (strcmp(pat->name, ATTR_l) == 0) {
				if (strcmp(pat->resource, "nodect") == 0) {
					nodect = pat->value;
					trunc_value(nodect, SIZENDS, SIZENDS_W, wide);
				} else if (strcmp(pat->resource, "ncpus") == 0) {
					if (strcmp(pat->value, "0") != 0) {
						tasks = pat->value;
						trunc_value(tasks, SIZETSK, SIZETSK_W, wide);
					}
				} else if (strcmp(pat->resource, "mppe") == 0) {
					if (strcmp(pat->value, "0") != 0) {
						tasks = pat->value;
						trunc_value(tasks, SIZETSK, SIZETSK_W, wide);
					}
				} else if (strcmp(pat->resource, "mem") == 0) {
					pbs_strncpy(rqmem,
						    cnv_size(pat->value, alt_opt), sizeof(rqmem));
				} else if (strcmp(pat->resource, "walltime") == 0) {
					rqtimewal = pat->value;
				} else if (strcmp(pat->resource, "cput") == 0) {
					rqtimecpu = pat->value;
					usecput = 1;
				} else if (strcmp(pat->resource, "srfs_big") == 0) {
					pbs_strncpy(srfsbig,
						    cnv_size(pat->value, alt_opt), sizeof(srfsbig));
				} else if (strcmp(pat->resource, "srfs_fast") == 0) {
					pbs_strncpy(srfsfast,
						    cnv_size(pat->value, alt_opt), sizeof(srfsfast));
				} else if (strcmp(pat->resource, "piofs") == 0) {
					pbs_strncpy(pfs,
						    cnv_size(pat->value, alt_opt), sizeof(pfs));
				}

			} else if (strcmp(pat->name, ATTR_exechost) == 0) {
				exechost = pat->value;
			} else if (strcmp(pat->name, ATTR_estimated) == 0) {
				if (strcmp(pat->resource, "start_time") == 0) {
					est_time = pat->value;
				}
			} else if (strcmp(pat->name, ATTR_used) == 0) {
				if (strcmp(pat->resource, "walltime") == 0) {
					eltimewal = pat->value;
				} else if (strcmp(pat->resource, "cput") == 0) {
					eltimecpu = pat->value;
				}
			} else if (strcmp(pat->name, ATTR_comment) == 0) {
				/* there are 3 blank spaces after & before comment string */
				/* hence, for 80char line - 74 chars are displayed and for */
				/* 120 char line - 114 chars are displayed */
				if (strlen(pat->value) > COMMENTLENSCOPE_SHORT) {
					if (wide) {
						if (strlen(pat->value) > COMMENTLENSCOPE_WIDE) {
							pbs_strncpy(buf, pat->value, COMMENTLEN_WIDE);
							strcat(buf, "...");
							comment = buf;
						} else {
							comment = pat->value;
						}
					} else {
						pbs_strncpy(buf, pat->value, COMMENTLEN_SHORT);
						strcat(buf, "...");
						comment = buf;
					}
				} else {
					comment = pat->value;
				}
			}

			pat = pat->next;
		}

		if (alt_opt & ALT_DISPLAY_T) {
			pc = get_attr(pstat->attribs, ATTR_state, NULL);
			if (pc != NULL && (*pc == 'Q' || *pc == 'S' || *pc == 'B'))
				timeval = cnvt_est_start_time(est_time, 0);
			else
				timeval = "--";
		} else
			timeval = usecput ? eltimecpu : eltimewal;

		id_len = how_opt & ALT_DISPLAY_INCR_WIDTH ? SIZEJOBID_INCR : SIZEJOBID;
		trunc_value(pstat->name, id_len, SIZEJOBID_W, wide);

		if (wide) {
			/* dynamic formatting of values as defined by constants */
			printf("%-*.*s %-*.*s %-*.*s %-*.*s %*.*s %*.*s %*.*s ", SIZEJOBID_W, SIZEJOBID_W, pstat->name,
			       SIZEUSER_W, SIZEUSER_W, usern,
			       PBS_MAXQUEUENAME, PBS_MAXQUEUENAME, queuen,
			       SIZEJOBNAME_W, SIZEJOBNAME_W, jobn,
			       SIZESESSID_W, SIZESESSID_W, sess,
			       SIZENDS_W, SIZENDS_W, nodect,
			       SIZETSK_W, SIZETSK_W, tasks);
			/* static formatting of fixed size values */
			printf("%6.6s %5.5s %1s %5.5s",
			       rqmem,
			       usecput ? rqtimecpu : rqtimewal,
			       jstate,
			       timeval);
		} else {
			if (how_opt & ALT_DISPLAY_INCR_WIDTH) {
				printf("%-*.*s %-*.*s %-*.*s ",
				       SIZEJOBID_INCR, SIZEJOBID_INCR, pstat->name,
				       SIZEUSER, SIZEUSER, usern,
				       SIZEQUEUENAME, SIZEQUEUENAME, queuen);
			} else {
				printf("%-*.*s %-*.*s %-*.*s ",
				       SIZEJOBID, SIZEJOBID, pstat->name,
				       SIZEUSER, SIZEUSER, usern,
				       SIZEQUEUENAME, SIZEQUEUENAME, queuen);
			}
			/* dynamic formatting of values as defined by constants */
			printf("%-*.*s %*.*s %*.*s %*.*s ",
			       SIZEJOBNAME, SIZEJOBNAME, jobn,
			       SIZESESSID, SIZESESSID, sess,
			       SIZENDS, SIZENDS, nodect,
			       SIZETSK, SIZETSK, tasks);
			/* static formatting of fixed size values */
			printf("%6.6s %5.5s %1.1s %5.5s",
			       rqmem,
			       usecput ? rqtimecpu : rqtimewal,
			       jstate,
			       timeval);
		}
		if (!(alt_opt & ALT_DISPLAY_1l))
			printf("\n");
		else
			printf(" ");

		if (alt_opt & ALT_DISPLAY_n) {
			/* print assigned nodes */
			prt_nodes(exechost, alt_opt & ALT_DISPLAY_1l);
		}
		if (alt_opt & ALT_DISPLAY_s) {
			/* print (scheduler) comment */
			if (*comment != '\0')
				printf("   %s\n", show_nonprint_chars(comment));
		}

		pstat = pstat->next;
	}
}

/**
 * @brief
 * 	get_ct - get count of jobs in queue/run state
 *	support function for altdsp_statque()
 */
static void
get_ct(char *str, int *jque, int *jrun)
{
	char *ps;
	int colon = (int) ':';

	ps = strchr(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);
}

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

static void
altdsp_statque(char *serv, struct batch_status *pstat, int opt)
{
	char rmem[SIZEL];
	char *cput;
	char *wallt;
	char *jmax;
	char *nodect;
	char *blank = "  --   ";
	int jrun;
	int jque;
	char qenabled;
	char qstarted;
	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) {
		/* *rmem = '\0'; */
		strcpy(rmem, "--  ");
		cput = blank;
		wallt = blank;
		nodect = "-- ";
		jrun = 0;
		jque = 0;
		jmax = blank;

		qenabled = 'D';
		qstarted = 'S';
		pat = pstat->attribs;

		while (pat) {
			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 if (strcmp(pat->name, ATTR_start) == 0) {
				if (*pat->value == 'T')
					qstarted = 'R';
			} 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) {
					pbs_strncpy(rmem,
						    cnv_size(pat->value, opt), sizeof(rmem));
				} 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 %4.4s ",
		       pstat->name, rmem, cput, wallt, nodect);
		printf("%5d %5d %4.4s   %c %c\n",
		       jrun, jque, jmax, qenabled, qstarted);

		pstat = pstat->next;
	}
	printf("                                               ----- -----\n");
	printf("                                               %5d %5d\n", tot_jrun, tot_jque);
}

/* 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 *) malloc(sizeof(struct attropl));
	if (patro == 0)
		exit_qstat("out of memory");
	patro->next = *list;
	patro->name = name;
	patro->resource = resc;
	patro->value = value;
	patro->op = op;
	*list = patro;
}

static long
cvt_time_to_seconds(char *ts)
{
	char *workval;
	char *pc;
	char *pv;
	long rv = 0;

	if ((workval = strdup(ts)) == NULL)
		exit_qstat("out of memory");
	for (pc = workval, pv = workval; *pc; ++pc) {
		if (*pc == ':') {
			*pc = '\0';
			rv = (rv * 60) + atol(pv);
			pv = pc + 1;
		}
	}
	rv = rv * 60 + atol(pv);
	free(workval);
	return rv;
}

/**
 * @brief
 * 	percent_cal - calculate the percent done for the -p option
 *	Calculation is either:
 *	1.  expired / total subjobs for an array,
 *	2.  cput_used / cput_requested, if cput specified, or
 *	3.  walltime_used / walltime_requested if walltime specified, or
 *	4.  "--" if none of the above apply
 */
char *
percent_cal(char *state, char *timeu, char *timer, char *wtimu, char *wtimr, char *arsct)
{
	char *rtn = NULL;
	long bot = 0;
	long top = 0;
	int qu, ru, ex, ep;

	switch (*state) {

		case 'Q':
		case 'T':
		case 'W':
			pbs_asprintf(&rtn, "%3s", "-- ");
			return (rtn);

		case 'X':
			pbs_asprintf(&rtn, "%3s", "100");
			return (rtn);
	}

	if (arsct) { /* array job: percent expired */
		long percexp = -1;
		sscanf(arsct, "Queued:%d Running:%d Exiting:%d Expired:%d", &qu, &ru, &ex, &ep);
		bot = qu + ru + ex + ep;
		top = ep;
		if (bot != 0)
			percexp = (top * 100) / bot;
		if ((percexp >= 0) && (percexp < 1000)) {
			pbs_asprintf(&rtn, "%3ld ", percexp);
		}
	} else {
		long perccpu = -1;
		long percwal = -1;
		if (timer && timeu) { /* if cput specified */
			top = cvt_time_to_seconds(timeu);
			bot = cvt_time_to_seconds(timer);
			if (bot != 0)
				perccpu = (top * 100) / bot;
		}
		if (wtimr && wtimu) { /* if walltime specified */
			top = cvt_time_to_seconds(wtimu);
			bot = cvt_time_to_seconds(wtimr);
			if (bot != 0)
				percwal = (top * 100) / bot;
		}
		if ((perccpu != -1) || (percwal != -1)) {
			pbs_asprintf(&rtn, "%3ld ",
				     perccpu > percwal ? perccpu : percwal);
		}
	}
	if (rtn == NULL) {
		pbs_asprintf(&rtn, "%3s", "-- ");
	}
	return (rtn);
}

/** @fn display_statjob
 * @brief	display job status in specific format.
 *
 * @return int
 * @retval	0	- success
 * @retval	1	- failure
 *
 */

int
display_statjob(struct batch_status *status, struct batch_status *prtheader, int full, int how_opt, int alt_opt, int wide)
{
	struct batch_status *p;
	struct attrl *a;
	int l;
	char *c;
	char *jid;
	char *name;
	char *owner;
	char *timeu;
	char *timer;
	char *wtimu;
	char *wtimr;
	char *arsct;
	char *state;
	char *location;
	char format[80];
	char long_name[NAMEL + 1] = {'\0'};
	char *cmdargs = NULL;
	char *hpcbp_executable;
	json_data *json_jobs = NULL;
	json_data *json_job = NULL;

	if (wide) {
		sprintf(format, "%%-%ds %%-%ds %%-%ds  %%%ds %%%ds %%-%ds\n",
			SIZEJOBID_W, SIZEJOBNAME_W, SIZEUSER_W, TIMEUL, STATEL, PBS_MAXQUEUENAME);
	} else if (how_opt & ALT_DISPLAY_INCR_WIDTH) {
		sprintf(format, "%%-%ds %%-%ds %%-%ds  %%%ds %%%ds %%-%ds\n",
			PBS_MAXSEQNUM + 10, NAMEL, OWNERL, TIMEUL, STATEL, LOCL);
	} else {
		sprintf(format, "%%-%ds %%-%ds %%-%ds  %%%ds %%%ds %%-%ds\n",
			PBS_MAXSEQNUM + 5, NAMEL, OWNERL, TIMEUL, STATEL, LOCL);
	}

	if (!full && prtheader && output_format == FORMAT_DEFAULT) {
		c = get_attr(prtheader->attribs, ATTR_comment, NULL);
		if (c)
			printf("%s\n", show_nonprint_chars(c));
		if (how_opt & ALT_DISPLAY_p) {
			if (wide) {
				printf("Job id                         Name            User              %% done  S Queue\n");
				printf("-----------------------------  --------------- ---------------  -------- - ---------------\n");
			} else if (how_opt & ALT_DISPLAY_INCR_WIDTH) {
				printf("Job id                 Name             User               %% done  S Queue\n");
				printf("---------------------  ---------------- ----------------  -------- - -----\n");
			} else {
				printf("Job id            Name             User               %% done  S Queue\n");
				printf("----------------  ---------------- ----------------  -------- - -----\n");
			}
		} else {
			if (wide) {
				printf("Job id                         Name            User             Time Use S Queue\n");
				printf("-----------------------------  --------------- ---------------  -------- - ---------------\n");
			} else if (how_opt & ALT_DISPLAY_INCR_WIDTH) {
				printf("Job id                 Name             User              Time Use S Queue\n");
				printf("---------------------  ---------------- ----------------  -------- - -----\n");
			} else {
				printf("Job id            Name             User              Time Use S Queue\n");
				printf("----------------  ---------------- ----------------  -------- - -----\n");
			}
		}
	}

	if (output_format == FORMAT_JSON && first_stat) {
		if ((json_jobs = pbs_json_create_object()) == NULL)
			return 1;
		pbs_json_insert_item(json_root, "Jobs", json_jobs);
		first_stat = 0;
	}
	p = status;
	while (p != NULL) {
		jid = NULL;
		name = NULL;
		owner = NULL;
		timeu = NULL;
		timer = NULL;
		wtimu = NULL;
		wtimr = NULL;
		arsct = NULL;
		state = NULL;
		location = NULL;
		hpcbp_executable = NULL;
		prev_resc_name = NULL;
		json_job = NULL;
		if (full) {
			if (output_format == FORMAT_DSV || output_format == FORMAT_DEFAULT)
				printf("Job Id: %s%s", p->name, delimiter);
			else if (output_format == FORMAT_JSON) {
				if ((json_job = pbs_json_create_object()) == NULL)
					return 1;
				pbs_json_insert_item(json_jobs, p->name, json_job);
			}
			a = p->attribs;
			while (a != NULL) {
				if (a->name != NULL) {
					time_t epoch;

					if (strcmp(a->name, ATTR_ctime) == 0 ||
					    strcmp(a->name, ATTR_etime) == 0 ||
					    strcmp(a->name, ATTR_stime) == 0 ||
					    strcmp(a->name, ATTR_obittime) == 0 ||
					    strcmp(a->name, ATTR_mtime) == 0 ||
					    strcmp(a->name, ATTR_qtime) == 0 ||
					    strcmp(a->name, ATTR_resv_start) == 0 ||
					    strcmp(a->name, ATTR_resv_end) == 0 ||
					    strcmp(a->name, ATTR_cred_validity) == 0 ||
					    (strcmp(a->name, ATTR_estimated) == 0 &&
					     strcmp(a->resource, "start_time") == 0) ||
					    strcmp(a->name, ATTR_a) == 0) {
						epoch = (time_t) atol(a->value);
						if (epoch == 0 &&
						    strcmp(a->name, ATTR_estimated) == 0 &&
						    strcmp(a->resource, "start_time") == 0) {
							/*
							 * Must not pass constant string to
							 * ptr_attr due to strtok bug in Linux.
							 * Use a stack variable instead.
							 */
							char noval[] = "UNKNOWN";
							prt_attr(a->name, a->resource, noval, alt_opt & ALT_DISPLAY_w, json_job);
						} else {
							char time_buffer[32];
							pbs_strncpy(time_buffer, ctime(&epoch), sizeof(time_buffer));
							time_buffer[strlen(time_buffer) - 1] = '\0';
							prt_attr(a->name, a->resource, time_buffer, alt_opt & ALT_DISPLAY_w, json_job);
						}
					} else if (strcmp(a->name, ATTR_resv_state) == 0) {
						prt_attr(a->name, a->resource, cvtResvstate(a->value), alt_opt & ALT_DISPLAY_w, json_job);
					} else if (strcmp(a->name, ATTR_submit_arguments) == 0) {
						if (decode_xml_arg_list_str((a->value), &cmdargs) == -1)
							exit_qstat("out of memory");
						prt_attr(a->name, a->resource, cmdargs, alt_opt & ALT_DISPLAY_w, json_job);
						free(cmdargs);
					} else if (strcmp(a->name, ATTR_executable) == 0) {
						/*
						 * Prefix and suffix attribute value with
						 * HPCBP_EXEC_TAG value.
						 */
						hpcbp_executable =
							malloc((strlen(HPCBP_EXEC_TAG) * 2) +
							       sizeof("<></>") + strlen(a->value) + 1);
						if (hpcbp_executable == NULL)
							exit_qstat("out of memory");
						(void) sprintf(hpcbp_executable, "<%s>%s</%s>",
							       HPCBP_EXEC_TAG, a->value, HPCBP_EXEC_TAG);
						prt_attr(a->name, a->resource, hpcbp_executable, alt_opt & ALT_DISPLAY_w, json_job);
						free(hpcbp_executable);
					} else {
						prt_attr(a->name, a->resource, a->value, alt_opt & ALT_DISPLAY_w, json_job);
					}
				}
				a = a->next;
				if (a)
					printf("%s", delimiter);
			}
			if (output_format == FORMAT_DEFAULT)
				printf("%s", delimiter);
		} else {
			if (p->name != NULL) {
				c = p->name;
				while (*c != '.' && *c != '\0')
					c++;
				c++; /* List the first part of the server name, too. */
				while (*c != '.' && *c != '\0')
					c++;
				*c = '\0';
				l = strlen(p->name);
				if (wide) {
					if (l > SIZEJOBID_W) {
						c = p->name + SIZEJOBID_W;
						*(c - 1) = DISPLAY_TRUNC_CHAR;
						*c = '\0';
					}
				} else if (how_opt & ALT_DISPLAY_INCR_WIDTH) {
					if (l > (PBS_MAXSEQNUM + 10)) {
						c = p->name + PBS_MAXSEQNUM + 10;
						*(c - 1) = DISPLAY_TRUNC_CHAR;
						*c = '\0';
					}
				} else {
					if (l > (PBS_MAXSEQNUM + 5)) {
						c = p->name + PBS_MAXSEQNUM + 5;
						*(c - 1) = DISPLAY_TRUNC_CHAR;
						*c = '\0';
					}
				}
				jid = p->name;
			}
			a = p->attribs;
			while (a != NULL) {
				if (a->name != NULL) {
					if (strcmp(a->name, ATTR_name) == 0) {
						l = strlen(a->value);
						if (wide) {
							if (l >= SIZEJOBNAME_W) {
								snprintf(long_name, SIZEJOBNAME_W + 1, "%.*s%c", (SIZEJOBNAME_W - 1), a->value, DISPLAY_TRUNC_CHAR);
								c = long_name;
							} else {
								c = a->value;
							}
						} else {
							if (l >= NAMEL) {
								snprintf(long_name, NAMEL + 1, "%.*s%c", (NAMEL - 1), a->value, DISPLAY_TRUNC_CHAR);
								c = long_name;
							} else
								c = a->value;
						}
						name = c;
					} else if (strcmp(a->name, ATTR_owner) == 0) {
						c = a->value;
						while (*c != '@' && *c != '\0')
							c++;
						*c = '\0';
						l = strlen(a->value);
						if (wide) {
							if (l > SIZEUSER_W) {
								c = a->value + SIZEUSER_W;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
						} else {
							if (l > OWNERL) {
								c = a->value + OWNERL;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
						}
						owner = a->value;
					} else if (strcmp(a->name, ATTR_used) == 0) {
						if (strcmp(a->resource, "cput") == 0) {
							l = strlen(a->value);
							if (l > TIMEUL) {
								c = a->value + TIMEUL;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
							timeu = a->value;
						} else if (strcmp(a->resource, "walltime") == 0) {
							l = strlen(a->value);
							if (l > TIMEUL) {
								c = a->value + TIMEUL;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
							wtimu = a->value;
						}
					} else if (strcmp(a->name, ATTR_l) == 0) {
						if (strcmp(a->resource, "cput") == 0) {
							l = strlen(a->value);
							if (l > TIMEUL) {
								c = a->value + TIMEUL;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
							timer = a->value;
						} else if (strcmp(a->resource, "walltime") == 0) {
							l = strlen(a->value);
							if (l > TIMEUL) {
								c = a->value + TIMEUL;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
							wtimr = a->value;
						}
					} else if (strcmp(a->name, ATTR_state) == 0) {
						l = strlen(a->value);
						if (l > STATEL) {
							c = a->value + STATEL;
							*(c - 1) = DISPLAY_TRUNC_CHAR;
							*c = '\0';
						}
						state = a->value;
					} else if (strcmp(a->name, ATTR_queue) == 0) {
						c = a->value;
						while (*c != '@' && *c != '\0')
							c++;
						*c = '\0';
						l = strlen(a->value);
						if (wide) {
							if (l > PBS_MAXQUEUENAME) {
								c = a->value + PBS_MAXQUEUENAME;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
						} else {
							if (l > LOCL) {
								c = a->value + LOCL;
								*(c - 1) = DISPLAY_TRUNC_CHAR;
								*c = '\0';
							}
						}
						location = a->value;
					} else if (strcmp(a->name, ATTR_array_state_count) == 0) {
						arsct = a->value;
					}
				}
				a = a->next;
			}
			if (timeu == NULL)
				timeu = "0";
			if (how_opt & ALT_DISPLAY_p) {
				char *pc = percent_cal(state, timeu, timer, wtimu, wtimr, arsct);
				printf(format, jid, name, owner, pc, state, location);
				free(pc);
			} else
				printf(format, jid, name, owner, timeu, state, location);
		}
		if (full && output_format != FORMAT_JSON)
			printf("\n");
		p = p->next;
	}
	return 0;
}

#define TYPEL 4

/**
 * @brief
 *	Displays the status of queue.
 *
 * @param[in] status - batch request for queue status
 * @param[in] prtheader - true or false
 * @param[in] full - server full name
 *
 * @return	int
 * @retval	0	- success
 * @retval	1	- failure
 *
 */
int
display_statque(struct batch_status *status, int prtheader, int full, int alt_opt)
{
	struct batch_status *p;
	struct attrl *a;
	int l;
	char *c;
	char *name;
	char *max;
	char *tot;
	char ena[3 + 1];
	char str[3 + 1];
	char que[NUML + 1];
	char run[NUML + 1];
	char hld[NUML + 1];
	char wat[NUML + 1];
	char trn[NUML + 1];
	char ext[NUML + 1];
	char *type;
	char format[80];
	json_data *json_queues = NULL;
	json_data *json_queue = NULL;

	sprintf(format, "%%-%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%-%ds\n",
		NAMEL, NUML, NUML, 3, 3, NUML,
		NUML, NUML, NUML, NUML, NUML, TYPEL);

	if (!full && prtheader && output_format == FORMAT_DEFAULT) {
		printf("Queue              Max   Tot Ena Str   Que   Run   Hld   Wat   Trn   Ext Type\n");
		printf("---------------- ----- ----- --- --- ----- ----- ----- ----- ----- ----- ----\n");
	}

	if (output_format == FORMAT_JSON && first_stat) {
		if ((json_queues = pbs_json_create_object()) == NULL)
			return 1;
		pbs_json_insert_item(json_root, "Queue", json_queues);
		first_stat = 0;
	}
	p = status;
	while (p != NULL) {
		name = NULL;
		max = "0";
		tot = "0";
		strcpy(ena, "no");
		strcpy(str, "no");
		strcpy(que, "0");
		strcpy(run, "0");
		strcpy(hld, "0");
		strcpy(wat, "0");
		strcpy(trn, "0");
		strcpy(ext, "0");
		type = "not defined";
		prev_resc_name = NULL;
		if (full) {
			if (output_format == FORMAT_DSV || output_format == FORMAT_DEFAULT)
				printf("Queue: %s%s", p->name, delimiter);
			else if (output_format == FORMAT_JSON) {
				if ((json_queue = pbs_json_create_object()) == NULL)
					return 1;
				pbs_json_insert_item(json_queues, p->name, json_queue);
			}
			a = p->attribs;
			while (a != NULL) {
				if (a->name != NULL) {
					prt_attr(a->name, a->resource, a->value,
						 alt_opt & ALT_DISPLAY_w, json_queue);
				}
				a = a->next;
				if (a)
					printf("%s", delimiter);
			}
			if (output_format == FORMAT_DEFAULT)
				printf("%s", delimiter);
		} else {
			if (p->name != NULL) {
				l = strlen(p->name);
				if (l > NAMEL) {
					c = p->name + NAMEL;
					*(c - 1) = DISPLAY_TRUNC_CHAR;
					*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 - 1) = DISPLAY_TRUNC_CHAR;
							*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 - 1) = DISPLAY_TRUNC_CHAR;
							*c = '\0';
						}
						tot = a->value;
					} else if (strcmp(a->name, ATTR_enable) == 0) {
						if (istrue(a->value))
							strcpy(ena, "yes");
						else
							strcpy(ena, "no");
					} else if (strcmp(a->name, ATTR_start) == 0) {
						if (istrue(a->value))
							strcpy(str, "yes");
						else
							strcpy(str, "no");
					} else if (strcmp(a->name, ATTR_count) == 0) {
						states(a->value, que, run, hld, wat, trn, ext, NUML);
					} else if (strcmp(a->name, ATTR_qtype) == 0) {
						l = strlen(a->value);
						if (l > TYPEL) {
							c = a->value + TYPEL;
							*(c - 1) = DISPLAY_TRUNC_CHAR;
							*c = '\0';
						}
						type = a->value;
					}
				}
				a = a->next;
			}
			printf(format, name, max, tot, ena, str, que, run, hld, wat, trn, ext, type);
		}
		if (full && output_format != FORMAT_JSON)
			printf("\n");
		p = p->next;
	}
	return 0;
}

#define STATUSL 10

/**
 * @brief
 *      Displays the status of server.
 *
 * @param[in] status - batch request for server status
 * @param[in] prtheader - true or false
 * @param[in] full - server full name
 *
 * @return  int
 * @retval	0	- success
 * @retval	1	- failure
 *
 */

int
display_statserver(struct batch_status *status, int prtheader, int full, int alt_opt)
{
	struct batch_status *p;
	struct attrl *a;
	int l;
	char *c;
	char *name;
	char *max;
	char *tot;
	char que[NUML + 1];
	char run[NUML + 1];
	char hld[NUML + 1];
	char wat[NUML + 1];
	char trn[NUML + 1];
	char ext[NUML + 1];
	char *stats;
	char format[80];
	json_data *json_servers = NULL;
	json_data *json_server = NULL;

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

	if (!full && prtheader && output_format == FORMAT_DEFAULT) {
		printf("Server             Max   Tot   Que   Run   Hld   Wat   Trn   Ext Status\n");
		printf("---------------- ----- ----- ----- ----- ----- ----- ----- ----- -----------\n");
	}

	if (output_format == FORMAT_JSON && first_stat) {
		if ((json_servers = pbs_json_create_object()) == NULL)
			return 1;
		pbs_json_insert_item(json_root, "Server", json_servers);
		first_stat = 0;
	}
	p = status;
	while (p != NULL) {
		name = NULL;
		max = "0";
		tot = "0";
		strcpy(que, "0");
		strcpy(run, "0");
		strcpy(hld, "0");
		strcpy(wat, "0");
		strcpy(trn, "0");
		strcpy(ext, "0");
		stats = "";
		if (full) {
			if (output_format == FORMAT_DSV || output_format == FORMAT_DEFAULT)
				printf("Server: %s%s", p->name, delimiter);
			else if (output_format == FORMAT_JSON) {
				if ((json_server = pbs_json_create_object()) == NULL)
					return 1;
				pbs_json_insert_item(json_servers, p->name, json_server);
			}
			a = p->attribs;
			while (a != NULL) {
				if (a->name != NULL) {
					prt_attr(a->name, a->resource, a->value, alt_opt & ALT_DISPLAY_w, json_server);
				}
				a = a->next;
				if ((a || output_format == FORMAT_DEFAULT))
					printf("%s", delimiter);
			}
		} else {
			if (p->name != NULL) {
				l = strlen(p->name);
				if (l > NAMEL) {
					c = p->name + NAMEL;
					*(c - 1) = DISPLAY_TRUNC_CHAR;
					*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 - 1) = DISPLAY_TRUNC_CHAR;
							*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 - 1) = DISPLAY_TRUNC_CHAR;
							*c = '\0';
						}
						tot = a->value;
					} else if (strcmp(a->name, ATTR_count) == 0) {
						states(a->value, que, run, hld, wat, trn, ext, NUML);
					} else if (strcmp(a->name, ATTR_status) == 0) {
						l = strlen(a->value);
						if (l > STATUSL) {
							c = a->value + STATUSL;
							*(c - 1) = DISPLAY_TRUNC_CHAR;
							*c = '\0';
						}
						stats = a->value;
					}
				}
				a = a->next;
			}
			printf(format, name, max, tot, que, run, hld, wat, trn, ext, stats);
		}
		if (full && output_format != FORMAT_JSON)
			printf("\n");
		p = p->next;
	}
	return 0;
}

#if TCL_QSTAT
#ifdef NAS /* localmod 071 */
static Tcl_Obj *
attrlist(struct attrl *ap)
{
	char nameres[256];
	int rc;
	Tcl_Obj *ret;
	Tcl_Obj *sublist;

	/*
	 * Build a list out of sublists made from attribute name / value
	 * pairs
	 */
	ret = Tcl_NewListObj(0, NULL);
	if (ret == NULL)
		return ret;
	while (ap) {
		Tcl_Obj *twol[2];

		if (ap->resource) {
			sprintf(nameres, "%s%s%s",
				ap->name, tcl_atrsep, ap->resource);
			twol[0] = Tcl_NewStringObj(nameres, -1);
		} else
			twol[0] = Tcl_NewStringObj(ap->name, -1);
		twol[1] = Tcl_NewStringObj(ap->value, -1);
		sublist = Tcl_NewListObj(2, twol);
		if (sublist == NULL) {
			if (twol[0])
				Tcl_DecrRefCount(twol[0]);
			if (twol[1])
				Tcl_DecrRefCount(twol[1]);
			break;
		}
		rc = Tcl_ListObjAppendElement(NULL, ret, sublist);
		if (rc != TCL_OK) {
			Tcl_DecrRefCount(sublist);
			break;
		}
		ap = ap->next;
	}
	return (ret);
}
#else
#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) {
			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, twol);
		if (num == ARGNUM)
			break;
		ap = ap->next;
	}
	ret = Tcl_Merge(num, argv);
	for (i = 0; i < num; i++)
		free(argv[i]);
	return (ret);
}
#endif /* localmod 071 */

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

#ifdef NAS /* localmod 071 */
char log_buffer[4096];
extern int quiet;

extern void add_cmds(Tcl_Interp *interp);

/**
 * @brief
 *	log error msg on error
 *
 * @param[in] errnum - error number
 * @param[in] func - function name where error occured
 * @param[in] text - error msg
 *
 * @return	Void
 *
 */
void
log_err(int errnum, char *func, char *text)
{
	if (quiet)
		return;
	fprintf(stderr, "%s: %s: %s\n",
		(errnum < 0) ? "Internal error" : strerror(errnum),
		func, text);
}

/**
 * @brief
 *	initialise the tcl.
 */
void
tcl_init()
{
	struct passwd *pw;
	uid_t uid;
	struct stat sb;
	struct batch_status *bp;
	int i, ret = 1;
	char *home;

	if ((home = getenv("QSTATRCHOME")) == NULL &&
	    (home = getenv("HOME")) == NULL) {
		uid = getuid();
		pw = getpwuid(uid);
		if (pw == NULL)
			return;
		home = pw->pw_dir;
	}

	snprintf(script, sizeof(script), "%s/.qstatrc", home);
	if (stat(script, &sb) == -1) {
		pbs_strncpy(script, QSTATRC_PATH, sizeof(script));
		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",
			interp->result);
	}
#endif /* TCLX */
	add_cmds(interp);
	return;
}
#else

/**
 * @brief
 *      initialise the tcl lib.
 */

void
tcl_init()
{
	struct passwd *pw;
	uid_t uid;
	struct stat sb;
	struct batch_status *bp;
	int i, ret = 1;

	uid = getuid();
	pw = getpwuid(uid);
	if (pw == NULL)
		return;

	snprintf(script, sizeof(script), "%s/.qstatrc", pw->pw_dir);
	if (stat(script, &sb) == -1) {
		pbs_strncpy(script, QSTATRC_PATH, sizeof(script));
		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",
			interp->result);
	}
#endif /* TCLX */
	return;
}
#endif /* localmod 071 */

/**
 * @brief
 *	add argument to tcl list.
 *
 * @param[in] name - flag
 * @param[in] arg - argument
 *
 * @return	Void
 *
 */

void
tcl_addarg(char *name, 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);
}

/**
 * @brief
 *      set tcl status .
 *
 * @param[in] type - type
 * @param[in] bs - batch request for tcl status
 * @param[in] f_opt - file option
 *
 * @return      int
 * @retval      0       success
 * @retval      1       error
 *
 */

#ifdef NAS /* localmod 071 */
int tcl_stat(char *, struct batch_status *, int);
int
tcl_stat(char *type, struct batch_status *bs, int tcl_opt)
{
	struct batch_status *bp;
	Tcl_Obj *twol[2];
	Tcl_Obj *value;
	Tcl_Obj *result;
	Tcl_Obj *name;
	int rc;
	int i;
	int errs = 0;

	if (interp == NULL)
		return 1;

	if (tcl_opt == 0)
		return 1;

	value = Tcl_NewListObj(0, NULL);
	if (value == NULL)
		return 1;

	for (bp = bs; bp; bp = bp->next) {
		Tcl_Obj *threel[3];
		Tcl_Obj *sublist;

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

		sublist = Tcl_NewListObj(3, threel);
		if (sublist == NULL) {
			for (i = 0; i < 3; ++i) {
				if (threel[i] != NULL) {
					Tcl_DecrRefCount(threel[i]);
				}
			}
			++errs;
			break;
		}
		rc = Tcl_ListObjAppendElement(interp, value, sublist);
		if (rc != TCL_OK) {
			Tcl_DecrRefCount(sublist);
			++errs;
			break;
		}
	}
	if (errs) {
		Tcl_DecrRefCount(value);
		return 1;
	}
	twol[0] = Tcl_NewStringObj(type, -1);
	twol[1] = value;

	result = Tcl_NewListObj(2, twol);
	if (result == NULL) {
		for (i = 0; i < 2; ++i) {
			if (twol[i] != NULL) {
				Tcl_DecrRefCount(twol[i]);
			}
		}
		return 1;
	}
	name = Tcl_NewStringObj("objects", -1);
	if (name == NULL) {
		Tcl_DecrRefCount(result);
		return 1;
	}

	Tcl_ObjSetVar2(interp, name, NULL, result,
		       TCL_GLOBAL_ONLY |
			       TCL_LIST_ELEMENT |
			       TCL_APPEND_VALUE);
	return 0;
}
#else
/**
 * @brief
 *	set tcl status .
 *
 * @param[in] type - type
 * @param[in] bs - batch request for tcl status
 * @param[in] f_opt - file option
 *
 * @return	int
 * @retval	0	success
 * @retval	1	error
 *
 */
int
tcl_stat(char *type, struct batch_status *bs, int f_opt)
{
	struct batch_status *bp;
	char *twol[2];
	char *argv[ARGNUM];
	int i, num = 0;
	char *result;

	if (interp == NULL)
		return 1;

	if (f_opt == 0)
		return 1;

	twol[0] = type;
	for (bp = bs; bp; bp = bp->next) {
		char *threel[3];

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

		argv[num++] = Tcl_Merge(3, threel);
		free(threel[1]); /* malloc'ed in attrlist() */
		if (num == ARGNUM)
			break;
	}
	twol[1] = Tcl_Merge(num, argv);
	for (i = 0; i < num; i++)
		free(argv[i]);

	result = Tcl_Merge(2, twol);
	Tcl_SetVar(interp, "objects", result,
		   TCL_GLOBAL_ONLY |
			   TCL_LIST_ELEMENT |
			   TCL_APPEND_VALUE);
	free(twol[1]);
	free(result);
	return 0;
}
#endif /* localmod 071 */

void
#ifdef NAS /* localmod 071 */
tcl_run(int tcl_opt)
#else
tcl_run(int f_opt)
#endif /* localmod 071 */
{
	if (interp == NULL)
		return;

#ifdef NAS /* localmod 071 */
	if (tcl_opt &&
#else
	if (f_opt &&
#endif /* localmod 071 */
	    Tcl_EvalFile(interp, script) != TCL_OK) {
		char *trace;

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

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

#else
#define tcl_init()
#define tcl_addarg(name, arg)
#ifdef NAS /* localmod 071 */
#define tcl_stat(type, bs, tcl_opt) 1
#define tcl_run(tcl_opt)
#else
#define tcl_stat(type, bs, f_opt) 1
#define tcl_run(f_opt)
#endif /* localmod 071 */
#endif /* TCL_QSTAT */

int
main(int argc, char **argv, char **envp) /* qstat */
{
	int added_queue;
	int c;
	int errflg = 0;
	int any_failed = 0;
	extern char *optarg;
	char *conflict = "qstat: conflicting options.\n";
	char *pc;
	int located = FALSE;
	char extend[4];
	int wide = 0;
	int format = 0;
	time_t timenow;

#if TCL_QSTAT
	char option[3];
#endif

	char job_id[PBS_MAXCLTJOBID];

	char job_id_out[PBS_MAXCLTJOBID];
	char server_out[MAXSERVERNAME] = {0};
	char prev_server[MAXSERVERNAME] = {0};
	char server_old[MAXSERVERNAME] = "";
	char rmt_server[MAXSERVERNAME];
	char destination[PBS_MAXDEST + 1];

	char *queue_name_out;
	char *server_name_out;

	char operand[PBS_MAXCLTJOBID + 1];
	int alt_opt;
	int f_opt, B_opt, Q_opt, how_opt, E_opt;
	int p_header = TRUE;
	int stat_single_job = 0;
	int new_remote_server = 0;
	enum { JOBS,
	       QUEUES,
	       SERVERS } mode;
	struct batch_status *p_status;
	struct batch_status *p_server = NULL;
	struct attropl *p_atropl = 0;
	struct attropl *new_atropl;
#ifdef NAS /* localmod 071 */
	int tcl_opt;
	struct batch_status *p_rsvstat;
#endif /* localmod 071 */

	char *errmsg;
	char *job_list = NULL;
	size_t job_list_size = 0;
	char *query_job_list = NULL;

#if !defined(PBS_NO_POSIX_VIOLATION)
#ifdef NAS /* localmod 071 */
#define GETOPT_ARGS "aeinpqrstwxu:fGHJMQEBW:T1"
#else
#define GETOPT_ARGS "ainpqrstwxu:fGHJMQEBW:T1F:D:"
#endif /* localmod 071 */
#else
#define GETOPT_ARGS "fQBW:"
#endif /* PBS_NO_POSIX_VIOLATION */

	/*test for real deal or just version and exit*/

	PRINT_VERSION_AND_EXIT(argc, argv);
	delay_query();
	if (initsocketlib())
		return 1;

	mode = JOBS; /* default */
	alt_opt = 0;
	f_opt = 0;
	B_opt = 0;
	Q_opt = 0;
	E_opt = 0;
	how_opt = 0;
#ifdef NAS /* localmod 071 */
	tcl_opt = -2;
#endif /* localmod 071 */
	extend[0] = '\0';

#if TCL_QSTAT

	tcl_init();
	tcl_addarg(flags, argv[0]);
	option[0] = '-';
	option[2] = '\0';
#endif /* TCL_QSTAT */

	while ((c = getopt(argc, argv, GETOPT_ARGS)) != EOF) {
#if TCL_QSTAT
		option[1] = (char) c;
		tcl_addarg(flags, option);
		tcl_addarg(flags, optarg);
#endif /* TCL_QSTAT */
		switch (c) {

#if !defined(PBS_NO_POSIX_VIOLATION)

			case 'a':
				alt_opt |= ALT_DISPLAY_a;
				display_attribs = &alt_attribs[0];
				break;

#ifdef NAS /* localmod 071 */
			case 'e':
				tcl_opt += 1;
				break;
#endif /* localmod 071 */

			case 'T':
				alt_opt |= ALT_DISPLAY_T;
				display_attribs = &alt_attribs[0];
				add_atropl((struct attropl **) &display_attribs, ATTR_estimated, "start_time", "", SET);
				add_atropl((struct attropl **) &display_attribs, ATTR_stime, NULL, "", SET);
				break;

			case 'i':
				alt_opt |= ALT_DISPLAY_i;
				display_attribs = &alt_attribs[0];
#ifdef NAS /* localmod 071 */
				add_atropl(&p_atropl, ATTR_state, NULL, "HQTW", EQ);
#else
				add_atropl(&p_atropl, ATTR_state, NULL, "EHQTW", EQ);
#endif /* localmod 071 */
				break;

			case 'r':
				alt_opt |= ALT_DISPLAY_r;
				display_attribs = &alt_attribs[0];
#ifdef NAS /* localmod 071 */
				add_atropl(&p_atropl, ATTR_state, NULL, "BERS", EQ);
#else
				add_atropl(&p_atropl, ATTR_state, NULL, "RS", EQ);
#endif /* localmod 071 */
				break;

			case 'H':
				alt_opt |= ALT_DISPLAY_H;
				display_attribs = &alt_attribs[0];
				if (strchr(extend, (int) 'x') == NULL)
					strcat(extend, "x");
				add_atropl(&p_atropl, ATTR_state, NULL, "MFX", EQ);
				break;

			case 't':
				/* send 't' in extend field to include sub jobs */
				if (strchr(extend, (int) 't') == NULL)
					strcat(extend, "t");
				break;

			case 'x':
				/* send 'x' in extend field to include history jobs */
				if (strchr(extend, (int) 'x') == NULL)
					strcat(extend, "x");
				break;

			case 'u':
				alt_opt |= ALT_DISPLAY_u;
				display_attribs = &alt_attribs[0];
				add_atropl(&p_atropl, ATTR_u, NULL, optarg, EQ);
				break;

			case 'n':
				alt_opt |= ALT_DISPLAY_n;
				if (display_attribs == &basic_attribs[0] || f_opt == 1)
					display_attribs = &alt_attribs[0];
				add_atropl((struct attropl **) &display_attribs, ATTR_exechost,
					   NULL, "", SET);
				f_opt = 0;
				break;

			case 'p':
				how_opt |= ALT_DISPLAY_p;
				add_atropl((struct attropl **) &display_attribs, ATTR_l, NULL, "", EQ);
				add_atropl((struct attropl **) &display_attribs, ATTR_array_state_count, NULL, "", EQ);
				break;

			case 's':
				alt_opt |= ALT_DISPLAY_s;
				if (display_attribs == &basic_attribs[0] || f_opt == 1)
					display_attribs = &alt_attribs[0];
				add_atropl((struct attropl **) &display_attribs, ATTR_comment,
					   NULL, "", SET);
				f_opt = 0;
				break;

			case 'q':
				alt_opt |= ALT_DISPLAY_q;
				mode = QUEUES;
				break;

			case 'G':
				alt_opt |= ALT_DISPLAY_G;
				display_attribs = &alt_attribs[0];
				break;

			case 'J':
				add_atropl(&p_atropl, ATTR_array, NULL, "True", EQ);
				break;

			case 'M':
				alt_opt |= ALT_DISPLAY_Mw;
				display_attribs = &alt_attribs[0];
				break;

			case '1':
				alt_opt |= ALT_DISPLAY_1l;
				break;

			case 'w':
				alt_opt |= ALT_DISPLAY_w;
				wide = 1;
				break;
#endif /* PBS_NO_POSIX_VIOLATION */

			case 'f':
				f_opt = 1;
				display_attribs = NULL; /* get all attributes */
				break;

			case 'B':
				B_opt = 1;
				mode = SERVERS;
				if (Q_opt || (alt_opt && !wide)) {
					fprintf(stderr, "%s", conflict);
					errflg++;
				}
				break;

			case 'Q':
				Q_opt = 1;
				mode = QUEUES;
				if (B_opt || (alt_opt && !wide)) {
					fprintf(stderr, "%s", conflict);
					errflg++;
				}
				break;
			case 'E':
				E_opt = 1;
				break;

			case 'D':
				if (output_format == FORMAT_DSV)
					dsv_delim = optarg;
				else
					errflg++;
				break;

			case 'F':
				for (format = FORMAT_DEFAULT; format < FORMAT_MAX; format++) {
					if (strcasecmp(optarg, output_format_names[format]) == 0) {
						output_format = format;
						break;
					}
				}
				if (format >= FORMAT_MAX)
					errflg++;
				break;

			case 'W':
#if (TCL_QSTAT == 0)
				pc = optarg;
				while (*pc) {
					switch (*pc) {
						case 'a':
							alt_opt |= ALT_DISPLAY_a;
							break;

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

						case 'r':
							alt_opt |= ALT_DISPLAY_r;
#ifdef NAS /* localmod 071 */
							add_atropl(&p_atropl, ATTR_state, NULL, "BRS", EQ);
#else
							add_atropl(&p_atropl, ATTR_state, NULL, "RS", EQ);
#endif /* localmod 071 */
							break;

						case 'H':
							alt_opt |= ALT_DISPLAY_H;
							if (strchr(extend, (int) 'x') == NULL)
								strcat(extend, "x");
							add_atropl(&p_atropl, ATTR_state, NULL, "MF", 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 == ' ')
								;
							add_atropl(&p_atropl, ATTR_u, NULL, pc, EQ);
							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 'G':
							alt_opt |= ALT_DISPLAY_G;
							break;

						case 'M':
							alt_opt |= ALT_DISPLAY_Mw;
							break;

						case '1':
							alt_opt |= ALT_DISPLAY_1l;
							break;

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

						default:
							errflg++;
					}
					++pc;
				}
#endif /* (TCL_QSTAT == 0) */
				break;

			case '?':
			default:
				errflg++;
		}
	}

#if !defined(PBS_NO_POSIX_VIOLATION)
#ifdef NAS /* localmod 071 */
#if TCL_QSTAT
	if (tcl_opt) {
		display_attribs = NULL; /* get all attributes */
	}
#endif
#endif /* localmod 071 */

	/* certain combinations are not allowed */

#ifdef NAS /* localmod 071 */
	if (f_opt == 1 && alt_opt != 0) {
		fprintf(stderr, conflict);
		errflg++;
	}
#endif /* localmod 071 */
	c = alt_opt & (ALT_DISPLAY_a | ALT_DISPLAY_i | ALT_DISPLAY_r | ALT_DISPLAY_q | ALT_DISPLAY_H);
	if ((c != 0) && ((c != ALT_DISPLAY_a) && (c != ALT_DISPLAY_i) &&
			 (c != ALT_DISPLAY_r) && (c != ALT_DISPLAY_q) &&
			 (c != ALT_DISPLAY_H))) {
		fprintf(stderr, "%s", conflict);
		errflg++;
	}
	c = alt_opt & (ALT_DISPLAY_Mw | ALT_DISPLAY_G);
	if (c == (ALT_DISPLAY_Mw | ALT_DISPLAY_G)) {
		fprintf(stderr, "%s", conflict);
		errflg++;
	}
	if (!(output_format == FORMAT_DEFAULT || f_opt)) {
		fprintf(stderr, "%s", conflict);
		errflg++;
	}
#ifndef NAS /* localmod 071 */
	if ((alt_opt & ALT_DISPLAY_q) && (f_opt == 1)) {
		fprintf(stderr, "%s", conflict);
		errflg++;
	}
#endif /* localmod 071 */
	if ((alt_opt & ALT_DISPLAY_1l) && !(alt_opt & (ALT_DISPLAY_n | ALT_DISPLAY_s))) {
		fprintf(stderr, "%s", conflict);
		errflg++;
	}
	if (wide) {
		if (output_format != FORMAT_DEFAULT) {
			fprintf(stderr, "qstat: option w cannot be used with -F\n");
			errflg++;
		}
	}
#endif /* PBS_NO_POSIX_VIOLATION */

	if (errflg) {
		static char usag2[] = "qstat --version\n";
		static char usage[] = "usage: \n\
qstat [-f] [-J] [-p] [-t] [-x] [-E] [-F format | -w] [-D delim] [ job_identifier... | destination... ]\n\
qstat [-a|-i|-r|-H|-T] [-J] [-t] [-u user] [-n] [-s] [-G|-M] [-1] [-w]\n\
\t[ job_identifier... | destination... ]\n\
qstat -Q [-f] [-F format] [-D delim] [ destination... ]\n\
qstat -q [-G|-M] [ destination... ]\n\
qstat -B [-f] [-F format] [-D delim] [ server_name... ]\n";
		fprintf(stderr, "%s", usage);
		fprintf(stderr, "%s", usag2);
		exit(2);
	}

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

	/*perform needed security library initializations (including none)*/

	if (CS_client_init() != CS_SUCCESS)
		exit_qstat("unable to initialize security library.");

	/* keep original list for reuse with next operand */
	/* in case a queue_name is added to front of list */
	added_queue = 0;
	new_atropl = p_atropl;

	if (output_format == FORMAT_DSV)
		delimiter = dsv_delim;
	else if (output_format == FORMAT_JSON) {
		delimiter = "";
		/* adding prologue to json output. */
		timenow = time(0);
		if ((json_root = pbs_json_create_object()) == NULL)
			exit_qstat("json error");
		if (pbs_json_insert_number(json_root, "timestamp", (double) timenow))
			exit_qstat("json error");
		if (pbs_json_insert_string(json_root, "pbs_version", PBS_VERSION))
			exit_qstat("json error");
		if (pbs_json_insert_string(json_root, "pbs_server", def_server))
			exit_qstat("json error");
	}

	if (optind >= argc) { /* If no arguments, then set defaults */
		switch (mode) {

			case JOBS:
				server_out[0] = '@';
				pbs_strncpy(&server_out[1], def_server, sizeof(server_out) - 1);
				tcl_addarg(ops, server_out);

				job_id_out[0] = '\0';
				server_out[0] = '\0';
				goto job_no_args;
			case QUEUES:
				server_out[0] = '@';
				pbs_strncpy(&server_out[1], def_server, sizeof(server_out) - 1);
				tcl_addarg(ops, server_out);

				queue_name_out = NULL;
				server_out[0] = '\0';
				goto que_no_args;
			case SERVERS:
				tcl_addarg(ops, def_server);

				server_out[0] = '\0';
				goto svr_no_args;
		}
	}
	if (E_opt == 1 && mode == JOBS) {
		/* allocate enough memory to store list of job ids */
		job_list_size = ((argc - 1) * (PBS_MAXCLTJOBID + 1));
		job_list = calloc(argc - 1, PBS_MAXCLTJOBID + 1);
		if (job_list == NULL)
			exit_qstat("out of memory");
		/* sort all jobs */
		qsort(&argv[optind], (argc - optind), sizeof(char *), cmp_jobs);
	}
	for (; optind < argc; optind++) {

		located = FALSE;

		pbs_strncpy(operand, argv[optind], sizeof(operand));
		tcl_addarg(ops, operand);

		switch (mode) {

			case JOBS:			    /* get status of batch jobs */
				if (pbs_isjobid(operand)) { /* must be a job-id */
					stat_single_job = 1;
					pbs_strncpy(job_id, operand, sizeof(job_id));
					if (get_server(job_id, job_id_out, server_out)) {
						fprintf(stderr, "qstat: illegally formed job identifier: %s\n", job_id);
#ifdef NAS /* localmod 071 */
						(void) tcl_stat(error, NULL, tcl_opt);
#else
						(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
						any_failed = 1;
						break;
					}
					if (E_opt == 1) {
						/* Local Server */
						if (server_out[0] == '\0' || (strcmp(server_out, def_server) == 0)) {
							/* This is probably the first job id requested from primary server */
							if (prev_server[0] == '\0')
								pbs_strncpy(prev_server, def_server, sizeof(prev_server));
							strncat(job_list, job_id_out, job_list_size - strlen(job_list));
							strncat(job_list, ",", job_list_size - strlen(job_list));
							if (optind != argc - 1)
								continue;
							else {
								free(query_job_list);
								query_job_list = strdup(job_list);
								job_list[0] = '\0';
							}
						} else {
							/* Remote server but jobs in continuation */
							if ((prev_server[0] == '\0') || (strcmp(server_out, prev_server) == 0)) {
								/* This is probably the first job id requested and not from primary server */
								if (prev_server[0] == '\0')
									pbs_strncpy(prev_server, server_out, sizeof(prev_server));
								strncat(job_list, job_id_out, job_list_size - strlen(job_list));
								strncat(job_list, ",", job_list_size - strlen(job_list));
								if (optind != argc - 1)
									continue;
								else {
									/* It's a new remote server and the only job */
									new_remote_server = 1;
									free(query_job_list);
									query_job_list = strdup(job_list);
									job_list[0] = '\0';
								}
							} else {
								/* A new remote server */
								new_remote_server = 1;
								free(query_job_list);
								query_job_list = strdup(job_list);
								snprintf(job_list, job_list_size, "%s,", job_id_out);
							}
						}
					}
				} else { /* must be a destination-id */
					if (E_opt == 1) {
						fprintf(stderr, "qstat: Express option can only be used with job ids\n");
						return 1;
					}
					stat_single_job = 0;
					pbs_strncpy(destination, operand, sizeof(destination));
					if (parse_destination_id(destination,
								 &queue_name_out,
								 &server_name_out)) {
						fprintf(stderr, "qstat: illegally formed destination: %s\n", destination);
#ifdef NAS /* localmod 071 */
						(void) tcl_stat(error, NULL, tcl_opt);
#else
						(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
						any_failed = 1;
						break;
					} else {
						if (notNULL(server_name_out)) {
							pbs_strncpy(server_out, server_name_out, sizeof(server_out));
						} else {
							server_out[0] = '\0';
						}
						pbs_strncpy(job_id_out, queue_name_out, sizeof(job_id_out));
						if (*queue_name_out != '\0') {
							/* add "destination" to front of list */
							add_atropl(&new_atropl, ATTR_q, NULL, queue_name_out, EQ);
							added_queue = 1;
						}
					}
				}
			job_no_args:
				/* We could have been sent here after p_server was set. Free it. */
				pbs_statfree(p_server);
				p_server = NULL;
				if (E_opt == 1)
					conn = cnt2server(prev_server);
				else
					conn = cnt2server(server_out);

				if (conn <= 0) {
					fprintf(stderr, "qstat: cannot connect to server %s (errno=%d)\n",
						def_server, pbs_errno);
#ifdef NAS /* localmod 071 */
					(void) tcl_stat(error, NULL, tcl_opt);
#else
					(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
					any_failed = conn;
					break;
				}

				if (strcmp(pbs_server, server_old) != 0) {
					/* changing to a different server */
					p_server = pbs_statserver(conn, NULL, NULL);
#ifdef NAS /* localmod 071 */
					p_rsvstat = pbs_statresv(conn, NULL, NULL, NULL);
#endif /* localmod 071 */
					pbs_strncpy(server_old, pbs_server, sizeof(server_old));
				} else {
					p_server = NULL;
				}

				if (p_server == NULL && pbs_errno != PBSE_NONE) {
					any_failed = pbs_errno;
					if ((errmsg = pbs_geterrmsg(conn)) != NULL)
						fprintf(stderr, "qstat: %s\n", errmsg);
					else
						fprintf(stderr, "qstat: Error %d\n", pbs_errno);
					break;
				}

				/* check the server attribute max_job_sequence_id value */
				if (p_server != NULL) {
					int check_seqid_len; /* for dynamic qstat width */
					check_seqid_len = check_max_job_sequence_id(p_server);
					if (check_seqid_len == 1) {
						how_opt |= ALT_DISPLAY_INCR_WIDTH; /* increase column width */
					}
				}

				if ((stat_single_job == 1) || (new_atropl == 0)) {
					if (E_opt == 1)
						p_status = pbs_statjob(conn, query_job_list, display_attribs, extend);
					else
						p_status = pbs_statjob(conn, job_id_out, display_attribs, extend);
				} else {
					p_status = pbs_selstat(conn, new_atropl, NULL, extend);
				}

				if (added_queue) {
					/* added queue name as first entry in atropl list,  */
					/* remove it and reset list pointer to base list    */
					free(new_atropl);
					new_atropl = p_atropl;
					added_queue = 0;
				}
				if (p_status == NULL) {
					if ((pbs_errno == PBSE_UNKJOBID) && !located) {
						located = TRUE;
						if (locate_job(job_id_out, server_out, rmt_server)) {
							pbs_disconnect(conn);
							strcpy(server_out, rmt_server);
							goto job_no_args;
						}
#ifdef NAS /* localmod 071 */
						(void) tcl_stat("job", NULL, tcl_opt);
#else
						(void) tcl_stat("job", NULL, f_opt);
#endif /* localmod 071 */
						if (pbs_errno != PBSE_HISTJOBID) {
							prt_job_err("qstat", conn, job_id_out);
							any_failed = pbs_errno;
						}
					} else {
#ifdef NAS /* localmod 071 */
						if (p_server) {
							tcl_stat("serverhdr", p_server, tcl_opt);
							tcl_stat("resv", p_rsvstat, tcl_opt);
						}
						(void) tcl_stat("job", NULL, tcl_opt);
#else
						(void) tcl_stat("job", NULL, f_opt);
#endif /* localmod 071 */
						if (pbs_errno != PBSE_NONE && pbs_errno != PBSE_HISTJOBID) {
							if (pbs_errno == PBSE_ATTRRO && alt_opt & ALT_DISPLAY_T)
								fprintf(stderr, "qstat: -T option is unavailable.\n");
							else
								prt_job_err("qstat", conn, job_id_out);
							any_failed = pbs_errno;
						}
					}

					/*
					 * If it is qstat command and error is PBSE_HISTJOBID, then
					 * handle it separately without using prt_job_err() API as we
					 * are adding some extra message.
					 */
					if (pbs_errno == PBSE_HISTJOBID) {
						errmsg = pbs_geterrmsg(conn);
						if (errmsg) {
							fprintf(stderr,
								"qstat: %s %s, use -x or -H to obtain historical job information\n",
								job_id_out, errmsg);
						}
						any_failed = pbs_errno;
					}
				} else {

#ifdef NAS /* localmod 071 */
					if (p_server) {
						tcl_stat("serverhdr", p_server, tcl_opt);
						tcl_stat("resv", p_rsvstat, tcl_opt);
					}
					if (tcl_stat("job", p_status, tcl_opt)) {
						if (alt_opt != 0) {
							altdsp_statjob(p_status, p_server, alt_opt, wide, how_opt);
						} else if (display_statjob(p_status, p_server, f_opt, how_opt))
							exit_qstat("out of memory");
					}
#else

					if ((alt_opt & ~ALT_DISPLAY_w) != 0 && !(wide && f_opt)) {
						altdsp_statjob(p_status, p_server, alt_opt, wide, how_opt);
					} else if (f_opt == 0 || tcl_stat("job", p_status, f_opt))
						if (display_statjob(p_status, p_server, f_opt, how_opt, alt_opt, wide))
							exit_qstat("out of memory");
#endif /* localmod 071 */
					p_header = FALSE;
					pbs_statfree(p_status);
				}
				pbs_statfree(p_server);
				p_server = NULL;
				pbs_disconnect(conn);
				if (E_opt == 1) {
					free(query_job_list);
					query_job_list = NULL;
					if (new_remote_server == 1) {
						/* If there is a new remote server
						* then update the prev_server to new server
						*/
						strcpy(prev_server, server_out);
						new_remote_server = 0;
						/* If we are at the end of the loop then
						* query jobs one more time if and only if
						* there are jobs present in job_list
						*/
						if (optind == argc - 1) {
							if (query_job_list != NULL) {
								free(query_job_list);
								query_job_list = NULL;
							}
							if (job_list[0] != '\0') {
								query_job_list = strdup(job_list);
								optind++;
								goto job_no_args;
							}
						}
					}
				}
				break;

			case QUEUES: /* get status of batch queues */
				pbs_strncpy(destination, operand, sizeof(destination));
				if (parse_destination_id(destination,
							 &queue_name_out,
							 &server_name_out)) {
					fprintf(stderr, "qstat: illegal 'destination' value\n");
#ifdef NAS /* localmod 071 */
					(void) tcl_stat(error, NULL, tcl_opt);
#else
					(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
					any_failed = 1;
					break;
				} else {
					if (notNULL(server_name_out)) {
						strcpy(server_out, server_name_out);
					} else
						server_out[0] = '\0';
				}
			que_no_args:
				conn = cnt2server(server_out);
				if (conn <= 0) {
					fprintf(stderr, "qstat: cannot connect to server %s (errno=%d)\n", def_server, pbs_errno);
#ifdef NAS /* localmod 071 */
					(void) tcl_stat(error, NULL, tcl_opt);
#else
					(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
					any_failed = conn;
					break;
				}

				p_status = pbs_statque(conn, queue_name_out, NULL, NULL);
				if (p_status == NULL) {
					if (pbs_errno) {
						errmsg = pbs_geterrmsg(conn);
						if (errmsg != NULL) {
							fprintf(stderr, "qstat: %s ", errmsg);
						} else
							fprintf(stderr, "qstat: Error (%d) getting status of queue ", pbs_errno);
						fprintf(stderr, "%s\n", queue_name_out);
#ifdef NAS /* localmod 071 */
						(void) tcl_stat(error, NULL, tcl_opt);
#else
						(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
						any_failed = pbs_errno;
					}
				} else {
					if (alt_opt & ALT_DISPLAY_q) {
						altdsp_statque(pbs_server, p_status, alt_opt);
#ifdef NAS /* localmod 071 */
					} else if (tcl_stat("queue", p_status, tcl_opt)) {
#else
					} else if (tcl_stat("queue", p_status, f_opt)) {
#endif /* localmod 071 */
						if (display_statque(p_status, p_header, f_opt, alt_opt))
							exit_qstat("out of memory");
					}
					p_header = FALSE;
					pbs_statfree(p_status);
				}
				pbs_disconnect(conn);
				break;

			case SERVERS: /* get status of batch servers */
				pbs_strncpy(server_out, operand, sizeof(server_out));
			svr_no_args:
				conn = cnt2server(server_out);
				if (conn <= 0) {
					fprintf(stderr, "qstat: cannot connect to server %s (errno=%d)\n",
						def_server, pbs_errno);
#ifdef NAS /* localmod 071 */
					(void) tcl_stat(error, NULL, tcl_opt);
#else
					(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
					any_failed = conn;
					break;
				}

				p_status = pbs_statserver(conn, NULL, NULL);
				if (p_status == NULL) {
					if (pbs_errno) {
						errmsg = pbs_geterrmsg(conn);
						if (errmsg != NULL) {
							fprintf(stderr, "qstat: %s ", errmsg);
						} else
							fprintf(stderr, "qstat: Error (%d) getting status of server ", pbs_errno);
						fprintf(stderr, "%s\n", server_out);
#ifdef NAS /* localmod 071 */
						(void) tcl_stat(error, NULL, tcl_opt);
#else
						(void) tcl_stat(error, NULL, f_opt);
#endif /* localmod 071 */
						any_failed = pbs_errno;
					}
				} else {
#ifdef NAS /* localmod 071 */
					if (tcl_stat("server", p_status, tcl_opt))
#else
					if (tcl_stat("server", p_status, f_opt))
#endif /* localmod 071 */
						if (display_statserver(p_status, p_header, f_opt, alt_opt))
							exit_qstat("out of memory");
					p_header = FALSE;
					pbs_statfree(p_status);
				}
				pbs_disconnect(conn);
				break;

		} /* switch */

		if (any_failed == PBSE_PERM)
			break;
	}
	if (output_format == FORMAT_JSON) {
		if (pbs_json_print(json_root, stdout))
			fprintf(stderr, "json error\n");
		pbs_json_delete(json_root);
	}
#ifdef NAS /* localmod 071 */
	tcl_run(tcl_opt);
#else
	tcl_run(f_opt);
#endif /* localmod 071 */
	if (E_opt == 1) {
		if (query_job_list != NULL)
			free(query_job_list);
		free(job_list);
	}
	/*cleanup security library initializations before exiting*/
	CS_close_app();

	/*
	 * If the server is not configured for history jobs i.e. job_history_enable
	 * svr attr is unset/set to FALSE, qstat command with -x/-H option is being
	 * used, then pbs_selstat()/pbs_statjob() will return PBSE_JOBHISTNOTSET
	 * error code. But command will exit with exit code '0'after printing the
	 * corresponding error message. i.e. "job_history_enable is set to false".
	 */
	if (any_failed == PBSE_JOBHISTNOTSET)
		any_failed = 0;
	exit(any_failed);
}

/**
 * @brief
 *	cvtResvstate - converts a job reservation "state code" to
 *	descriptive text string
 *
 * @param[out] pcode - descriptive text string
 *
 * @return	string
 * @retval	Pointer to the descriptive text string
 * @retval	The input pointer if conversion fails
 */

static char *
cvtResvstate(char *pcode)
{
	int i;
	static char *resvStrings[] = {"RESV_NONE", "RESV_UNCONFIRMED",
				      "RESV_CONFIRMED", "RESV_WAIT",
				      "RESV_TIME_TO_RUN", "RESV_RUNNING",
				      "RESV_FINISHED", "RESV_BEING_DELETED",
				      "RESV_DELETED", "RESV_DELETING_JOBS",
				      "RESV_BEING_ALTERED"};

	/*Remark: the static buffer below is used to get around a problem with
	 *	the linux strtok() library function.  There is a "bugs" comment
	 *	on the man page for strtok() that mentions that the function can't
	 *	be called on constant strings.  I presume that this is what is
	 *	causing strtok in prt_attr() to fail when fed the return value
	 *	resvStrings[i], as we had been doing.  So, we return acopy instead
	 */
	static char acopy[25];

	switch (i = atoi(pcode)) {
		case RESV_NONE:
		case RESV_UNCONFIRMED:
		case RESV_CONFIRMED:
		case RESV_WAIT:
		case RESV_TIME_TO_RUN:
		case RESV_RUNNING:
		case RESV_FINISHED:
		case RESV_BEING_DELETED:
		case RESV_DELETED:
		case RESV_DELETING_JOBS:
		case RESV_BEING_ALTERED:
			pbs_strncpy(acopy, resvStrings[i], sizeof(acopy));
			return acopy;

		default:
			return pcode;
	}
}

/*!
 *	cmp_est_time -  compare function used with bs_isort
 *		        compares based on:
 *			1. stime
 *			2. if estimated.start_time < now sort to bottom
 *			3. estimated.start_time
 *
 *	\param a
 *	\param b
 *
 *	\return -1: a < b
 *	\return  0: a == b
 *	\return  1: a > b
 *
 */
static int
cmp_est_time(struct batch_status *a, struct batch_status *b)
{
	char *attrval;
	time_t est_a = -1;
	time_t est_b = -1;
	time_t stime_a = -1;
	time_t stime_b = -1;
	time_t now;

	attrval = get_attr(a->attribs, ATTR_estimated, "start_time");
	if (attrval != NULL)
		est_a = atol(attrval);

	attrval = get_attr(b->attribs, ATTR_estimated, "start_time");
	if (attrval != NULL)
		est_b = atol(attrval);

	attrval = get_attr(a->attribs, ATTR_stime, NULL);
	if (attrval != NULL)
		stime_a = atol(attrval);

	attrval = get_attr(b->attribs, ATTR_stime, NULL);
	if (attrval != NULL)
		stime_b = atol(attrval);

	/* sort running jobs first by stime */
	if (stime_a >= 0 || stime_b >= 0) {
		if (stime_a == -1 && stime_b >= 0)
			return 1;
		else if (stime_a >= 0 && stime_b == -1)
			return -1;
		else if (stime_a < stime_b)
			return -1;
		else if (stime_a > stime_b)
			return 1;
		else
			return 0;
	}

	if (est_a == est_b)
		return 0;

	time(&now);
	/* if estimated start time is before now, sort to bottom */
	if (est_a == -1 || est_a < now)
		return 1;

	if (est_b == -1 || est_b < now)
		return -1;

	if (est_a < est_b)
		return -1;

	if (est_a > est_b)
		return 1;

	return 0;
}

#define SEC_IN_WEEK 604800
/*!
 *	cnvt_est_start_time - convert estimated start time to a time form
 *			      either a short form of 5 characters or the
 *			      wider form of convert_time().
 *
 *	\param est_time - the string value of estimated.start time
 *				ex: "1247683654"
 *	\param wide	- 1 if we should use the wide format 0 if not
 *
 *	\return converted time string
 *	\return "--" if estimated.start_time  < now or est_time == NULL
 *	\return "?" if estimated.start_time == 0
 *
 */
char *
cnvt_est_start_time(char *est_time, int wide)
{
	time_t t;
	time_t start_time;
	char buf[16];
	char buf2[16];
	static char timebuf[32];
	struct tm *tmptr;
	struct tm nowtm;
	struct tm esttm;

	if (est_time == NULL)
		return "--";

	start_time = atol(est_time);

	/* special case: unknown estimated start time: return "?" */
	if (start_time == 0)
		return "?";

	time(&t);
	tmptr = localtime(&t);
	if (tmptr != NULL) {
		nowtm = *tmptr;
		tmptr = localtime(&start_time);
		if (tmptr != NULL)
			esttm = *tmptr;
		else
			return "--";
	} else
		return "--";

	/* special case: estimated start time before now: return "--" */
	if (start_time < t)
		return "--";

	if (wide)
		return convert_time(est_time);

	/* within the current day: HH:MM */
	if (nowtm.tm_year == esttm.tm_year && nowtm.tm_yday == esttm.tm_yday) {
		strftime(timebuf, 32, "%H:%M", &esttm);
	} /* within 7 days of now */
	else if ((start_time - t) < SEC_IN_WEEK) {
		strftime(buf, 16, "%a", &esttm);
		strftime(buf2, 16, "%H", &esttm);
		snprintf(timebuf, 32, "%2.2s %s", buf, buf2);
	} /* within the current year: short form of the month */
	else if (nowtm.tm_year == esttm.tm_year) {
		strftime(timebuf, 32, "%b", &esttm);
		/* after the current year: the 4 digit year */
	} else if (esttm.tm_year - nowtm.tm_year < 5) {
		strftime(timebuf, 32, "%Y", &esttm);
	} else { /* after 5 years, print ">5yrs" */
		strcpy(timebuf, ">5yrs");
	}

	return timebuf;
}
