/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR                     Initial version.
*       V01.20  JRR     29-Oct-1991	Added debug flag for interactive
*					programs.
*       V01.30  JRR     16-Jan-1992	Added support for RS6000.
*       V01.4   JRR     12-Feb-1992	Fixed declaration of static routines.
*       V01.5   JRR     02-Mar-1992	Added Cosmic V2 changes.
*       V01.6   JRR     13-Mar-1992	Check to see how big the screen is.
*       V01.7   JRR     08-Apr-1992     Added CERN enhancements.
*                       15-Apr-1992     Added snap.
*                       16-Apr-1992     Allow single command on command line.
*                       20-Apr-1992     Added Start Nqs.
*	V01.8	JRR	17-Jun-1992	Added header.
*	V01.9	JRR	24-Aug-1992	Added memdump.
*			12-Nov-1992	Added "set scheduler="
*	V01.10	JRR	07-Jan-1993	Added "set echo"
*	V01.11	JRR	08-Mar-1993	Added Boeing enhancment for files.
*	V01.12	JRR	10-Aug-1993	Removed scpflag.
*					Removed "Delete request" code.
*	V01.13	JRR	28-Feb-1994	Added support for SOLARIS.
*	V01.14	JRR	30-Mar-1994	Ranking compute servers.
*/
/*++ mgr_main.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.36/src/RCS/mgr_main.c,v $
 *
 * DESCRIPTION:
 *
 *	NQS manager program main module.
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.14 $ $Date: 1994/09/02 17:39:33 $ $State: Exp $)
 * $Log: mgr_main.c,v $
 * Revision 1.14  1994/09/02  17:39:33  jrroma
 * Version 3.36
 *
 * Revision 1.13  94/03/30  20:36:20  jrroma
 * Version 3.35.6
 * 
 * Revision 1.12  93/09/10  13:56:54  jrroma
 * Version 3.35
 * 
 * Revision 1.11  93/07/13  21:33:39  jrroma
 * Version 3.34
 * 
 * Revision 1.10  93/02/05  23:16:38  jrroma
 * Version 3.31
 * 
 * Revision 1.9  92/12/22  09:32:37  jrroma
 * Version 3.30
 * 
 * Revision 1.8  92/06/18  17:30:39  jrroma
 * Added gnu header
 * 
 * Revision 1.7  92/05/06  10:47:56  jrroma
 *  Version 3.20
 * 
 * Revision 1.6  92/03/13  07:31:47  jrroma
 * Help lines resized to fit screen.
 * 
 * Revision 1.5  92/03/02  09:52:30  jrroma
 * Begin to added Cosmic V2 changes.
 * 
 * Revision 1.4  92/02/12  15:51:41  jrroma
 * Fixed declaration of static routines.
 * 
 * Revision 1.3  92/01/17  08:54:13  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.2  91/10/29  10:25:06  jrroma
 * Added debug flag for interactive pmgs.
 * 
 * Revision 1.1  91/10/10  13:11:12  jrroma
 * Initial revision
 * 
 *
 */

#define	DEFAULT_HELPLINES 23	/* Default number of help page lines to */
				/* display at a time */
#include "nqs.h"
#include <unistd.h>
#include <sys/termio.h>
#include "nqsmgr.h"		/* Token types */
#include "nqsmgrhelp.h"		/* Offsets in help file */
#include "signal.h"		/* Signal handling */
#ifndef	QMGR_HELPFILE		/* Define the Qmgr help file path */
#define	QMGR_HELPFILE	"/usr/lib/nqs/qmgr.hlp"
#endif
#include "nqsvars.h"		/* NQS global vars */

#ifndef __CEXTRACT__
#if __STDC__

static void cleanup ( int sig );
static void help ( long page );
static void scan_modifiers ( int state, int helpseen );
static int search_state ( int state );
static void set_win_size ( int *num_lines );
static void v_shoversion ( void );

#else /* __STDC__ */

