/*
 * This CGI script demonstrates the use of multipart mixed/replace
 * content type to display a series of files automatically, pausing
 * between each picture.
 * 
 * The CGI PATH_TRANSLATED variable is used as the name of a program
 * file that is opened and read.  Each line of the program file specifies
 * a file to display.
 *
 * Date: 18-JAN-1996
 * Revised: 30-JAN-1996		added source field.
 */
#include <stdio.h>
#include <unixio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <chpdef.h>
#include <descrip.h>
#include <acldef.h>
#include <armdef.h>
#include <lckdef.h>
#include <prvdef.h>
#include <ssdef.h>
#include <rmsdef.h>

#include "cgilib.h"
#include "scriptlib.h"
#include "subaccess.h"
#include "template.h"

static int open_showfile ( char *file, int update_flag, subfield_ctx ctx );
static int do_show ( subfield_ctx ctx, char * );
static int show_show ( subfield_ctx ctx, char * );
static int do_post ( subfield_ctx, char *, char * );
/*
 * Make global definitions for database field names.
 */
static char *field_names = 
	"seq,owner,content-type,file,delay,expiration,source";

static char *form_names[7] = { "seq", "owner", "content-type", "file",
	"delay", "expiration", "source" };
static char frm_seq[16], frm_owner[16], frm_content[128];
static char frm_file[256], frm_delay[16], frm_exp[32], frm_source[512];
static char *form_values[7] = { frm_seq, frm_owner, frm_content, frm_file,
	frm_delay, frm_exp, frm_source };
struct sf_params {
    char *method, *query;
    int state;
    int tok_count, tok_used;
    int tok_inwhite;
    char *tok[10];
    char tok_buf[1024];
};

static int my_out ( struct sf_params *arg, char *buf, int length );
static int my_escape ( struct sf_params *arg, char *buf, int length );
static long elev_privs[2];
int SYS$SETPRV();
/*****************************************************************************/
int main ( int argc, char ** argv )
{
    int status, need_update;
    long prev_privs[2];
    char *method, *template, *showfile, *query, *user_agent;
    struct subfield_context sfctx;
    struct sf_params escape_arg;
    /*
     * Kill installed image privileges.
     */
    elev_privs[0] = PRV$M_SYSPRV;
    elev_privs[1] = 0;
    status = SYS$SETPRV ( 0, elev_privs, 0, prev_privs );
printf("disable priv status: %d, prev: %d\n", status, 
prev_privs[0]&elev_privs[0] );
    /*
     * Establish CGI environment and retrieve method and path_info.
     */
    status = cgi_init ( argc, argv );
    if ( (status&1) == 0 ) exit ( status );

    method = cgi_info ( "REQUEST_METHOD" );
    if ( !method ) method = "";
    escape_arg.method = method;
    template = cgi_info ( "PATH_TRANSLATED" );
    if ( !template ) {
	cgi_printf ( "content-type: text/plain\n" );
	cgi_printf ( "status: 500 bad info\n\nMissing or invalid path info\n");
        cgi_printf ( "Must supply template file name\n");
	exit ( 1 );
    }
    if ( !*template ) {
	cgi_printf ( "content-type: text/plain\n" );
	cgi_printf ( "status: 500 bad info\n\nNull or invalid path info\n");
        cgi_printf ( "Must supply template file name\n");
	exit ( 1 );
    }
    showfile = "";
    /*
     * Determine query string, if present then preload data
     */
    set_default_template_fields ( 5, form_names, form_values );
    frm_file[0] = frm_seq[0] = frm_owner[0] = frm_content[0] = '\0';
    frm_delay[0] = frm_exp[0] = frm_source[0] = '\0';
    query = cgi_info ( "QUERY_STRING" );
    if ( !query ) query = "";
    /*
     * Parse template.
     */
    escape_arg.state = 0;
    escape_arg.query = query;
    status = process_template_file ( template, (template_callback) my_out, 
	&escape_arg, (template_callback) my_escape, &escape_arg );
    if ( escape_arg.state < 2 ) {
	/*
	 * Fallback error message.
	 */
	cgi_printf ( "content-type: text/plain\n" );
	cgi_printf ( "status: 500 error\n\n" );
	cgi_printf ( "Error processing template file, final status: %d\n",
		status );
	return 1;
    }
    /*
     * Dispatch on method.
     */
    showfile = escape_arg.tok[0];
    if ( 0 == strcmp ( method, "GET" ) && !(*query) ) {
	/*
	 * Do server push.
	 */
	user_agent = cgi_info ( "HTTP_USER_AGENT" );
	if ( user_agent ) if ( strncmp ( user_agent,
		"NCSA Mosaic for the X Window System/2.0", 39 ) == 0 ) {
	    cgi_printf("Content-type: text/plain\nStatus: 500 bad client\n\n");
	    cgi_printf("Sorry, your client does not support multipart output\n");
	    exit(1);
	}
	/*
	 * Opend the show list database.
	 */
	if ( !*showfile ) {
	    cgi_printf ( "Content-type: text/plain\nStatus: 403 bad file\n\n");
	    cgi_printf("Bad file: '%s'\n", cgi_info ( "PATH_INFO" ) );
	    exit ( 1 );
	}
	status = open_showfile ( showfile, 0, &sfctx );
	if ( (status&1) == 0  ) {
	    cgi_printf ( "Content-type: text/plain\nStatus: 404 bad file\n\n");
	    cgi_printf("Error reading show file '%s'\n", 
			cgi_info ( "PATH_INFO" ) );
	    exit ( 1 );
	}
	/*
	 * Start the show.
	 */
	status = do_show ( &sfctx, showfile );

    } else if ( 0 == strcmp ( method, "GET" ) ) {
	/*
	 * Template parse handled display of form.
	 */
	status = 1;
    } else if ( 0 == strcmp ( method, "HEAD" ) ) {
	/*
	 * Make head request return success.
	 */
	cgi_printf ( "Content-type: text/plain\n\n");  /* I'm alive */
    } else if ( 0 == strcmp ( method, "POST" ) ) {
	/*
	 * Handle form input to edit the showfile, creating a new
	 * incarnation.
	 */
	if ( !*showfile ) {
	    cgi_printf ( "Content-type: text/plain\nStatus: 403 bad file\n\n");
	    cgi_printf("Bad file: '%s'\n", cgi_info ( "PATH_INFO" ) );
	    exit ( 1 );
	}
	status = open_showfile ( showfile, 1, &sfctx );
	if ( (status&1) == 0  ) {
	    cgi_printf ( "Content-type: text/plain\nStatus: 404 bad file\n\n");
	    cgi_printf("Error updating show file '%s'\n", 
			cgi_info ( "PATH_INFO" ) );
	    exit ( 1 );
	}
	 status = do_post ( &sfctx, template, showfile );
    } else {
	/*
	 * Invalid method, return error.
	 */
	cgi_printf ( "Content-type: text/plain\n" );
	cgi_printf ( "Status: 400 bad request\n\n Invalid method\n" );
    }
    return status;
}
/*************************************************************************/
/*
 * Open showfile with a 'create-if' option if update flag set.
 */
