home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / e / elv17src.zip / VMSWILD.C < prev    next >
C/C++ Source or Header  |  1992-12-30  |  19KB  |  514 lines

  1. /*
  2.    This 'C' module may be included prior to the ``main'' programs on VMS in
  3.    order to allow 'C' arguments to contain redirection symbols (<,>,>>) and
  4.    VMS wild cards (*,% ...], [-).  By including this module, two programs
  5.    redirect() and expand() are run prior to turning control over to
  6.    your main() entry point.
  7.  
  8. /*
  9.     redirect-- Gregg Townsend circa 1983,
  10.     expand-- John Campbell circa 1987
  11.  
  12.    This code is public domain, others may use it freely. Credit, however, to
  13.    Gregg Townsend (who wrote ``redirect()'') and John Campbell (who followed
  14.    with ``expand()'') would be appreciated.  If someone writes the next
  15.    logical successor ``pipe()'', please email a copy to
  16.    ...!arizona!naucse!jdc (John Campbell) :-).
  17. */
  18.  
  19. #include <rms>   /* No easy way to tell if this has already been included. */
  20. #ifndef ERANGE   /* Include only if missing. */
  21. /* #include <stdlib>  causes lots of warnings. */
  22. #endif
  23. #include <stdio.h>  /* Stdio.h won't include itself twice. */
  24.  
  25. /* Expansion of wild cards is done using RMS. */
  26. typedef struct NAMBLK { struct NAM nam;         /* VMS nam block structure */
  27.                  char es[NAM$C_MAXRSS],         /* Extended string         */
  28.                       rs[NAM$C_MAXRSS];         /* Resultant string        */
  29.                };
  30.  
  31. #define ErrorExit 2
  32.  
  33. /* Allow the user to override _N_FARGS or _E_FLAG if they wish. */
  34. #ifndef _N_FARGS
  35. #define _N_FARGS 0
  36. #endif
  37. #ifndef _E_FLAG
  38. #define _E_FLAG 2
  39. #endif
  40. /*
  41.    Since the following will possibly be included in a single module, try
  42.    hard to avoid name conflicts. (Just being static doesn't cut it if
  43.    compiled in the same module.)
  44. */
  45. #define redirect     _r_edirect
  46. #define filearg      _f_ilearg
  47. #define expand       _e_xpand
  48. #define wild_found   _w_ild_found
  49. #define wild_expand  _w_ild_expand
  50.  
  51. main(argc, argv, envp)
  52. int argc;
  53. char *argv[], *envp[];
  54. {
  55.    char **expand();
  56.  
  57.    redirect (&argc, argv, _N_FARGS);
  58.    argv = expand (&argc, argv, _E_FLAG);
  59.  
  60. /* Make the user's main entry point this routine's entry point. */
  61. #define main _user_main
  62.    _user_main (argc, argv, envp);
  63. }
  64.  
  65. /*
  66.  * redirect(&argc,argv,nfargs) - redirect standard I/O
  67.  *    int *argc         number of command arguments (from call to main)
  68.  *    char *argv[]      command argument list (from call to main)
  69.  *    int nfargs        number of filename arguments to process
  70.  *
  71.  * argc and argv will be adjusted by redirect.
  72.  *
  73.  * redirect processes a program's command argument list and handles redirection
  74.  * of stdin, and stdout.  Any arguments which redirect I/O are removed from the
  75.  * argument list, and argc is adjusted accordingly.  redirect would typically be
  76.  * called as the first statement in the main program.
  77.  *
  78.  * Files are redirected based on syntax or position of command arguments.
  79.  * Arguments of the following forms always redirect a file:
  80.  *
  81.  *    <file     redirects standard input to read the given file
  82.  *    >file     redirects standard output to write to the given file
  83.  *    >>file    redirects standard output to append to the given file
  84.  *
  85.  * It is often useful to allow alternate input and output files as the
  86.  * first two command arguments without requiring the <file and >file
  87.  * syntax.  If the nfargs argument to redirect is 2 or more then the
  88.  * first two command arguments, if supplied, will be interpreted in this
  89.  * manner:  the first argument replaces stdin and the second stdout.
  90.  * A filename of "-" may be specified to occupy a position without
  91.  * performing any redirection.
  92.  *
  93.  * If nfargs is 1, only the first argument will be considered and will
  94.  * replace standard input if given.  Any arguments processed by setting
  95.  * nfargs > 0 will be removed from the argument list, and again argc will
  96.  * be adjusted.  Positional redirection follows syntax-specified
  97.  * redirection and therefore overrides it.
  98.  *
  99.  */
  100.  
  101.  
  102. redirect(argc,argv,nfargs)
  103. int *argc, nfargs;
  104. char *argv[];
  105. {
  106.    int i;
  107.  
  108.    i = 1;
  109.    while (i < *argc)  {         /* for every command argument... */
  110.       switch (argv[i][0])  {            /* check first character */
  111.          case '<':                      /* <file redirects stdin */
  112.             filearg(argc,argv,i,1,stdin,"r");
  113.             break;
  114.          case '>':                      /* >file or >>file redirects stdout */
  115.             if (argv[i][1] == '>')
  116.                filearg(argc,argv,i,2,stdout,"a");
  117.             else
  118.                filearg(argc,argv,i,1,stdout,"w");
  119.             break;
  120.          default:                       /* not recognized, go on to next arg */
  121.             i++;
  122.       }
  123.    }
  124.    if (nfargs >= 1 && *argc > 1)        /* if positional redirection & 1 arg */
  125.       filearg(argc,argv,1,0,stdin,"r"); /* then redirect stdin */
  126.    if (nfargs >= 2 && *argc > 1)        /* likewise for 2nd arg if wanted */
  127.       filearg(argc,argv,1,0,stdout,"w");/* redirect stdout */
  128. }
  129.  
  130.  
  131.  
  132. /* filearg(&argc,argv,n,i,fp,mode) - redirect and remove file argument
  133.  *    int *argc         number of command arguments (from call to main)
  134.  *    char *argv[]      command argument list (from call to main)
  135.  *    int n             argv entry to use as file name and then delete
  136.  *    int i             first character of file name to use (skip '<' etc.)
  137.  *    FILE *fp          file pointer for file to reopen (typically stdin etc.)
  138.  *    char mode[]       file access mode (see freopen spec)
  139.  */
  140.  
  141. filearg(argc,argv,n,i,fp,mode)
  142. int *argc, n, i;
  143. char *argv[], mode[];
  144. FILE *fp;
  145. {
  146.    if (strcmp(argv[n]+i,"-"))           /* alter file if arg not "-" */
  147.       fp = freopen(argv[n]+i,mode,fp);
  148.    if (fp == NULL)  {                   /* abort on error */
  149.       fprintf(stderr,"%%can't open %s",argv[n]+i);
  150.       exit(ErrorExit);
  151.    }
  152.    for ( ;  n < *argc;  n++)            /* move down following arguments */
  153.       argv[n] = argv[n+1];
  154.    *argc = *argc - 1;                   /* decrement argument count */
  155. }
  156.  
  157. /* EXPAND code. */
  158.  
  159. /* Global prototype. */
  160. char **expand (int *argc, const char *argv[], const int flag);
  161. /*-
  162.    ``expand()'' is a routine to expand wild-cards to file specifications.
  163.    This routine is often used in conjunction with ``redirect()'' to provide
  164.    both wild card expansion and standard file redirection prior to doing
  165.    any real work in a 'C' program.
  166.  
  167.    Normal usage is to include the following line prior to using argc or
  168.    argv in main():
  169.  
  170.      argv = expand (&argc, argv, 0);
  171.  
  172.    ``argc'' will be adjusted by ``expand()'', the return value from expand
  173.    will replace ``argv''.
  174.  
  175.    ``expand()'' processes a program's command argument list and expands any
  176.    wild cards into zero or more argv entries. Only arguments that posses VMS
  177.    wild-cards are expanded. Wild cards searched for are ``*'', ``%'',
  178.    ``...]'', and ``[-''. If the wild-card is found inside a single or double
  179.    quote ("*" or '%') then they are not counted as wild-cards. Be aware that
  180.    the expansion of a VMS wild card will match all VMS files, including
  181.    directory files (".DIR;1").
  182.  
  183.    NOTE: The use of quotes in VMS requires thinking about how the CLI expands
  184.    things before handing the argument line over to your program.  Do not
  185.    expect "*" to avoid expansion, use """*""" instead.  Likewise, expression
  186.    substitution precludes the use of (') to quote wild cards:
  187.            $ A := HELLO
  188.            $ ECHO 'a'   ! 'C' program that calls ``expand()''
  189.            hello
  190.    The easiest way to escape a wild-card may be "'*'".  The point is that
  191.    ``expand()'' will only recognize quotes passed into main().
  192.  
  193.    ``expand()'' references the VMS runtime routines, you will need to
  194.    link with the 'C' RTL whenever expand is used.
  195.  
  196.    Parameters:
  197.  
  198.          argc:  Pointer to the number of command arguments (from main),
  199.                 the contents of this parameter are modified.
  200.  
  201.          argv:  Pointer to the initial command argument list (from main),
  202.                 the contents are copied into a new array which is returned
  203.                 from this routine.
  204.  
  205.          flag:  Flag indicating how to expand wild-cards:
  206.                    0 - Complete file name expansion
  207.                    1 - only file name (no directory or version).
  208.                    2 - directory info and file name (no version).
  209.                    3 - file name and version info (no directory).
  210.  -*/
  211.  
  212. /* Local prototypes. */
  213. int wild_found (char *string);
  214. char **wild_expand (const char *string, char **argv, int *argc,
  215.                     int extra, int flag);
  216. /*
  217.    General note: removing the prototyping and const keywords should
  218.    allow this code to compile with VMS 'C' compilers prior to version
  219.    2.3-024.
  220. */
  221.  
  222.  
  223. char **expand (int *argc, char *argv[], int flag)
  224. /*
  225.    Routine to expand all the arguments from main(argc,argv).  The
  226.    return value is a pointer to a new (expanded) argv array.
  227.  
  228.    Parameters:
  229.  
  230.          argc:  Pointer to the number of command arguments (from main),
  231.                 the contents of this parameter are modified.
  232.  
  233.          argv:  Pointer to the initial command argument list (from main),
  234.                 the contents are copied into a new array which is returned
  235.                 from this routine.
  236.  
  237.          flag:  Flag indicating how to expand wild-card:
  238.                    0 - Complete file name expansion
  239.                    1 - only file name (no directory or version).
  240.                    2 - directory info and file name (no version).
  241.                    3 - file name and version info (no directory).
  242. */
  243. {
  244.    int i, nargc;
  245.    char **nargv, **wild_expand();
  246.    char *start, *end;
  247.  
  248. /* Get an initial amount of memory for the master nargv array. */
  249.    if ((nargv = (char **)malloc ((*argc+1) * sizeof (char *))) == NULL) {
  250.       fprintf (stderr, "Not enough memory to expand argument list\n");
  251.       exit (ErrorExit);
  252.    }
  253.  
  254. /*
  255.    Fix the command string so that it only has the name and not the path of
  256.    the function (more in line with what unix reports with argv[0]
  257. */
  258.    start = argv[0];
  259.    end = argv[0] + strlen (argv[0]);
  260.    while (start < end) {
  261.    /* Scan from the back for the first '.' and replace it with a '\0' */
  262.       if (*end == '.') *end = '\0';
  263.    /* And trim off path if it is found. */
  264.       if (*end == ']') {
  265.          end++;
  266.          break;
  267.       }
  268.       --end;
  269.    }
  270.    nargv[0] = end;
  271.  
  272. /* Copy each argument, expanding those that have wild characters. */
  273.    for (nargc = i = 1; i < *argc; i++) {
  274.       if (wild_found(argv[i]))
  275.          nargv = wild_expand(argv[i], nargv, &nargc, *argc-i, flag);
  276.       else
  277.          nargv[nargc++] = argv[i];
  278.    }
  279.    *argc = nargc;
  280.    nargv[nargc] = NULL;  /* realloc always 0 fills, but... */
  281.  
  282.    return nargv;
  283. }
  284.  
  285.  
  286. static int wild_found (char *string)
  287. /*
  288.    Routine to search the given string for a VMS wild-card pattern.
  289.    Returns 1 if "*", "%", "[-", or "...]" is found.  (This may not
  290.    be all VMS wild-cards but it is enough for now--anyone that wants
  291.    to recognize others can change this code.)
  292.  
  293.    Parameter:
  294.  
  295.       string: '\0' terminated character array.
  296. */
  297. {
  298.    int state = 0;
  299.  
  300. /*
  301.    State of 0 is "rest" state.  State 1 on our way to [-, states 2-4
  302.    on our way to ...], negative states indicate the two quotes (' -10,
  303.    " -1).
  304. */
  305.    for ( ;*string; string++) {
  306.       switch (*string) {
  307.       case '*':
  308.       case '%':
  309.          if (state >= 0)
  310.             return 1;                    /* Unquoted % or * found. */
  311.       break;
  312.       case '[':
  313.          if (state >= 0)
  314.             state = 1;
  315.       break;
  316.       case ']':
  317.          if (state == 4)
  318.             return 1;                    /* Unquoted ...] found. */
  319.          else if (state >= 0)
  320.             state = 0;
  321.       break;
  322.       case '-':
  323.          if (state == 1)
  324.             return 1;                    /* Unquoted [- found. */
  325.          else if (state >= 0)
  326.             state = 0;
  327.       break;
  328.       case '.':
  329.          if (state == 1 || state == 0)
  330.             state = 2;                   /* First '.' */
  331.          else if (state > 1 && state < 5)
  332.             state++;                     /* ... == states 2, 3, 4 */
  333.          else if (state >= 0)
  334.             state = 0;
  335.       break;
  336.       case '\'':
  337.          if (state <= -10)
  338.             state += 10;           /* One ', possibly one " also */
  339.          else if (state < 0)
  340.             state -= 10;           /* 0 ', possibly one " */
  341.          else
  342.             state = -10;           /* No ' or " prior to this ' */
  343.       break;
  344.       case '"':
  345.          if (state == -11)
  346.             state = -10;           /* Both " and ' prior to this. */
  347.          else if (state == -10)
  348.             state = -11;           /* A ' prior to this. */
  349.          else if (state == -1)
  350.             state = 0;             /* A " prior to this. */
  351.          else
  352.             state = -1;            /* No ' or " prior to this " */
  353.       break;
  354.       }
  355.    }
  356.    return 0;
  357. }
  358.  
  359.  
  360. static char **wild_expand (const char *wild, char **argv,
  361.                                         int *argc, int extra, int flag)
  362. /*
  363.    Routine to expand wild into new arguments appended to the end
  364.    of argv[*argc].  This routine must realloc in order to make room
  365.    for the individual arguments and malloc for enough space for each
  366.    of the arguments.  The return value is a new **argv.
  367.  
  368.    Parameters:
  369.  
  370.          wild:  '\0' terminated string that needs to be expanded.
  371.  
  372.          argv:  initial starting address of the argv array.
  373.  
  374.          argc:  pointer to an integer that tells the current end of the
  375.                 argument list.
  376.  
  377.         extra:  The number of extra pointers that the returned argv
  378.                 must have available for future assignments.
  379.  
  380.          flag:  Flag indicating how to expand wild-card:
  381.                   0 - Complete file name expansion
  382.                   1 - only file name (no directory or version).
  383.                   2 - directory info and file name (no version)
  384.                   3 - file name and version info (no directory).
  385. */
  386. {
  387.    int more_to_go = 1, err, length, status, len_wild;
  388.    char *namptr; /* , *strncpy();  Picky, but bothers other main programs */
  389.    struct FAB fab_blk;
  390.    struct NAMBLK nam_blk;
  391.  
  392.    len_wild = strlen(wild);
  393.  
  394. /* Initialize all the fab and nam fields needed for parse and search */
  395.  
  396.    fab_blk = cc$rms_fab;                   /* Initialize FAB structure */
  397.  
  398.    nam_blk.nam    = cc$rms_nam;            /* Initialize NAM structure */
  399.    fab_blk.fab$l_dna = ".*";               /* Default file specif.     */
  400.    fab_blk.fab$b_dns = 2;                  /* Length of default spec.  */
  401.    fab_blk.fab$l_nam = &nam_blk.nam;       /* Set address of NAM in FAB*/
  402.    nam_blk.nam.nam$b_ess = NAM$C_MAXRSS;   /* Set extended  string size*/
  403.    nam_blk.nam.nam$l_esa = &nam_blk.es;    /* and address              */
  404.    nam_blk.nam.nam$b_rss = NAM$C_MAXRSS;   /* Set resultant string size*/
  405.    nam_blk.nam.nam$l_rsa = &nam_blk.rs;    /* and address              */
  406.    nam_blk.nam.nam$l_rlf = NULL;           /* No related file address  */
  407.  
  408.    fab_blk.fab$l_fna = wild;           /* Address of file name string  */
  409.    fab_blk.fab$b_fns = len_wild;       /* Length of file name string   */
  410.  
  411. /* Prepare to enter the search loop, parse fab. */
  412.    err = SYS$PARSE (&fab_blk);
  413.  
  414. /* Catch the directory not found error and return no files found. */
  415.    if (err != RMS$_NORMAL)
  416.       exit(err);
  417.  
  418.    while (more_to_go) {
  419.       err = SYS$SEARCH (&fab_blk);
  420.       if (err == RMS$_NMF || err == RMS$_FNF)
  421.          more_to_go = 0;               /* Done, no more files found */
  422.       else if (err != RMS$_NORMAL)
  423.          exit (err);
  424.       else {
  425.       /* Count that we now have this many arguments. */
  426.          (*argc)++;
  427.  
  428.       /* Make sure there is room for a new pointer. */
  429.          if ((argv = realloc (argv, (*argc + extra)*sizeof(char *))) == NULL) {
  430.             fprintf (stderr, "Not enough memory to expand argument list\n");
  431.             exit(ErrorExit);
  432.          }
  433.  
  434.       /* Move the right name into the list. */
  435.          switch (flag) {
  436.          case 0:             /* Complete file name */
  437.             length = nam_blk.nam.nam$b_rsl;
  438.             namptr = &nam_blk.rs;
  439.             break;
  440.          case 1:            /* File name only (no directory or version). */
  441.             length = nam_blk.nam.nam$b_name + nam_blk.nam.nam$b_type;
  442.             namptr = nam_blk.nam.nam$l_name;
  443.             break;
  444.          case 2:            /* directory and file name (no version) */
  445.             length = nam_blk.nam.nam$b_rsl - nam_blk.nam.nam$b_ver;
  446.             namptr = &nam_blk.rs;
  447.             break;
  448.          case 3:            /* File name and version (no directory). */
  449.             length = nam_blk.nam.nam$b_name +
  450.                      nam_blk.nam.nam$b_type +
  451.                      nam_blk.nam.nam$b_ver;
  452.             namptr = nam_blk.nam.nam$l_name;
  453.             break;
  454.          default:
  455.             fprintf (stderr, "illegal flag used in VMS expand call\n");
  456.             exit (ErrorExit);
  457.          }
  458.       /* Copy the requested string into the argument array. */
  459.          if ((argv[*argc-1] = malloc (length+1)) == NULL) {
  460.             fprintf (stderr, "Not enough memory to expand argument list\n");
  461.             exit (ErrorExit);
  462.          }
  463.          (void )strncpy (argv[*argc-1], namptr, length);
  464.          argv[*argc-1][length] = '\0';
  465.       }
  466.    }
  467.    return (argv);
  468. }
  469.  
  470. /* Remove all the defines that might affect the user's code. */
  471.  
  472. #undef redirect
  473. #undef filearg
  474. #undef expand
  475. #undef wild_found
  476. #undef wild_expand
  477.  
  478. #ifdef __TST_ECHO     /* Example code using expand(). */
  479.  
  480. # ifndef __FILE
  481. #include stdio
  482. #endif
  483.  
  484. main(argc, argv)
  485. int argc;
  486. char *argv[];
  487. /*
  488.    This main program allows you to run experiments with ``expand()''.
  489.    Try $ echo *.*, $ echo -f1 [-...]*.*, $ echo -f[0-3] *.*.
  490.    Questions about using "%", "\", etc. may be answered by testing
  491.    with this version of echo.
  492.  
  493.    To use this, of course, you need to link directly with expand--
  494.    avoiding the substitution of main with _user_main above.
  495. */
  496. {
  497.     int i, flag=0;
  498.     char **expand();
  499.  
  500.     for(i=1; i<argc; i++)
  501.         printf("%s %c", argv[i], (i<argc-1) ? '  ':'\n');
  502.  
  503.     if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f')
  504.        flag = atoi (&argv[1][2]);
  505.  
  506.     argv = expand (&argc, argv, flag);
  507.  
  508.     printf ("\n\n");
  509.     for(i=1; i<argc; i++)
  510.         printf("%s %c", argv[i], flag%2 == 0 ? '\n' : i%4 == 0 ? '\n':'\t');
  511.  
  512. }
  513. #endif  /* __TST_ECHO */
  514.