              <<< VAXAXP::NOTES$:[NOTES$LIBRARY]VMSNOTES.NOTE;1 >>>
               -< VAX and Alpha VMS - Digital Internal Use Only >-
================================================================================
Note 729.1     Security risk if this is installed with WORLD priv?        1 of 5
AD::DEVER                                           905 lines  18-APR-1996 15:45
                                  -< Source >-
--------------------------------------------------------------------------------
/*	SHOW_PROC.C 
 *	Dan Dever
 */

#include <ctype>
#include <stdio>
#include <stdlib>
#include <string>
#include <descrip>
#include <jbcmsgdef>
#include <jpidef>
#include <pscandef>
#include <quidef>
#include <rms>
#include <ssdef>
#include <starlet>
#include <stsdef>

/* from $STATEDEF in SYS$LIBRARY:LIB.MLB */
#define	SCH$C_COLPG	1
#define	SCH$C_MWAIT	2
#define	SCH$C_CEF	3
#define	SCH$C_PFW	4
#define	SCH$C_LEF	5
#define	SCH$C_LEFO	6
#define	SCH$C_HIB	7
#define	SCH$C_HIBO	8
#define	SCH$C_SUSP	9
#define	SCH$C_SUSPO	10
#define	SCH$C_FPG	11
#define	SCH$C_COM	12
#define	SCH$C_COMO	13
#define	SCH$C_CUR	14

#define MY_FP_NODE 0x1
#define MY_FP_DEV  0x2
#define MY_FP_DIR  0x4
#define MY_FP_NAME 0x8
#define MY_FP_TYPE 0x10
#define MY_FP_VER  0x20

#define GOTQUEUENAME 0x1
#define IMAGEINJOB   0x2
#define STATEINJOB   0x4
#define PRINTJOB     0x8

#define CPULEN 		10
#define IMAGELEN 	NAM$C_MAXRSS
#define IOLEN		8
#define MEMLEN 		7
#define MODELEN 	11
#define NODELEN 	6
#define PGFLTLEN 	9
#define PIDLEN 		8
#define PRIBLEN 	2
#define PROCLEN 	15
#define QUEUENAMELEN	31
#define SCANOPTIONS	10
#define STATELEN	5
#define USERLEN 	12
#define VPGLEN 		7
#define PRTIMAGELEN (132 - NODELEN - MODELEN - USERLEN - PRIBLEN - PIDLEN - PROCLEN -\
                           CPULEN - IOLEN - PGFLTLEN - MEMLEN - VPGLEN - STATELEN - 12)

typedef struct {short   buflen;								/* Length of output buffer */
		short	itmcode;							/* Item code */
		void    *buffer;							/* Buffer address */
		void    *retlen;							/* Return length address */
	    } ITMLST;									/* Layout of item-list elements */

typedef struct {short   buflen;								/* Length of buffer */
		short	itmcode;							/* Item code */
		void    *buffer;							/* Buffer address */
		int     flags;								/* item specific flags */
	    } SITMLST;									/* Layout of item-list elements */

struct procdata {
    int bufio;
    int cputim;
    int dirio;
    int gpgcnt;
    int imagelen;
    int info;
    int jobtype;
    int masterpid;
    int mode;
    int nodelen;
    int owner;
    int pageflts;
    int pagfilcnt;
    int pgflquota;
    int pid;
    int ppgcnt;
    int prib;
    int proclen;
    int qentry;
    int qnamelen;
    int state;
    int userlen;
    int wsextent;
    int wssize;
    char imagename[IMAGELEN+1];
    char nodename[NODELEN+1];
    char procname[PROCLEN+1];
    char qname[QUEUENAMELEN+1];
    char username[USERLEN+1];
    struct procdata *sub;
    struct procdata *subchain;
    struct procdata *next;
};

/* globals */
int ImageSearch = 0;
int PrintCount = 0;
int PrintEntry = 1;
int PrintFull = 0;
long JPIControl = JPI$M_NO_TARGET_INSWAP;
SITMLST  ScanItemList[SCANOPTIONS] = {							/* Initialize $PROCESS_SCAN item list... */
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0},
    {0, 0, 0,0}};