static void cleanup (/* int sig */);
static void help (/* long page */);
static void scan_modifiers (/* int state, int helpseen */);
static int search_state (/* int state */);
static void set_win_size (/* int *num_lines */);
static void v_shoversion (/* void */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */


int DEBUG;				/* Debug flag for interactive pgms. */
int Qmgr_echo;				/* True if echoing input (for testing) */
Mid_t Local_mid;			/* Machine-id of local machine */
char *Nqsmgr_prefix = "NQS manager";	/* NQS manager message prefix */
int Mgr_priv;				/* NQS manager privilege bits */
int Help_lines;				/* Number of lines to display */
					/* at a time for help pages */
char *our_cwd;					
int Command_on_line;
char Command_line[512];
/*
 *
 *	Command parsing tables and help file.
 */
static FILE *Helpfile;		/* NQS qmgr help file */
static struct cmd_struct {
	char *vmod;		/* Command verb or modifier */
	long help_offset;	/* Offset in help file of help info */
	void (*cf)();		/* Do command function */
} cset [] = {
	{ "ABort", HELP_ABOQUE, (void (*)()) 0 },
	{ "Queue", HELP_ABOQUE, v_aboque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "ADd", HELP_ADD, (void (*)()) 0 },
	{ "DEStination", HELP_ADDDES, v_adddes },
	{ "DEVice", HELP_ADDDEV, v_adddev },
	{ "Forms", HELP_ADDFOR, v_addfor },
	{ "Groups", HELP_ADDGID, v_addgid },
	{ "Managers", HELP_ADDMAN, v_addman },
	{ "Queues", HELP_ADDQUE, v_addque },
	{ "Users", HELP_ADDUID, v_adduid },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "Create", HELP_CRE, (void (*)()) 0 },
	{ "Batch_queue", HELP_CREBATQUE, v_crebatque },
	{ "Complex", HELP_CRECOM, v_crecom },
	{ "DEVICE", HELP_CREDEV, v_credev },
	{ "DEVICE_queue", HELP_CREDEVQUE, v_credevque },
	{ "Pipe_queue", HELP_CREPIPQUE, v_crepipque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "DElete", HELP_DEL, (void (*)()) 0 },
	{ "Complex", HELP_DELCOM, v_delcom },
	{ "DEStination", HELP_DELDES, v_deldes },
	{ "DEVice", HELP_DELDEV, v_deldev },
	{ "Forms", HELP_DELFOR, v_delfor },
	{ "Groups", HELP_DELGID, v_delgid },
	{ "Managers", HELP_DELMAN, v_delman },
	{ "Queue", HELP_DELQUE, v_delque },
	{ "Users", HELP_DELUID, v_deluid },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "DIsable", HELP_DIS, (void (*)()) 0 },
	{ "Device", HELP_DISDEV, v_disdev },
	{ "Queue", HELP_DISQUE, v_disque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "ENable", HELP_ENA, (void (*)()) 0 },
	{ "Device", HELP_ENADEV, v_enadev },
	{ "Queue", HELP_ENAQUE, v_enaque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "EXit", HELP_EXI, v_exi },
	{ "HOld", HELP_HOLDREQ, (void (*)()) 0 },
	{ "Request", HELP_HOLDREQ, v_holdreq },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "Lock", HELP_LOCDAE, (void (*)()) 0 },
	{ "Local_daemon", HELP_LOCDAE, v_locdae },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "Memdump",  HELP_MEMDUMP,  v_memdump }, 
	{ "MODify", HELP_MODREQ, (void (*)()) 0 },
	{ "Request", HELP_MODREQ, v_modreq },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "MOVe", HELP_MOV, (void (*)()) 0 },
	{ "Queue", HELP_MOVQUE, v_movque },
	{ "Request", HELP_MOVREQ, v_movreq },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "Purge", HELP_PURQUE, (void (*)()) 0 },
	{ "Queue", HELP_PURQUE, v_purque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "Quit", HELP_QUIT, v_exi },
	{ "RELease", HELP_RELREQ, (void (*) ()) 0},
	{ "Request", HELP_RELREQ, v_relreq },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "REMove", HELP_REMQUE, (void (*)()) 0 },
	{ "Queue", HELP_REMQUE, v_remque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "SEt", HELP_SET, (void (*)()) 0 },
	{ "COMplex", HELP_SETCOMPLEX, (void (*)()) 0 },
	{ "Run_limit", HELP_SETCOMRUNLIM, v_setcomrunlim },
	{ "User_limit", HELP_SETCOMUSERLIM, v_setcomuserlim },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt COMplex */
	{ "CORefile_limit", HELP_SETCOR, v_setppcore },
	{ "DAta_limit", HELP_SETDAT, v_setppdata },
	{ "DEBug", HELP_SETDEB, v_setdeb },
	{ "Echo", HELP_SETECHO, v_setecho },
	{ "Global", HELP_SETGBL, (void (*)()) 0 },
	{ "Batch_limit", HELP_SETGBATLIM, v_setgbatlim },
	{ "Pipe_limit", HELP_SETGBLPIPLIM, v_setgblpiplim },
        { (char *) 0, 0L, (void (*)()) 0 },     /* SEt Global */
	{ "DEFault", HELP_SETDEF, (void (*)()) 0 },
	{ "Batch_request", HELP_SETDEFBAT, (void (*)()) 0 },
	{ "Priority", HELP_SETDEFBATPRI, v_setdefbatpri },
	{ "Queue", HELP_SETDEFBATQUE, v_setdefbatque },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt DEFault Batch_request */
	{ "DEStination_retry", HELP_SETDEFDES, (void (*)()) 0 },
	{ "Time", HELP_SETDEFDESTIM, v_setdefdestim },
	{ "Wait", HELP_SETDEFDESWAI, v_setdefdeswai },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt DEFault DEStination_retry */
	{ "DEVice_request", HELP_SETDEFDEVPRI, (void (*)()) 0 },
	{ "Priority", HELP_SETDEFDEVPRI, v_setdefdevpri },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt DEFault DEVice_request */
	{ "Load_interval", HELP_SETDEFLOADINT, v_setdefloadint },
	{ "Print_request", HELP_SETDEFPRI, (void (*)()) 0 },
	{ "Forms", HELP_SETDEFPRIFOR, v_setdefprifor },
	{ "Queue", HELP_SETDEFPRIQUE, v_setdefprique },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt DEFault Print_request */
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt DEFault */
	{ "DEStination", HELP_SETDES, v_setdes },
	{ "DEVICE", HELP_SETDEV, v_setdev },
	{ "DEVICE_server", HELP_SETDEVSER, v_setdevser },
	{ "Forms", HELP_SETFOR, v_setfor },
	{ "LB_In", HELP_SETLBIN, v_setlbin },
	{ "LB_Out", HELP_SETLBOUT, v_setlbout },
	{ "LIfetime", HELP_SETLIFTIM, v_setliftim },
	{ "LOAd_daemon", HELP_SETLOADDAE, v_setloaddae },
	{ "LOG_file", HELP_SETLOGFIL, v_setlogfil },
	{ "MAIl", HELP_SETMAI, v_setmai },
	{ "MANagers", HELP_SETMAN, v_setman },
	{ "MAXimum", HELP_SETMAX, (void (*)()) 0 },
	{ "Copies", HELP_SETMAXCOP, v_setmaxcop },
	{ "Open_retries", HELP_SETMAXOPERET, v_setmaxoperet },
	{ "Print_size", HELP_SETMAXPRISIZ, v_setmaxprisiz },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt MAXimum */
	{ "NEtwork", HELP_SETNET, (void (*)()) 0 },
	{ "Client", HELP_SETNETCLI, v_setnetcli },
	{ "Daemon", HELP_SETNETDAE, v_setnetdae },
	{ "Server", HELP_SETNETSER, v_setnetser },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt NEtwork */
	{ "NDp", HELP_SETNDP, v_setndp },
	{ "NIce_value_limit", HELP_SETNIC, v_setppnice },
	{ "NO_Default", HELP_SETNDF, (void (*)()) 0 },
	{ "Batch_request", HELP_SETNDFBATQUE, (void (*)()) 0 },
	{ "Queue", HELP_SETNDFBATQUE, v_setndfbatque },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt NO_Default Batch_request */
	{ "Print_request", HELP_SETNDFPRI, (void (*)()) 0 },
	{ "Forms", HELP_SETNDFPRIFOR, v_setndfprifor },
	{ "Queue", HELP_SETNDFPRIQUE, v_setndfprique },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt NO_Default Print_request */
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt NO_Default */
	{ "NO_Access", HELP_SETNOACC, v_setnoacc },
        { "NO_LB_In", HELP_SETNOLBIN, v_setnolbin },
        { "NO_LB_Out", HELP_SETNOLBOUT, v_setnolbout },
	{ "NO_LOad_daemon", HELP_SETNLOADDAE, v_setnloaddae },
	{ "NO_Network_daemon", HELP_SETNNEDAE, v_setnnedae },
        { "NO_Pipeonly", HELP_SETNOPIPEONLY, v_setnopipeonly },
	{ "NO_Scheduler", HELP_SETNOSCHEDULER, v_setnoscheduler },
	{ "Open_wait", HELP_SETOPEWAI, v_setopewai },
	{ "PER_Process", HELP_SETPERPRO, (void (*)()) 0 },
	{ "Cpu_limit", HELP_SETPERPROCPUT, v_setppcput },
	{ "Memory_limit", HELP_SETPERPROMEM, v_setppmem },
	{ "Permfile_limit", HELP_SETPERPROPER, v_setpppfile },
	{ "Tempfile_limit", HELP_SETPERPROTEM, v_setpptfile },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt PER_Process */
	{ "PER_Request", HELP_SETPERREQ, (void (*)()) 0 },
	{ "Cpu_limit", HELP_SETPERREQCPUT, v_setprcput },
	{ "Memory_limit", HELP_SETPERREQMEM, v_setprmem },
	{ "Permfile_limit", HELP_SETPERREQPER, v_setprpfile },
	{ "Tempfile_limit", HELP_SETPERREQTEM, v_setprtfile },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt PER_Request */
	{ "PIPE_Client", HELP_SETPIPCLI, v_setpipcli },
	{ "PIPEOnly", HELP_SETPIPEONLY, v_setpipeonly },
	{ "PRiority", HELP_SETPRI, v_setpri },
	{ "Run_limit", HELP_SETRUNLIM, v_setrunlim },
	{ "SCheduler", HELP_SETSCHEDULER, v_setscheduler },
	{ "SErver", HELP_SETSERVER, (void (*)()) 0 },
	{ "Available", HELP_SETSERVAVAIL, v_setservavail },
	{ "Noperformance", HELP_SETSERVNOPERF, v_setservnoperf },
	{ "Performance", HELP_SETSERVPERF, v_setservperf },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt SErver */
	{ "SHell_strategy", HELP_SETSHS, (void (*)()) 0 },
	{ "FIxed", HELP_SETSHSFIX, v_setshsfix },
	{ "FRee", HELP_SETSHSFRE, v_setshsfre },
	{ "Login", HELP_SETSHSLOG, v_setshslog },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt SHell_strategy */
	{ "STack_limit", HELP_SETSTA, v_setppstack },
	{ "UNrestricted_access", HELP_SETUNRACC, v_setunracc },
	{ "USer_limit", HELP_SETUSRLIM, v_setusrlim },
	{ "Working_set_limit", HELP_SETWOR, v_setppwork },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SEt */
	{ "SHOw", HELP_SHO, (void (*)()) 0 },
	{ "All", HELP_SHOALL, v_shoall },
	{ "Devices", HELP_SHODEV, v_shodev },
	{ "Forms", HELP_SHOFOR, v_shofor },
	{ "LImits_supported", HELP_SHOLIM, v_sholim },
	{ "LOng", HELP_SHOLONGQUE, (void (*)()) 0 },
	{ "Queues", HELP_SHOLONGQUE, v_sholongque },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SHOw LOng */
	{ "Managers", HELP_SHOMAN, v_shoman },
	{ "Parameters", HELP_SHOPAR, v_shopar },
	{ "Queues", HELP_SHOQUE, v_shoque },
	{ "Servers", HELP_SHOSERVERS, v_shoservers },
	{ "Version", HELP_SHOVERSION, v_shoversion },
	{ (char *) 0, 0L, (void (*)()) 0 },	/* SHOw */
	{ "SHUtdown", HELP_SHUTDOWN, v_shutdown },
	{ "SNap", HELP_SNAP, v_snap },
	{ "STArt", HELP_START, (void (*)()) 0 },
	{ "Load_daemon", HELP_STARTLOAD, v_startload },
	{ "NEtwork_daemon", HELP_STARTNETDAE, v_startnetdae },
	{ "NQs", HELP_STARTNQS, v_startnqs },
	{ "Queue", HELP_STAQUE, v_staque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "STOp", HELP_STOP, (void (*)()) 0 },
	{ "Load_daemon", HELP_STOLOAD, v_stoload },
	{ "Network_daemon", HELP_STONETDAE, v_stonetdae },
	{ "Queue", HELP_STOQUE, v_stoque },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ "Unlock", HELP_UNLDAE, NULL },
	{ "Local_daemon", HELP_UNLDAE, v_unldae },
	{ (char *) 0, 0L, (void (*)()) 0 },
	{ (char *) 0, 0L, (void (*)()) 0 }
};


