[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
[0431]
[0432]
[0433]
[0434]
[0435]
[0436]
[0437]
[0438]
[0439]
[0440]
[0441]
[0442]
[0443]
[0444]
[0445]
[0446]
[0447]
[0448]
[0449]
[0450]
[0451]
[0452]
[0453]
[0454]
[0455]
[0456]
[0457]
[0458]
[0459]
[0460]
[0461]
[0462]
[0463]
[0464]
[0465]
[0466]
[0467]
[0468]
[0469]
[0470]
[0471]
[0472]
[0473]
[0474]
[0475]
[0476]
[0477]
[0478]
[0479]
[0480]
[0481]
[0482]
[0483]
[0484]
[0485]
[0486]
[0487]
[0488]
[0489]
[0490]
[0491]
[0492]
[0493]
[0494]
[0495]
[0496]
[0497]
[0498]
[0499]
[0500]
[0501]
[0502]
[0503]
[0504]
[0505]
[0506]
[0507]
[0508]
[0509]
[0510]
[0511]
[0512]
[0513]
[0514]
[0515]
[0516]
[0517]
[0518]
[0519]
[0520]
[0521]
[0522]
[0523]
[0524]
[0525]
[0526]
[0527]
[0528]
[0529]
[0530]
[0531]
[0532]
[0533]
[0534]
[0535]
[0536]
[0537]
[0538]
[0539]
[0540]
[0541]
[0542]
[0543]
[0544]
[0545]
[0546]
[0547]
[0548]
[0549]
[0550]
[0551]
[0552]
[0553]
[0554]
[0555]
[0556]
[0557]
[0558]
[0559]
[0560]
[0561]
[0562]
[0563]
[0564]
[0565]
[0566]
[0567]
[0568]
[0569]
[0570]
[0571]
[0572]
[0573]
[0574]
[0575]
[0576]
[0577]
[0578]
[0579]
[0580]
[0581]
[0582]
[0583]
[0584]
[0585]
[0586]
[0587]
[0588]
[0589]
[0590]
[0591]
[0592]
[0593]
[0594]
[0595]
[0596]
[0597]
[0598]
[0599]
[0600]
[0601]
[0602]
[0603]
[0604]
[0605]
[0606]
[0607]
[0608]
[0609]
[0610]
[0611]
[0612]
[0613]
[0614]
[0615]
[0616]
[0617]
[0618]
[0619]
[0620]
[0621]
[0622]
[0623]
[0624]
[0625]
[0626]
[0627]
[0628]
[0629]
[0630]
[0631]
[0632]
[0633]
[0634]
[0635]
[0636]
[0637]
[0638]
[0639]
[0640]
[0641]
[0642]
[0643]
[0644]
[0645]
[0646]
[0647]
[0648]
[0649]
[0650]
[0651]
[0652]
[0653]
[0654]
[0655]
[0656]
[0657]
[0658]
[0659]
[0660]
[0661]
[0662]
[0663]
[0664]
[0665]
[0666]
[0667]
[0668]
[0669]
[0670]
[0671]
[0672]
[0673]
[0674]
[0675]
[0676]
[0677]
[0678]
[0679]
[0680]
[0681]
[0682]
[0683]
[0684]
[0685]
[0686]
[0687]
[0688]
[0689]
[0690]
[0691]
[0692]
[0693]
[0694]
[0695]
[0696]
[0697]
[0698]
[0699]
[0700]
[0701]
[0702]
[0703]
[0704]
[0705]
[0706]
[0707]
[0708]
[0709]
[0710]
[0711]
[0712]
[0713]
[0714]
[0715]
[0716]
[0717]
[0718]
[0719]
[0720]
[0721]
[0722]
[0723]
[0724]
[0725]
[0726]
[0727]
[0728]
[0729]
[0730]
[0731]
[0732]
[0733]
[0734]
/*
 * Define support routines for porting CGI (Common Gateway Interface)
 * conforming programs to the DECthreads script environment.
 *
 * Routines:
 *    int cgi_init ( int argc, char **argv );
 *		Initialize support routines.  Command line arguments will
 *		be retrieved.
 *
 *    char *cgi_info ( char *name );
 *		This routine returns environment and request information
 *		for the current request.  If run in the CERN server
 *		environment, this routine simply performs a get
 *
 *    int cgi_write ( char *string, int length );
 *		Send output.
 *
 *    int cgi_printf ( char *ctlstr, ... );
 *
 *    int cgi_read ( char *buffer, int bufsize );
 *
 *    int cgi_set_dcl_env ( char *prefix );
 *    int cgi_set_cli_env ( char *prefix, char *table_name );
 *
 *    FILE *cgi_content_file();
 *
 *  Author:	David Jones
 *  Revised:	2-JUN-1994		! don't decode query_string (unless
 *					! module compiled with DECODE_QUERY
 *  Revised:	27-JUN-1994		! corrected cgi_read to use contentf.
 *  Revised:	19-JUL-1994		! Fixed bad parse of content_length.
 *  Revised:	27-SEP-1994		! added cgi_set_dcl_env() routine.
 *  Revised:    18-OCT-1994		! Switch to <DNETID2> to get host name.
 *  Revised:    20-DEC-1994		! Fixed bug in allocating scriptname
 *					! storage, wrong size being used.
 *  Revised:	14-JAN-1995		! Added missing REMOTE_PORT to env
 *					! table,  fixed length allocation in
 *					! fetch value.
 *  Revised:	26-APR-1995		! Build path_info from requested
 *					! url when script_path null.
 *  Revised:	16-MAY-1995		! additional routines for special
 *					! interface for defering output.
 *  Revised:	20-MAR-1996		! add cgi_set_cli_env function
 *  Revised:	22-MAR-1996		! add content cache.
 *  Revised:	27-AUG-1996		! fix bug in cgi_write, (large writes).
 *  Revised:	 6-MAY-1997		! handle case where script path
 *					! template used by server does not
 *					! end in "/" or "/*".
 *  Revised:	24-JUN-1997		! have cgi_set_cli_env create
 *					! multi-value logicals.
 *  Revised:	14-OCT-1998		! Fix bugs in multi-value logical
 *					! creation.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <descrip.h>
#include <lnmdef.h>
#include <libclidef.h>
#define strerror strerror_1
#include <string.h>
#undef strerror
char *strerror ( int errnum, ... );
#include <ctype.h>
#include <errno.h>
#include "cgilib.h"
#include "scriptlib.h"
/*
 * Define static structure to hold environment information expect by CGI
 * programs.
 */
struct envdef { char *name, *value; int state; };

#define ENV_LIMIT 200
#define NET_IO_SIZE 1000
#define CONTENT_CACHE_SIZE 4096
static int content_length=0, env_used = 19;
/*
 * Content cache will hold request content in memory if possible.
 * Iff content_cache_length is negative, content is in file opened on contentf,
 * otherwise content_cache_pos holds next read position from cache array.
 */
static int content_cache_length, content_cache_pos = 0;
static FILE *contentf;
static char content_cache[CONTENT_CACHE_SIZE];
static struct envdef env[ENV_LIMIT] = {
 /* Request independent variables */
 { "SERVER_SOFTWARE", "", 0 },
 { "SERVER_NAME", "", 0 },
 { "GATEWAY_INTERFACE", "CGI/1.0", 1 },

 /* Request-specific variables */
 { "SERVER_PROTOCOL", "", 0 },
 { "SERVER_PORT", "", 0 },
 { "REQUEST_METHOD", "", 0 },
 { "PATH_INFO", "", 0 },
 { "PATH_TRANSLATED", "", 0 },
 { "SCRIPT_NAME", "", 0 },
 { "SCRIPT_PATH", "", 0 },
 { "QUERY_STRING","", 0 },
 { "REMOTE_USER", "", 0 },
 { "REMOTE_ADDR", "", 0 },
 { "REMOTE_PORT", "", 0 },
 { "REMOTE_HOST", "", 0 },
 { "AUTH_TYPE", "", 0 },
 { "REMOTE_IDENT", "", 0 },
 { "CONTENT_TYPE", "", 0 },
 { "CONTENT_LENGTH", "", 0 }
};

static int env_index ( char *name )
{
    int i;
    for (i = 0; i < env_used; i++) if (0==strcmp ( name, env[i].name)) {
	return i;
    }
    return ENV_LIMIT;
}

/* Query server for tag and place result in environment array 
 */
static int fetch_value ( char *name, char *tag, int *length, char **value )
{
    int status, ndx;
    char buffer[4096];

    status = net_link_query ( tag, buffer, sizeof(buffer), length );
    if ( (status&1) == 0 ) return status;
    if ( *length > 0 ) {
	ndx = env_index ( name );
	if ( ndx >= ENV_LIMIT ) return 20;
	env[ndx].value = *value = malloc ( (*length) + 1 );
	strncpy ( *value, buffer, *length );
	env[ndx].value[*length] = '\0';
	env[ndx].state = 1;
    }
    return status;
}

static int load_translation ( char *name, char *tag, char *arg )
{
    int status, ndx, length;
    char *value, buffer[4096];
    status = net_link_write ( tag, strlen(tag) );
    if ( (status&1) == 0 ) return status;
    status = net_link_query ( arg, buffer, sizeof(buffer), &length );
    if ( (status&1) == 0 ) return status;
    if ( length > 0 ) {
	ndx = env_index ( name );
	if ( ndx >= ENV_LIMIT ) return 20;
	env[ndx].value = value = malloc ( (length) + 1 );
	strncpy ( value, buffer, length );
	env[ndx].value[length] = '\0';
	env[ndx].state = 1;
    }
    return status;
}
/***************************************************************************/
/* Return copy of content_file pointer.  Must convert cached content.
 */
FILE *cgi_content_file() { 
   if ( content_cache_length >= 0 ) {
	/*
	 * We have been reading from internal cache, convert it to real file.
	 */
	 contentf = fopen ( tmpnam(NULL), "w+", "fop=dlt", "mbc=64" );
	if ( contentf ) {
	    /* put cache contents into file and match current position. */
	    fwrite ( content_cache, content_cache_length, 1, contentf );
	    fseek ( contentf, content_cache_pos, 0 );
	}
	content_cache_length = -1;	/* cache no longer valid */
   }
   return contentf; 
}
/***************************************************************************/
/* Initialize CGI envrironment and place net_link in CGI mode.
 */
int cgi_init ( int argc, char **argv )
{
    int status;
    status = cgi_init_env ( argc, argv );
    if ( status&1 ) status = cgi_begin_output(1);
    return status;
}
/***************************************************************************/
/* Send <DNETCGI> tag to server to place link into CGI mode.  If rundown
 * flag true, exit handler will be setup to automatically terminate CGI
 * mode on image exit (sends </DNETCGI> tag).
 */
int cgi_begin_output ( int rundown_on_exit )
{
   int status;
   status = net_link_write ( "<DNETCGI>", 9 );
   if ( rundown_on_exit ) net_link_set_rundown ( "</DNETCGI>" );
   return status;
}
/***************************************************************************/
/* Initialize CGI environment array.  Return value is VMS condition code.
 */
int cgi_init_env ( int argc, char **argv )
{
    int status, ndx, i, colon, length;
    char *label, *value, buffer[4096];
    /*
     * Establish connection to server.
     */
    content_cache_length = -1;
    content_cache_pos = 0;
    status = net_link_open();
    if ( (status&1) == 0 ) return status;
    /*
     * Assume program invoked with arguments: method url protocol
     */
    if ( (ndx = env_index ( "REQUEST_METHOD" )) < ENV_LIMIT ) {
	env[ndx].state = 1;
	if ( argc > 1 ) env[ndx].value = argv[1];
    }
    if ( (ndx = env_index ( "SCRIPT_NAME" )) < ENV_LIMIT ) {
	if ( argc > 2 ) {
	    /* Duplicate argv[2] string. */
	    env[ndx].value = malloc ( strlen(argv[2])+1 );
	    strcpy ( env[ndx].value, argv[2] );
	    env[ndx].state = 1;
	}
    }
    if ( (ndx = env_index ( "SERVER_PROTOCOL" )) < ENV_LIMIT ) {
	env[ndx].state = 1;
	if ( argc > 3 ) env[ndx].value = argv[3];
    }
    /*
     * Ask server for its version and connection information.
     */
    status = fetch_value ( "SERVER_SOFTWARE", "<DNETID2>", &length, &value );
    if ( (status&1) == 1  ) value = strchr(value, ' ');
    else value = NULL;
    if ( value ) {
	*value++ = '\0';
	ndx = env_index ( "SERVER_NAME" );
	env[ndx].state = 1; env[ndx].value = value;
	value = strchr(value, ' ');
    }
    if ( value ) {
	*value++ = '\0';
	ndx = env_index ( "SERVER_PORT" );
	env[ndx].state = 1; env[ndx].value = value;
	value = strchr(value, ' ');
    }
    if ( value ) {
	*value++ = '\0';
	ndx = env_index ( "REMOTE_PORT" );
	env[ndx].state = 1; env[ndx].value = value;
	value = strchr(value, ' ');
    }
    if ( value ) {
	int addr;
	char *nextval;
	*value++ = '\0';
	ndx = env_index ( "REMOTE_ADDR" );
	nextval = strchr(value,' ');		/* find end of string */
	if ( nextval ) *nextval = '\0';		/* truncate it for atoi() */
	addr = atoi ( value );
	env[ndx].state = 1; env[ndx].value = malloc(16);
	sprintf ( env[ndx].value, "%d.%d.%d.%d", (addr&255),
	    ((addr>>8)&255), ((addr>>16)&255), ((addr>>24)&255) );
	value = nextval;
    }
    if ( value ) {
	*value++ = '\0';
	ndx = env_index ( "REMOTE_USER" );
	/*
	 * Only set value if string is non-null.
	 */
	if ( *value != ' ' ) env[ndx].state = 1; env[ndx].value = value;
	value = strchr(value,' ');		/* find end of string */
    }
    if ( value ) {
	*value++ = '\0';
	ndx = env_index ( "REMOTE_HOST" );
	env[ndx].state = 1; env[ndx].value = value;
	value = strchr(value,' ');		/* Find end of string */
	if ( value ) *value = '\0';		/* Terminate it */
    } else {
	/* Fallback remote host to REMOTE_ADDR */
	int andx;
	andx = env_index ( "REMOTE_ADDR" );
	if ( env[andx].state ) {
	    ndx = env_index ( "REMOTE_HOST" );
	    env[ndx].state = 1; env[ndx].value = env[andx].value;
	}
    }
    /*
     * Ask for name of script being executed and derive path_info and
     * script name from that.
     */
    status = fetch_value ( "SCRIPT_PATH", "<DNETPATH>", &length, &value );
    if ( ((status&1) == 1) && (strncmp(argv[2], value, length )==0) ) {
	/*
	 * add path_info entry.
	 */
	char *p;
	if ( (ndx = env_index ( "PATH_INFO" )) < ENV_LIMIT ) {
	    if ( length > 0 ) {
		/*
		 * path info follows script_name, (1st slash after script_path)
		 */
	        env[ndx].state = 1;
	        for ( p = &argv[2][length]; *p && (*p != '/'); p++ );
#ifndef NOHACK1
		if ( *p == '/' && value[length-1] != '/' ) {
		    /* Rewrite script_path, make new value and length */
		    int path_ndx;
		    while ( p != &argv[2][length] ) length++;
		    length++;
		    path_ndx = env_index ( "SCRIPT_PATH" );
		    env[path_ndx].value = value = realloc ( value, length+1 );
		    strncpy ( value, argv[2], length );
		    value[length] = '\0';
		    /* script path didn't end in '/', take next slash. */
		    for ( p++; *p && (*p != '/'); p++ );
		}
#endif
	        env[ndx].value = p;
	    } else {
		/*
		 * Get original URL to bypass server translation done on arg2
		 * Fixup path_info: strip search arg and unescape.
		 */
		int plen;
		fetch_value ( "PATH_INFO", "<DNETRQURL>", &plen, &p );
		for ( plen = 0; p[plen]; plen++ ) if ( p[plen] == '?' )
			{ p[plen] = 0; break; }
		net_unescape_string ( p, &plen );
		p[plen] = '\0';
	    }

	    load_translation ( "PATH_TRANSLATED", "<DNETXLATE>", p );

	}
	if ( (ndx = env_index ( "SCRIPT_NAME" )) < ENV_LIMIT ) {
	        if ( strlen(env[ndx].value) > length ) {
	        for ( p = &env[ndx].value[length]; *p && (*p != '/'); p++ );
	        *p = '\0';		/* Truncate script name */
	    }
	}
    }
    /*
     * Get query string and convert escaped characters.
     */
    status = fetch_value ( "QUERY_STRING", "<DNETARG>", &length, &value );
    if ( (status&1) == 0 ) return status;
    if ( length > 0 ) {
	length = length - 1;		/* remove leading '?' */
	for ( i = 0; i < length; i++ ) value[i] = value[i+1];
#ifdef DECODE_QUERY
        net_unescape_string ( value, &length );
#endif
	value[length] = '\0';
    }
    /*
     * Get header lines from HTTP server and save in environment array.
     */
    status = net_link_write ( "<DNETHDR>", 9 );
    if ( (status&1) == 0 ) return status;
    content_length = 0;
    do {
	status = net_link_read ( buffer, sizeof(buffer), &length );
	if ( (status &1) == 0 ) return status;
	/* Parse out header label */
	for ( colon = 0; colon < length; colon++ ) if ( buffer[colon]==':' ) {
	    value = malloc ( length+1 );
	    strncpy ( value, buffer, length );
	    value[length] = '\0';
	    /*
	     * Construct label.  Upcase characters and convert '-' to '_'.
	     */
	    label = malloc ( colon + 6 );
	    
	    strcpy ( label, "HTTP_" );
	    for ( i = 0; i < colon; i++ ) {
		label[i+5] = _toupper(buffer[i]);
		if ( label[i+5] == '-' ) label[i+5] = '_';
	    }
	    label[colon+5] = '\0';
	    /*
	     * Trim leading whitespace.
	     */
	    for ( colon++; isspace(value[colon]); colon++ );
	    /*
	     * Check for special header lines that go into pre-defined
	     * variables.
	     */
	    if ( 0 == strcmp(label,"HTTP_CONTENT_LENGTH") ) {
		ndx = env_index ( "CONTENT_LENGTH" );
		content_length = atoi ( &value[colon] );
		free ( label );
	    } else if ( 0 == strcmp ( label, "HTTP_CONTENT_TYPE") ) {
		ndx = env_index ( "CONTENT_TYPE" );
		free ( label );
	    } else if ( (ndx=env_index(label)) < ENV_LIMIT ) {
		/*
		 * Append string to existing.  Allocate extra.
		 */
		int old_length;
		char *cat_value;
		old_length = strlen ( env[ndx].value );
		cat_value = malloc ( old_length + length + 3 );
		strcpy ( cat_value, env[ndx].value );
		strcpy ( &cat_value[old_length], ", ");
		strcpy ( &cat_value[old_length+2], &value[colon] );

		free ( env[ndx].value );
		free ( value );
		value = cat_value;
		colon = 0;
	    } else {
		/* Make new */
		ndx = env_used;
		if ( ndx < ENV_LIMIT ) { 
		    env_used = ndx + 1; 
		    env[ndx].name = label;
		}
	    }
	    if ( ndx < ENV_LIMIT ) {
		char *in, *out;
		env[ndx].state = 1;		/* mark as valid */
		env[ndx].value = value;
		/* Trim label from value buffer. */
		if ( colon != 0) {
		    in = &value[colon]; 
		    for ( out = value; *in; *out++ = *in++ );
		    *out = '\0';
		}
	    }
	    break;
	}

	if ( (colon >= length) && (length > 0) ) {
	   /* Continuation header line, handle later */
	}
    } while ( length > 0 );	/* continue until null line read */
    /*
     * If request includes data, read it into temporary file.
     */
    if ( content_length > 0 ) {
	/*
	 * Check for content-length limit.
	 */
	char *cl_limit;
	cl_limit = getenv("WWW_MAX_CGILIB_CONTENT");
	if ( cl_limit ) {
	    if ( content_length > atoi(cl_limit) ) content_length = 0;
	}
	if ( content_length < CONTENT_CACHE_SIZE ) {
	    /* Make internal copy rather than creating file. */
	    content_cache_length = content_length;
	    contentf = (FILE *) 0;
	} else {
	    /* Content to big to go to cache, go directly to file. */
	    contentf = fopen ( tmpnam(NULL), "w+", "fop=dlt", "mbc=64" );
	}
	if ( contentf || content_cache_length >= 0 ) {
	    int remaining, k;
	    char *content_type;
	    /*
	     * Request the file a chunk at a time.
	     */
	    for ( remaining = content_length; remaining > 0;
		  remaining = remaining - length ) {
		/*
		 * Get server to read more data from client and sent it to us.
		 */
	        status = net_link_write ( "<DNETINPUT>", 11 );
		if ( (status&1) == 0 ) break;
		/*
		 * Read the sent data and output to contents file.
		 */
		status = net_link_read ( buffer, sizeof(buffer), &length );
		if ( (status&1) == 0 ) break;
		if ( length > 0 ) {
		    if ( contentf ) {
		        status = fwrite(buffer, length, 1, contentf);
		    } else {
			if ( length+content_cache_pos > CONTENT_CACHE_SIZE )
			    length = CONTENT_CACHE_SIZE - content_cache_pos;
			memcpy ( &content_cache[content_cache_pos],
				buffer,length);
			content_cache_pos += length;
		    }
		}

		if ( status == 0 ) { status = vaxc$errno; break; }/* error */
	    }
	    if ( remaining > 0 ) {
		fprintf ( stderr,
			"Error getting request contents: %s\n(continuing)\n",
			strerror ( EVMSERR, status ) );
	    }
	    /*
	     * Reset for reads.
	     */
	    if ( contentf ) fseek ( contentf, 0, 0 );
	    else content_cache_pos = 0;

	} else {
	    fprintf(stderr,"Error opening request contents temp file:\n%s\n",
			strerror(errno,vaxc$errno) );
	    content_length = 0;
	}
    }
    return status;
}
/************************************************************************/
/* Do server callback to translate server path according to server's rule
 * file translation rules, optionally doing protection check as well.
 * An invalid or protected path will return a zero in the length argument
 * and a 1 status, errors communicating with the server will return a VMS 
 * error condition code.
 */
int cgi_translate_path ( char *path, int check_protection,
	char *buffer, int bufsize, int *length )
{
    int status, tag_len;
    char *tag;

    *length = 0;
    if ( check_protection ) { tag = "<DNETXLATEV>"; tag_len = 12; }
    else { tag = "<DNETXLATE>", tag_len = 11; }
    /*
     * Send tag followed by path and read response.
     */
    status = net_link_write ( tag, tag_len );
    if ( (status&1) == 0 ) return status;
    status = net_link_query ( path, buffer, bufsize, length );
    return status;
}
/************************************************************************/
/* Return CGI variable.  In standard CGI this would just be a getenv call.
 * The lookup will ignore a "WWW_" prefix, if present, on the variable name
 * to ease porting of CERN VMS clients.
 */
char *cgi_info ( char *name ) 
{
    int ndx;

    if ( strncmp ( name, "WWW_", 4 ) == 0 )  name = &name[4];
    ndx = env_index ( name );
#ifdef DEBUG
printf("/cgilib/ info index for %s is %d\n", name, ndx );
#endif
    if ( ndx < env_used ) return env[ndx].value;

    return getenv ( name );
}
/*****************************************************************************/
/* Write data to CGI result file.  For CERN server this would go to
 * standard output.  Return value is number of characters written or -1;
 */
int cgi_write ( char *buffer, int bufsize )
{
    int status, remaining, length, segment;
    /*
     * Network link can't handle arbitrary write lengths, break it up.
     */
    for (remaining = bufsize; remaining > 0; remaining = remaining - length) {
	length = remaining > NET_IO_SIZE ? NET_IO_SIZE : remaining;
	status = net_link_write ( buffer, length );
        if ( (status&1) == 0 ) return -1;    /* error */
	buffer += length;
    }
    return (bufsize - remaining);
}
/*****************************************************************************/
/* Read data from contents file.  In unix CGI this would read from standard
 * input.  Return value is number of character read or zero for eof.
 */
int cgi_read ( char *buffer, int bufsize )
{
    int status, length;
    if ( content_length <= 0 ) return 0;	/* no data in request */
    if ( content_cache_length < 0 ) {
	return fread ( buffer, 1, bufsize, contentf );
    } else {
	int count;
	count = content_cache_length - content_cache_pos;
	if ( count > bufsize ) count = bufsize;
	if ( count <= 0 ) return -1;
	memcpy ( buffer, &content_cache[content_cache_pos], count );
	content_cache_pos += count;
	return count;
    }
}
/************************************************************************/
/* Convert internal environment list to global DCL symbols with the
 * specified prefix.  Number of symbols created is returned.  The
 * rundown string in the exit handler is reset and a DCL symbol
 * WWWEXEC_RUNDOWN_STRING, is defined as well.  Any content file data
 * will be lost when program exits (not available from DCL).
 */
int cgi_set_dcl_env ( char *prefix )
{
    return cgi_set_cli_env ( prefix, (char *) 0 );
}
int cgi_set_cli_env ( char *prefix,
	char *log_table )	/* if null or zero length, use DCL symbols */
{
    int i, lnm, hits, status, prefix_len, length, table, LIB$SET_SYMBOL();
    int LIB$SET_LOGICAL();
    $DESCRIPTOR(symbol,"");
    $DESCRIPTOR(value,"");
    $DESCRIPTOR(parent,"");
    char symname[256], table_name[256];
    /*
     * Define global symbol used by WWWEXEC to terminate connection properly.
     */
    symbol.dsc$a_pointer = symname;
    strcpy ( symname, "WWWEXEC_RUNDOWN_STRING" );
    symbol.dsc$w_length = strlen(symbol.dsc$a_pointer);
    value.dsc$a_pointer = "</DNETCGI>";
    value.dsc$w_length = strlen(value.dsc$a_pointer);

    table = LIB$K_CLI_GLOBAL_SYM;
    status = LIB$SET_SYMBOL ( &symbol, &value, &table );
    if ( (status&1) == 1 ) net_link_set_rundown ( "" );	/* eliminate rundown output */
    else printf("Error in set_symbol: %d, len: %d\n", status,
	symbol.dsc$w_length );
    /*
     * Determine output mode.
     */
    if ( !log_table ) lnm = 0;
    else if ( *log_table == '\0' ) lnm = 0;
    else {
	/*
	 * Set flag to cause logical name creation and upcase name.
	 */
	lnm = 1;
	for (i = 0; log_table[i]; i++) table_name[i] = toupper(log_table[i]);
	table_name[i] = '\0';
	parent.dsc$w_length = i;
	parent.dsc$a_pointer = table_name;
    }
    /*
     * Define local symbols to be used by calling script.
     */
    table = LIB$K_CLI_LOCAL_SYM;
    prefix_len = strlen ( prefix );
    if ( prefix_len > 255 ) prefix_len = 255;
    strncpy ( symname, prefix, prefix_len );

    for ( hits = i = 0; i < env_used; i++ ) if ( env[i].state ) {
	/*
	 * Construct symbol name.
	 */
	length = strlen ( env[i].name );
	if ( (prefix_len + length) > 255 ) length = 255 - prefix_len;
	symbol.dsc$w_length = prefix_len + length;
	strncpy ( &symname[prefix_len], env[i].name, length );
	/*
	 * Make descriptor for value.
	 */
	value.dsc$a_pointer = env[i].value;
	value.dsc$w_length = strlen ( env[i].value );
	/*
	 * upload symbol to environment, either logical name or CLI symbol.
	 */
	if ( lnm ) {
	    struct item_list { 
		short length, code; char *ptr; short *retlen; } *itm;
	    itm = (struct item_list *) 0;
/* 12-MAY-2000  MGD  (short)! */
	    if ( (short)value.dsc$w_length <= 0 ) continue;
	    else if ( value.dsc$w_length > 255 ) {
		/*
		 * Construct item list to define multiple names.
		 */
		int k, count;
		count = (value.dsc$w_length+255)/255;
		itm = (struct item_list *) malloc ( 
			sizeof(struct item_list) * (count+1) );
		for ( k = 0; value.dsc$w_length > 0; k++ ) {
		    itm[k].length = value.dsc$w_length;
		    if ( itm[k].length > 255 ) itm[k].length = 255;
		    itm[k].code = LNM$_STRING;
		    itm[k].ptr = value.dsc$a_pointer;
		    itm[k].retlen = (short *) 0;
		    value.dsc$a_pointer += itm[k].length;
		    value.dsc$w_length = value.dsc$w_length - itm[k].length;
		}
		itm[k].code = itm[k].length = 0;
	    }
	    status = LIB$SET_LOGICAL ( &symbol, &value, &parent, 0, itm );
	    if ( itm ) free ( itm );
	} else {
	    /* Truncate DCL symbol to 255 characters */
	    if ( value.dsc$w_length > 255 ) value.dsc$w_length = 255;
	    status = LIB$SET_SYMBOL ( &symbol, &value, &table );
	}
    }
    return hits;
}
/*****************************************************************************/
/* Dump environement array to specified output file.
 */
int cgi_show_env ( int (*user_printf)() )
{
    int i, hits;
    for ( hits = i = 0; i < env_used; i++ ) {
	user_printf("%s %s%s\n", env[i].name, 
		env[i].state ? "= ": "*undefined* ", env[i].value);
	if ( env[i].state ) hits++;
    }
    return hits;
}
/**************************************************************************/
/* Provide formatted output to net_link, replacing printf calls to stdout.
 * Newline chars in format string are replaced with carriage-return/linefeed
 * pairs.
 */
int cgi_printf ( const char *fmt, ... )
{
    va_list alist;
    int state, out_len, status, j;
    char buffer[8192];
    /*
     * Setup pointer to variable argument list.
     */
    va_start ( alist, fmt );
    status = vsprintf ( buffer, fmt, alist );
    if ( status == EOF ) return status;
    va_end ( alist );

    status = net_link_printf ( "%s", buffer );
    return status;
}