ITMLST  JPIItemList[] = {								/* Initialize $GETJPI item list... */
    {sizeof(long), JPI$_GETJPI_CONTROL_FLAGS,&JPIControl,0},				/* [0] */
    {sizeof(long), JPI$_CPUTIM,		0,0},						/* [1] */
    {sizeof(long), JPI$_GPGCNT,		0,0},						/* [2] */
    {IMAGELEN,	   JPI$_IMAGNAME,	0,0},						/* [3] */
    {sizeof(long), JPI$_MODE,		0,0},						/* [4] */
    {NODELEN,	   JPI$_NODENAME,	0,0},						/* [5] */
    {sizeof(long), JPI$_OWNER,		0,0},						/* [6] */
    {sizeof(long), JPI$_PAGEFLTS,	0,0},						/* [7] */
    {sizeof(long), JPI$_PAGFILCNT,	0,0},						/* [8] */
    {sizeof(long), JPI$_PGFLQUOTA,	0,0},						/* [9] */
    {sizeof(long), JPI$_PID,	 	0,0},						/* [10] */
    {sizeof(long), JPI$_PPGCNT,	  	0,0},						/* [11] */
    {PROCLEN,	   JPI$_PRCNAM,		0,0},						/* [12] */
    {sizeof(long), JPI$_PRIB,		0,0},						/* [13] */
    {sizeof(long), JPI$_STATE,		0,0},						/* [14] */
    {USERLEN,	   JPI$_USERNAME,	0,0},						/* [15] */
    {sizeof(long), JPI$_WSEXTENT,	0,0},						/* [16] */
    {sizeof(long), JPI$_WSSIZE,		0,0},						/* [17] */
    {sizeof(long), JPI$_JOBTYPE,	0,0},						/* [18] */
    {sizeof(long), JPI$_BUFIO,		0,0},						/* [19] */
    {sizeof(long), JPI$_DIRIO,		0,0},						/* [20] */
    {sizeof(long), JPI$_MASTER_PID,	0,0},						/* [21] */
    {0, 0, 0,0}};

/* prototypes */
void	exit_withmsg(int status);
int 	my_fparse(char *result,char *file_spec,char *default_spec,char *related_spec,int mask,int options);
int 	my_getopt(int argc,char *argv[], char *options);
int 	pid_compare(const struct procdata **proc1,const struct procdata **proc2);
void 	print_proc(struct procdata *proc,int depth);
int 	proc_compare(const struct procdata **proc1,const struct procdata **proc2);
struct 	procdata *initprocdata(struct procdata **new);
int 	scanprocs(struct procdata *procinfo,int *count);
void 	usage(void);

