home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / flistfrontend / src / dclarg.c < prev    next >
C/C++ Source or Header  |  1995-10-27  |  16KB  |  523 lines

  1. #ifndef NO_IDENT
  2. static char *Id = "$Id: dclarg.c,v 1.8 1995/10/27 23:05:48 tom Exp $";
  3. #endif
  4.  
  5. /*
  6.  * Title:    dclarg.c
  7.  * Author:    T.E.Dickey
  8.  * Created:    24 May 1984
  9.  * Last update:
  10.  *        27 Oct 1995, suppress DNF-error so "create directory" works.
  11.  *        18 Mar 1995, prototyped
  12.  *        05 Dec 1989, patched out VMS-bug (PATCH_DEC89)
  13.  *        20 Mar 1989, bypass VMS bug which returns occasional illegal
  14.  *                 status code.
  15.  *        11 Jul 1988, permit '-' in filenames.
  16.  *        30 Sep 1985, added this comment!
  17.  *        23 Sep 1985, added this comment!
  18.  *        08 May 1985, added this comment!
  19.  *        27 Apr 1985, added visible-test for sid here to confuse thieves.
  20.  *        28 Jan 1985, the search here is done only if 'cpy_dft' is set,
  21.  *                 to provide (temporary?) support for FLIST/COPY.
  22.  *                 Patch: remove search ultimately, since this does
  23.  *                 not provide support for multiple-outputs.
  24.  *        17 Jan 1985, options on command-keyword should be always set
  25.  *                 with mfld=0.  That way, we consistently number
  26.  *                 "CMD/OPT A,B" and "CMD A/OPT,B".
  27.  *        26 Dec 1984, added NAM-block stuff to reduce re-parsing.
  28.  *        15 Dec 1984, added 'cpy_dft' argument.
  29.  *        14 Oct 1984, fixed bug in option-value parsing
  30.  *        10 Sep 1984, use "rmsinit"
  31.  *        06 Sep 1984, filespec may begin with '_'
  32.  *        26 Aug 1984, cleanup buffer sizes
  33.  *        06 Aug 1984, added 'dclarg_keyw' entrypoint
  34.  *        04 Aug 1984, test tabs/spaces as blanks, misc.
  35.  *        01 Aug 1984, corrected default-field propagation
  36.  *        24 Jul 1984, added 'cmd_arg' flag
  37.  *        13 Jul 1984, fixes for "[x/x]", unparsed names.
  38.  *        04 Jul 1984, option keyword is a/n + '_'
  39.  *        27 Jun 1984, added ',' check to support UIC-directories.
  40.  *        26 Jun 1984, to add '_from', '_size' data for error reporting
  41.  *        25 Jun 1984, to add 'dclarg_text' procedure
  42.  *        19 Jun 1984, to add '$' to name-characters
  43.  *        28 May 1984
  44.  *
  45.  * Function:    This procedure splits a DCL-like command string into a series
  46.  *        of strings in a linked list.  The objective is to permit the
  47.  *        calling program to perform simple syntax analysis of a string
  48.  *        to detect (and parse) options, and filenames, particularly in
  49.  *        setting up SPAWN-calls.  It does some of the functions of
  50.  *        DEC's DCL:
  51.  *
  52.  *           (a)    Punctuation is used to parse the string.  DCLARG tests
  53.  *            for '/' (to begin option fields) and parentheses (to
  54.  *            group items for propagating defaults) and commas (also
  55.  *            for defaulting).
  56.  *
  57.  *           (b)    The parsed list is returned as a linked list.  No
  58.  *            attempt is made to decode option values; these are
  59.  *            part of the same string as the "/" options.
  60.  *
  61.  *        No tests are made for the fancier features of DCL (quoted
  62.  *        names, or nested parentheses).  Nor is any attempt made to
  63.  *        default filetypes, etc., in option-strings which may be
  64.  *        filenames.
  65.  *
  66.  *        By observing the VMS TYPE and COPY programs, the following
  67.  *        rules for defaulting have been adopted:
  68.  *
  69.  *           (a)    The initial default is used until overridden by
  70.  *            explicit use (e.g., CMD A.TMP overrides ".LIS").
  71.  *           (b)    If an output is used, it defaults by the first
  72.  *            name returned by SYS$SEARCH from the first input
  73.  *            specification (e.g., COPY A.*,B C will default C's
  74.  *            filetype to the first filetype found for A).
  75.  *           (c)    The above rule, however, applies only if the first
  76.  *            parameter is a list.  If it is not, use as default
  77.  *            the expanded string (from SYS$PARSE).  This permits
  78.  *            support for "COPY A.* B" (i.e., "B" inherits a wildcard
  79.  *            in the filetype).
  80.  *
  81.  * Parameters:    inp_    => string containing DCL to parse
  82.  *        dft_    => string defining defaults for filenames in list
  83.  *            (e.g., for PRINT, assume ".LIS").  It may not
  84.  *            specify the directory, since this defaults in a list.
  85.  *        cmd_arg    =  A count of the number of leading tokens which are
  86.  *            command verbs.  Normally this is 1 or 0 (the verb was
  87.  *            already absorbed).  It is "2" in the special case of
  88.  *            functions (BROWSE) called from FLIST, since the
  89.  *            procedure 'argvdcl' does not know about this special
  90.  *            case.  The counter specifies the number of tokens to
  91.  *            pass before treating them as filenames (and propagating
  92.  *            defaults, and related-name fields).
  93.  *        cpy_dft = A flag which is TRUE if we use the defaulting rule
  94.  *            for COPY.  The output file in this case does not
  95.  *            inherit the input's directory name, but (implicitly)
  96.  *            the current default device/directory.
  97.  *
  98.  * Returns:    A pointer to the resulting linked-list of DCLARG structures.
  99.  *        If the list is blank, the pointer is null.  No direct error
  100.  *        reporting is performed from this routine; entries containing
  101.  *        errors have a nonzero '.dcl_stat' entry (-1 if non-RMS).
  102.  *
  103.  * Entry:    dclarg:        Main entry of this module
  104.  *        dclarg_text:    Allocate/reallocate DCLARG-link
  105.  *        dclarg_init:    Initialize 'dclarg', 'dclarg_make'
  106.  *        dclarg_spec:    Skip string pointer past filespec
  107.  *        dclarg_keyw:    Skip string pointer past option keyword.
  108.  *
  109.  * Patch:    This procedure does not properly detect such syntax errors as
  110.  *
  111.  *            COMMAND/NAME.TYP (should be illegal delimiter '.')
  112.  *            COMMAND,NAME (can be tested by 'dclinx (p, 0, 1)')
  113.  *            COMMAND    FILE.TYP.TYP  (except as an illegal filename)
  114.  */
  115.  
  116. #include    <starlet.h>
  117. #include    <stdlib.h>
  118. #include    <stdio.h>
  119. #include    <rms.h>
  120. #include    <ctype.h>
  121. #include    <string.h>
  122.  
  123. #include    "bool.h"
  124. #include    "crt.h"
  125. #include    "dclarg.h"
  126. #include    "rmsinit.h"
  127.  
  128. /*
  129.  * Forward declarations and external procedures:
  130.  */
  131. static    DCLARG    *dclarg_make (DCLARG *last_, char *s_, char *cmd, int from, int size, struct NAM *nam_);
  132. static    int    dclarg_bracket (char c);
  133. static    int    dclarg_name (char c);
  134. static    void    dclarg__copy (char *dft, struct NAM *nam_, int cpy_dft);
  135. static    void    dclarg_init (void);
  136.  
  137. #define    SPC(s)    while (isspace(*s))    s++
  138.  
  139. #define    FROM    inp_, (j_-inp_), (i_-j_)
  140.  
  141. #define    STRNCPY(out,in,xlen)    {strncpy(out, in, len = (xlen));\
  142.                  out[len] = EOS;}
  143.  
  144. static    DCLARG    *first_;    /* Pointer to linked-list        */
  145. static    unsigned status;    /* RMS error code, or 0 (ok) or -1 (own)*/
  146. static    int    mfld;        /* Increment between grouped names    */
  147. static    int    sfld;        /* Increment after ","            */
  148.  
  149. static    char    err_null[] = "null file specification";
  150. static    char    err_parm[] = "invalid parameter delimiter";
  151.  
  152. DCLARG    *dclarg(char *inp_, char *dft_, int cmd_arg, int cpy_dft)
  153. {
  154.     DCLARG    *arg_    = 0;
  155.     struct    FAB    fab;
  156.     struct    NAM    nam,    nam2;
  157.     int    len,            /* (misc) string length        */
  158.         use_dna2 = FALSE;    /* TRUE if 'dna2[]' is default    */
  159.     char    rsa    [NAM$C_MAXRSS],    /* resultant string (SYS$SEARCH)*/
  160.         esa    [NAM$C_MAXRSS],    /* expanded string (SYS$PARSE)    */
  161.         dna    [NAM$C_MAXRSS],    /* next 'sfld' default string    */
  162.         dna2    [NAM$C_MAXRSS],    /* next 'mfld' default if list    */
  163.         dna3    [NAM$C_MAXRSS],    /* next 'mfld' default if single*/
  164.         cmdtok    [NAM$C_MAXRSS],
  165.         *i_    = inp_,        /* beginning of item to parse    */
  166.         *j_,            /* end+1 of item to parse    */
  167.         *k_;            /* miscellaneous pointer    */
  168.  
  169.     if (!dft_)    dft_ = "";
  170.     strcpy (dna, dft_);
  171.     strcpy (dna2,dft_);
  172.     strcpy (dna3,dft_);
  173.  
  174.     dclarg_init ();
  175.     for (;;)
  176.     {
  177.         SPC(i_);    /* Ignore blanks between tokens */
  178.  
  179.         j_ = i_;
  180.         if (*i_ == EOS)
  181.         return (first_);
  182.         /*
  183.          * Options begin with "/", and are followed by:
  184.          *    (a) an optional alphanumeric+'_' name, then
  185.          *    (b) an optional (if name given) "="
  186.          *    (c) an option-value if "=", perhaps enclosed in
  187.          *        "(" and ")".
  188.          *
  189.          * SET-commands may use an "=" without a preceding "/"
  190.          * (e.g., "SET PROTECTION=(OWN:RE)").
  191.          */
  192.         else if (isopt(*i_))
  193.         {
  194.         if (*i_ == '/')
  195.         {
  196.             i_++;
  197.             SPC(i_);
  198.             i_ = dclarg_keyw (i_);
  199.             k_ = i_;
  200.             SPC(k_);
  201.             if (isopt2(*k_))    i_ = k_;
  202.         }
  203.         if (isopt2(*i_))
  204.         {
  205.             i_++;
  206.             SPC(i_);
  207.             if (*i_ == '(')
  208.             {
  209.             while (*i_ && (*i_ != ')'))    i_++;
  210.             if (*i_ == ')')            i_++;
  211.             }
  212.             else
  213.             i_ = dclarg_spec (i_, "-");
  214.         }
  215.         status = 0;
  216.         STRNCPY(esa, j_, i_ - j_);
  217.         arg_ = dclarg_make (arg_, esa, FROM, (struct NAM *)0);
  218.         }
  219.         /*
  220.          * File names contain a mixture of ".", ":", ";", alphanumeric
  221.          * characters, and "[", "]" to delimit directories.  Many
  222.          * DCL-oriented programs permit a list of filenames separated
  223.          * by comma to indicate that the directory defaults.  Finally,
  224.          * wildcard characters "*" and "%" are used.
  225.          *
  226.          * Perform enough parsing here to pass along the defaults to
  227.          * the calling program.
  228.          */
  229.         else if (dclarg_name(*i_))
  230.         {
  231.         i_ = dclarg_spec (i_, 0);
  232.         if (cmd_arg > 0)
  233.         {
  234.             STRNCPY(cmdtok, j_, i_ - j_);
  235.             arg_ = dclarg_make (arg_, cmdtok, FROM, (struct NAM *)0);
  236.         }
  237.         else
  238.         {
  239.             if (mfld <= 0)    mfld = 1, sfld = 0;
  240.             rmsinit_fab (&fab, &nam,
  241.                     (sfld    ? dna
  242.                         : (use_dna2 ? dna2
  243.                                 : dna3)), 0);
  244.             fab.fab$l_fna    = j_;
  245.             fab.fab$b_fns    = i_-j_;
  246.  
  247.             rmsinit_nam (&nam, rsa, esa);
  248.             status = sys$parse(&fab);
  249.  
  250.             /*
  251.              * If we're referring to a directory that doesn't exist,
  252.              * the parse will return a directory-not-found (something
  253.              * that we'd normally expect from sys$search).  That
  254.              * breaks the "create" command.
  255.              */
  256.             if (status == RMS$_DNF)
  257.                 status = 0;
  258.  
  259.             /*
  260.              * Use the most recent name as a default name for the next
  261.              * parse:
  262.              */
  263.             STRNCPY(dna, esa, nam.nam$b_esl);
  264.             nam2 = nam;        /* Save to use in 'dclarg_make' */
  265.  
  266.             /*
  267.              * If this was the first item in any list, (sfld==0),
  268.              * obtain the parsed string, and the first-found from a
  269.              * search for the defaults 'dna3' and 'dna2', respectively.
  270.              */
  271.             if (sfld == 0)
  272.             {
  273.             dclarg__copy (dna3, &nam, cpy_dft);
  274.             if (cpy_dft)
  275.             {
  276.                 sys$search (&fab);
  277.                 dclarg__copy (dna2, &nam, cpy_dft);
  278.             }
  279.             use_dna2 = FALSE;
  280.             }
  281.  
  282.             /*
  283.              * Make a new DCLARG-entry:
  284.              */
  285.             arg_ = dclarg_make (arg_,
  286.                 (nam.nam$b_esl ? dna : j_), FROM, &nam2);
  287.         }
  288.         }
  289.         else
  290.         {
  291.         while (!dclarg_name(*i_) && !isopt(*i_))    i_++;
  292.         status = -1;
  293.         arg_ = dclarg_make (arg_, err_parm, FROM, (struct NAM *)0);
  294.         }
  295.  
  296.         SPC(i_);
  297.         if (*i_ == ',')
  298.         {
  299.         use_dna2 = cpy_dft;     /* COPY-output inherits 1st input */
  300.         i_++; sfld++;
  301.         SPC(i_);
  302.         j_ = i_;
  303.         while (isspace(*i_) || *i_ == ',')    i_++;
  304.         if (j_ != i_ || isopt(*i_))
  305.         {
  306.             status = -1;
  307.             arg_ = dclarg_make (arg_, err_null, FROM, (struct NAM *)0);
  308.         }
  309.         }
  310.         else if (!isopt(*i_))
  311.         {
  312.         strcpy (dna, dft_);
  313.         if (cmd_arg <= 0)
  314.         {
  315.             mfld++;
  316.             sfld = 0;
  317.         }
  318.         }
  319.  
  320.         if (cmd_arg > 0)
  321.         cmd_arg--;    /* (On zero, look for filenames) */
  322.     }
  323. }
  324.  
  325. /* <dclarg__copy>:
  326.  * Copy default-strings from the first file-specification in a main-field.
  327.  * If the 'cpy_dft' flag is set, don't copy the pathname, but begin with the
  328.  * filename.
  329.  */
  330. static
  331. void    dclarg__copy (char *dft, struct NAM *nam_, int cpy_dft)
  332. {
  333.     char    *c_    = nam_->nam$l_node;
  334.     int    len    = nam_->nam$b_ver + (nam_->nam$l_ver - c_),
  335.         adj    = 0;
  336.  
  337.     if (cpy_dft)
  338.     {
  339.         adj = nam_->nam$l_name - c_;
  340.         len -= adj;
  341.         c_  += adj;
  342.     }
  343.     strncpy (dft, c_, len);
  344.     dft[len] = EOS;
  345. }
  346.  
  347. /* <dclarg_text>:
  348.  * Allocate (or re-allocate) a 'DCLARG' entry, given the text-buffer to load.
  349.  * This procedure can be used to allocate a new entry (if 'this_' is null), or
  350.  * to reallocate an entry when the text buffer's size has altered.  The status
  351.  * and field indices are unaltered.
  352.  */
  353. DCLARG    *dclarg_text(
  354.     DCLARG    *this_,        /* Entry to (re)allocate    */
  355.     DCLARG    *last_,        /* Entry containing link to it    */
  356.     char    *s_,        /* Text-buffer (mustn't be null)*/
  357.     int    uc)        /* TRUE if uppercase        */
  358. {
  359.     size_t    size    = sizeof(DCLARG) + strlen(s_) + 2;
  360.     char    *text_;
  361.  
  362.     if (this_)    this_    = realloc (this_, size);
  363.     else        this_    = calloc (1, size);
  364.  
  365.     this_->dcl_size    = strlen(s_);
  366.     text_    = (char *) this_ + sizeof(DCLARG);
  367.  
  368.     /*
  369.      * Strip out blanks from the string as I load it.  (patch: Should look
  370.      * for quoted strings here.)
  371.      */
  372.     for (this_->dcl_text = text_; *s_; s_++)
  373.     {
  374.         if (!uc || !isspace(*s_))    *text_++ = *s_;
  375.     }
  376.     *text_ = EOS;
  377.  
  378.     /*
  379.      * Do uppercase conversion if needed.  This is used for all filenames
  380.      * and option strings.  Error messages are left alone.
  381.      */
  382.     if (uc)
  383.     {
  384.         for (text_ = this_->dcl_text; *text_; text_++)
  385.             *text_ = _toupper(*text_);
  386.     }
  387.     if (last_ != 0)
  388.         last_->dcl_next = (DCLARG *)this_;
  389.     /*
  390.      * Don't allow empty name ".;" -- trim it off
  391.      */
  392.     if ((size = strlen(text_ = this_->dcl_text)) > 2)
  393.     {
  394.         if (!strcmp(text_ + size - 2, ".;"))
  395.             text_[size-2] = EOS;
  396.     }
  397.     return (this_);
  398. }
  399.  
  400. /* <dclarg_make>:
  401.  * Form a new entry in the linked-list of parsed items
  402.  */
  403. static
  404. DCLARG    *dclarg_make (
  405.     DCLARG    *last_,        /* last entry in DCLARG-list    */
  406.     char    *s_,        /* option, filename, or message    */
  407.     char    *cmd,
  408.     int    from,        /* index and length in command    */
  409.     int    size,        /* index and length in command    */
  410.     struct    NAM *nam_)    /* => NAM-block, if we use result */
  411. {
  412.     DCLARG    *this_;
  413.     char    bfr[CRT_COLS];    /* Assume command is narrower than screen */
  414.  
  415. #ifdef    PATCH_DEC89
  416.     if (status == (RMS$_NORMAL & ~7)) {
  417.         status = RMS$_NORMAL;
  418.         flist_log("! dclarg found parse-bug");
  419.     }
  420. #endif    /* PATCH_DEC89 */
  421.  
  422.     if (status == -1)
  423.     {
  424.         sprintf (bfr, "%s: \\%.8s\\", s_, &cmd[from]);
  425.         s_ = bfr;
  426.     }
  427.     this_ = dclarg_text (0, last_, s_, (status == RMS$_NORMAL));
  428.  
  429.     this_->dcl_stat = (status == RMS$_NORMAL) ? 0 : status;
  430.     this_->dcl_mfld = mfld;
  431.     this_->dcl_sfld = sfld;
  432.     this_->dcl_from = from;
  433.     this_->dcl_size = size;
  434.     if (nam_)
  435.     {
  436.         this_->dcl$l_fnb    = nam_->nam$l_fnb;
  437.         this_->dcl$b_node    = nam_->nam$b_node;
  438.         this_->dcl$b_dev    = nam_->nam$b_dev;
  439.         this_->dcl$b_dir    = nam_->nam$b_dir;
  440.         this_->dcl$b_name    = nam_->nam$b_name;
  441.         this_->dcl$b_type    = nam_->nam$b_type;
  442.         this_->dcl$b_ver    = nam_->nam$b_ver;
  443.     }
  444.     if (!last_)
  445.         first_ = this_;
  446.     return (this_);
  447. }
  448.  
  449. /* <dclarg_init>:
  450.  * Initialize the state used in 'dclarg_make' to enable external use of
  451.  * the routine:
  452.  */
  453. static
  454. void    dclarg_init (void)
  455. {
  456.     status = mfld = sfld = 0;
  457. }
  458.  
  459. /* <dclarg_spec>:
  460.  * Skip a character-pointer past a filespec, option-keyword (including date).
  461.  */
  462. char    *dclarg_spec (char *i_, char *also)
  463. {
  464.     int    brackets = 0;
  465.  
  466.     if (!also)    also = "$";
  467.  
  468.     while ( *i_ && (    dclarg_name(*i_)
  469.             ||    strrchr(also, *i_)
  470.             ||    (*i_ == ',' && brackets) )
  471.         )
  472.     {
  473.         brackets += dclarg_bracket(*i_);
  474.         if (brackets < 0)    brackets = 0;
  475.         i_++;
  476.     }
  477.     return (i_);
  478. }
  479.  
  480. /* <dclarg_name>:
  481.  * Test a character to see if it is part of a legal VMS wildcard-file spec.
  482.  */
  483. static
  484. int    dclarg_name(char c)
  485. {
  486.     return (isalnum(c)
  487.     ||    c == '-'
  488.     ||    c == '_'
  489.     ||    c == '$'
  490.     ||     c == '.' || c == ':' || c == ';'
  491.     ||    dclarg_bracket (c)
  492.     ||    c == '*' || c == '%');
  493. }
  494.  
  495. /* <dclarg_bracket>:
  496.  * VMS/DCL permits either '<','>' or '[',']' to delimit directory pathname.
  497.  * Test for either.
  498.  */
  499. static
  500. int    dclarg_bracket (char c)
  501. {
  502.     switch (c)
  503.     {
  504.     case '[':
  505.     case '<':
  506.         return (1);
  507.     case ']':
  508.     case '>':
  509.         return (-1);
  510.     default:
  511.         return (0);
  512.     }
  513. }
  514.  
  515. /* <dclarg_keyw>:
  516.  * Skip a character-pointer past an option keyword only.
  517.  */
  518. char    *dclarg_keyw (char *c_)
  519. {
  520.     while (isalnum(*c_) || *c_ == '_')    c_++;
  521.     return (c_);
  522. }
  523.