static char *fdl_string = "\
 FILE; ORGANIZATION indexed; PROTECTION (system:RWED,owner:RWED,group,world);\
  RECORD; CARRIAGE_CONTROL carriage_return; FORMAT variable; SIZE 8192;\
  KEY 0; CHANGES no; PROLOG 3; SEG0_LENGTH 8; SEG0_POSITION 0; TYPE string;\
  KEY 1; CHANGES yes; DUPLICATES yes; SEG0_LENGTH 12; SEG0_POSITION 9; \
  TYPE string;\
";

static int open_showfile ( char *file, int update_flag, subfield_ctx ctx )
{
    int status, i, j, scount, last_dot;
    /*
     * attempt to open indexed file.
     */
    status = subfield_open ( file, update_flag ? "r+" : "r", 4096, ctx );
    if ( ((status&1) == 0) && update_flag ) {
	/*
	 * Open failure, attempt to create new file (only attempt if
         * open for update specified.
	 */
	IFILE *tmp;
	char head_rec[512];
	status = ifdlcreate ( fdl_string, file, "sys$disk:[]" );
	if ( (status&1) == 0 ) return status;
	/*
	 * Initialize header record so that subfield_open can determine
	 * what the fields names are and which are index keys (fixed length).
	 */
	sprintf ( head_rec, "%8s%c%12s%c%s%c%s", "", 0, "", 0,
		field_names, 0, "0" );
	tmp = ifopen ( file, "r+" );
	ifwrite_rec ( head_rec, 24 + 1 + strlen(field_names), tmp );
	ifclose ( tmp );
	/*
         * Re-attempt subfield open and take whatever the result.
	 */
        status = subfield_open ( file, "r+", 4096, ctx );
    }
    return status;
}
/*************************************************************************/
/* Process show file sf, each line holds a filename and flags controlling
 * display of the file.
 */