main(int argc, char *argv[])
{
    int count = 0;
    int error_flag = 0;
    int i;
    int opt;
    int scan_idx = 0;
    int scanjobtype = 0;
    int scanmode = 0;
    int status;
    int usersearch = 0;
    static int scanstate[2] = {0,0};
    char *p;
    char *searchimage;
    char searchuser[USERLEN+1];
    struct procdata **sortpid,**sortproc,**index;
    struct procdata *procbase,*procinfo;
    struct procdata proctmp, *ptmp = &proctmp;
    extern char *my_optarg;
    extern int my_optind;
    extern int my_optopt;
    extern int my_optargcnt;

    /* process input options */
    /********************************************************************************/
    while((opt = my_getopt(argc,argv,":u:i:j:m:s:fbhe")) != -1) {
	if (scan_idx>=SCANOPTIONS-2) {
	    printf("%%E- Too many options, limit is %d\n",SCANOPTIONS-1);
	    error_flag++;
	    break;
	} else if (opt == 'e') {							/* supress printing of entry nos */
	    PrintEntry = 0;
	} else if (opt == 'h') {							/* print help info */
	    usage();
	    exit(EXIT_SUCCESS);
	} else if (opt == 'b') {							/* search for batch processes */
	    if (scanmode) {
		printf("Option -b is exclusive with -m\n");
		error_flag++;
		break;
	    } else {
		scanmode = JPI$K_BATCH;
		ScanItemList[scan_idx].itmcode = PSCAN$_MODE;
		ScanItemList[scan_idx].buffer = (void *) scanmode;
		scan_idx++;
	    }
	} else if (opt == 's') {							/* search for processes in state */
	    for(p=my_optarg;*p;p++) *p = toupper(*p);
	    if (!strcmp(my_optarg,"COLPG")) {
		scanstate[0] = SCH$C_COLPG;
	    } else if (!strcmp(my_optarg,"MWAIT")) {
		scanstate[0] = SCH$C_MWAIT;
	    } else if (!strcmp(my_optarg,"CEF")) {
		scanstate[0] = SCH$C_CEF;
	    } else if (!strcmp(my_optarg,"PFW")) {
		scanstate[0] = SCH$C_PFW;
	    } else if (!strcmp(my_optarg,"LEF")) {
		scanstate[0] = SCH$C_LEF;
		scanstate[1] = SCH$C_LEFO;
	    } else if (!strcmp(my_optarg,"LEFO")) {
		scanstate[0] = SCH$C_LEFO;
	    } else if (!strcmp(my_optarg,"HIB")) {
		scanstate[0] = SCH$C_HIB;
		scanstate[1] = SCH$C_HIBO;
	    } else if (!strcmp(my_optarg,"HIBO")) {
		scanstate[0] = SCH$C_HIBO;
	    } else if (!strcmp(my_optarg,"SUSP")) {
		scanstate[0] = SCH$C_SUSP;
		scanstate[1] = SCH$C_SUSPO;
	    } else if (!strcmp(my_optarg,"SUSPO")) {
		scanstate[0] = SCH$C_SUSPO;
	    } else if (!strcmp(my_optarg,"FPG")) {
		scanstate[0] = SCH$C_FPG;
	    } else if (!strcmp(my_optarg,"COM")) {
		scanstate[0] = SCH$C_COM;
		scanstate[1] = SCH$C_COMO;
	    } else if (!strcmp(my_optarg,"COMO")) {
		scanstate[0] = SCH$C_COMO;
	    } else if (!strcmp(my_optarg,"CUR")) {
		scanstate[0] = SCH$C_CUR;
	    } else {
		printf("Bad argument for option -%c: %s\n",my_optopt,my_optarg);
		error_flag++;
		break;
	    }
	} else if (opt == 'f') {							/* full output format */
	    PrintFull = 1;
	    PrintEntry = 0;
	} else if (opt == 'i') {							/* search for image containing string */
	    for(p=my_optarg;*p;p++) *p = toupper(*p);
	    ImageSearch = 1;
	    searchimage = my_optarg;
	} else if (opt == 'j') {							/* search for process in job type */
	    for(p=my_optarg;*p;p++) *p = toupper(*p);
	    if (*my_optarg == 'D') {
		if (*(my_optarg+1) == 'E') {
		    scanjobtype = JPI$K_DETACHED;
		} else if (*(my_optarg+1) == 'I') {
		    scanjobtype = JPI$K_DIALUP;
		} else {
		    printf("Bad argument for option -%c: %s\n",my_optopt,my_optarg);
		    error_flag++;
		    break;
		}
	    } else if (*my_optarg == 'N') {
		scanjobtype = JPI$K_NETWORK;
	    } else if (*my_optarg == 'B') {
		scanjobtype = JPI$K_BATCH;
	    } else if (*my_optarg == 'L') {
		scanjobtype = JPI$K_LOCAL;
	    } else if (*my_optarg == 'R') {
		scanjobtype = JPI$K_REMOTE;
	    } else {
		printf("Bad argument for option -%c: %s\n",my_optopt,my_optarg);
		error_flag++;
		break;
	    }
	    ScanItemList[scan_idx].itmcode = PSCAN$_JOBTYPE;
	    ScanItemList[scan_idx].buffer = (void *) scanjobtype;
	    scan_idx++;
	} else if (opt == 'm') {							/* search for process in mode */
	    if (scanmode) {
		printf("Option -b is exclusive with -m\n");
		error_flag++;
		break;
	    } else {
		for(p=my_optarg;*p;p++) *p = toupper(*p);
		if (*my_optarg == 'I') {
		    scanmode = JPI$K_INTERACTIVE;
		} else if (*my_optarg == 'N') {
		    scanmode = JPI$K_NETWORK;
		} else if (*my_optarg == 'B') {
		    scanmode = JPI$K_BATCH;
		} else if (*my_optarg == 'O') {
		    scanmode = JPI$K_OTHER;
		} else {
		    printf("Bad argument for option -%c: %s\n",my_optopt,my_optarg);
		    error_flag++;
		    break;
		}
		ScanItemList[scan_idx].itmcode = PSCAN$_JOBTYPE;
		ScanItemList[scan_idx].buffer = (void *) scanmode;
		scan_idx++;
	    }
	} else if (opt == 'u') {							/* search for processes by user */
	    for(p=my_optarg;*p;p++) *p = toupper(*p);
	    /* Processes on VAXes (not alphas) try to match ending blanks */
	    strcpy(searchuser,my_optarg);
	    if (strlen(my_optarg)<USERLEN) {
		strcat(searchuser,"*");
	    }
	    ScanItemList[scan_idx].itmcode = PSCAN$_USERNAME;
	    ScanItemList[scan_idx].buffer = searchuser;
	    ScanItemList[scan_idx].buflen = strlen(searchuser);
	    ScanItemList[scan_idx].flags = PSCAN$M_WILDCARD;
	    scan_idx++;
	    usersearch++;
	} else if (opt == ':') {							/* missing argument */
	    printf("%%E- Option -%c requires an argument\n",my_optopt);
	    error_flag++;
	    break;
	} else if (opt == '?') {							/* unknown option */
	    printf("%%E- Unrecognized option -%c\n",my_optopt);
	    error_flag++;
	    break;
	} else {
	    printf("Unknown problem with options\n");
	    error_flag++;
	    break;
	}
    }
    if (error_flag) {									/* exit on error while proc. options */
	usage();
	exit(EXIT_FAILURE);
    }
    if (my_optargcnt > 1) {								/* search for processes on system */
	printf("%%E- Too many arguments\n");
	usage();
	exit(EXIT_FAILURE);
    } else if (my_optargcnt == 1) {
	ScanItemList[scan_idx].itmcode = PSCAN$_NODENAME;
	ScanItemList[scan_idx].buffer = argv[my_optargcnt];
	ScanItemList[scan_idx].buflen = strlen(argv[my_optargcnt]);
	ScanItemList[scan_idx].flags = PSCAN$M_WILDCARD;
	for(p=ScanItemList[scan_idx].buffer;*p;p++) *p = toupper(*p);
	scan_idx++;
    } else if (usersearch) {								/* if user specified, default to all sys */
	ScanItemList[scan_idx].itmcode = PSCAN$_NODENAME;
	ScanItemList[scan_idx].buffer = "*";
	ScanItemList[scan_idx].buflen = 1;
	ScanItemList[scan_idx].flags = PSCAN$M_WILDCARD;
	scan_idx++;
    }

    /* Get the process info */
    /********************************************************************************/
    if ((procinfo = initprocdata(&procbase)) == NULL) {
	perror("procdata");
	exit_withmsg(EXIT_FAILURE);
    }
    status = scanprocs(procinfo,&count);						/* Scan the processes */
    if (!(status & STS$M_SUCCESS)) {
	exit_withmsg(status);
    }

    /* sort according to PID */
    /********************************************************************************/
    if ((sortpid = malloc(count*sizeof(struct procdata *))) == NULL) {
	perror("malloc");
	exit_withmsg(EXIT_FAILURE);
    }
    for(i=0;i<count;i++) {
	sortpid[i] = procinfo;
	procinfo = procinfo->next;
    }
    qsort((char *)sortpid,count,sizeof(struct procdata *),(int (*)(const void*,const void*))pid_compare);
	
    /* sort according to desired order */
    /********************************************************************************/
    if ((sortproc = malloc(count*sizeof(struct procdata *))) == NULL) {
	perror("malloc");
	exit_withmsg(EXIT_FAILURE);
    }
    procinfo = procbase;
    for(i=0;i<count;i++) {
	sortproc[i] = procinfo;
	procinfo = procinfo->next;
    }
    qsort((char *)sortproc,count,sizeof(struct procdata *),(int (*)(const void*,const void*))proc_compare);

    /* place subprocesses */
    /********************************************************************************/
    for(i=count-1;i>=0;i--) {
	if (sortproc[i]->owner) {
	    ptmp->pid = sortproc[i]->owner;
	    index = bsearch((void *)&ptmp,(void *)sortpid,count,sizeof(struct procdata *),
			    (int (*)(const void*,const void*))pid_compare);
	    procinfo = *index;
	    if (procinfo) {
		sortproc[i]->subchain = procinfo->sub;
		procinfo->sub = sortproc[i];
	    } else {
		printf("%%E- Error in sort\n");
		exit_withmsg(EXIT_FAILURE);
	    }
	}
    }
	
    /* State Search */
    /********************************************************************************/
    if (scanstate[0]) {
	for(i=0;i<count;i++) {
	    if (sortproc[i]->state == scanstate[0] || sortproc[i]->state == scanstate[1]) {
		if (sortproc[i]->owner) {
		    ptmp->pid = sortproc[i]->masterpid;
		    index = bsearch((void *)&ptmp,(void *)sortpid,count,sizeof(struct procdata *),
				    (int (*)(const void*,const void*))pid_compare);
		    procinfo = *index;
		} else {
		    procinfo = sortproc[i];
		}
		if (procinfo) {
		    procinfo->info |= STATEINJOB|PRINTJOB;
		} else {
		    printf("%%E- Error in state search\n");
		    exit_withmsg(EXIT_FAILURE);
		}
	    }
	}
    }

    /* Image Search */
    /********************************************************************************/
    if (ImageSearch) {
	for(i=0;i<count;i++) {
	    if (sortproc[i]->imagelen && strstr(sortproc[i]->imagename,searchimage)) {
		if (sortproc[i]->owner) {
		    ptmp->pid = sortproc[i]->masterpid;
		    index = bsearch((void *)&ptmp,(void *)sortpid,count,sizeof(struct procdata *),
				    (int (*)(const void*,const void*))pid_compare);
		    procinfo = *index;
		} else {
		    procinfo = sortproc[i];
		}
		if (procinfo) {
		    procinfo->info |= IMAGEINJOB|PRINTJOB;
		} else {
		    printf("%%E- Error in image search\n");
		    exit_withmsg(EXIT_FAILURE);
		}
	    }
	}
    }

    /* Print the results */
    /********************************************************************************/
    printf("%-*s %-*s %-*s %*s %-*s %-*s %-*s %*s %*s %*s %*s %*s %-s\n",
	   NODELEN,"Node",
	   MODELEN,"Jobtype",
	   USERLEN,"User",
	   PRIBLEN,"P",
	   PIDLEN,"PID",
	   PROCLEN,"Process Name",
	   CPULEN,"CPU Time",
	   IOLEN,"I/O",
	   PGFLTLEN,"Page Flt",
	   MEMLEN,"Paglets",
	   VPGLEN,"V Pgs",
	   STATELEN,"State",
	   "Image Name");
    printf("------------------------------------------------------------------");
    printf("------------------------------------------------------------------\n");
    for(i=0;i<count;i++) {
	if (!sortproc[i]->owner) {
	    if (!(ImageSearch || scanstate[0]) ||
		sortproc[i]->info & PRINTJOB) {
		print_proc(sortproc[i],0);
	    }
	}
    }
    if (!ImageSearch && !scanstate[0]) {						/* print count if not being selective */
	printf("Count: %d\n",PrintCount);
	if (PrintCount != count) {
	    printf("%%E- Error, only %d of %d items printed\n",PrintCount,count);
	    exit_withmsg(EXIT_FAILURE);
	}
    }
    exit(EXIT_SUCCESS);
} /* end of main() */

