# include	<stdio.h>
# include	<signal.h>
# include	"ctlmod.h"
# include	"pipes.h"
# include	<resp.h>
# include	<ingres.h>
# include	<aux.h>
# include       <access.h>
# include	<lock.h>
# include	<sccs.h>

SCCSID(@(#)main.c	8.1	12/31/84)

/*
**  MAIN -- initialize control module
**
**	Called only once, this routine sets everything up.
**
**	The format of the argv is as follows:
**		argv[0] -- pathname
**		argv[1] -- file descriptor for input pipe or'ed with
**			0100 to make it printable in a 'ps'.
**		argv[2] -- Fileset
**		argv[3] -- Usercode
**		argv[4] -- Database
**		argv[5] -- Pathname
**
**	Parameters:
**		argc, argv -- as usual
**
**	Returns:
**		none
**
**	Side Effects:
**		Many; see code.
**		Proc_name is set to the working process name.
**		Fileset, Database, Usercode -- set from argv.
**		Trace vectors are initialized.
**
**	Requires:
**		markopen -- to mark the open files.
**		The input pipe must have the Cm struct in it as
**			the first data.
**
**	Trace Flags:
**		1
**
*/

struct resp	Resp;		/* State response structure */
struct _cm_t	Cm;		/* the system topography map */
struct _ctx_t	Ctx;		/* the current context */
int		Syncs[CM_MAXPROC];/* expected SYNC's from each proc */

/* General System Information */
char		*Proc_name;	/* the 'name' of the currently running proc */
char		*Fileset;	/* a unique string to make filenames from */
char		*Database;	/* the name of the current database */
char		*Usercode;	/* the code of the current user */
char		*Pathname;	/* the pathname of the root of INGRES */
int		Equel;		/* set if running an Equel program */
int		RubLevel;	/* rubout level, -1 if ignored */
extern struct out_arg	Out_arg;	/* output arguments */
jmp_buf		CmReset;	/* restart addr on interrupt */
# ifdef xMONITOR
struct monitor	CmMonBuf;	/* monitor buffer for CM overhead */
# endif xMONITOR

extern	void	rubcatch();
extern	int	error();
extern pb_t	*MonPpb;
extern long	CmOfiles;	/* defined in markopen.c */


main(argc, argv)
int	argc;
char	**argv;
{
	register int	i;
	struct fn_def	*f;
	register char	**q;
	register char	*p;
	pb_t		pb;
	static int	reenter;
	bool		nobuffer;
	int		lock_type = -1;	/* type of data base lock to request */
	int		wait_action = -1;	/* type of wait action on the data abse */
	char		*args; 	/* used to process args of sysmod */
	int		j;

	Ctx.ctx_name = Proc_name = argv[0];
	argv[argc] = NULL;
	nobuffer = tTrace(argv, argv[1][1], FuncVect[0]->fn_tvect, 30);
	Ctx.ctx_tvect = tT;
	reenter = 0;
	setjmp(CmReset);
	if (reenter++)
		exit(-1);
	if (signal(SIGINT, SIG_IGN) == SIG_DFL)
		signal(SIGINT, rubcatch);
	else
		RubLevel = -1;
	MonPpb = &pb;

	/* mark all currently open files */
	acc_init();
	markopen(&CmOfiles);

	/*
	**  Process argument vector.
	**	The easy ones just involve saving a pointer.
	**	argv[1] is used to get a file descriptor; this
	**		becomes the initial input.  This file
	**		is read to fill in the Cm (configuration)
	**		structure.
	*/

	if (tTf(1, 0) || argc < 6)
		prargs(argc, argv);
		
	if (argc < 6)
		syserr("main: argc=%d", argc);
	q = &argv[2];
	Fileset = *q++;
	Usercode = *q++;
	Database = *q++;
	Pathname = *q++;

	i = read(argv[1][0] & 077, (char *) &Cm, sizeof Cm);
	if (i != sizeof Cm)
		syserr("main: read %d", i);


	/* set up other globals */
	Ctx.ctx_name = Proc_name = Cm.cm_myname;
	initbuf(Qbuf, QbufSize, ERR_QBUF, error);
	Ctx.ctx_cmark = Ctx.ctx_pmark = markbuf(Qbuf);

	
	if ( !sequal( Proc_name, "SYSMOD"))
	{
		/* process flags */
		for (; (p = *q) != NULL; q++)
		{
			if (p[0] != '-')
				continue;
			switch (p[1])
			{
		  	case 'l':	/* Lock type */
				if ( Alockdes < 0 )
					break;
				if ( p[2] < '0' || p[2] > '9' )
					syserr("Illegal lock number %s",&p[2]);
				lock_type = atoi(&p[2]);
				if ( wait_action < 0 )
					break;
                                if ( setdbl(wait_action,lock_type) < 0 )
				{
					syserr("Data base temporarily unavailable");
				}
				break;
			  case 'W':
				/*
				** type of data base wait to preform
				*/
				if ( Alockdes < 0 )
					break;
				if ( p[2] < '0' || p[2] > '9' )
					syserr("Illegal wait action %s",&p[2]);
				wait_action = atoi(&p[2]);
				if ( lock_type < 0 )
					break;
				if ( setdbl(wait_action,lock_type) < 0 )
				{
					syserr("Data base temporarily unavailable");
				}
				break;
			  case '&':	/* equel program */
				Equel = 1;
				if (p[6] != '\0')
					Equel = 2;
				break;
	
			  case 'c':	/* c0 sizes */
				Out_arg.c0width = atoi(&p[2]);
				break;
	
			  case 'i':	/* iNsizes */
				switch (p[2])
				{
	
				  case '1':
					Out_arg.i1width = atoi(&p[3]);
					break;
	
				  case '2':
					Out_arg.i2width = atoi(&p[3]);
					break;
	
				  case '4':
					Out_arg.i4width = atoi(&p[3]);
					break;
	
				}
				break;
	
			  case 'f':	/* fN sizes */
				p = &p[3];
				i = *p++;
				while (*p != '.')
					p++;
				*p++ = 0;
				if ((*q)[2] == '4')
				{
					Out_arg.f4width = atoi(&(*q)[4]);
					Out_arg.f4prec = atoi(p);
					Out_arg.f4style = i;
				}
				else
				{
					Out_arg.f8width = atoi(&(*q)[4]);
					Out_arg.f8prec = atoi(p);
					Out_arg.f8style = i;
				}
				*--p = '.';	/* restore parm for dbu's */
				break;
	
			  case 'v':	/* vertical seperator */
				Out_arg.coldelim = p[2];
				break;
	
			}
		}
	
	}
		/* set up trace flags */
		for (i = 0; i < NumFunc; i++)
		{
			f = FuncVect[i];
			if (f->fn_tflag != '\0')
				nobuffer |= tTrace(argv, f->fn_tflag, f->fn_tvect, f->fn_tsize);
			Ctx.ctx_name = Proc_name = f->fn_name;
			(*f->fn_initfn)(argc, argv);
		}
	/* end of ingres flags */

	/*
	**  Buffer standard output
	**
	**	Since VM/UNIX always buffers, we force non-buffered
	**	output if any trace flags are set.
	*/

	if (!nobuffer)
		set_so_buf();
	else
		setbuf(stdout, NULL);

	/* if Equel, tell the program to go ahead */
	if (Equel && Cm.cm_myproc == 1)
	{
		pb_prime(&pb, PB_REG);
		pb.pb_st = PB_FRONT;
		pb_flush(&pb);
	}

	/*
	**  Start executing routines.
	**
	**	Do_seq knows to exit if we get an EOF on the input pipe.
	*/

	i = setjmp(CmReset);
# ifdef xMONITOR
	markperf(&CmMonBuf);
# endif xMONITOR
	initbuf(Qbuf, QbufSize, ERR_QBUF, error);
	clrmem((char *) &Ctx, sizeof Ctx);
	Ctx.ctx_cmark = Ctx.ctx_pmark = markbuf(Qbuf);
	Ctx.ctx_name = Proc_name = Cm.cm_myname;
	Ctx.ctx_tvect = tT = FuncVect[0]->fn_tvect;
# ifdef xCTR2
	if (tTf(1, 1))
		lprintf("main: setjmp: %d\n", i);
# endif
	if (RubLevel >= 0)
		signal(SIGINT, rubcatch);
	closeall(FALSE, CmOfiles);
	for (;;)
	{
		Cm.cm_input = Cm.cm_rinput;
		pb.pb_st = PB_UNKNOWN;
		do_seq(&pb);
	}
}
/*
**  RUBPROC -- process rubout signals
**
**	This routine does the processing needed on rubouts
**	when running with the control module.  It basically
**	flushes pipes.
**
**	Parameters:
**		none.
**
**	Returns:
**		never
**
**	Side Effects:
**		Flushes pipes, etc.
**
**      This module should be renamed to compile the stuffs in the ../support
**          directory, because the latter modules also define rubproc() in
**          a different manner.
*/

void
rubproc()
{
	register int	i;
	pb_t		pb;
	register int	stat;

	/*
	**  Update the world for consistency.
	*/

	fflush(stdout);
	closecatalog(FALSE);
	i = pageflush(NULL);
	if (i != 0)
		syserr("rubproc: pageflush %d", i);

	/*
	**  Send SYNC blocks to all processes that are adjacent
	**	in the write direction.
	**  Arrange to ignore blocks from all processes that
	**	are adjacent in the read direction.
	*/

	pb_prime(&pb, PB_SYNC);
	for (i = 0; i < CM_MAXPROC; i++)
	{
		stat = Cm.cm_proc[i].pr_stat;
		if ((stat & PR_RADJCT) != 0)
			Syncs[i]++;
		if ((stat & PR_WADJCT) != 0)
		{
			pb.pb_proc = i;
			pb_write(&pb);
		}
	}

	/*
	**  Cleanup and exit.
	*/

	cm_cleanup(2);
}