static int do_show ( subfield_ctx ctx, char *showfile )
{
    int status, length, need_update, bnd_len, i, j, count, max_count;
    FILE *df;	/* display file */
    char *boundary_tag, *line;
    char control_line[2048], display_buffer[2048];
    /*
     * Check client software for known varieties that don't support server push.
     */
    /*
     * Set content to multipart/mixed-replace
     */
    boundary_tag = "-azzadfiwerowg\n";		/* note final linefeed */
    bnd_len = strlen ( boundary_tag );
    cgi_printf ( "Content-type: multipart/x-mixed-replace;boundary=%s\n",
		boundary_tag );
    cgi_printf ( "%s", boundary_tag );	/* initial boundary */
    /*
     * Get count.
     */
    max_count = atoi ( ctx->hdr_data );
    /*
     * Outer loop scans show file.
     */
    for ( count = i = 0; i < max_count; count++ ) {
	/*
	 * Get next record from file
	 */
	char *seq, *owner, *content_type, *url, *delay, *expiration;

	status = subfield_read ( ctx, "", 0, 0 );
	if ( (status&1) == 0 ) {
	    status = subfield_close ( ctx );
	    if ( (status&1) == 0 ) break;
	    if ( count == 0 ) break;	/* empty file */
	    status = open_showfile ( showfile, 0, ctx );
	    if ( (status&1) == 0 ) break;
	    continue;
	}
	/*
	 * Skip if record expired.
	 */
	expiration = subfield_value ( ctx, "expiration" );
	if ( !expiration ) expiration = "";
	if ( *expiration ) {
	    int exp_day, cur_day, LIB$DAY();
	    LIB$DAY ( &cur_day, 0, 0 );
	    exp_day = atoi ( expiration );
	    if ( exp_day < cur_day ) {
		/* Don't count as valid record */
		--count; continue;
	    }
	}
	/*
	 * Interpret line and output next part.
	 */
	url = subfield_value ( ctx, "file" );
	df = fopen ( url, "r", "mbc=32" );
	if ( !df ) {
	    cgi_printf ( "Content-type: text/plain\n\n" );
	    cgi_printf ( "Error openning '%s'\n", url );
	} else {
	    content_type = subfield_value ( ctx, "content-type" );
	    if ( !content_type ) content_type = "text/plain";
	    if ( !*content_type ) content_type = "text/plain";
	    cgi_printf ( "Content-type: %s\n\n", content_type );
	    for ( ; ; ) {
		line = fgets ( display_buffer, sizeof(display_buffer), df );
		if ( !line ) break;
		cgi_printf ( "%s", line );
	    }
	    fclose ( df );
	}
	/*
	 * Send to boundary tag to mark part completed.  Use
	 * low-level scriptlib routine to force flush of buffer.
	 * Also check status and abort if client closed connection.
	 */
	status = net_link_write ( boundary_tag, bnd_len );
	if ( (status&1) == 0 ) {
	    fprintf(stderr,"Error writing boundary tag" );
	    return 1;    /* broken connection */
	}
	delay = subfield_value ( ctx, "delay" );
	sleep ( delay ? atoi(delay) : 5 );
    }
    /*
     * Send end-of-parts message, append "--" to boundary tag.
     */
    strcpy ( display_buffer, boundary_tag );
    strcpy ( &display_buffer[bnd_len-1], "--" );
    cgi_printf ( "Content-type: text/plain\n\nEnd of show!\n%s\n",
	display_buffer );
    return 1;
}
/*************************************************************************/
/* Retrieve ACL on indicated filename and see if it permits read access
 * to specified UIC.
 */