/**********************************************************************************************************************************/
void print_proc(struct procdata *proc,int depth)
{
    char buffer[256];
    char iname[256];
    int sec,min,hour,day;
    int ilen;
    int tmp;
    int cnt;
    int status;

    if (proc->pid) {
	sec   = proc->cputim/100;
	min   = sec/60;
	hour  = min/60;
	day   = hour/24;
	sec  -= 60*min;
	min  -= 60*hour;
	hour -= 24*day;

	if (!depth) {
	    if (PrintEntry && (proc->info & GOTQUEUENAME)) {
		tmp = (NODELEN+MODELEN-4) > proc->qnamelen ? proc->qnamelen : (NODELEN+MODELEN-4);
		cnt = sprintf(buffer,"%-*.*s ",NODELEN+MODELEN-4,tmp,proc->qname);	/* Batch queue name */
		cnt += sprintf(buffer+cnt,"%4d ",proc->qentry);				/* Queue entry */
	    } else {
		cnt = sprintf(buffer,"%-*s ",NODELEN,proc->nodename);			/* node */
		if (proc->jobtype == JPI$K_DETACHED) {					/* jobtype */
		    cnt += sprintf(buffer+cnt,"%-*s ",MODELEN,"DETACHED");
		} else if (proc->jobtype == JPI$K_NETWORK) {
		    cnt += sprintf(buffer+cnt,"%-*s ",MODELEN,"NETWORK");
		} else if (proc->jobtype == JPI$K_BATCH) {
		    cnt += sprintf(buffer+cnt,"%-*s ",MODELEN,"BATCH");
		} else if (proc->jobtype == JPI$K_LOCAL) {
		    cnt += sprintf(buffer+cnt,"%-*s ",MODELEN,"LOCAL");
		} else if (proc->jobtype == JPI$K_DIALUP) {
		    cnt += sprintf(buffer+cnt,"%-*s ",MODELEN,"DIALUP");
		} else if (proc->jobtype == JPI$K_REMOTE) {
		    cnt += sprintf(buffer+cnt,"%-*s ",MODELEN,"REMOTE");
		} else {
		    cnt += sprintf(buffer+cnt,"%-*s ",MODELEN,"Jobtype=?");
		}
	    }
	} else if (depth < 3) {								/* flag as subprocess */
	    cnt = sprintf(buffer,"%*s sub %*s",2*depth," ",NODELEN+MODELEN-3-2*depth," ");
	} else {
	    cnt = sprintf(buffer,"       sub %2d%*s ",depth,NODELEN+MODELEN-12," ");
	}
	cnt += sprintf(buffer+cnt,"%-*.*s ",USERLEN,proc->userlen,proc->username);	/* user name */
	cnt += sprintf(buffer+cnt,"%2d ",proc->prib);					/* Base priority */
	cnt += sprintf(buffer+cnt,"%0*X ",PIDLEN,proc->pid);				/* PID */
	cnt += sprintf(buffer+cnt,"%-*.*s ",PROCLEN,proc->proclen,proc->procname);	/* process name */
	cnt += sprintf(buffer+cnt,"%1d %02d:%02d:%02d ",day,hour,min,sec);		/* CPU time */
	cnt += sprintf(buffer+cnt,"%*d ",IOLEN,proc->bufio+proc->dirio);		/* I/O count */
	cnt += sprintf(buffer+cnt,"%*d ",PGFLTLEN,proc->pageflts);			/* page faults */
	cnt += sprintf(buffer+cnt,"%*d ",MEMLEN,proc->gpgcnt+proc->ppgcnt);		/* pages */
	if (!proc->owner) {								/* Virtual pages */
	    cnt += sprintf(buffer+cnt,"%*d ",VPGLEN,proc->pgflquota-proc->pagfilcnt);
	} else {
	    cnt += sprintf(buffer+cnt,"%*s ",VPGLEN,"\"");
	}
	if (proc->state == SCH$C_CEF) {							/* state */
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"CEF");
	} else if (proc->state == SCH$C_COM) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"COM");
	} else if (proc->state == SCH$C_COMO) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"COMO");
	} else if (proc->state == SCH$C_CUR) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"CUR");
	} else if (proc->state == SCH$C_COLPG) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"COLPG");
	} else if (proc->state == SCH$C_FPG) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"FPG");
	} else if (proc->state == SCH$C_HIB) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"HIB");
	} else if (proc->state == SCH$C_HIBO) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"HIBO");
	} else if (proc->state == SCH$C_LEF) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"LEF");
	} else if (proc->state == SCH$C_LEFO) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"LEFO");
	} else if (proc->state == SCH$C_MWAIT) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"MWAIT");
	} else if (proc->state == SCH$C_PFW) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"PFW");
	} else if (proc->state == SCH$C_SUSP) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"SUSP");
	} else if (proc->state == SCH$C_SUSPO) {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"SUSPO");
	} else {
	    cnt += sprintf(buffer+cnt,"%-*s ",STATELEN,"???");
	}
	if (!PrintFull && proc->imagelen) {						/* image name */
	    status = my_fparse(iname,proc->imagename,"","",MY_FP_NAME,NAM$M_SYNCHK);
	    ilen = strlen(iname);
	    tmp = ilen-PRTIMAGELEN > 0 ? ilen-PRTIMAGELEN : 0;
	    cnt += sprintf(buffer+cnt,"%-*.*s",PRTIMAGELEN,ilen-tmp,iname+tmp);
	}
	puts(buffer);
	if (PrintFull) {								/* extra output on `full' */
	    cnt = 0;
	    if (proc->info & GOTQUEUENAME) {
		cnt = sprintf(buffer,"%*s %4d ",QUEUENAMELEN,proc->qname,proc->qentry);
	    }
	    if (proc->imagelen) {
		cnt += sprintf(buffer+cnt,"%*s",132-cnt,proc->imagename);
	    }
	    if (cnt) {
		puts(buffer);
	    }
	}
	if (proc->sub) {								/* Recursive print of subprocesses */
	    print_proc(proc->sub,depth+1);
	}
	if (proc->subchain) {								/* Recur prt of sub proc. at this level */
	    print_proc(proc->subchain,depth);
	}
	PrintCount++;
    }
}

