source: trunk/Jgraph/redexp.vms @ 662

Last change on this file since 662 was 418, checked in by Nicholas Riley, 12 years ago

Jgraph 8.3 from http://www.cs.utk.edu/~plank/plank/jgraph/jgraph.tar.gz

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