/* * This program is run by the WWWEXEC scriptserver to do pre-processing of * html files, dynamically inserting files or other generated data. * * To specify the preprocessing you must give the file a distinct file type * (e.g. htmlx) and add the following to the configuration file: * * suffix .htmlx text/x-server-parsed-html * presentation text/x-server-parsed-html html_preproc * * WWWEXEC will then execute the following command line: * * html_preproc method url protocol * * argv[1] Method specified in request (e.g. GET). * argv[2] Ident portion of requested URL, after translation by * rule file. See special parsing note below. * argv[3] Protocol specified in request, "" or "HTTP/1.0". * * None of the resulting file contents is returned until the entire file * has been processed. This is required for 2 reasons: * * 1. The HTTP status, which is the first part of the response, is not known * until processing is complete. * * 2. Processing of the file may require using additional server functions, * such as , which become unavailable once the HTTP response * is started. * * Argv[2] parsing: * If the path in argv[2] is of the form /dir/filename.partname.type, * then partname is extracted from /dir/filename.type. * * Author: David Jones * Date: 25-AUG-1994 * Revised: 3-SEP-1994 Re-coded parsing. * Revised: 6-OCT-1994 Support fsize/flastmod. * Revised: 11-OCT_1994 Adjust length in translate. * Revised: 20-DEC-1994 Fix length in do_include * Revised: 22-APR-1995 Added extra echo variables suggested by * Kent Covert (kacovert@miavx1.acs.muohio.edu): * DOCUMENT_NAME, LAST_MODIFIED, * ACCESSES/ACCESSES_ORDINAL * Revised: 23-APR-1995 Add permissions file checks to ACCESSES var. * Revised: 25-APR-1995 Overhaul: * - Add strftime() formatting options to * LAST_MODIFIED, DATE_LOCAL, ACCESSES* * - Add version numbering to ACCESSES. * - Rename indexio.h to access_db.h * Revised: 15-MAY-1995 Use for virtual includes * (does protection check). * Revised: 21-JUN-1995 Remove '\r's from net_link_printf formats as * net_link_printf will add them. * Revised: 8-AUG-1995 Use CGI mode rather than RAW mode (stsline * can be sent using status: CGI header) * Revised: 31-AUG-1995 Fixup formatting of ACCESSES values for * VAXC compatibility. * Revised: 13-NOV-1995 Add tag_verify config option. * Revised: 12-MAY-1996 Convert for MST support, (symbol IS_MST defined * when compiled as part of MST image). * Revised: 18-MAY-1996 Add part include idea of Richard Levitte. * Revised: 19-MAY-1996 Changed to understand multiple instances of * a part. * Revised: 27-MAY-1996 Changed to accept multiple part names in * #begin and #end directives. Separate names * with whitespace, e.g.: * * (allows you to condense comments around * command parts such as header tags). * Revised: 28-MAY-1996 Bug fix, supply missing argument in open error * messages (MST's only). * Revised: 9-SEP-1996 Change member names to avoid recursive * macro substituition on MST variant. * Revised: 25-OCT-1996 Place limit on size of file to process, * 100,000 for script, 60000 for MST. * Revised: 25-DEC-1996 Split main routine into 2 parts to enable * pre-processor to be called from other * scripts (must define CALLABLE_PREPROC). * Revised: 3-FEB-1997 Fix bug handling directives that return zero * length segment. * Revised: 26-AUG-1997 Add last-modified header if not using counter. * Revised: 8-DEC-1997 Add limited support for nested includes. * (lastmod date is always for primary file). * Revised: 12-DEC-1997 Consolidate file loads into single routine. * Revised: 15-DEC-1997 Save to cache in 4000 byte chunks vs. 16000 * Revised: 28-JAN-1998 Modify parse_part_name to ignore null part * names in the filename, allowing specification * of files with multiple dots: * foo.htmlx file foo.htmlx * foo.bar.htmlx part bar in foo.htmlx * foo.bar.x.htmlx part x in foo.bar.htmlx * foo..htmlx file foo.htmlx * foo.bar..html file foo.bar.htmlx */ #ifndef IS_MST /* * Include files only needed when not an MST */ #include #include #include #include #include #include "scriptlib.h" #include "access_db.h" #define LOCK_C_RTL #define UNLOCK_C_RTL #define INPUT_LIMIT 100000 #else #define INPUT_LIMIT 60000 #endif #include #include #include #include int LIB$GETSYI(); #if defined(DEBUG) && defined(IS_MST) #error "The combination of DEBUG and IS_MST is not allowed." #endif struct segment { int length; char *addr; }; typedef struct segment *segptr; static int send_http_header ( char *stsline, char *content ); static int parse_directive ( char *path, char **targ, int *outlen, char **outbuf ); static int parse_tag ( char *tag, int maxlen, int *taglen, char **targ ); static int parse_part_name ( char *fname, char **pfile, char **pname ); static int extract_part ( char *, char *, int *, char ** ); static segptr alloc_segment ( segptr seg, int sc, int *seg_size, char *start ); static int tag_verify, abort_status; static char *access_file_fdl = "\ FILE; ORGANIZATION indexed; PROTECTION (system:RWED,owner:RWED,group,world);\ RECORD; CARRIAGE_CONTROL carriage_return; FORMAT fixed; SIZE 120;\ KEY 0; CHANGES no; PROLOG 3; SEG0_LENGTH 100; SEG0_POSITION 0; TYPE string;\ "; static int preprocess_html_source ( char *method, char *ident, char *source, int source_length ); static int load_file ( char *fname, char * lname, int *outlen, char **outbuf, int initial ); /****************************************************************************/ /* Main program/routine */ #ifndef CALLABLE_PREPROC #ifdef IS_MST static int preproc_main ( int argc, char **argv ) #else int main ( int argc, char **argv ) #endif { int status, i, j, k, length, s_size, used, sc, sg_size; char *source, *part_file, *part_name; char hdrline[64]; #ifdef IS_MST struct private_ctx *ctx; void *hfile, *link; char errmsg[256]; /* * Get pointer to thread's private data (particularly link). */ GET_SPECIFIC ( private_context, ctx ) if ( http_log_level > 6 ) tlog_putlog ( 7, "Context address: !XL, args: !AZ '!AZ' !AZ!/", ctx, argv[1], argv[2], argv[3] ); #else FILE *hfile; /* * Make connection back to server and set protocol version field for * the response to server. */ status = net_link_open(); if ( 0 == (status&1) ) exit ( status ); #endif /* * Validate method (argv[1]). We only understand GET and HEAD. */ if ( argc < 4 ) exit ( 20 ); if ( strncmp("GET",argv[1],4) && strncmp("HEAD",argv[1],5) ) { send_http_header ( "501 unsupported method", "text/plain" ); net_link_printf ("Unsupported method (%s)\n", argv[1] ); return 1; } if ( parse_part_name ( argv[2], &part_file, &part_name ) ) { /* * argv[2] was of form /path/filename.partname.type. */ status = extract_part ( part_file, part_name, &used, &source ); if ( status < 0 ) return status; argv[2] = part_file; } else { /* * Open file specified by argv[2] (fixed up and returned in part_file). */ #ifdef IS_MST ctx->last_mod = 0; #endif status = load_file ( part_file, part_file, &used, &source, 1 ); if ( status < 0 ) return status; } /* * Parse source. */ status = preprocess_html_source ( argv[1], argv[2], source, used ); return status; } #else /* * Set scriptlib mode to buffer all output. Call instead of cgi_begin_output * (called after cgi_init_env). Do not next CGI response header * (content-type) as this is assumed to be text/html. */ #include "scriptlib.h" static int old_nl_mode; static char *pp_method, *pp_ident; int cgi_begin_preprocessed ( char *method, char *ident ) { int status, old_mode; pp_method = malloc ( strlen(method)+1 ); strcpy ( pp_method, method ); pp_ident = malloc ( strlen(ident)+1 ); if ( !pp_ident ) return 0; strcpy ( pp_ident, ident ); old_nl_mode = net_link_set_mode ( 2 ); /* text mode, save ouput */ if ( (old_nl_mode&1) == 1 ) net_link_set_mode ( 3 ); return 1; } int cgi_end_preprocessed() { char *buffer; int status, length; /* * reset mode and get address of buffer holding saved data. */ net_link_set_mode ( old_nl_mode ); status = net_link_saved_output ( &buffer, &length ); if ( status&1 ) status = preprocess_html_source ( pp_method, pp_ident, buffer, length ); free ( pp_ident ); free ( pp_method ); return status; } #endif /* CALLABLE_PREPROC */ /****************************************************************************/ /* Recursive routine to scan source text for tags to pre-process. * Return value is number of segments. */ static int build_segment ( char *ident, char *source, int source_length, segptr *seglist, int *seglist_size, int seglist_start ) { int status, is_directive, i, j, k, length, s_size, sc, sg_size; segptr seg; char *targ[11], hdrline[128]; #ifdef IS_MST struct private_ctx *ctx; int reslen, tag_verify; /* * Get pointer to thread's private data and initialize local tag_verify. */ GET_SPECIFIC ( private_context, ctx ) tag_verify = ctx->tag_verify; #endif /* * Parse file, making list of individual blocks of text to send * back to server. */ seg = *seglist; sg_size = *seglist_size; /* make local copies of parameters */ sc = seglist_start; /* first element to load */ seg[sc].length = 0; seg[sc].addr = source; status = 1; for ( i = 0; i < source_length; i++ ) { char cur; cur = source[i]; if ( cur == '<' ) { /* * At beggining of HTML tag, go to state machine to parse. */ is_directive = parse_tag ( &source[i], source_length-i, &j, targ ); #ifdef DEBUG printf("Tag at %d (seg[%d].addr[%d]), length: %d, directive: %d verify: %d\n", i, sc, seg[sc].length, j, is_directive, tag_verify ); #endif if ( j > 0 && (!is_directive || tag_verify) ) { /* * Include parsed tag in output, as a comment the client * will ignore it. */ seg[sc].length += j; while ( seg[sc].length >= 1024 ) { /* Make current segment 1024 and put rest in new segment */ seg = alloc_segment ( seg, sc, &sg_size, &seg[sc].addr[1024] ); seg[sc+1].length = seg[sc].length - 1024; seg[sc++].length = 1024; } } i += j - 1; if ( is_directive ) { /* * Terminate current segment and make fresh one to hold * included info. Init start address of new segment to * point after tag. */ if ( seg[sc].length > 0 ) { seg = alloc_segment ( seg, sc++, &sg_size, &source[i] ); } else seg[sc].addr = &source[i]; j = 0; /* remove already added. */ status = parse_directive ( ident, targ, &seg[sc].length, &seg[sc].addr ); if ( status < 0 ) return status; #ifdef IS_MST tag_verify = ctx->tag_verify; #endif if ( status > 1 ) { /* recusive scan */ #ifdef IS_MST ctx->recur_level++; if ( ctx->recur_level < 5 ) sc = build_segment ( ident, seg[sc].addr, seg[sc].length, &seg, &sg_size, sc ); --ctx->recur_level; #else sc = build_segment ( ident, seg[sc].addr, seg[sc].length, &seg, &sg_size, sc ); #endif if ( sc < 0 ) return sc; } while ( seg[sc].length > 1024 ) { /* Make current segment 1024 and put rest in new segment */ seg = alloc_segment ( seg, sc, &sg_size, &seg[sc].addr[1024] ); seg[sc+1].length = seg[sc].length - 1024; seg[sc++].length = 1024; } if ( seg[sc].length > 0 ) { seg = alloc_segment ( seg, sc++, &sg_size, &source[i+1] ); } else { /* * result of directive was zero length, update start * point */ seg[sc].addr = &source[i+1]; } } } else { /* * Extend current segment to include this character. */ seg[sc].length++; if ( seg[sc].length >= 1024 ) { /* Segment at write limit, advance to next */ seg = alloc_segment ( seg, sc++, &sg_size, &source[i+1] ); } } } /* * Check for error status. */ if ( status < 0 ) return status; *seglist = seg; *seglist_size = sg_size; return sc; } /****************************************************************************/ /* Main engine for interpreting the HTML source for pre-processor directives. * When finished, network link is placed in CGI mode and result is dumped * to net_link. */ static int preprocess_html_source ( char *method, char *ident, char *source, int source_length ) { int status, is_directive, i, j, k, length, s_size, sc, sg_size; segptr seg; char *targ[11], hdrline[128]; #ifdef IS_MST struct private_ctx *ctx; int reslen, tag_verify; /* * Get pointer to thread's private data and initialize local tag_verify. */ GET_SPECIFIC ( private_context, ctx ) tag_verify = ctx->tag_verify; #else /* * Initialize global tag_verify. */ tag_verify = 0; #endif /* * Parse file, making list of individual blocks of text to send * back to server. */ sg_size = 1000; seg = (struct segment *) malloc ( sg_size * sizeof(struct segment) ); sc = build_segment ( ident, source, source_length, &seg, &sg_size, 0 ); if ( sc < 0 ) return -1; /* * Flush accumulated segments, compute total length and include in header. */ for (k = i = 0; i <= sc; i++) if (seg[i].length > 0) k += seg[i].length; #ifdef IS_MST /* 123456789012345678901234567 89012345678901234 */ tu_strcpy ( hdrline, "200 Sending Processed HTMLX\nContent-length: " ); tu_strint ( k, &hdrline[44] ); if ( !ctx->accessesKnown && ctx->last_mod ) { int hlen, cstatus, i, j; struct mstshr_envbuf env; char *if_modified_since; /* * Check for if-modified-since header. */ env.used = 1; env.prolog[0] = ctx->prolog[0]; env.prolog[1] = ctx->prolog[1]; env.prolog[2] = ctx->prolog[2]; env.prolog[3] = ctx->prolog[3]; cstatus = mstshr_cgi_symbols2 ( ctx->link, "", &env ); if_modified_since = mstshr_getenv ( "HTTP_IF_MODIFIED_SINCE", &env ); if ( http_log_level > 6 ) tlog_putlog ( 7, "if-modified header: !XL '!AZ'!/", if_modified_since, if_modified_since ? if_modified_since : "" ); if ( if_modified_since ) { unsigned int cache_time; for ( i = j = 0; if_modified_since[i]; i++ ) { if ( if_modified_since[i] != ' ' ) { if_modified_since[j++] = if_modified_since[i]; } } if_modified_since[j] = '\0'; cache_time = tf_decode_time ( if_modified_since ); if ( ctx->last_mod <= cache_time ) { tu_strcpy ( hdrline, "304 Cached copy good\nContent-length: " ); tu_strint ( k, &hdrline[37] ); method = "HEAD"; /* eliminate body */ } } /* Append last-modified header since counter not used */ hlen = tu_strlen ( hdrline ); tu_strcpy ( &hdrline[hlen], "\nLast-modified: " ); hlen += 16; tf_format_time ( ctx->last_mod, &hdrline[hlen] ); } #else sprintf ( hdrline, "200 Sending Processed HTMLX\nContent-length: %d", k ); #endif send_http_header ( hdrline, "text/html" ); if ( 0 == strncmp(method,"HEAD",5) ) return 1; for ( i = 0; i <= sc; i++ ) if ( seg[i].length > 0 ) { #ifdef IS_MST status = mst_write ( ctx->link, seg[i].addr, seg[i].length, &reslen ); #else status = net_link_write ( seg[i].addr, seg[i].length ); #endif if ( (status&1) == 0 ) break; } return 1; } /**************************************************************************/ /* Convert binary time to ascii string. If fmt_str does not start with '=', * use ctime format, otherwise use string after '=' as format for strftime(). */ static char *format_time (char *buffer, int bufsize, char *fmt, void *timebuf) { int i; #ifdef __DECC if ( fmt ) if ( *fmt == '=' ) { size_t size; LOCK_C_RTL size = strftime ( buffer, bufsize, &fmt[1], localtime((time_t *)timebuf) ); UNLOCK_C_RTL if ( (size > 0) && (size < bufsize) ) buffer[size] = '\0'; return buffer; } #endif /* * fallback to using ctime() routine. */ #ifdef IS_MST pthread_lock_global_np(); strncpy ( buffer, ctime((unsigned long *) timebuf), bufsize-1 ); buffer[bufsize-1] = '\0'; pthread_unlock_global_np(); #else strncpy ( buffer, ctime((unsigned long *) timebuf), bufsize-1 ); buffer[bufsize-1] = '\0'; #endif for ( i = 0; buffer[i]; i++ ) if ( buffer[i] == '\n' ) { buffer[i] = '\0'; break; } return buffer; } /**************************************************************************/ static segptr alloc_segment ( segptr seg, int sc, int *seg_size, char *start ) { sc = sc + 1; if ( sc >= *seg_size ) { *seg_size += 1000; seg = realloc ( seg, sizeof(struct segment)*(*seg_size) ); } seg[sc].length = 0; seg[sc].addr = start; return seg; } /**************************************************************************/ /* Prepare to send back response. Build standard response header. */ static int send_http_header ( char *stsline, char *content ) { int status; #ifdef IS_MST struct private_ctx *ctx; int reslen; /* * Get pointer to thread's private data (particularly link). */ GET_SPECIFIC ( private_context, ctx ); #endif /* * Enter CGI mode, set rundown to terminate mode on exit. */ #ifdef IS_MST status = mst_write ( ctx->link, "", 9, &reslen ); if ( (status&1) == 0 ) { abort_status = status; pthread_exit (&abort_status); } ctx->cgimode_active = 1; #else status = net_link_write ( "", 9 ); if ( 0 == (status&1) ) exit ( status ); status = net_link_set_rundown ( "" ); #endif /* * Send back standard header. */ if ( status&1 ) status = net_link_printf ( "Content-type:%s\nstatus: %s\n\n", content, stsline ); return status; } /*****************************************************************************/ static int extract_part ( char *fname, /* pathname of file being parsed */ char *partname, /* name of part to extract */ int *outlen, char **outbuf ) { char *ibuf, *tag, *name; int length, buf_used, buf_size; int state, tagstate, tagstart, isquoted, ibuf_i; char *partname_p; struct part_chunk { int start, end; } *parts; int current_part; int parts_size; #ifdef IS_MST void *ifile; char errmsg[256]; /* * Get pointer to private data. */ #else FILE *ifile; #endif *outlen = 0; /* * Open the file. */ length = strlen ( fname ); #ifdef IS_MST ifile = (length > 0) ? tf_open ( fname, "r", errmsg ) : (void *) 0; if ( ifile ) { /* Update last mod date in ctx if later than existing */ int size; unsigned uic, cdate, mdate; struct private_ctx *ctx; GET_SPECIFIC ( private_context, ctx ) tf_header_info ( ifile, &size, &uic, &cdate, &mdate ); if ( mdate > ctx->last_mod ) ctx->last_mod = mdate; } #else ifile = (length > 0) ? fopen ( fname, "r", "mbc=32" ) : (FILE *) 0; #endif if ( !ifile ) { send_http_header ( "500 Failed to open include file", "text/plain"); #ifdef IS_MST net_link_printf ( "Could not open include part file (%s)\n%s", fname, errmsg ); #else net_link_printf ( "Could not open include part file (%s)\n%s", fname, strerror ( errno, vaxc$errno ) ); #endif return -1; } buf_size = 10000; buf_used = 0; ibuf = malloc ( buf_size ); /* I make sure to allocate 8192 bytes, because that's the minimum memory page on an Alpha, and malloc() allocates full memory pages anyway... */ parts_size = 8192 / sizeof (struct part_chunk); parts = malloc ( parts_size * sizeof ( struct part_chunk ) ); current_part = 0; parts[current_part].start = -1; parts[current_part].end = 0; state = 0; tagstate = 10; /* 10 when looking for , 20 when looking for */ isquoted = 0; ibuf_i = 0; #ifdef IS_MST while ( (length=tf_read(ifile,&ibuf[buf_used], buf_size-buf_used)) > 0) { #else while ( (length=fread(&ibuf[buf_used], 1, buf_size-buf_used, ifile)) > 0) { #endif if (parts[current_part].end == buf_used) parts[current_part].end += length; buf_used += length; if ( buf_used >= buf_size ) { buf_size += 10000; ibuf = realloc ( ibuf, buf_size ); } for (; state >= 0 && ibuf_i < buf_used; ibuf_i++) { char cur = ibuf[ibuf_i]; #ifdef DEBUG printf ("ibuf_i{1,2,3} = {%d, %d, %d}, ", parts[current_part].start, ibuf_i, parts[current_part].end); printf ("state = %d, ", state); printf ("tagstate = %d, tagstart = %d, ", tagstate, tagstart); printf ("cur = '%c' (0%%x%X), *partname_p = '%c' (0%%x%X)\n", (cur & 127) >= 32 ? cur : '.', cur, (*partname_p & 127 ) >= 32 ? *partname_p : '.', *partname_p); #endif switch (state) { case 0: if (cur == '<') { state = 1; tagstart = ibuf_i; } break; case 1: if (cur == '!') { state = 3; break; } state = 2; case 2: if (cur == '>') state = 0; break; case 3: if (cur != '-') { state = 2; ibuf_i--; break; } state = 4; break; case 4: if (cur != '-') { state = 2; ibuf_i--; break; } state = 5; break; case 5: if (cur != '#') { state = 2; ibuf_i--; break; } state = tagstate; break; case 6: /* Find terminator */ if (cur == '-') state = 7; else if (tagstate > 0 && !isspace(cur) && cur != '\n') state = 2; break; case 7: if (cur == '-') state = 8; else if (tagstate > 0) state = 2; else state = 6; break; case 8: if (isspace(cur) || cur == '\n') break; if (cur != '>') { tagstate = -tagstate; state = 6; break; } state = 0; #ifdef DEBUG { int saved_part = current_part; printf ("before: tagstate = %d, part = %d, start = %d, end = %d\n", tagstate, saved_part, parts[current_part].start, parts[current_part].end); #endif if (tagstate < 0) { /* restart from the beginning */ tagstate = -tagstate; if (tagstate == 20) parts[current_part].end = buf_used; } else if (tagstate == 10) { parts[current_part].start = tagstart; parts[current_part].end = buf_used; tagstate = 20; } else if (tagstate == 20) { parts[current_part++].end = ibuf_i + 1; if ( current_part >= parts_size ) { parts_size += 8192 / sizeof ( struct part_chunk ); parts = realloc ( parts, parts_size * sizeof ( struct part_chunk ) ); } /* -1 indicates it's not been set yet */ parts[current_part].start = -1; parts[current_part].end = buf_used; tagstate = 10; } #ifdef DEBUG printf ("after: tagstate = %d, part = %d, start = %d, end = %d\n", tagstate, saved_part, parts[current_part].start, parts[current_part].end); } #endif break; case 10: if (isspace(cur) || cur == '\n') break; if (cur != 'b' && cur != 'B') { state = 2; break; } state = 11; break; case 11: if (cur != 'e' && cur != 'E') { state = 2; break; } state = 12; break; case 12: if (cur != 'g' && cur != 'G') { state = 2; break; } state = 13; break; case 13: if (cur != 'i' && cur != 'I') { state = 2; break; } state = 14; break; case 14: if (cur != 'n' && cur != 'N') { state = 2; break; } state = 30; partname_p = partname; break; case 20: if (isspace(cur) || cur == '\n') break; if (cur != 'e' && cur != 'E') { state = 2; break; } state = 21; break; case 21: if (cur != 'n' && cur != 'N') { state = 2; break; } state = 22; break; case 22: if (cur != 'd' && cur != 'D') { state = 2; break; } state = 30; partname_p = partname; break; case 30: if (isspace(cur) || cur == '\n') break; partname_p = partname; state = 31; isquoted = 0; if (cur == '"') { isquoted = 1; break; } case 31: if (*partname_p == cur) { partname_p++; break; } if ( (*partname_p == '\0') ) { if ( isquoted ) { if ( cur == '"' ) { state = 33; break; } } else { if ( (cur == '-') || isspace(cur) || (cur == '\n') ) { if ( cur == '-' ) ibuf_i--; state = 33; break; } } } if ( cur == '-' ) { tagstate = -tagstate; ibuf_i--; state = 6; break; } partname_p = partname; /* restart search */ if ( isspace(cur) || (cur == '\n') ) state = 30; else state = 32; break; case 32: /* remainder of rejected label label */ if ( isspace(cur) || (cur == '\n') ) state = 30; if ( cur == '-' ) { ibuf_i--; tagstate = (-tagstate); state = 6; } break; case 33: if ( cur == '-' || cur == '>' ) { state = 6; ibuf_i--; } break; } } if (tagstate == 20 && state == -1) break; } /* * rundown file. */ #ifdef IS_MST tf_close ( ifile ); #else fclose(ifile); #endif { int i, tmp; for (i = 0, ibuf_i = 0; i <= current_part; i++) if (parts[i].start >= 0) { /* * Copy contents. */ strncpy(ibuf + ibuf_i, &ibuf[parts[i].start], tmp = parts[i].end - parts[i].start); ibuf_i += tmp; } if (ibuf_i) buf_used = ibuf_i; } ibuf[buf_used] = '\0'; *outbuf = ibuf; *outlen = buf_used; return *outlen; } #ifdef IS_MST /*****************************************************************************/ /* Read named file into memory. Return -1 on error, 2 on success. */ static int load_file ( char *fname, char *lname, int *outlen, char **outbuf, int initial ) { char *ibuf; int length, buf_used, buf_size, cache_allowed, cache_status; struct private_ctx *ctx; void *ifile; struct tfc_context tfc; char errmsg[256]; /* * Get pointer to private data. */ GET_SPECIFIC ( private_context, ctx ) /* * cache status: 0-nocache, 1-reading from cache, 2 writing cache. */ cache_status = tfc.mode = 0; if ( *fname && ctx->cache_allowed ) { cache_status = tfc_cached_open ( 1, fname, &ifile, errmsg, &tfc ); #ifdef DEBUG printf("Cache lookup on %s, status: %d\n", fname, cache_status ); #endif } else ifile = *fname ? tf_open ( fname, "r", errmsg ) : (void *) 0; if ( ifile ) { /* Update last mod date in ctx */ int size; unsigned uic, cdate, mdate; tf_header_info ( ifile, &size, &uic, &cdate, &mdate ); if ( mdate > ctx->last_mod ) ctx->last_mod = mdate; } else if ( cache_status == 1 ) { /* Update last mod date from tfc header */ if ( tfc.hdr.mdate > ctx->last_mod ) ctx->last_mod = tfc.hdr.mdate; } if ( !ifile && (cache_status != 1) ) { send_http_header ( initial ? "500 Failed to open file" : "500 Failed to open include file", "text/plain"); net_link_printf ( initial ? "Could not open file for '%s' HTML pre-processing\n%s" : "Could not open include file (%s)\n%s", lname, errmsg ); return -1; } buf_used = 0; if ( ifile ) { /* * Read file contents. */ buf_size = initial ? 20000 : 10000; ibuf = malloc ( buf_size ); while ( ibuf && (length=tf_read(ifile,&ibuf[buf_used], buf_size-buf_used)) > 0) { buf_used += length; if ( buf_used >= buf_size ) { buf_size += (initial ? 20000 : 10000); ibuf = realloc ( ibuf, buf_size ); } } tf_close ( ifile ); if ( cache_status == 2 && ibuf ) { /* * Save contents in cache in 4000 byte chunks. */ int i, rec_size;; for ( i = 0; i < buf_used; i += rec_size ) { rec_size = buf_used-i; if ( rec_size > 4000 ) rec_size = 4000; if ( 1 != tfc_put ( &tfc, &ibuf[i], rec_size ) ) break; } } } else if ( cache_status == 1 ) { /* * copy cache contents. */ buf_size = tfc.hdr.size; ibuf = malloc ( buf_size ); for ( buf_used = 0; ibuf && (buf_used < buf_size); buf_used+=length) { void *rec; if ( 1 != tfc_get ( &tfc, &rec, &length ) ) break; memcpy ( &ibuf[buf_used], rec, length ); } } /* * Delete cache entry on error (null ibuf) */ if ( tfc.mode ) tfc_rundown_context ( &tfc, ibuf ? 0 : 1 ); *outbuf = ibuf; *outlen = buf_used; if ( !ibuf ) { send_http_header ( initial ? "500 Failed to load file" : "500 Failed to load include file", "text/plain"); net_link_printf ( "Memory allocation failure while loading %s", lname); return -1; } return 2; } #else /* no MST */ /*****************************************************************************/ /* Read named file into memory. Return -1 on error, 2 on success. */ static int load_file ( char *fname, char *lname, int *outlen, char **outbuf, int initial ) { char *ibuf; int length, buf_used, buf_size; FILE *ifile; ifile = *fname ? fopen ( fname, "r", "mbc=32" ) : (FILE *) 0; if ( !ifile ) { send_http_header ( initial ? "500 Failed to open file" : "500 Failed to open include file", "text/plain"); net_link_printf ( initial ? "Could not open file for '%s' HTML pre-processing\n%s" : "Could not open include file (%s)\n%s", lname, strerror ( errno, vaxc$errno ) ); return -1; } buf_used = 0; buf_size = initial ? 20000 : 10000; ibuf = malloc ( buf_size ); while ( ibuf && (length=fread(&ibuf[buf_used], 1, buf_size-buf_used, ifile)) > 0) { buf_used += length; if ( buf_used >= buf_size ) { buf_size += (initial ? 20000 : 10000); ibuf = realloc ( ibuf, buf_size ); } } fclose(ifile); *outbuf = ibuf; *outlen = buf_used; if ( !ibuf ) { send_http_header ( initial ? "500 Failed to load file" : "500 Failed to load include file", "text/plain"); net_link_printf ( "Memory allocation failure while loading %s", lname); return -1; } return 2; } #endif /* MST */ /*****************************************************************************/ /* Handle the file-related server directives (include, fsize, flastmod). */ static int do_include ( int opcode, /* 1 - include, 2-fsize, 3-flastmod */ char *path, /* pathname of file being parsed */ char **drctv, /* Parsed directive tokens 1=tag, 2=fname */ int *outlen, char **outbuf ) { char *ibuf, *tag, *name, fname[600]; int length, buf_used, buf_size; int have_part; #ifdef IS_MST struct private_ctx *ctx; void *ifile; /* * Get pointer to private data. */ GET_SPECIFIC ( private_context, ctx ) #else FILE *ifile; #endif /* * Examine field1 argument to determine include type. */ tag = drctv[1]; name = drctv[2]; *outlen = 0; have_part = 0; if ( opcode == 1 && 0 == strncmp ( drctv[3], "PART", 5 ) ) { have_part = 1; #ifdef DEBUG printf("Part name is %s\n", fname); #endif } if ( 0 == strncmp ( tag, "FILE", 5 ) ) { /* * Construct filename relative to current path. */ strncpy ( fname, path, 255 ); fname[255] = '\0'; for ( length = strlen(fname); (length > 0) && (fname[length] != '/'); --length ); length++; strcpy ( &fname[length], name ); length = strlen ( fname ); if ( length > 0 ) if ( fname[length-1] == '"' ) fname[length-1] = '\0'; } else if ( 0 == strncmp ( tag, "VIRTUAL", 8 ) ) { /* * Have server translate name. */ int status; #ifdef IS_MST mst_write ( ctx->link, "", 12, &length ); #else net_link_write ( "", 12 ); #endif status = net_link_query ( name, fname, sizeof(fname)-1, &length); if ( 0 == (status&1) ) length = 0; fname[length] = '\0'; } /* * Take action based upon opcode. */ if ( opcode == 1 && !have_part ) { /* * Include the file. */ int status; status = load_file ( fname, name, outlen, outbuf, 0 ); if ( status < 0 ) return status; return status; } else if ( opcode == 1 && have_part ) { /* * Scan file and return named part. */ length = extract_part ( fname, drctv[4], outlen, outbuf ); return 2; } else { /* * fsize(2) and flastmod(3). Load stat structure. */ stat_t info; int status; LOCK_C_RTL status = (length > 0) ? stat ( fname, &info ) : -1; UNLOCK_C_RTL if ( status != 0 ) { send_http_header ( "500 Failed to access file", "text/plain"); net_link_printf ( "Could not read file attributes (%s)\n", name ); return -1; } /* * Format information. */ *outbuf = malloc ( 64 ); if ( opcode == 2 ) { sprintf ( *outbuf, "%d", info.st_size ); } else { /* * Check if user included FMT tag. */ char fmt[100]; fmt[0] = '\0'; if ( strncmp(drctv[3],"FMT",4) == 0 ) { fmt[0] = '='; strncpy ( &fmt[1], drctv[4], sizeof(fmt)-2 ); fmt[99] = '\0'; } format_time ( *outbuf, 64, fmt, &info.st_mtime ); } *outlen = strlen ( *outbuf ); } return 1; } /*****************************************************************************/ /* Return accesses count for indicated path, updating count by 1 on first call * A version number less than or equal to 0 means ignore the version number. */ static int get_access_count ( char *path, int version ) { #ifdef IS_MST IFILE *fp; struct private_ctx *ctx; #define accesses_known ctx->accessesKnown #define accesses ctx->Accesses #define rec_version ctx->version #else IFILE *fp; static int accesses_known = 0, accesses, rec_version; #endif char accessesRecord[128]; char accessesPath[104]; char accessesStr[20]; int status, i, new_rec; size_t length; stat_t info; #ifdef IS_MST /* * Get private data. */ GET_SPECIFIC ( private_context, ctx ) #endif /* * Reset accesses_known if version changes. */ if ( accesses_known ) if ( (version > 0) && (version != rec_version) ) { accesses_known = 0; } if ( !accesses_known ) { /* * Get/update access count/version in file. Try to open file. */ #ifdef IS_MST iacquire_db(); /* serialize access */ ctx->have_lock = 1; fp = count_db ? count_db : ifopen ( "www_root:[000000]accesses.dat","r+" ); #else fp = ifopen ( "www_root:[000000]accesses.dat","r+" ); #endif if ( !fp ) { /* * See if we have permission to write new records. */ LOCK_C_RTL status = stat ( path, &info ); UNLOCK_C_RTL if ( status == 0 ) status = icheck_access ( "www_root:[000000]accesses.permissions", info.st_uid ); if ( (status&1) == 0 ) { send_http_header ( "500 Failed to create file", "text/plain"); net_link_printf ( "No permission to create accesses.dat file, code %d\n", status ); return -1; } if ( (status&1) == 0 ) { } /* * Attempt to create indexed file and retry open. */ status = ifdlcreate ( access_file_fdl, "accesses.dat", "www_root:[000000]" ); if ( status & 1 ) fp = ifopen ( "www_root:[000000]accesses.dat","r+" ); } if ( !fp ) { send_http_header ( "500 Failed to access file", "text/plain" ); net_link_printf ( "Could not open accesses.dat file."); return -1; } /* * File is now open, do indexed read to get record for specified path. */ #ifdef IS_MST count_db = fp; /* share amongst several threads */ tu_strnzcpy ( accessesPath, path, 100 ); tu_strupcase ( accessesPath, accessesPath ); i = tu_strlen ( accessesPath ); #else for ( i = 0; (i < 100) && path[i]; i++ ) accessesPath[i] = _toupper ( path[i] ); #endif while ( i < 100 ) accessesPath[i++] = ' '; /* pad to 100 bytes */ accessesPath[i] = '\0'; status = ifread_rec(accessesRecord,120,&length,fp,0,accessesPath,100); if ( status & 1 ) { accessesRecord[120] = '\0'; sscanf(accessesRecord+100,"%10d%10d",&accesses, &rec_version); new_rec = 0; } else { /* * Read error on record, assume non-existent and make initial. */ accesses = 0; rec_version = 0; new_rec = 1; /* * Check if file owner has permission to add to accesses.dat file. */ LOCK_C_RTL status = stat ( path, &info ); UNLOCK_C_RTL if ( status == 0 ) status = icheck_access ( "www_root:[000000]accesses.permissions", info.st_uid ); if ( (status&1) == 0 ) { send_http_header ( "500 Failed to extend file", "text/plain"); net_link_printf ( "No permission to add records to accesses.dat, code %d\n", status ); return -1; } } /* * Reset or update access count by 1 and update record in file. */ if ( version <= 0 ) version = rec_version; else if ( version != rec_version ) accesses = 0; accesses++; sprintf(accessesRecord,"%-100.100s%10.10d%10.10d",accessesPath,accesses, version); if ( new_rec ) { #ifndef IS_MST iferror(fp); #endif status = ifwrite_rec(accessesRecord,120,fp); } else { status = ifupdate_rec(accessesRecord,120,fp); } #ifdef IS_MST ctx->have_lock = 0; irelease_db(); #else if ( (status&1) == 0 ) iferror(fp); ifclose(fp); #endif accesses_known = 1; } return accesses; } #ifdef IS_MST #undef accesses #undef accesses_known #undef rec_version #endif /*****************************************************************************/ /* Lookup indicate variables and return their values. */ static int do_echo ( char *path, /* pathname of file being parsed */ char **drctv, /* Elements of directive */ int *outlen, char **outbuf ) { int SYS$ASCTIM(), timlen, i, j, length; char *timestr, *tag, *name; static enum vcodes { C_DATE_LOCAL, C_DOCUMENT_NAME, C_LAST_MODIFIED, C_ACCESSES, C_ACCESSES_ORDINAL, C_VMS_VERSION, C_HW_NAME, C_SERVER_ACCOUNT, C_SERVER_NAME, C_SERVER_VERSION, C_GETENV, C_FINIS }; enum vcodes vcode; static struct { enum vcodes code; char delimiter; int min_len; char *keyword; } vtbl[] = { { C_DATE_LOCAL, '=', 10, "DATE_LOCAL" }, { C_DOCUMENT_NAME, '\0', 13, "DOCUMENT_NAME" }, { C_LAST_MODIFIED, '=', 13, "LAST_MODIFIED" }, { C_ACCESSES, ';', 8, "ACCESSES" }, { C_ACCESSES_ORDINAL, ';', 16,"ACCESSES_ORDINAL" }, { C_SERVER_NAME, '\0', 11, "SERVER_NAME" }, { C_SERVER_VERSION, '\0', 14, "SERVER_VERSION" }, { C_GETENV, '=', 6, "GETENV" }, { C_VMS_VERSION, '\0', 11, "VMS_VERSION" }, { C_HW_NAME, '\0', 7, "HW_NAME" }, { C_FINIS, '\0', 0, "" } }; static char hardware[31], os_version[12]; static $DESCRIPTOR(vms_version_dx, os_version); static $DESCRIPTOR(hardware_dx,hardware); tag = drctv[1]; name = drctv[2]; *outlen = 0; if ( strncmp ( tag, "VAR",4 ) ) return 0; /* syntax error */ /* * Lookup var keyword in table, putting it's code number in vcode. */ length = strlen ( name ); for ( vcode = C_FINIS, i = 0; vtbl[i].code != C_FINIS; i++ ) { if ( length >= vtbl[i].min_len ) { j = vtbl[i].min_len; if ( 0 == strncmp ( name,vtbl[i].keyword,j) ) { if ( name[j] == '\0' || name[j] == vtbl[i].delimiter ) { vcode = vtbl[i].code; break; } } } } /* * Process based upon vcode. */ switch ( vcode ) { stat_t info; int status, code, accesses, version, last, last2; char *env_val; case C_DATE_LOCAL: /* * See if format info was supplied. */ if ( name[10] == '=' ) { time_t now; *outbuf = malloc ( 64 ); now = time(NULL); format_time ( *outbuf, 64, &name[10], &now ); *outlen = strlen ( *outbuf ); } else { struct { long l; char * a; } timebuf; *outbuf = malloc ( 24 ); timebuf.l = 24; timebuf.a = *outbuf; SYS$ASCTIM ( outlen, &timebuf, 0, 0 ); } break; case C_DOCUMENT_NAME: /* Echo filename of document (part to right of last /) */ *outbuf = strrchr(path,'/'); if ( *outbuf ) *outbuf = *outbuf+1; else *outbuf = path; *outlen = strlen ( *outbuf ); break; case C_LAST_MODIFIED: /* * Echo last modified date of current file path. */ LOCK_C_RTL status = stat ( path, &info ); UNLOCK_C_RTL if ( status != 0 ) { send_http_header ( "500 Failed to access file", "text/plain"); net_link_printf ( "Could not read file attributes (%s)\n", path ); return -1; } /* * Format information. */ *outbuf = malloc ( 64 ); format_time ( *outbuf, 64, &name[13], &info.st_mtime ); *outlen = strlen ( *outbuf ); break; case C_ACCESSES: case C_ACCESSES_ORDINAL: /* * Echo access count. */ /* * see if name includes version number. */ version = 0; if ( (vcode == C_ACCESSES) && (name[8] == ';') ) version = atoi(&name[9]); else if ( (vcode == C_ACCESSES_ORDINAL) && (name[16] == ';') ) version = atoi(&name[17]); accesses = get_access_count ( path, version ); if ( accesses < 0 ) return accesses; *outbuf = malloc ( 24 ); if ( accesses < 1000 ) sprintf(*outbuf, "%d",accesses ); else if ( accesses < 1000000 ) sprintf(*outbuf, "%d,%03.3d", accesses/1000, accesses%1000 ); else sprintf ( *outbuf, "%d,%03.3d,%03.3d", accesses/1000000, accesses%1000000/1000, accesses%1000 ); if ( vcode == C_ACCESSES_ORDINAL ) { /* tack on suffix */ static char *ordination[10] = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; char *suffix; last2 = accesses %100; suffix = &(*outbuf)[strlen(*outbuf)]; last = accesses % 10; if ( (last2 >= 11) && (last2 <= 13) ) last = 0; strcpy ( suffix, ordination[last] ); } *outlen = strlen ( *outbuf ); break; case C_SERVER_NAME: *outbuf = malloc ( 256 ); status = net_link_query ( "", *outbuf, 255, outlen ); if ( (status&1) == 0 ) *outlen = 0; break; case C_SERVER_VERSION: *outbuf = malloc ( 256 ); status = net_link_query ( "", *outbuf, 255, outlen ); if ( (status&1) == 0 ) *outlen = 0; else *outlen = strchr(*outbuf, ' ') - *outbuf; break; case C_GETENV: /* * Return value of arbitrary logical or DCL symbol. */ if ( length < 8 ) break; /* invalid syntax */ #ifdef IS_MST pthread_lock_global_np(); #endif env_val = getenv ( &name[7] ); if ( env_val ) { *outlen = strlen ( env_val ); *outbuf = malloc ( *outlen + 1 ); strcpy ( *outbuf, env_val ); } else { *outbuf = "unknown"; *outlen = 7; } #ifdef IS_MST pthread_unlock_global_np(); #endif break; case C_VMS_VERSION: code = SYI$_VERSION; #ifdef IS_MST *outbuf = malloc ( sizeof(os_version)+1 ); pthread_lock_global_np(); LIB$GETSYI ( &code, 0, &vms_version_dx, outlen, 0, 0 ); tu_strnzcpy ( *outbuf, vms_version_dx.dsc$a_pointer, *outlen ); pthread_unlock_global_np(); #else *outbuf = os_version; LIB$GETSYI ( &code, 0, &vms_version_dx, outlen, 0, 0 ); if ( *outlen < sizeof(os_version) ) os_version[*outlen] = '\0'; #endif break; case C_HW_NAME: code = SYI$_HW_NAME; #ifdef IS_MST *outbuf = malloc ( sizeof(hardware)+1 ); pthread_lock_global_np(); LIB$GETSYI ( &code, 0, &hardware_dx, outlen, 0, 0 ); tu_strnzcpy ( *outbuf, hardware_dx.dsc$a_pointer, *outlen ); pthread_unlock_global_np(); #else *outbuf = hardware; LIB$GETSYI ( &code, 0, &hardware_dx, outlen, 0, 0 ); if ( *outlen < sizeof(hardware) ) hardware[*outlen] = '\0'; #endif break; default: /* * Unknown var. */ *outbuf = malloc ( length + 3 ); sprintf ( *outbuf, "?%s?", name ); *outlen = length + 2; break; } return 1; } /*****************************************************************************/ /* Handle config directive. * Tags: * verify=[0|1] */ static int do_config ( char *path, char *field1, char *field2, int *outlen, char **outbuf ) { if ( strncmp ( field1, "VERIFY", 7) == 0 ) { /* * Set tag verify global that governs whether tag will be included. */ #ifdef IS_MST struct private_ctx *ctx; GET_SPECIFIC ( private_context, ctx ) if ( *field2 == '1' || *field2 == 'Y' ) ctx->tag_verify = 1; else ctx->tag_verify = 0; #else if ( *field2 == '1' || *field2 == 'Y' ) tag_verify = 1; else tag_verify = 0; #endif } *outlen = 0; *outbuf=" "; return 0; } /*****************************************************************************/ /* Top routine to handle interpreting an server-side html directive, * optionally returning to the caller a pointer to buffer containing * additional data to send to the client (inserted at the point of * the directive). A zero outlen indicates no optional data is returned. * * The directive takes the form * * Note that tag array is modified by this routine. * Note also that output buffers must be statically allocated, they are not * read by the caller until final processing. * * If this routine generates an error response, it must return -1 as the * function value. */ static int parse_directive ( char *path, /* Pathname of file being parsed */ char **drctv, /* 5 elements of parsed directive */ int *outlen, /* Size of result output buffer */ char **outbuf ) /* Address of result buffer */ { int i, taglen; char *command; /* * Scan the directive tag and parse into command and tags. */ #ifdef DEBUG printf("Directive: '%s' '%s'='%s' '%s'='%s'\n\n", drctv[0], drctv[1], drctv[2],drctv[3],drctv[4] ); #endif command = drctv[0]; /* * Now interpret command. */ if ( 0 == strncmp(command,"INCLUDE",8) ) { /* * Include file. */ return do_include ( 1, path, drctv, outlen, outbuf ); } else if ( 0 == strncmp ( command, "ECHO",5 ) ) { /* * Echo will insert values of special variables into stream. */ return do_echo ( path, drctv, outlen, outbuf ); } else if ( 0 == strncmp(command,"FSIZE",6) ) { /* * File attributes. */ return do_include ( 2, path, drctv, outlen, outbuf ); } else if ( 0 == strncmp(command,"FLASTMOD",9) ) { return do_include ( 3, path, drctv, outlen, outbuf ); } else if ( 0 == strncmp ( command, "CONFIG",7 ) ) { /* * Set configuration parameters. */ return do_config ( path, drctv[1], drctv[2], outlen, outbuf ); } else if ( 0 == strncmp ( command, "BEGIN", 6 ) || 0 == strncmp ( command, "END", 4 ) ) { /* For some reason, I HAVE to put one space in the output buffer. If I just do '*outlen = 0;', the ending ">" will be preserved in the .htmlx file. If I do '*outbuf = ""; *outlen = 0;', everything after on of these directives will be ignored... -- Richard Levitte */ *outbuf = " "; *outlen = 0; /* fixed bug, so can use zero length, DLJ */ return 1; } /* * Invalid directive in file. */ send_http_header ( "500 Bad directive in file", "text/plain" ); net_link_printf("invalid directive: (%s %s %s)\n", command, drctv[1], drctv[2] ); *outlen = 0; return -1; } /*****************************************************************************/ /* Parse HTML tag. If HTML tag looks like a pre-processor directive, * fill in targ array with parsed elments. (command, (tag,value) pairs). * * Return value: * 0 Tag is NOT a pro */ static int parse_tag ( char *tag, /* start of tag */ int maxlen, /* remaining bytes in input file */ int *taglen, /* Size of tag, including closing '>'*/ char **targ ) /* Elements, cmd and up to 5 tag/value pairs */ { int state, i, j, eot_ok; char *directive, cur; /* * First, determine if tag if directive or not, Size of tag is * also determined. */ if ( *tag != '<' ) { *taglen = 0; return 0; } for ( state = i = 0; (state >= 0) && (i < maxlen); i++ ) { cur = tag[i]; switch (state) { case 0: if ( cur != '<' ) { *taglen = i+1; return 0; } state = 1; break; case 1: if ( cur == '!' ) { state = 3; break; } state = 2; case 2: /* Not a directive, search for end. */ if ( cur == '>' ) { *taglen = i+1; return 0; } break; case 3: if ( cur != '-' ) { state = 2; --i; break; } state = 4; break; case 4: if ( cur != '-' ) { state = 2; --i; break; } state = 5; break; case 5: if ( cur != '#' ) { state = 2; --i; break; } state = 6; break; case 6: /* Find terminator */ if ( cur == '-' ) state = 7; break; case 7: if ( cur == '-' ) state = 8; else state = 6; break; case 8: if ( cur == '>' ) state = -1; else state = (cur=='-') ? 8 : 6; break; } } /* * Getting to this point means we parsed a directive (state = -1). * Make copy of tag and parse the copy (portions of copy will be upcased). */ directive = malloc ( i + 1 ); strncpy ( directive, tag, i ); directive[i] = '\0'; tag = directive; #ifdef DEBUG printf("comment: '%s' l = %d\n", directive, i ); #endif /* * Initialize return array to all null strings. */ *taglen = i; targ[0] = &tag[5]; for ( i = 1; i < 11; i++ ) targ[i] = ""; eot_ok = 1; /* * Parse tag into directive components using state machine. */ for ( state = j = 0, i = 5; (state >=0) && (i < *taglen-3); i++ ) { #ifdef DEBUG printf("state = %d, tag[%d] = '%c'\n", state, i, tag[i] ); #endif cur = tag[i]; switch ( state ) { case 0: if ( isspace(cur) || (cur=='\n') ) { /* found end of targ[0] */ tag[i] = '\0'; #ifdef IS_MST tu_strupcase ( tag, tag ); #endif state = 1; j = 1; targ[j] = &tag[i]; } #ifndef IS_MST else tag[i] = _toupper(cur); #endif break; case 1: /* look for start of targ[j] */ if ( !isspace(cur) /* || (cur == '\n') */ ) { targ[j] = &tag[i]; state = 2; eot_ok = 0; } else break; case 2: /* look for end of targ[1] */ if ( cur == '=' ) { tag[i] = '\0'; state = 3; #ifdef IS_MST tu_strupcase ( targ[j], targ[j] ); #endif } else if ( isspace(cur) ) state = -1; /* syntax error */ #ifndef IS_MST else tag[i] = _toupper(cur); #endif break; case 3: /* Check for proper syntax */ if ( cur == '"' ) { j++; /* Advance to value part */ targ[j] = &tag[i+1]; state = 4; } else state = -1; break; case 4: if ( cur == '"' ) { /* end of targ[2] */ tag[i] = '\0'; j++; targ[j] = &tag[i]; state = 1; /* look for next value */ eot_ok = 1; } break; } } if ( state == -1 ) { free ( directive ); return 0; /* improper syntax */ } return 1; } /*****************************************************************************/ /* * Scan file specification to see if the filename contains an embedded * part name. If a partname is present, copy the partname to a separate * string and reconstruct filename without the embedded partname. * * Function value returned is 1 if partname present and 0 if not. */ static int parse_part_name ( char *fname, char **pfile, char **pname ) { int length, i, dot1, dot2; char cur, *tail; /* * Parse from back, looking for postiions in fname of 1st 2 dots after * the last slash. */ dot1 = dot2 = -1; length = strlen ( fname ); for ( i = length - 1; i >= 0; --i ) { cur = fname[i]; if ( (cur == '/') || (cur == '>') || (cur == ']') ) break; if ( cur == '.' ) { dot2 = dot1; dot1 = i; } } *pfile = fname; if ( dot2 < 0 ) return 0; /* normal filename */ if ( fname[dot2+1] >= '0' && fname[dot2+1] <= '9' ) return 0; /* * Reconstruct filename without part and put part in separate. */ *pname = malloc ( dot2 - dot1 ); strncpy ( *pname, &fname[dot1+1], (dot2-dot1-1) ); (*pname)[dot2-dot1-1] = '\0'; *pfile = malloc ( length +1 ); strncpy ( *pfile, fname, dot1 ); /* all before 1st dot */ strcpy ( (*pfile)+dot1, &fname[dot2] ); /* rest */ if ( (dot1+1) == dot2 ) return 0; /* part name null */ return 1; }