/**********************************************************************************************************************************/
int pid_compare(const struct procdata **proc1,const struct procdata **proc2)
{
    return (unsigned) (*proc1)->pid - (unsigned) (*proc2)->pid;
}

/**********************************************************************************************************************************/
int proc_compare(const struct procdata **proc1,const struct procdata **proc2)
{
    int status;

    status = strcmp((*proc1)->nodename,(*proc2)->nodename);				/* by nodename first */
    if (!status) {
	status = strcmp((*proc1)->username,(*proc2)->username);				/* then by username */
	if (!status) {
	    status = (*proc1)->jobtype - (*proc2)->jobtype;				/* then by jobtype */
	    if (!status) {
		status = strcmp((*proc1)->procname,(*proc2)->procname);			/* then by process name */
		if (!status) {
		    status = (int) ((unsigned) (*proc1)->pid - (unsigned) (*proc2)->pid); /* then by PID */
		}
	    }
	}
    }
    return status;
}

/**********************************************************************************************************************************/
struct procdata *initprocdata(struct procdata **new)
{
    if ((*new = calloc(1,sizeof(struct procdata))) != NULL) {				/* calloc to clear unused entries */
	JPIItemList[1].buffer = &(*new)->cputim;
	JPIItemList[2].buffer = &(*new)->gpgcnt;
	JPIItemList[3].buffer = (*new)->imagename;
	JPIItemList[3].retlen = &(*new)->imagelen;
	JPIItemList[4].buffer = &(*new)->mode;
	JPIItemList[5].buffer = &(*new)->nodename;
	JPIItemList[5].retlen = &(*new)->nodelen;
	JPIItemList[6].buffer = &(*new)->owner;
	JPIItemList[7].buffer = &(*new)->pageflts;
	JPIItemList[8].buffer = &(*new)->pagfilcnt;
	JPIItemList[9].buffer = &(*new)->pgflquota;
	JPIItemList[10].buffer = &(*new)->pid;
	JPIItemList[11].buffer = &(*new)->ppgcnt;
	JPIItemList[12].buffer = (*new)->procname;
	JPIItemList[12].retlen = &(*new)->proclen;
	JPIItemList[13].buffer = &(*new)->prib;
	JPIItemList[14].buffer = &(*new)->state;
	JPIItemList[15].buffer = (*new)->username;
	JPIItemList[15].retlen = &(*new)->userlen;
	JPIItemList[16].buffer = &(*new)->wsextent;
	JPIItemList[17].buffer = &(*new)->wssize;
	JPIItemList[18].buffer = &(*new)->jobtype;
	JPIItemList[19].buffer = &(*new)->bufio;
	JPIItemList[20].buffer = &(*new)->dirio;
	JPIItemList[21].buffer = &(*new)->masterpid;
    }
    return *new;
}

