diff options
author | Arnold D. Robbins <arnold@skeeve.com> | 2010-07-16 11:58:26 +0300 |
---|---|---|
committer | Arnold D. Robbins <arnold@skeeve.com> | 2010-07-16 11:58:26 +0300 |
commit | 765c7494b3dac62207e6cd57fb839997e237f292 (patch) | |
tree | f7da12ffdb85d9f82671cb3122775b2ce73f7ad9 /vms/vms_args.c | |
parent | cce5115e21db1702e0617afdca36633e7e2c9eae (diff) | |
download | egawk-765c7494b3dac62207e6cd57fb839997e237f292.tar.gz egawk-765c7494b3dac62207e6cd57fb839997e237f292.tar.bz2 egawk-765c7494b3dac62207e6cd57fb839997e237f292.zip |
Moving to 2.13.2.
Diffstat (limited to 'vms/vms_args.c')
-rw-r--r-- | vms/vms_args.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/vms/vms_args.c b/vms/vms_args.c new file mode 100644 index 00000000..b6736ff3 --- /dev/null +++ b/vms/vms_args.c @@ -0,0 +1,398 @@ +/* + * vms_args.c -- command line parsing, to emulate shell i/o redirection. + * [ Escape sequence parsing now suppressed. ] + */ + +/* + * Copyright (C) 1991 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Progamming Language. + * + * GAWK is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * GAWK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GAWK; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * [.vms]vms_arg_fixup - emulate shell's command line processing: handle + * stdio redirection, backslash escape sequences, and file wildcard + * expansion. Should be called immediately upon image startup. + * + * Pat Rankin, Nov'89 + * rankin@eql.Caltech.EDU + * + * <ifile - open 'ifile' (readonly) as 'stdin' + * >nfile - create 'nfile' as 'stdout' (stream-lf format) + * >>ofile - append to 'ofile' for 'stdout'; create it if necessary + * >&efile - point 'stderr' (SYS$ERROR) at 'efile', but don't open + * >$vfile - create 'vfile' as 'stdout', using rms attributes + * appropriate for a standard text file (variable length + * records with implied carriage control) + * 2>&1 - special case: direct error messages into output file + * 1>&2 - special case: direct output data to error destination + * <<sentinal - error; reading stdin until 'sentinal' not supported + * <-, >- - error: stdin/stdout closure not implemented + * | anything - error; pipes not implemented + * & <end-of-line> - error; background execution not implemented + * + * any\Xany - convert 'X' as appropriate; \000 will not work as + * intended since subsequent processing will misinterpret + * + * any*any - perform wildcard directory lookup to find file(s) + * any%any - " " ('%' is vms wildcard for '?' [ie, /./]) + * any?any - treat like 'any%any' unless no files match + * *, %, ? - if no file(s) match, leave original value in arg list + * + * + * Notes: a redirection operator can have optional white space between it + * and its filename; the operator itself *must* be preceded by white + * space so that it starts a separate argument. '<' is ambiguous + * since "<dir>file" is a valid VMS file specification; leading '<' is + * assumed to be stdin--use "\<dir>file" to override. '>$' is local + * kludge to force stdout to be created with text file RMS attributes + * instead of stream format; file sharing is disabled for stdout + * regardless. Multiple instances of stdin or stdout or stderr are + * treated as fatal errors rather than using the first or last. If a + * wildcard file specification is detected, it is expanded into a list + * of filenames which match; if there are no matches, the original + * file-spec is left in the argument list rather than having it expand + * into thin air. No attempt is made to identify and make $(var) + * environment substitutions--must draw the line somewhere! + */ + +#include "awk.h" /* really "../awk.h" */ +#include "vms.h" + + void v_add_arg(int, const char *); +static char *skipblanks(const char *); +static void vms_expand_wildcards(const char *); +static u_long vms_define(const char *, const char *); +static char *t_strstr(const char *, const char *); +#define strstr t_strstr /* strstr() missing from vaxcrtl for V4.x */ + +static int v_argc, v_argz = 0; +static char **v_argv; + +/* vms_arg_fixup() - scan argv[] for i/o redirection and wildcards and also */ +/* rebuild it with those removed or expanded, respectively */ +void +vms_arg_fixup( int *pargc, char ***pargv ) +{ + char *f_in, *f_out, *f_err, + *out_mode, *rms_opt1, *rms_opt2; + char **argv = *pargv; + int i, argc = *pargc; + int err_to_out_redirect = 0, out_to_err_redirect = 0; + +#ifndef NO_CHECK_SHELL + if (shell$is_shell()) + return; /* don't do anything if we're running DECshell */ +#endif +#ifndef NO_DCL_CMD + for (i = 1; i < argc ; i++) /* check for dash or other non-VMS args */ + if (strchr("->\\|", *argv[i])) break; /* found => (i < argc) */ + if (i >= argc && (v_argc = vms_gawk()) > 0) { /* vms_gawk => dcl_parse */ + /* if we successfully parsed the command, replace original argv[] */ + argc = v_argc, argv = v_argv; + v_argz = v_argc = 0, v_argv = NULL; + } +#endif + v_add_arg(v_argc = 0, basename(argv[0])); /* store arg #0 (image name) */ + + f_in = f_out = f_err = NULL; /* stdio setup (no filenames yet) */ + out_mode = "w"; /* default access for stdout */ + rms_opt1 = rms_opt2 = "ctx=stm"; /* ("context = stream") == no-opt */ + + for (i = 1; i < argc; i++) { + char *p, *fn; + int is_arg; + + is_arg = 0; /* current arg does not begin with dash */ + p = argv[i]; /* current arg */ + switch (*p) { + case '<': /* stdin */ + /*[should try to determine whether this is really a directory + spec using <>; for now, force user to quote them with '\<']*/ + if ( f_in ) { + fatal("multiple specification of '<' for stdin"); + } else if (*++p == '<') { /* '<<' is not supported */ + fatal("'<<' not available for stdin"); + } else { + p = skipblanks(p); + fn = (*p ? p : argv[++i]); /* use next arg if necessary */ + if (i >= argc || *fn == '-') + fatal("invalid i/o redirection, null filespec after '<'"); + else + f_in = fn; /* save filename for stdin */ + } + break; + case '>': { /* stdout or stderr */ + /*[vms-specific kludge '>$' added to force stdout to be created + as record-oriented text file instead of in stream-lf format]*/ + int is_out = 1; /* assume stdout */ + if (*++p == '>') /* '>>' => append */ + out_mode = "a", p++; + else if (*p == '&') /* '>&' => stderr */ + is_out = 0, p++; + else if (*p == '$') /* '>$' => kludge for record format */ + rms_opt1 = "rfm=var", rms_opt2 = "rat=cr", p++; + else /* '>' => create */ + ; /* use default values initialized prior to loop */ + p = skipblanks(p); + fn = (*p ? p : argv[++i]); /* use next arg if necessary */ + if (i >= argc || *fn == '-') { + fatal("invalid i/o redirection, null filespec after '>'"); + } else if (is_out) { + if (out_to_err_redirect) + fatal("conflicting specifications for stdout"); + else if (f_out) + fatal("multiple specification of '>' for stdout"); + else + f_out = fn; /* save filename for stdout */ + } else { + if (err_to_out_redirect) + fatal("conflicting specifications for stderr"); + else if (f_err) + fatal("multiple specification of '>&' for stderr"); + else + f_err = fn; /* save filename for stderr */ + } + } break; + case '2': /* check for ``2>&1'' special case'' */ + if (strcmp(p, "2>&1") != 0) + goto ordinary_arg; + else if (f_err || out_to_err_redirect) + fatal("conflicting specifications for stderr"); + else { + err_to_out_redirect = 1; + f_err = "SYS$OUTPUT:"; + } break; + case '1': /* check for ``1>&2'' special case'' */ + if (strcmp(p, "1>&2") != 0) + goto ordinary_arg; + else if (f_out || err_to_out_redirect) + fatal("conflicting specifications for stdout"); + else { + out_to_err_redirect = 1; + f_out = "SYS$ERROR:"; + } break; + case '|': /* pipe */ + /* command pipelines are not supported */ + fatal("command pipes not available ('|' encountered)"); + break; + case '&': /* background */ + /*[we could probably spawn or fork ourself--maybe someday]*/ + if (*(p+1) == '\0' && i == argc - 1) { + fatal("background tasks not available ('&' encountered)"); + break; + } else /* fall through */ + ; /*NOBREAK*/ + case '-': /* argument */ + is_arg = 1; /*(=> skip wildcard check)*/ + default: /* other (filespec assumed) */ +ordinary_arg: + /* process escape sequences or expand wildcards */ + v_add_arg(++v_argc, p); /* include this arg */ + p = strchr(p, '\\'); /* look for backslash */ + if (p != NULL) { /* does it have escape sequence(s)? */ +#if 0 /* disable escape parsing; it's now done elsewhere within gawk */ + register int c; + char *q = v_argv[v_argc] + (p - argv[i]); + do { + c = *p++; + if (c == '\\') + c = parse_escape(&p); + *q++ = (c >= 0 ? (char)c : '\\'); + } while (*p != '\0'); + *q = '\0'; +#endif /*0*/ + } else if (!is_arg && strchr(v_argv[v_argc], '=') == NULL) { + vms_expand_wildcards(v_argv[v_argc]); + } + break; + } /* end switch */ + } /* loop */ + + /* + * Now process any/all I/O options encountered above. + */ + + /* must do stderr first, or vaxcrtl init might not see it */ + /*[ catch 22: we'll also redirect errors encountered doing <in or >out ]*/ + if (f_err) { /* define logical name but don't open file */ + int len = strlen(f_err); + if (strncasecmp(f_err, "SYS$OUTPUT", len) == 0 + && (f_err[len] == ':' || f_err[len] == '\0')) + err_to_out_redirect = 1; + else + vms_define("SYS$ERROR", f_err); + } + /* do stdin before stdout, so we bomb we won't create empty output file */ + if (f_in) { /* [re]open file and define logical name */ + stdin = freopen(f_in, "r", stdin, "mbf=2"); + if (stdin != NULL) + vms_define("SYS$INPUT", f_in); + else + fatal("<%s (%s)", f_in, strerror(errno)); + } + if (f_out) { /* disallow file sharing to reduce overhead */ + stdout = freopen(f_out, out_mode, stdout, + rms_opt1, rms_opt2, "shr=nil", "mbf=2"); /*VAXCRTL*/ + if (stdout != NULL) { +#ifdef crtl_bug /* eof sometimes doesn't get set properly for stm_lf file */ +# define BIGBUF 8*BUFSIZ /* maximum record size: 4096 instead of 512 */ + setvbuf(stdout, malloc(BIGBUF), _IOFBF, BIGBUF); +#endif + vms_define("SYS$OUTPUT", f_out); + } else + fatal(">%s%s (%s)", (*out_mode == 'a' ? ">" : ""), + f_out, strerror(errno)); + } + if (err_to_out_redirect) { /* special case for ``2>&1'' construct */ + fclose(stderr); + dup(1, 2); /* make file 2 (stderr) share file 1 (stdout) */ + stderr = stdout; + vms_define("SYS$ERROR", "SYS$OUTPUT:"); + } else if (out_to_err_redirect) { /* ``1>&2'' */ + fclose(stdout); + dup(2, 1); /* make file 1 (stdout) share file 2 (stderr) */ + stdout = stderr; + vms_define("SYS$OUTPUT", "SYS$ERROR:"); + } + +#ifndef NO_DCL_CMD + /* if we replaced argv[] with our own, we can release it now */ + if (argv != *pargv) + free((void *)argv), argv = NULL; +#endif + *pargc = ++v_argc; /* increment to account for argv[0] */ + *pargv = v_argv; + return; +} + +/* vms_expand_wildcards() - check a string for wildcard punctuation; */ +/* if it has any, attempt a directory lookup */ +/* and store resulting name(s) in argv array */ +static void +vms_expand_wildcards( const char *prospective_filespec ) +{ + char *p, spec_buf[255+1], res_buf[255+1], *strstr(); + Dsc spec, result; + void *context; + register int len = strlen(prospective_filespec); + + if (len >= sizeof spec_buf) + return; /* can't be valid--or at least we can't handle it */ + strcpy(spec_buf, prospective_filespec); /* copy the arg */ + p = strchr(spec_buf, '?'); + if (p != NULL) /* change '?' single-char wildcard to '%' */ + do *p++ = '%', p = strchr(p, '?'); + while (p != NULL); + else if (strchr(spec_buf, '*') == strchr(spec_buf, '%') /* => both NULL */ + && strstr(spec_buf, "...") == NULL) + return; /* no wildcards present; don't attempt file lookup */ + spec.len = len, spec.adr = spec_buf; + result.len = sizeof res_buf - 1, result.adr = res_buf; + + /* The filespec is already in v_argv[v_argc]; if we fail to match anything, + we'll just leave it there (unlike most shells, where it would evaporate). + */ + len = -1; /* overload 'len' with flag value */ + context = NULL; /* init */ + while (vmswork(LIB$FIND_FILE(&spec, &result, &context))) { + for (len = sizeof(res_buf)-1; len > 0 && res_buf[len-1] == ' '; len--) ; + res_buf[len] = '\0'; /* terminate after discarding trailing blanks */ + v_add_arg(v_argc++, strdup(res_buf)); /* store result */ + } + (void)LIB$FIND_FILE_END(&context); + if (len >= 0) /* (still -1 => never entered loop) */ + --v_argc; /* undo final post-increment */ + return; +} + +/* v_add_arg() - store string pointer in v_argv[]; expand array if necessary */ +void +v_add_arg( int idx, const char *val ) +{ +#ifdef DEBUG_VMS + fprintf(stderr, "v_add_arg: v_argv[%d] ", idx); +#endif + if (idx + 1 >= v_argz) { /* 'v_argz' is the current size of v_argv[] */ + int old_size = v_argz; + + v_argz = idx + 10; /* increment by arbitrary amount */ + if (old_size == 0) + v_argv = (char **)malloc((unsigned)(v_argz * sizeof(char **))); + else + v_argv = (char **)realloc((char *)v_argv, + (unsigned)(v_argz * sizeof(char **))); + if (v_argv == NULL) { /* error */ + fatal("%s: %s: can't allocate memory (%s)", "vms_args", + "v_argv", strerror(errno)); + } else { + memmsg((oldsize == 0 ? "v_argv" : "re: v_argv"), v_argz, + "vms_args", v_argv); + while (old_size < v_argz) v_argv[old_size++] = NULL; + } + } + v_argv[idx] = (char *)val; +#ifdef DEBUG_VMS + fprintf(stderr, "= \"%s\"\n", val); +#endif +} + +/* skipblanks() - return a pointer to the first non-blank in the string */ +static char * +skipblanks( const char *ptr ) +{ + if (ptr) + while (*ptr == ' ' || *ptr == '\t') + ptr++; + return (char *)ptr; +} + +/* vms_define() - assign a value to a logical name [define/process/user_mode] */ +static u_long +vms_define( const char *log_name, const char *trans_val ) +{ + Dsc log_dsc, trn_dsc; +# define LOG_PROCESS_TABLE 2 /* <obsolete> */ +# define LOG_USERMODE 3 /* PSL$C_USER */ + extern u_long SYS$CRELOG(); /* <superceded by $CRELNM> */ + + /* avoid "define SYS$OUTPUT sys$output:" for redundant ">sys$output:" */ + if (strncasecmp(log_name, trans_val, strlen(log_name)) == 0) + return 0; + + log_dsc.len = strlen(log_dsc.adr = (char *)log_name); + trn_dsc.len = strlen(trn_dsc.adr = (char *)trans_val); + return SYS$CRELOG(LOG_PROCESS_TABLE, &log_dsc, &trn_dsc, LOG_USERMODE); +} + +/* t_strstr -- strstr() substitute; search 'str' for 'sub' */ +static char *t_strstr ( const char *str, const char *sub ) +{ + register const char *s0, *s1, *s2; + + /* special case: empty substring */ + if (!*sub) return (char *)str; + + /* brute force method */ + for (s0 = s1 = str; *s1; s1 = ++s0) { + s2 = sub; + while (*s1++ == *s2++) + if (!*s2) return (char *)s0; /* full match */ + } + return (char *)0; /* not found */ +} |