source: trunk/Jgraph/redexp.vms@ 445

Last change on this file since 445 was 418, checked in by Nicholas Riley, 16 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.