|
|
1.1 ! root 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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.