static int check_access ( char *fname, char *user )
{
    struct { short len, code; char *buffer; int *retlen; } itemlist[10];
#define SETITEM(itm,a,b,c,d) \
	itm.len=a; itm.code=b; itm.buffer=(char *) c; itm.retlen=(int *) d;
    int status, acllen,  context, objtype, i, j, slash, SYS$CHECK_ACCESS();
    long access, rights[2], privs[2];
    char vmsfile[256];
    $DESCRIPTOR(username,"");
    $DESCRIPTOR(filename,"");
    /*
     * Convert filename to VMS syntax.
     */
    if ( fname[0] == '/' ) {
	slash = 0;
	for ( j = i = 1; fname[i]; i++ ) {
	    vmsfile[i-j] = fname[i];
	    if ( fname[i] == '/' ) {
		if ( slash == 0 ) { 
		    vmsfile[i-j] = ':'; 
		    vmsfile[i] = '[';
		    j = 0;
		} else {
		    vmsfile[i] = '.';
		}
		slash = i;
	    }
	    if ( i > 254 ) break;
	}
	vmsfile[i-j] = '\0';
	if ( slash ) {
	    if ( vmsfile[slash] == '[' ) 
		strcpy ( &vmsfile[slash], &fname[slash+1] );
	    else vmsfile[slash] = ']';
	}
    } else {
	strncpy ( vmsfile, fname, 255 );
	vmsfile[255] = '\0';
    }
    /*
     * Setup arguments and call $CHECK_ACCESS
     */
    access = ARM$M_READ;
    SETITEM(itemlist[0],4,CHP$_ACCESS,&access,0)
    itemlist[1].len = itemlist[1].code = 0;

    objtype = ACL$C_FILE;
    context = 0;
    filename.dsc$w_length = strlen ( vmsfile );
    filename.dsc$a_pointer = vmsfile;
    username.dsc$w_length = strlen ( user );
    username.dsc$a_pointer = user;

    status = SYS$SETPRV ( 1, elev_privs, 0, 0 );
printf("Enable priv status: %d\n", status );
    status = SYS$CHECK_ACCESS ( &objtype, &filename, &username, &itemlist );
    SYS$SETPRV ( 0, elev_privs, 0, 0 );
    if ( (status&1) == 0 ) return status;
    return status;
}
/****************************************************************************/
/* Read form content and parse.  Return value is number of keywords parsed,
 * -1 for error.
 */
static int parse_form ( int key_limit, char **key, char **value )
{
    int i, j, content_length, length, count, start, finish, flen;
    char *var, *fdata;
    /*
     * Read form content into memory in preparation for parsing.
     */
    count = 0;
    var = cgi_info ( "CONTENT_LENGTH" );
    content_length = var ? atoi(var) : 0;
    
    if ( content_length > 0 ) {
	/*
	 * Allocate buffer and read entire form data into it, forcing final &.
	 */
	fdata = malloc ( content_length+1 );
	if ( !fdata ) return -1;
	
	length = cgi_read ( fdata, content_length );
    } else {
	/* See if form method was GET by mistake. */
	var = cgi_info ( "QUERY_STRING" );
	if ( var ) {
	    length = strlen ( var );
	    fdata = malloc ( length + 1 );
	    if ( !fdata ) return -1;
	    strcpy ( fdata, var );
	} else length = 0;
    }
    if ( length <= 0 ) return length;
    /*
     * Parse into keyword/value pairs, ampersands delimit the pairs.
     */
    if ( fdata[length-1] != '&' ) fdata[length++] = '&';
    start = finish = 0;
    for ( i = 0; i < length; i++ ) if ( !fdata[i] || (fdata[i] == '&') ) {
	/*
	 * Value parsed.  Unescape string and look for first equals sign
	 * to delimit field name from value.  Convert pluses to spaces.
	 */
	flen = i - start;
	for ( j = start; j < i; j++ ) if ( fdata[j] == '+' ) fdata[j] = ' ';
	net_unescape_string ( &fdata[start], &flen );
	finish = start + flen;
	fdata[finish] = '\0';
	for ( j = start; j < finish; j++ ) if ( fdata[j] == '=' ) {
	    /* Save pointers to pair in callers buffer, terminate strings */
	    key[count] = &fdata[start];
	    fdata[j] = '\0';
	    value[count] = &fdata[j+1];
	    count++;
	    if ( count >= key_limit ) return count;
	    break;
	} else {
	    /* Make characters in field name upper case */
	    fdata[j] = _toupper(fdata[j]);
	}
	start = i + 1;
    }
    return count;
}
/****************************************************************************/
/* Update header data field.
 */
int increment_header ( subfield_ctx ctx )
{
    int num;
    num = atoi ( ctx->hdr_data );
    sprintf ( ctx->hdr_data, "%d", num + 1 );
    return 1;
}
/****************************************************************************/
/* Post method is used to submit a form
 */