/**********************************************************************************************************************************/
int scanprocs(struct procdata *procinfo,int *count)
{
    int status;										/* Status of system calls */
    int pid;
    int qstatus;
    int qctx;
    long iosb[2];
    static long qflags = QUI$M_SEARCH_BATCH|QUI$M_SEARCH_WILDCARD;
    static int qpid;
    static ITMLST quiitemlist[] = {
	{sizeof(long),	QUI$_SEARCH_FLAGS,	&qflags,	0},			/* [0] */
	{sizeof(int),	QUI$_JOB_PID,		&qpid,		0},			/* [1] */
	{sizeof(int),	QUI$_ENTRY_NUMBER,	0,		0},			/* [2] */
	{QUEUENAMELEN,	QUI$_QUEUE_NAME,	0,		0},			/* [3] */
	{0,		QUI$_SEARCH_USERNAME,	0,		0},			/* [4] */
	{0,0,0,0}};
    static ITMLST quicancel[] = {
	{sizeof(long),	QUI$_CANCEL_OPERATION,	0,		0},
	{0,0,0,0}};

    status = sys$process_scan(&pid,ScanItemList);					/* Establish context */
    if (!(status & STS$M_SUCCESS)) {
	return status;
    }
    do {										/* Loop on processes */
	status = sys$getjpiw(0,&pid,0,JPIItemList,&iosb,0,0);				/* Perform GETJPI call */
	if (status & STS$M_SUCCESS) {							/* string terminators added by calloc */
	    if (iosb[0] & STS$M_SUCCESS) {
		(*count)++;								/* count process */
		if (!procinfo->owner && (procinfo->mode == JPI$K_BATCH)) {		/* Get queue name & entry for batch procs */
		    qctx = -1;
		    quiitemlist[4].buffer = procinfo->username;
		    quiitemlist[4].buflen = procinfo->userlen;
		    quiitemlist[2].buffer = &procinfo->qentry;
		    quiitemlist[3].buffer = procinfo->qname;
		    quiitemlist[3].retlen = &procinfo->qnamelen;
		    do {								/* Search queue entries */
			qstatus = sys$getquiw(0,QUI$_DISPLAY_ENTRY,&qctx,quiitemlist,&iosb,0,0);
			if (qstatus & STS$M_SUCCESS) {
			    if (!(iosb[0] & STS$M_SUCCESS)) {
				printf("%%E- problem with $GETQUI iosb pid: %0X iosb: %0X\n",procinfo->pid,iosb[0]);
				return iosb[0];
			    }
			    if (qpid == procinfo->pid) {				/* Found it */
				procinfo->qname[procinfo->qnamelen] = '\0';		/* terminate */
				if (procinfo->qnamelen) {
				    procinfo->info |= GOTQUEUENAME;
				}
				qstatus = sys$getquiw(0,&qctx,quicancel,0,0,0);
				break;
			    }
			} else {
			    return qstatus;
			}
		    } while((qstatus != JBC$_NOMOREENT) && (qstatus != JBC$_NOSUCHENT));
		}
		if ((procinfo->next = initprocdata(&procinfo->next)) == NULL) {		/* allocate next structure */
		    perror("procdata");
		    return EXIT_FAILURE;
		}
		procinfo = procinfo->next;
	    } else if (iosb[0] != SS$_NOMOREPROC) {
		printf("%%E- problem with $GETJPI iosb pid: %0X iosb: %0X\n",procinfo->pid,iosb[0]);
		return iosb[0];
	    }
	} else if ((status != SS$_NOPRIV) && (status != SS$_NOMOREPROC)	&&		/* skip these */
		   (status != SS$_SUSPENDED)) {
	    return (status);
	}
    } while (status != SS$_NOMOREPROC);
    return (SS$_NORMAL);
}