/*** main
 *
 *
 *	void main():
 *	NQS manager main function.
 */
main (argc, argv)
int argc;
char *argv[];
{
	static char *ambverb = "Ambiguous command verb.";
	static char *invverb = "Invalid command verb.";


	int i;
	int size;			/* Generic descr size checking */
	int residual;			/* Generic descr size checking */
	int state;			/* State */
	int tokentype;			/* Scanned token type */
	short helpseen;			/* Help command prefix seen */
	char *root_dir;
	char *help_file;

        if ( ! buildenv()) {
            fprintf (stderr, "%s(FATAL): Unable to establish directory ",
                     Nqsmgr_prefix);
            fprintf (stderr, "independent environment.\n");
            exit (1);
        }

	signal (SIGINT, cleanup);	/* Catch common signals */
	signal (SIGQUIT, cleanup);	/* and exit */
	signal (SIGHUP, cleanup);
	signal (SIGTERM, cleanup);
	signal (SIGPIPE, cleanup);	/* For broken stream socket */
					/* connection to a remote machine */
	Command_on_line = 0;
	Qmgr_echo = 0;			/* No echoing yet */
	if (argc > 1) {
	    Command_on_line = 1;
	    strcpy(Command_line, argv[1]);
	    for (i = 2; i < argc; i++) {
		strcat (Command_line, " ");
		strcat (Command_line, argv[i]);
	    }
	    strcat (Command_line, " \n");
	}
	/*
	 * Get the current working directory (for snap file).
	 */
        if ((our_cwd = getcwd((char *)NULL, 128)) == NULL) {
		fprintf (stderr, "%s(FATAL): Unable to determine current ", 
			 Nqsmgr_prefix);
		fprintf (stderr, "working directory\n");
		exit (1);
	}
	/*
	 *  Set the default number of lines to display at a time,
	 *  when showing a help page to the user.
	 */
	set_win_size(&Help_lines);
	/*
	 *  Determine the machine-id of the local machine.
	 */
	if (localmid (&Local_mid) != 0) {
		fprintf (stderr, "%s(FATAL): Unable to determine machine-id ",
			 Nqsmgr_prefix);
		fprintf (stderr, "of local host.\n");
		exit (1);
	}
	/*
	 *  Determine the NQS privileges (or lack thereof), of the
	 *  invoking user.
	 */
	Mgr_priv = nqspriv (getuid(), Local_mid, Local_mid);
	/*
	 *  Check to see that a single instance of any NQS database
	 *  structure in the queue or device descriptor file will fit
	 *  within a single block of size ATOMICBLKSIZ.  This constraint
	 *  must be satisfied so that updates to the NQS status and
	 *  configuration database files will happen atomically.
	 */
	size = sizeof (struct gendescr);
	residual = size % sizeof (ALIGNTYPE);
	if (residual) size += sizeof (ALIGNTYPE) - residual;
	if (size > ATOMICBLKSIZ) {
		fprintf (stderr, "%s(FATAL): Configuration database ",
			 Nqsmgr_prefix);
		fprintf (stderr, "descriptor size exceeds ATOMICBLKSIZ.\n");
		exit(2);
	}

	root_dir = getfilnam (Nqs_root, SPOOLDIR);
	if (root_dir == (char *)NULL) {
	    fprintf (stderr, "%s(FATAL): Unable to ", Nqsmgr_prefix);
	    fprintf (stderr, "determine root directory name.\n");
	    exit(2);
	}
	if (chdir (root_dir) == -1) {
	    fprintf (stderr, "%s(FATAL): Unable to chdir() to the NQS ",
                 Nqsmgr_prefix);
	    fprintf (stderr, "root directory.\n");
	    relfilnam (root_dir);
	    exit (1);
	}
	relfilnam (root_dir);
	/*
	 *  Open the network queue descriptor file.
	 */
	if ((Netqueuefile = opendb (Nqs_netqueues, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the network queue descriptor file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "network queue descriptor file.\n");
		exit(4);
	}
	/*
	 *  Open the non-network queue descriptor file.
	 */
	if ((Queuefile = opendb (Nqs_queues, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the non-network queue descriptor file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "non-network queue descriptor file.\n");
		exit(5);
	}
	/*
	 *  Open the device descriptor file.
	 */
	if ((Devicefile = opendb (Nqs_devices, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the device descriptor file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "device descriptor file.\n");
		exit(6);
	}
	/*
	 *  Open the queue/device/destination mapping descriptor file.
	 */
	if ((Qmapfile = opendb (Nqs_qmaps, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the queue/device/destination mapping
		 *  descriptor file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "queue/device/destination mapping file.\n");
		exit(7);
	}
	/*
	 *  Open the pipe queue destination descriptor file.
	 */
	if ((Pipeqfile = opendb (Nqs_pipeto, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the pipe queue destination
		 *  descriptor file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "pipe queue destination mapping file.\n");
		exit(8);
	}
	/*
	 *  Open the general parameters file.
	 */
	if ((Paramfile = opendb (Nqs_params, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the general parameters file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "general parameters file.\n");
		exit(9);
	}
        /*
         *  Open the queue complex descriptor file.
         */
        if ((Qcomplexfile = opendb (Nqs_qcomplex, O_RDONLY)) == NULL) {
                /*
                 *  We could not open the queue complex descriptor file.
                 */
                fprintf (stderr, "%s(FATAL): Unable to open the ",
                         Nqsmgr_prefix);
                fprintf (stderr, "queue complex descriptor file.\n");
                exit(10);
        }
	/*
	 *  Open the NQS managers file.
	 */
	if ((Mgrfile = opendb (Nqs_mgracct, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the NQS manager access list file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "NQS manager access list file.\n");
		exit(11);
	}
	/*
	 *  Open the NQS forms file.
	 */
	if ((Formsfile = opendb (Nqs_forms, O_RDONLY)) == NULL) {
		/*
		 *  We could not open the NQS forms list file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "NQS forms file.\n");
		exit(12);
	}
	/*
	 *  Open the NQS servers file.
	 */
	if ((Serverfile = opendb (Nqs_servers, O_CREAT | O_RDONLY)) == NULL) {
		/*
		 *  We could not open the NQS compute server list file.
		 */
		fprintf (stderr, "%s(FATAL): Unable to open the ",
			 Nqsmgr_prefix);
		fprintf (stderr, "NQS compute server list file.\n");
		exit(13);
	}
	/*
	 *  Open the NQS qmgr help file.
	 */
        help_file = getfilnam ("qmgr.hlp", LIBDIR);
        if (help_file == (char *)NULL) {
                fprintf (stderr, "%s(FATAL): Unable to ", Nqsmgr_prefix);
                fprintf (stderr, "determine name of NQS Qmgr help file.\n");
                exit (14);
        }
        if ((Helpfile = fopen (help_file, "r")) == NULL) {
                /*
                 *  Unable to open the NQS help file.
                 */
                fprintf (stderr, "%s(FATAL): Unable to open the ",
                         Nqsmgr_prefix);
                fprintf (stderr, "NQS Qmgr help file.\n");
                relfilnam (help_file);
                exit (15);
        }
        relfilnam (help_file);
	/*
	 *  Get a pipe to the local daemon.
	 *  On systems with named pipes, we get a pipe to the local
	 *  daemon automatically the first time we call inter().
	 */
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
#else
#if	BSD43
	if (interconn () < 0) {
		fprintf (stderr, "%s(FATAL): Unable to get ", Nqsmgr_prefix);
		fprintf (stderr, "a pipe to the local daemon.\n");
		exit (16);
	}
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  Block buffer stdout for maximum efficiency.
	 *  This will be particularly important in the networked version
	 *  of Qmgr....
	 */
	bufstdout();
	/*
	 *  Loop forever to process commands until exit or EOF.
	 */
	for (;;) {
		do {
			new_command();		/* Get a new command */
			tokentype = scan();	/* Get type of first token */
		} while (tokentype == T_EOC || tokentype == T_ABORT);
		if (tokentype == T_EOF) {	/* Exit, no more commands */
			putchar ('\n');
			exiting();		/* Relinquish any connection */
						/* to the local NQS daemon */
			fflush (stdout);	/* Flush all output */
			fflush (stderr);
			exit (0);
		}
		if (tokentype != T_LITERAL) {
			scan_error (invverb);	/* Invalid command verb */
			continue;		/* Get a new command */
		}
		/*
		 *  The first token in the command line is a character
		 *  string literal, which may be a valid NQS manager
		 *  program command verb.
		 */
		helpseen = 0;			/* No help prefix seen */
		if (matchkeyword (get_literal(), "HElp") == 1 ||
		    strcmp (get_literal(), "?") == 0) {
			/*
			 *  Scan next token to get command verb.
			 */
			if ((tokentype = scan()) == T_EOC) {
				help (HELP);	/* Help */
				continue;
			}
			else if (tokentype == T_ABORT) continue;
			else if (tokentype != T_LITERAL) {
				scan_error (invverb);	/* Invalid command */
				continue;
			}
			if (matchkeyword (get_literal(), "HElp") == 1 ||
			    strcmp (get_literal(), "?") == 0) {
				help (HELP);
				continue;
			}
			helpseen = 1;		/* Help command prefix seen */
		}
		/*
		 *  The current get_literal() token under inspection needs
		 *  to be a valid NQS manager program command verb.  It is
		 *  already known that the text of this token is not
		 *  equivalent to "help", or any abbreviation thereof.
		 */
		state = 0;		/* Verb table scan state is 0 */
		while (state >= 0) {
			switch (matchkeyword (get_literal(),
				cset [state].vmod)){
			case  1:			 /* Verb matched */
				if (cset [state].cf == NULL) {
					scan_modifiers (state, helpseen);
				}
				else if (helpseen) {
					help (cset [state].help_offset);
				}
				else (*cset [state].cf)();
				state = -1;		/* Exit loop */
				break;
			case  0:			/* No verb match */
				state = search_state (state);
				if (cset [state].vmod == NULL) {
					scan_error (invverb);
					state = -1;	/* Exit loop */
				}
				break;
			case -1:			/* Ambiguous match */
				scan_error (ambverb);	/* Show error */
				state = -1;		/* Exit loop */
				break;
			}
		}
		if (Command_on_line) {
		    exiting();		        /* Relinquish any connection */
			   	                /* to the local NQS daemon */
		    fflush (stdout);	/* Flush all output */
		    fflush (stderr);
		    exit (0);
		}
	}
}


/*** cleanup
 *
 *
 *	void cleanup():
 *
 *	Relinquish any connection to the local NQS daemon on receipt
 *	of signal.
 */
static void cleanup (sig)
int sig;				/* Signal received */
{
	signal (sig, SIG_IGN);		/* Ignore multiple signals */
	exiting();			/* Relinquish connection to the */
					/* local NQS daemon */
	fflush (stdout);		/* Flush all output */
	fflush (stderr);
	exit (14);
}


/*** help
 *
 *
 *	void help():
 *	Display help information on a given topic from the help file.
 */
static void help (page)
long page;				/* Offset of help page in help file */
{
	register short ch;		/* Character to write */
	register short lines;		/* Number of lines written */
	register short totty;		/* Boolean TRUE if writing to a tty */

	lines = 0;
	totty = isatty (1);		/* Stdout going to tty? */
	fseek (Helpfile, page, 0);	/* Seek to 1st char of help page */
	while ((ch = getc (Helpfile)) != EOF && ch != '#') {
		putchar (ch);
		if (totty && ch == '\n') {
			lines++;	/* One more line written */
			if (lines == Help_lines) {
				/*
				 *  Stop here, prompting for more
				 *  help.
				 */
				printf ("Press <RETURN> to continue.");
				fflush (stdout);
				while ((ch = getchar()) != EOF && ch != '\n')
					;
				lines = 0;	/* Start a new screen */
			}
		}
	}
}


/*** scan_modifiers
 *
 *
 *	void scan_modifiers():
 *	A valid command verb has been found which has required modifiers.
 */
static void scan_modifiers (state, helpseen)
register int state;			/* Parse state */
int helpseen;				/* Help verb seen */
{
	static char *invmod = "Invalid command verb modifier.";
	static char *ambmod = "Ambiguous command verb modifier.";
	static char *partcmd = "Incomplete command specified.";

	register int tokentype;

	do {
		tokentype = scan();	/* Get type of next token */
		if (tokentype == T_EOC) {
			/*
			 *  A command verb modifier was expected.
			 */
			if (helpseen) help (cset [state].help_offset);
			else scan_error (partcmd);	/* Partial command */
			state = -1;			/* Exit loop */
		}
		else if (tokentype != T_LITERAL) {
			/*
			 *  A character string literal is needed as a
			 *  command verb modifier, and we did not get
			 *  it.
			 */
			scan_error (invmod);		/* Invalid modifier */
			state = -1;			/* Exit loop */
		}
		else {
			/*
			 *  The current token is a character string
			 *  literal, which may be a valid (and required)
			 *  command verb modifier.
			 */
			state++;	/* Increment state to modifier */
					/* descriptors for command verb */
			do {
				/*
				 *  Loop to identify command verb modifier.
				 */
				switch (matchkeyword (get_literal(),
					cset [state].vmod)) {
				case  1:		/* Modifier matched */
					if (cset [state].cf == NULL) {
						/*
						 *  More required modifiers....
						 */
						scan_modifiers(state, helpseen);
					}
					else if (helpseen) {
						help (cset [state].help_offset);
					}
					else (*cset [state].cf)();
					state = -1;	/* Exit loop */
					break;
				case  0:		/* No match */
					state = search_state (state);
					if (cset [state].vmod == NULL) {
						scan_error (invmod);
						state = -1;	/* Exit loop */
					}
					break;
				case -1:		/* Ambiguous match */
					scan_error (ambmod);	/* Show error */
					state = -1;		/* Exit loop */
					break;
				}
			} while (state >= 0);
		}
	} while (state >= 0);
}


/*** search_state
 *
 *
 *	int search_state():
 *
 *	Adjust verb [modifier] searching state to next verb [modifier]
 *	in command set (cset).
 *
 *	Returns:
 *		The new command state integer.
 */
static int search_state (state)
register int state;
{
	register int modifier_level;

	/*
	 *  Scan past any modifier descriptors for the current command
	 *  verb [modifier] to reach the next command descriptor (state)
	 *  entry.
	 */
	modifier_level = 0;
	do {
		if (cset [state].vmod == NULL) {
			/*
			 *  We have reached the end of one modifier subtree
			 *  within the possible parse tree for the current
			 *  command verb [modifier].
			 */
			modifier_level--;
		}
		else if (cset [state].cf == NULL) {
			/*
			 *  We have reached another modifier subtree in
			 *  the possible parse tree for the current
			 *  command verb [modifier].
			 */
			modifier_level++;
		}
		state++;			/* Next state */
	} while (modifier_level > 0);
	return (state);
}
static void
v_shoversion()
{
#if	TEST
	printf("NQS test version is %s\n", NQS_VERSION);
#else
	printf("NQS version is %s\n", NQS_VERSION);
#endif
}
static void set_win_size(num_lines)
int *num_lines;
{
#ifdef TIOCGWINSZ
        struct winsize win;
        int num_cols;
        if (ioctl (0, TIOCGWINSZ, &win) == 0) {
                if (win.ws_row != 0) {
                        *num_lines = win.ws_row - 1;
                }
                if (win.ws_col != 0) {
                        num_cols = win.ws_col;
                }
        }
#else
	*num_lines = DEFAULT_HELPLINES;
#endif
}