static int do_post ( subfield_ctx ctx, char *template, char *showfile )
{
    long rem_uic, f_uic;
    int i, status, length, key_count;
    int msg_ndx, mtype_ndx, dly_ndx, exp_ndx, del_ndx, src_ndx;
    char *remote_user, *keyword[20], *value[20], new_file[256], ctype[256];
    FILE *newsf;
    char prev_hdr[32], *space, *line, **elem;
    /*
     * Parse form data and save indices.
     */
    key_count = parse_form ( 20, keyword, value );
    msg_ndx = mtype_ndx = dly_ndx = exp_ndx = del_ndx = src_ndx = -1;
    for ( i = 0; i < key_count; i++ ) {
	if ( strcmp ( keyword[i], "MESSAGE_TYPE" ) == 0 ) mtype_ndx = i;
	else if ( strcmp ( keyword[i], "MESSAGE" ) == 0 ) msg_ndx = i;
	else if ( strcmp ( keyword[i], "DELAY" ) == 0 ) dly_ndx = i;
	else if ( strcmp ( keyword[i], "EXPIRATION" ) == 0 ) exp_ndx = i;
	else if ( strcmp ( keyword[i], "DELETE_ID" ) == 0 ) del_ndx = i;
    }
    /*
     * Convert username of poster to UIC for ACL testing.
     */
    remote_user = cgi_info ( "REMOTE_USER" );
    if ( !remote_user ) {
	remote_user = "WWW_SERVER";
    }
    status = check_access ( template, remote_user );
    if ( (status&1) == 0 ) {
	cgi_printf ( "Content-type: text/plain\n\n" );
	cgi_printf ( "No permission to post to '%s'.\nReason: ", template );

	if ( status == SS$_NOPRIV ) cgi_printf ( "no access\n" );
	else if ( status == RMS$_RNF ) cgi_printf ( "unknown user\n");
	else cgi_printf ( "status=%d\n", status );
	return 1; 
    }
    /*
     * Check for delete request.
     */
    if ( del_ndx >= 0 ) {
	char *rec_owner, *rec_file;
	/*
	 * Read record and verify owner.
	 */
	cgi_printf("Content-type: text/plain\n\n" );
	status = subfield_read ( ctx, value[del_ndx], 0, 
		strlen(value[del_ndx]) );
	if ( (status&1) == 0 ) {
	    cgi_printf("Error reading record %s\n", value[del_ndx] );
	} else {
	    rec_owner = subfield_value ( ctx, "owner" );
	    if ( strcmp ( rec_owner, remote_user ) != 0 ) {
	 	cgi_printf("Not owner of record %s\n", value[del_ndx] );
	    } else {
		/*
		 * Onwer checks out, delete record.
		 */
		rec_file = subfield_value ( ctx, "file" );
		status = subfield_delete_rec ( ctx );
		if ( (status&1) == 0 ) {
		    cgi_printf("Error deleting record %s: %d\n", 
			value[del_ndx], status );
		} else {
		    cgi_printf("Record %s deleted\n", value[del_ndx] );
		}
	        /*
		 * Remove file.
		 */
		if ( rec_file ) {
		    if ( 0 == delete ( rec_file ) ) {
			cgi_printf("Deleted file %s\n", rec_file );
		    } else {
			cgi_printf("Error deleting %s\n", rec_file );
		    }
		} else {
		    cgi_printf("No file specified in record\n");
		}
	    }
	}
	return status;
    }
    /*
     * Get record number to add.
     */
    status = subfield_update_header ( ctx, increment_header );
    /*
     * build new record.
     */
    subfield_set ( ctx, "seq", ctx->hdr_data );
    subfield_set ( ctx, "owner", remote_user );
    subfield_set ( ctx, "content-type", "text/plain" );
    subfield_set ( ctx, "file", msg_ndx >= 0 ? value[msg_ndx] : "" );
    if ( dly_ndx < 0 ) {
        subfield_set ( ctx, "delay", "5" );
    } else {
	int delay_val; char delay_str[8];
	delay_val = atoi(value[dly_ndx]);
	if ( delay_val < 1 ) delay_val = 5;
	else if ( delay_val > 30 ) delay_val = 30;
	sprintf ( delay_str, "%d", delay_val );
        subfield_set ( ctx, "delay",  delay_str );
    }
    if ( exp_ndx < 0 ) {
	subfield_set ( ctx, "expiration", "" );
    } else {
	int LIB$DAY(), cur_day, delta;
	char exp_value[32];
	LIB$DAY ( &cur_day, 0, 0 );
	delta = atoi ( value[exp_ndx] );
	sprintf ( exp_value, "%d", cur_day + delta );
        subfield_set ( ctx, "expiration",  exp_value );
    }
    subfield_set ( ctx, "source", msg_ndx >= 0 ? value[msg_ndx] : "" );
    if ( (mtype_ndx >= 0) && (msg_ndx >= 0) ) {
	/*
	 * Make new messagefile.
	 */
	int copy_message( char * user, int text_flag, char *arg, 
		char *showfile, char *new, char *content_type );
	status = copy_message ( remote_user, strcmp(value[mtype_ndx],"URL"),
		value[msg_ndx], showfile, new_file, ctype );
	src_ndx = msg_ndx;
	subfield_set ( ctx, "file", new_file );
	subfield_set ( ctx, "content-type", ctype );
    }
    /*
     * Add record.
     */
    status = subfield_write ( ctx );
    /*
     * Report status.
     */
    cgi_printf ( "Content-type: text/plain\n" );
    cgi_printf ( "\n" );
    if ( (status&1) == 1 ) {
	cgi_printf ( "Added record to slideshow %s, id: %s\n", showfile,
		ctx->hdr_data );
        cgi_printf ( "Content file: %s\n", new_file );
	cgi_printf ( "Content type: %s\n", ctype );
#ifdef DEBUG
        cgi_printf ( "Form fields: \n" );
        for ( i = 0; i < key_count; i++ ) cgi_printf("  %s = '%s'\n", keyword[i],
	    value[i] );
#endif
    } else {
	cgi_printf ( "Error updating slideshow %s, status: %d\n", showfile,
		status );
    }
    return 1;
}
static int my_out ( struct sf_params *arg, char *buf, int length )
{
    int sent, status;
    /*
     * Ignore all received data until my_escape doen parsing file info.
     */
    if ( arg->state < 2 ) return 1;
    /*
     * Check if query defined.
     */
    if ( arg->state == 2 ) return 0;
    buf[length] = '\0';
    cgi_printf ( "%s", buf, length );
    return 1;
}