/**********************************************************************************************************************************/
/*  Routine to perform a parse
    Dan Dever

    This does not translate concealed logicals.

    ret	= RMS status from sys$parse function
          Check for:
	  RMS$_NORMAL
	  RMS$_SYN	file specification syntax error
	  RMS$_DNF      directory not found
	  others:
	      RMS$_ACS RMS$_BLN RMS$_BUG_DDI RMS$_CDA RMS$_CHN RMS$_DEV
	      RMS$_DIR RMS$_DME RMS$_DNA     RMS$_DNF RMS$_DNR RMS$_ESA
	      RMS$_ESS RMS$_FAB RMS$_FNA     RMS$_FNM RMS$_IFI RMS$_LNE
	      RMS$_NAM RMS$_NOD RMS$_NORMAL  RMS$_QUO RMS$_RLF RMS$_RUNDOWN
	      RMS$_STR RMS$_SUC RMS$_SYN     RMS$_TYP RMS$_VER RMS$_WCC
*/
int my_fparse(char *result,  	  /* String to contain resultant file spec (space already alloc) */
	      char *file_spec, 	  /* String containing file specification */
	      char *default_spec, /* String containing default file specification */
	      char *related_spec, /* String containing related file specification */
	      int mask,		  /* Mask specifying which file name fields to get */
	      int options)	  /* Can specify NAM$M_SYNCHK (syntax only), NAM$M_NOCONCEAL, or NAM$M_PWD */
{
    int status;
    struct FAB fab = cc$rms_fab;
    struct NAM nam = cc$rms_nam;
    struct NAM rnam = cc$rms_nam;							/* nam block for related file */
    char esa[NAM$C_MAXRSS];								/* expanded string area address     */
  
    fab.fab$l_nam = &nam;								/* initialize fab and nam block */
    nam.nam$l_esa = esa;								/* expanded string area address     */
    nam.nam$b_ess = NAM$C_MAXRSS;							/* espanded string area size        */
    /* nam.nam$b_esl */            							/* expanded string length  (returned) */

    fab.fab$l_fna = file_spec;								/* Initialize the file name */
    fab.fab$b_fns = strlen(file_spec);
    
    fab.fab$l_dna = default_spec;							/* Initialize the default file name */
    fab.fab$b_dns = strlen(default_spec);

    nam.nam$l_rlf = &rnam;								/* Initialize the related file name */
    rnam.nam$l_rsa = related_spec;
    rnam.nam$b_rsl = strlen(related_spec);
    
    if (options) {									/* Initialize parse options */
	nam.nam$b_nop = options;							/* NAM$M_SYNCHK,NAM$M_NOCONCEAL,NAM$M_PWD */
    }
    status = sys$parse(&fab);								/* Perform parse */
    if (status == RMS$_NORMAL) {							/* Create new filename */
	if (mask & MY_FP_NODE) {							/* NODE */
	    strncpy(result, nam.nam$l_node,  nam.nam$b_node);
	    result += nam.nam$b_node;
	}
	if (mask & MY_FP_DEV) {								/* DEVICE */
	    strncpy(result, nam.nam$l_dev,  nam.nam$b_dev);
	    result += nam.nam$b_dev;
	}
	if (mask & MY_FP_DIR) {								/* DIRECTORY */
	    strncpy(result, nam.nam$l_dir,  nam.nam$b_dir);
	    result += nam.nam$b_dir;
	}
	if (mask & MY_FP_NAME) {							/* NAME */
	    strncpy(result, nam.nam$l_name, nam.nam$b_name);
	    result += nam.nam$b_name;
	}
	if (mask & MY_FP_TYPE) {							/* TYPE */
	    strncpy(result, nam.nam$l_type, nam.nam$b_type);
	    result += nam.nam$b_type;
	}
	if (mask & MY_FP_VER) {								/* VERSION */
	    strncpy(result, nam.nam$l_ver, nam.nam$b_ver);
	    result += nam.nam$b_ver;
	}
    }
    *result = '\0';
    return status;
}

