home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / gawk-2.15.6-src.tgz / tar.out / fsf / gawk / vms / vms_args.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  16KB  |  417 lines

  1. /*
  2.  * vms_args.c -- command line parsing, to emulate shell i/o redirection.
  3.  *        [ Escape sequence parsing now suppressed. ]
  4.  */
  5.  
  6. /*
  7.  * Copyright (C) 1991-1995 the Free Software Foundation, Inc.
  8.  *
  9.  * This file is part of GAWK, the GNU implementation of the
  10.  * AWK Progamming Language.
  11.  *
  12.  * GAWK is free software; you can redistribute it and/or modify
  13.  * it under the terms of the GNU General Public License as published by
  14.  * the Free Software Foundation; either version 2 of the License, or
  15.  * (at your option) any later version.
  16.  *
  17.  * GAWK is distributed in the hope that it will be useful,
  18.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20.  * GNU General Public License for more details.
  21.  *
  22.  * You should have received a copy of the GNU General Public License
  23.  * along with GAWK; see the file COPYING.  If not, write to
  24.  * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  25.  */
  26.  
  27. /*
  28.  * [.vms]vms_arg_fixup - emulate shell's command line processing: handle
  29.  *        stdio redirection, backslash escape sequences, and file wildcard
  30.  *        expansion.    Should be called immediately upon image startup.
  31.  *
  32.  *                              Pat Rankin, Nov'89
  33.  *                            rankin@eql.Caltech.EDU
  34.  *
  35.  *    <ifile        - open 'ifile' (readonly) as 'stdin'
  36.  *    >nfile        - create 'nfile' as 'stdout' (stream-lf format)
  37.  *    >>ofile     - append to 'ofile' for 'stdout'; create it if necessary
  38.  *    >&efile     - point 'stderr' (SYS$ERROR) at 'efile', but don't open
  39.  *    >$vfile     - create 'vfile' as 'stdout', using rms attributes
  40.  *              appropriate for a standard text file (variable length
  41.  *              records with implied carriage control)
  42.  *    >+vfile     - create 'vfile' as 'stdout' in binary mode (using
  43.  *              variable length records with implied carriage control)
  44.  *    2>&1        - special case: direct error messages into output file
  45.  *    1>&2        - special case: direct output data to error destination
  46.  *    <<sentinal  - error; reading stdin until 'sentinal' not supported
  47.  *    <-, >-        - error: stdin/stdout closure not implemented
  48.  *    | anything  - error; pipes not implemented
  49.  *    & <end-of-line> - error; background execution not implemented
  50.  *
  51.  *    any\Xany    - convert 'X' as appropriate; \000 will not work as
  52.  *              intended since subsequent processing will misinterpret
  53.  *
  54.  *    any*any     - perform wildcard directory lookup to find file(s)
  55.  *    any%any     -     "       "    ('%' is vms wildcard for '?' [ie, /./])
  56.  *    any?any     - treat like 'any%any' unless no files match
  57.  *    *, %, ?     - if no file(s) match, leave original value in arg list
  58.  *
  59.  *
  60.  * Notes:  a redirection operator  can have optional white space between it
  61.  *    and its filename; the  operator itself *must* be preceded by  white
  62.  *    space  so that it starts  a  separate  argument.  '<' is  ambiguous
  63.  *    since "<dir>file" is a valid VMS file specification; leading '<' is
  64.  *    assumed  to be    stdin--use "\<dir>file" to override.  '>$' is local
  65.  *    kludge to force  stdout to be created with text file RMS attributes
  66.  *    instead of  stream  format;  file  sharing is disabled    for  stdout
  67.  *    regardless.  Multiple  instances of  stdin  or stdout or stderr are
  68.  *    treated as fatal errors  rather than using the first or last.  If a
  69.  *    wildcard file specification is detected, it is expanded into a list
  70.  *    of  filenames  which match; if there  are no  matches, the original
  71.  *    file-spec is left in the argument list rather than having it expand
  72.  *    into thin  air.   No  attempt is made to identify  and    make $(var)
  73.  *    environment substitutions--must draw the line somewhere!
  74.  *
  75.  *   Oct'91, gawk 2.13.3
  76.  *    Open '<' with full sharing allowed, so that we can  read batch logs
  77.  *    and other open files.  Create record-format output ('>$') with read
  78.  *    sharing permited,  so that others can read our output file to check
  79.  *    progess.  For stream  output ('>' or  '>>'), sharing is  disallowed
  80.  *    (for performance reasons).
  81.  *
  82.  *   Sep'94, gawk 2.15.6        [pr]
  83.  *    Add '>+' to force binary mode output, to enable better control
  84.  *    for the user when the output destination is a mailbox or socket.
  85.  *    (ORS = "\r\n" for tcp/ip.)  Contributed by Per Steinar Iversen.
  86.  */
  87.  
  88. #include "awk.h"    /* really "../awk.h" */
  89. #include "vms.h"
  90. #include <lnmdef.h>
  91.  
  92.        void   v_add_arg(int, const char *);
  93. static char  *skipblanks(const char *);
  94. static void   vms_expand_wildcards(const char *);
  95. static u_long vms_define(const char *, const char *);
  96. static char  *t_strstr(const char *, const char *);
  97. #define strstr t_strstr        /* strstr() missing from vaxcrtl for V4.x */
  98.  
  99. static    int    v_argc,  v_argz = 0;
  100. static    char  **v_argv;
  101.  
  102. /* vms_arg_fixup() - scan argv[] for i/o redirection and wildcards and also */
  103. /*            rebuild it with those removed or expanded, respectively */
  104. void
  105. vms_arg_fixup( int *pargc, char ***pargv )
  106. {
  107.     const char *f_in, *f_out, *f_err,
  108.     *out_mode, *rms_rfm, *rms_shr, *rms_mrs;
  109.     char **argv = *pargv;
  110.     int i, argc = *pargc;
  111.     int err_to_out_redirect = 0, out_to_err_redirect = 0;
  112.  
  113. #ifdef CHECK_DECSHELL        /* don't define this if linking with DECC$SHR */
  114.     if (shell$is_shell())
  115.     return;            /* don't do anything if we're running DEC/Shell */
  116. #endif
  117. #ifndef NO_DCL_CMD
  118.     for (i = 1; i < argc ; i++)     /* check for dash or other non-VMS args */
  119.     if (strchr("->\\|", *argv[i]))    break;        /* found => (i < argc) */
  120.     if (i >= argc && (v_argc = vms_gawk()) > 0) {   /* vms_gawk => dcl_parse */
  121.     /* if we successfully parsed the command, replace original argv[] */
  122.     argc = v_argc,    argv = v_argv;
  123.     v_argz = v_argc = 0,  v_argv = NULL;
  124.     }
  125. #endif
  126.     v_add_arg(v_argc = 0, argv[0]);    /* store arg #0 (image name) */
  127.  
  128.     f_in = f_out = f_err = NULL;    /* stdio setup (no filenames yet) */
  129.     out_mode = "w";            /* default access for stdout */
  130.     rms_rfm = "rfm=stmlf";        /* stream_LF format */
  131.     rms_shr = "shr=nil";        /* no sharing (for '>' output file) */
  132.     rms_mrs = "mrs=0";            /* no maximum record size */
  133.  
  134.     for (i = 1; i < argc; i++) {
  135.     char *p, *fn;
  136.     int  is_arg;
  137.  
  138.     is_arg = 0;        /* current arg does not begin with dash */
  139.     p = argv[i];        /* current arg */
  140.     switch (*p) {
  141.       case '<':        /* stdin */
  142.           /*[should try to determine whether this is really a directory
  143.          spec using <>; for now, force user to quote them with '\<']*/
  144.         if ( f_in ) {
  145.             fatal("multiple specification of '<' for stdin");
  146.         } else if (*++p == '<') {   /* '<<' is not supported */
  147.             fatal("'<<' not available for stdin");
  148.         } else {
  149.             p = skipblanks(p);
  150.             fn = (*p ? p : argv[++i]);    /* use next arg if necessary */
  151.             if (i >= argc || *fn == '-')
  152.             fatal("invalid i/o redirection, null filespec after '<'");
  153.             else
  154.             f_in = fn;        /* save filename for stdin */
  155.         }
  156.         break;
  157.       case '>':   {        /* stdout or stderr */
  158.           /*[vms-specific kludge '>$' added to force stdout to be created
  159.          as record-oriented text file instead of in stream-lf format]*/
  160.         int is_out = 1;            /* assume stdout */
  161.         if (*++p == '>')    /* '>>' => append */
  162.             out_mode = "a",  p++;
  163.         else if (*p == '&')    /* '>&' => stderr */
  164.             is_out = 0,  p++;
  165.         else if (*p == '$')    /* '>$' => kludge for record format */
  166.             rms_rfm = "rfm=var",  rms_shr = "shr=get,upi",
  167.             rms_mrs = "mrs=32767",  p++;
  168.         else if (*p == '+')    /* '>+' => kludge for binary output */
  169.             out_mode = "wb",  rms_rfm = "rfm=var",
  170.             rms_mrs = "mrs=32767",  p++;
  171.         else            /* '>'    => create */
  172.             {}        /* use default values initialized prior to loop */
  173.         p = skipblanks(p);
  174.         fn = (*p ? p : argv[++i]);    /* use next arg if necessary */
  175.         if (i >= argc || *fn == '-') {
  176.             fatal("invalid i/o redirection, null filespec after '>'");
  177.         } else if (is_out) {
  178.             if (out_to_err_redirect)
  179.             fatal("conflicting specifications for stdout");
  180.             else if (f_out)
  181.             fatal("multiple specification of '>' for stdout");
  182.             else
  183.             f_out = fn;        /* save filename for stdout */
  184.         } else {
  185.             if (err_to_out_redirect)
  186.             fatal("conflicting specifications for stderr");
  187.             else if (f_err)
  188.             fatal("multiple specification of '>&' for stderr");
  189.             else
  190.             f_err = fn;        /* save filename for stderr */
  191.         }
  192.         }    break;
  193.       case '2':        /* check for ``2>&1'' special case'' */
  194.         if (strcmp(p, "2>&1") != 0)
  195.             goto ordinary_arg;
  196.         else if (f_err || out_to_err_redirect)
  197.             fatal("conflicting specifications for stderr");
  198.         else {
  199.             err_to_out_redirect = 1;
  200.             f_err = "SYS$OUTPUT:";
  201.         }  break;
  202.       case '1':        /* check for ``1>&2'' special case'' */
  203.         if (strcmp(p, "1>&2") != 0)
  204.             goto ordinary_arg;
  205.         else if (f_out || err_to_out_redirect)
  206.             fatal("conflicting specifications for stdout");
  207.         else {
  208.             out_to_err_redirect = 1;
  209.             f_out = "SYS$ERROR:";
  210.         }  break;
  211.       case '|':        /* pipe */
  212.           /* command pipelines are not supported */
  213.         fatal("command pipes not available ('|' encountered)");
  214.         break;
  215.       case '&':        /* background */
  216.           /*[we could probably spawn or fork ourself--maybe someday]*/
  217.         if (*(p+1) == '\0' && i == argc - 1) {
  218.             fatal("background tasks not available ('&' encountered)");
  219.             break;
  220.         } else {    /* fall through */
  221.             ;    /*NOBREAK*/
  222.         }
  223.       case '-':        /* argument */
  224.         is_arg = 1;        /*(=> skip wildcard check)*/
  225.       default:        /* other (filespec assumed) */
  226. ordinary_arg:
  227.           /* process escape sequences or expand wildcards */
  228.         v_add_arg(++v_argc, p);        /* include this arg */
  229.         p = strchr(p, '\\');        /* look for backslash */
  230.         if (p != NULL) {    /* does it have escape sequence(s)? */
  231. #if 0    /* disable escape parsing; it's now done elsewhere within gawk */
  232.             register int c;
  233.             char *q = v_argv[v_argc] + (p - argv[i]);
  234.             do {
  235.             c = *p++;
  236.             if (c == '\\')
  237.                 c = parse_escape(&p);
  238.             *q++ = (c >= 0 ? (char)c : '\\');
  239.             } while (*p != '\0');
  240.             *q = '\0';
  241. #endif    /*0*/
  242.         } else if (!is_arg && strchr(v_argv[v_argc], '=') == NULL) {
  243.             vms_expand_wildcards(v_argv[v_argc]);
  244.         }
  245.         break;
  246.     } /* end switch */
  247.     } /* loop */
  248.  
  249.     /*
  250.      * Now process any/all I/O options encountered above.
  251.      */
  252.  
  253.     /* must do stderr first, or vaxcrtl init might not see it */
  254.     /*[ catch 22:  we'll also redirect errors encountered doing <in or >out ]*/
  255.     if (f_err) {    /* define logical name but don't open file */
  256.     int len = strlen(f_err);
  257.     if (len >= (sizeof "SYS$OUTPUT" - sizeof "")
  258.      && strncasecmp(f_err, "SYS$OUTPUT:", len) == 0)
  259.         err_to_out_redirect = 1;
  260.     else
  261.         (void) vms_define("SYS$ERROR", f_err);
  262.     }
  263.     /* do stdin before stdout, so if we bomb we won't make empty output file */
  264.     if (f_in) {        /* [re]open file and define logical name */
  265.     if (freopen(f_in, "r", stdin,
  266.             "ctx=rec", "shr=get,put,del,upd",
  267.             "mrs=32767", "mbc=32", "mbf=2"))
  268.         (void) vms_define("SYS$INPUT", f_in);
  269.     else
  270.         fatal("<%s (%s)", f_in, strerror(errno));
  271.     }
  272.     if (f_out) {
  273.     if (freopen(f_out, out_mode, stdout,
  274.             rms_rfm, rms_shr, rms_mrs,
  275.             "rat=cr", "mbc=32", "mbf=2"))
  276.         (void) vms_define("SYS$OUTPUT", f_out);
  277.     else
  278.         fatal(">%s%s (%s)", (*out_mode == 'a' ? ">" : ""),
  279.           f_out, strerror(errno));
  280.     }
  281.     if (err_to_out_redirect) {    /* special case for ``2>&1'' construct */
  282.     (void) dup2(1, 2);    /* make file 2 (stderr) share file 1 (stdout) */
  283.     (void) vms_define("SYS$ERROR", "SYS$OUTPUT:");
  284.     } else if (out_to_err_redirect) {    /* ``1>&2'' */
  285.     (void) dup2(2, 1);    /* make file 1 (stdout) share file 2 (stderr) */
  286.     (void) vms_define("SYS$OUTPUT", "SYS$ERROR:");
  287.     }
  288.  
  289. #ifndef NO_DCL_CMD
  290.     /* if we replaced argv[] with our own, we can release it now */
  291.     if (argv != *pargv)
  292.     free((void *)argv),  argv = NULL;
  293. #endif
  294.     *pargc = ++v_argc;        /* increment to account for argv[0] */
  295.     *pargv = v_argv;
  296.     return;
  297. }
  298.  
  299. /* vms_expand_wildcards() - check a string for wildcard punctuation; */
  300. /*               if it has any, attempt a directory lookup */
  301. /*               and store resulting name(s) in argv array */
  302. static void
  303. vms_expand_wildcards( const char *prospective_filespec )
  304. {
  305.     char *p, spec_buf[255+1], res_buf[255+1], *strstr();
  306.     Dsc   spec, result;
  307.     void *context;
  308.     register int len = strlen(prospective_filespec);
  309.  
  310.     if (len >= sizeof spec_buf)
  311.     return;        /* can't be valid--or at least we can't handle it */
  312.     strcpy(spec_buf, prospective_filespec);    /* copy the arg */
  313.     p = strchr(spec_buf, '?');
  314.     if (p != NULL)    /* change '?' single-char wildcard to '%' */
  315.     do  *p++ = '%',  p = strchr(p, '?');
  316.         while (p != NULL);
  317.     else if (strchr(spec_buf, '*') == strchr(spec_buf, '%')  /* => both NULL */
  318.       && strstr(spec_buf, "...") == NULL)
  319.     return;        /* no wildcards present; don't attempt file lookup */
  320.     spec.len = len,  spec.adr = spec_buf;
  321.     result.len = sizeof res_buf - 1,  result.adr = res_buf;
  322.  
  323.     /* The filespec is already in v_argv[v_argc]; if we fail to match anything,
  324.        we'll just leave it there (unlike most shells, where it would evaporate).
  325.      */
  326.     len = -1;            /* overload 'len' with flag value */
  327.     context = NULL;        /* init */
  328.     while (vmswork(LIB$FIND_FILE(&spec, &result, &context))) {
  329.     for (len = sizeof(res_buf)-1; len > 0 && res_buf[len-1] == ' '; len--) ;
  330.     res_buf[len] = '\0';    /* terminate after discarding trailing blanks */
  331.     v_add_arg(v_argc++, strdup(res_buf));        /* store result */
  332.     }
  333.     (void)LIB$FIND_FILE_END(&context);
  334.     if (len >= 0)        /* (still -1 => never entered loop) */
  335.     --v_argc;        /* undo final post-increment */
  336.     return;
  337. }
  338.  
  339. /* v_add_arg() - store string pointer in v_argv[]; expand array if necessary */
  340. void
  341. v_add_arg( int idx, const char *val )
  342. {
  343. #ifdef DEBUG_VMS
  344.     fprintf(stderr, "v_add_arg: v_argv[%d] ", idx);
  345. #endif
  346.     if (idx + 1 >= v_argz) {    /* 'v_argz' is the current size of v_argv[] */
  347.     int old_size = v_argz;
  348.  
  349.     v_argz = idx + 10;    /* increment by arbitrary amount */
  350.     if (old_size == 0)
  351.         v_argv = (char **)malloc((unsigned)(v_argz * sizeof(char **)));
  352.     else
  353.         v_argv = (char **)realloc((char *)v_argv,
  354.                      (unsigned)(v_argz * sizeof(char **)));
  355.     if (v_argv == NULL) {    /* error */
  356.         fatal("%s: %s: can't allocate memory (%s)", "vms_args",
  357.           "v_argv", strerror(errno));
  358.     } else {
  359.         while (old_size < v_argz)  v_argv[old_size++] = NULL;
  360.     }
  361.     }
  362.     v_argv[idx] = (char *)val;
  363. #ifdef DEBUG_VMS
  364.     fprintf(stderr, "= \"%s\"\n", val);
  365. #endif
  366. }
  367.  
  368. /* skipblanks() - return a pointer to the first non-blank in the string */
  369. static char *
  370. skipblanks( const char *ptr )
  371. {
  372.     if (ptr)
  373.     while (*ptr == ' ' || *ptr == '\t')
  374.         ptr++;
  375.     return (char *)ptr;
  376. }
  377.  
  378. /* vms_define() - assign a value to a logical name [define/process/user_mode] */
  379. static u_long
  380. vms_define( const char *log_name, const char *trans_val )
  381. {
  382.     Dsc log_dsc;
  383.     static Descrip(lnmtable,"LNM$PROCESS_TABLE");
  384.     static u_long attr = LNM$M_CONFINE;
  385.     static Itm itemlist[] = { {0,LNM$_STRING,0,0}, {0,0} };
  386.     static unsigned char acmode = PSL$C_USER;
  387.     unsigned len = strlen(log_name);
  388.  
  389.     /* avoid "define SYS$OUTPUT sys$output:" for redundant ">sys$output:" */
  390.     if (strncasecmp(log_name, trans_val, len) == 0
  391.      && (trans_val[len] == '\0' || trans_val[len] == ':'))
  392.     return 0;
  393.  
  394.     log_dsc.adr = (char *)log_name;
  395.     log_dsc.len = len;
  396.     itemlist[0].buffer = (char *)trans_val;
  397.     itemlist[0].len = strlen(trans_val);
  398.     return SYS$CRELNM(&attr, &lnmtable, &log_dsc, &acmode, itemlist);
  399. }
  400.  
  401. /* t_strstr -- strstr() substitute; search 'str' for 'sub' */
  402. static char *t_strstr ( const char *str, const char *sub )
  403. {
  404.     register const char *s0, *s1, *s2;
  405.  
  406.     /* special case: empty substring */
  407.     if (!*sub)    return (char *)str;
  408.  
  409.     /* brute force method */
  410.     for (s0 = s1 = str; *s1; s1 = ++s0) {
  411.     s2 = sub;
  412.     while (*s1++ == *s2++)
  413.         if (!*s2)  return (char *)s0;    /* full match */
  414.     }
  415.     return (char *)0;    /* not found */
  416. }
  417.