static int my_escape ( struct sf_params *arg, char *buf, int length )
{
    int i, j, sent, status, subfield_count, content_length;
    char *var;
    struct subfield_context subfield;
    /*
     * collect data until we have enough to process.
     */
    if ( arg->state == 0 ) {
	arg->tok_count = 0;
	arg->tok_used = 0;
	arg->tok_inwhite = 1;
	arg->state = 1;
    } else if ( arg->state == 3 ) {
	if ( buf[length] ) {
	    char save_c;
	    save_c = buf[length];
	    buf[length] = '\0';
	    status = cgi_printf("%s",buf);
	    buf[length] = save_c;
	} else {
	    status = cgi_printf("%s",buf);
	}
	if ( (status&1) == 0 ) return status;
    }
    if ( arg->state > 1 ) return 1;	/* ignore rest */
    /*
     * Parse next string out of escape infor.
     */
    for ( i = 0; i < length; i++ ) {
	if ( arg->tok_used >= sizeof ( arg->tok_buf ) ) break;
	if ( arg->tok_inwhite ) {
	    if ( !isspace ( buf[i] ) ) {
		arg->tok_inwhite = 0;
		arg->tok[arg->tok_count] = &arg->tok_buf[arg->tok_used];
		arg->tok_buf[arg->tok_used++] = buf[i];
	    }
	} else {
	    if ( isspace ( buf[i] ) ) {
		arg->tok_inwhite = 1;
		arg->tok_buf[arg->tok_used++] = '\0';
		arg->tok_count++;
		if ( arg->tok_count >= 3 ) {
		    arg->state = 2;
		    break;
		}
	    } else {
		arg->tok_buf[arg->tok_used++] = buf[i];
	    }
	}
    }
    /*
     * If arg-state reaches 2 we have all we need.
     */
    if ( arg->state == 2 ) {
	/*
	 * Determine operating mode
	 */
printf("escape done, method: '%s', query: '%s'\n", arg->method, arg->query );
	if ( strcmp(arg->method, "GET") == 0 ) {
	    int q_val;
	    if ( *arg->query ) {
		/*
		 * Set state to force return of template data to client,
		 */
		arg->state = 3;
	        q_val = *arg->query ? atoi(arg->query) : 0;
    		if ( q_val ) {
		    /*
		     * Pre-load field values.
		     */
		    /* status = load_fields ( arg );
		    if ( (status&1) == 0 ) return status; */
		}
		cgi_printf("content-type: text/html\nstatus: 200 gotcha\n\n");
		return 1;
	    }
	} else if ( strcmp(arg->method, "POST") == 0 ) {
	}
	return 998;
    }
    return 1;
}
