aboutsummaryrefslogtreecommitdiffstats
path: root/vms/vms_args.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-16 11:58:26 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-16 11:58:26 +0300
commit765c7494b3dac62207e6cd57fb839997e237f292 (patch)
treef7da12ffdb85d9f82671cb3122775b2ce73f7ad9 /vms/vms_args.c
parentcce5115e21db1702e0617afdca36633e7e2c9eae (diff)
downloadegawk-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.c398
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 */
+}