/**********************************************************************************************************************************/
int my_optopt = '?';
int my_optind = 1;
int my_optargcnt = 0;
char *my_optarg = NULL;

int my_getopt(int argc,char *argv[], char *options)
{
    static int inarg = 0;
    static char *idx;
    char *p;
    
    while(my_optind < argc) {
	if (!inarg) {
	    if (*argv[my_optind] == '-') {
		idx=argv[my_optind]+1;
		inarg = 1;
	    } else {
		argv[++my_optargcnt] = argv[my_optind++];
	    }
	} else {
	    while(*idx) {
		my_optopt = *idx;
		p = strchr(options,*idx++);
		if (!p) return '?';
		if (*(p+1) == ':') {
		    if (*idx) {
			if ((*idx == ' ') && *(idx+1)) {
			    my_optarg = idx+1;
			    my_optind++;
			    inarg=0;
			} else {
			    return ':';
			}
		    } else {
			inarg=0;
			my_optind++;
			if (!(my_optind < argc) || (*argv[my_optind] == '-')) {
			    return ':';
			}
			my_optarg = argv[my_optind];
			my_optind++;
		    }
		}
		return *p;
	    }
	    inarg=0;
	    my_optind++;
	}
    }
    return -1;
}

/**********************************************************************************************************************************/
void exit_withmsg(int status)
{
    printf("\nPlease report all problems to Dan Dever\n");
    printf("    at AD::DEVER (true in 1996)\n\n");
    exit(status);
}

/**********************************************************************************************************************************/
void usage(void)
{
    printf("Usage: show_proc [options] [nodename]\n");
    printf("        -b          = search for batch processes\n");
    printf("        -e          = suppress printing of entry numbers\n");
    printf("        -f          = full form output\n");
    printf("        -h          = help\n");
    printf("        -i image    = search for image\n");
    printf("        -j jobtype  = search for processes with jobtype\n");
    printf("        -m mode     = search for processes in mode\n");
    printf("        -s state    = search for processes in state\n");
    printf("        -u username = search for user's processes\n");
}
