rpf%OVRO.Caltech.Edu@HAMLET.CALTECH.EDU (Ray Finch) (11/03/86)
I am writing a user interface program for radio telescopes and am trying to locate a piece of code to parse VMS file names. I would prefer C but we also have Fortran and Pascal compilers. I'm trying to avoid having figuring out the call to SYS$PARSE and would rather not have to open the file at this point in the program. Thanks in advance for any helpful responses. Ray Finch Owens Valley Radio Observatory P.O. Box 387 Big Pine, CA. 93513 (619)938-2828
LEICHTER-JERRY@YALE.ARPA (11/06/86)
I am writing a user interface program for radio telescopes and am trying to locate a piece of code to parse VMS file names. I would prefer C but we also have Fortran and Pascal compilers. I'm trying to avoid having figuring out the call to SYS$PARSE and would rather not have to open the file at this point in the program. Try the following (I didn't write it but I do use it). -- Jerry ---------------------------------Cut here------------------------------------- /*)LIBRARY */ /* fparse.c - fparse routine to emulate f$parse in DCL command files Synposis: char *fparse( file_name, default_name, related_name, field) char *file_name, default_name, related_name, field; Description: All strings are null-terminated. Only the first one or two characters are checked in field names. Parameters can be omitted by passing NULL. Fields are: "node" node name "device" device name "directory" directory name "name" file name "type" file type "version" file version number If the field parameter is null, all fields are expanded, except that the node name is included only if it appears in the file_name, default_name or related_name. Note that field parameters must be given in lower-case. Within each field, the expanded name is taken from the file_name, default_name and related_name, in that order. The value returned is the address of the null-terminated file name; fparse calls malloc to reserve space for the string. The string "" is returned on either an RMS parse error, or an erroroneous field parameter name. The RMS status error is not available. Only one field may be given. Example: To parse a command line LINK/EXE=exefile objfile, the default extension for "objfile" is .OBJ. The default file name for "exefile" is the file name of "objfile", and the default extension is .EXE. Say that the char * variable "objfile" points to the object file name from the command line, and the char * "exefile" points to the exe file from the command line. Then to expand these into file names for calls to open() or fopen(): objfile = fparse( objfile, ".OBJ", NULL, NULL); exefile = fparse( exefile, ".EXE", objfile, NULL); To find only the directory of the object file: dir = fparse( objfile, ".OBJ", NULL, "directory"); The field name could also be abbreviated: dir = fparse( objfile, ".OBJ", NULL, "di"); */ #include <stdio.h> #include <rms.h> #include <ssdef.h> char *fparse( file_name, default_name, related_name, field) char *file_name, *default_name, *related_name, *field; { struct FAB ff_file_fab; struct NAM fn_file_nam, rn_related_nam, *nam; char *expanded_name, *eptr; int expanded_length, rms_status; char expand_buffer[ NAM$C_MAXRSS]; /* initialize all the blocks for RMS */ ff_file_fab = cc$rms_fab; ff_file_fab.fab$l_nam = &fn_file_nam; ff_file_fab.fab$l_fna = file_name; ff_file_fab.fab$b_fns = (file_name == NULL) ? 0 : strlen( file_name); ff_file_fab.fab$l_dna = default_name; ff_file_fab.fab$b_dns = (default_name == NULL) ? 0 : strlen( default_name); fn_file_nam = cc$rms_nam; nam = &fn_file_nam; nam->nam$l_esa = expand_buffer; nam->nam$b_ess = NAM$C_MAXRSS; nam->nam$l_rlf = &rn_related_nam; rn_related_nam = cc$rms_nam; rn_related_nam.nam$l_rsa = related_name; rn_related_nam.nam$b_rsl = (related_name == NULL) ? 0 : strlen( related_name); /* call SYS$PARSE to parse the file name */ rms_status = sys$parse( &ff_file_fab); if (rms_status != RMS$_NORMAL) { /* error in parse, so return empty string */ expanded_name = malloc(1); expanded_name[0] = '\0'; return( expanded_name); } /* construct the expanded file name */ if (field == NULL || field[0] == '\0') { /* caller wants all fields */ expanded_length = nam->nam$b_esl; eptr = expand_buffer; } else { /* caller wants just one field */ switch (field[0]) { case 'n': /* node or name */ if (field[1] == 'o') { /* node */ expanded_length = nam->nam$b_node; eptr = nam->nam$l_node; } else { /* name */ expanded_length = nam->nam$b_name; eptr = nam->nam$l_name; } break; case 'd': /* device or directory */ if (field[1] == 'e') { /* device */ expanded_length = nam->nam$b_dev; eptr = nam->nam$l_dev; } else { /* directory */ expanded_length = nam->nam$b_dir; eptr = nam->nam$l_dir; } break; case 't': /* type */ expanded_length = nam->nam$b_type; eptr = nam->nam$l_type; break; case 'v': /* version */ expanded_length = nam->nam$b_ver; eptr = nam->nam$l_ver; break; default: expanded_length = 0; break; } } expanded_name = malloc( expanded_length + 1); strncpy( expanded_name, eptr, expanded_length); expanded_name[ expanded_length] = '\0'; return( expanded_name); } -------
McGuire_Ed@GRINNELL.MAILNET (11/06/86)
You're not going to find my answer much help, because I can't provide a piece of code to parse VMS file names, nor have I used SYS$PARSE. But I do feel motivated to provide some advice (it's free :-) Don't parse it yourself. Use the system services instead. What happens to your nice application program if DEC extends the syntax of filenames like they did in V4.0 (with long name and type) and V4.4 (is that right?) with the addition of the hyphen? Any user-written parsing code will probably trap an apparent error in the file name instead of handling the additional syntax!
LEICHTER-JERRY@YALE.ARPA (11/08/86)
In my previous posting on this subject, I included source code for a function called fparse() that did more or less what the DCL lexical function F$PARSE() does. Staring at the code inspired me to fix it up and extend it a bit. Below is a better - slightly incompatible! - version. (The test program calls a function called vms_etext, which is just a C interface to SYS$GETMSG, and is omitted.) -- Jerry ----------------------------------Cut here------------------------------------ /* * F P A R S E * * C-callable equivalent of the DCL F$PARSE lexical function. */ /*)LIBRARY */ #ifdef DOCUMENTATION title fparse Parse VMS file specification a la DCL's F$PARSE index Parse VMS file specification a la DCL's F$PARSE synopsis char * fparse(file_name,default_name,related_name,fields,options) char *file_name; /* Optional */ char *default_name; /* Optional */ char *related_name; /* Optional */ char *fields; /* Optional */ char *options; /* Optional */ extern noshare int fparse_dev; extern noshare int fparse_sdc; extern noshare int fparse_sts; extern noshare int fparse_stv; extern noshare int fparse_fnb; description fparse() provides a simple interface to the SYS$PARSE() service. It's interface is based on the DCL F$PARSE() lexical function. All strings are null-terminated. Parameters can be omitted by passing NULL; trailing parameters may be omitted completely. Fields are: "node" node name "device" device name "directory" directory name "name" file name "type" file type "version" file version number Options to control the parse: "noconceal" Don't conceal device name "pwd" Leave actual DECnet password in place "synchk" No I/O - check syntax only "ofp" Do an output file parse Any number of field names and options may be specified in any order, separated by commas. Only the first one or two characters of field and option names are checked (enough to identify the name uniquely). All field and options names must be given in lower case only. If the fields parameter is NULL or omitted, all fields are returned, except that the node name is included only if it appears explicitly. (This is the underlying behavior of SYS$PARSE.) The value returned is the address of the null-terminated expanded specification. fparse calls malloc() to reserve space for the string; you should free it with free() when you are done with it. NULL is returned on either an RMS parse error, or an erroneous field parameter name or option. The RMS status is is available in symbol fparse_sts; for a bad field or option, it will contain SS$_BADPARAM. If malloc() returns NULL, so does fparse(), but in this case fparse_sts will contain contain RMS$_NORMAL. Other values returned as a result of the parse are available as follows: FAB$L_DEV fparse_dev FAB$L_SDC fparse_sdc FAB$L_STS fparse_sts FAB$L_STV fparse_stv NAM$L_FNB fparse_fnb Wild-card context for the fab used is always discarded by fparse(); it cannot be used to set up the control blocks for a call to $SEARCH. Certain additional RMS fields - FAB$W_DID, for example - are not available. examples In determining the actual file specifications to use on a command line such as LINK/EXE=exefile objfile, the default type for "objfile" is .OBJ. The default file name for "exefile" is the file name of "objfile", and the default type is .EXE. If the (char *) variable "objfile" points to the object file name from the command line, and the (char *) "exefile" points to the exe file from the command line. Then to expand these into file names for calls to open() or fopen(): objfile = fparse(objfile,".OBJ"); exefile = fparse(exefile,".EXE",objfile,NULL,"ofp"); (The "ofp" option was specified to avoid copying anything but the name and type from the object file - the exe file should go, for example, to the current default directory, not the object file's directory.) To find only the device and directory of the object file: dir = fparse(objfile,".OBJ",NULL,"device,directory"); This could as well have been written: dir = fparse(objfile,".OBJ",NULL,"de,di"); bugs Since the various fparse_... fields are statically allocated, the code is not reentrant. authors Bob Messenger; re-written by Jerry Leichter #endif /* * Revision History * 1.0 ??-???-85 BM Invention * 2.0 6-Nov-86 JSL Extensive re-write; added options, ability * to specify multiple fields at once. Clean * up fab context before returning. Now returns * NULL, not an empty string, on errors, and * makes the various RMS values accessible. */ #include stdio #include rms #include ssdef #include varargs #define EOS '\0' static char *copyn(); extern char *malloc(); noshare int fparse_dev; noshare int fparse_sdc; noshare int fparse_sts; noshare int fparse_stv; noshare int fparse_fnb; char * fparse(va_alist) va_dcl { char *file; char *defaultn; char *related; char *fields; char *options; struct FAB fab; struct NAM nam; struct NAM rnam; char *result; int len; char expand_buf[NAM$C_MAXRSS]; int nargs; va_list va; int fbits; #define NODE 1 #define DEV 2 #define DIR 4 #define NAME 8 #define TYPE 16 #define VER 32 #define ALL (NODE|DEV|DIR|NAME|TYPE|VER) /* * Pick up the arguments, forcing all missing ones to NULL */ va_count(nargs); va_start(va); file = (nargs > 0) ? va_arg(va,char *) : NULL; defaultn = (nargs > 1) ? va_arg(va,char *) : NULL; related = (nargs > 2) ? va_arg(va,char *) : NULL; fields = (nargs > 3) ? va_arg(va,char *) : NULL; options = (nargs > 4) ? va_arg(va,char *) : NULL; va_end(va); /* * Initialize all the blocks for RMS */ fab = cc$rms_fab; fab.fab$l_nam = &nam; fab.fab$l_fna = file; fab.fab$b_fns = (file == NULL) ? 0 : strlen(file); fab.fab$l_dna = defaultn; fab.fab$b_dns = (defaultn == NULL) ? 0 : strlen(defaultn); nam = cc$rms_nam; nam.nam$l_esa = expand_buf; nam.nam$b_ess = NAM$C_MAXRSS; nam.nam$l_rlf = &rnam; rnam = cc$rms_nam; rnam.nam$l_rsa = related; rnam.nam$b_rsl = (related == NULL) ? 0 : strlen(related); fparse_sts = SS$_BADPARAM; /* Assume failure */ /* * Figure out what fields the user wants returned */ if (fields == NULL || *fields == EOS) fbits = ALL; else { fbits = 0; while (*fields != EOS) { switch (*fields) { case 'n': /* node or name */ if (*++fields == 'o') /* node */ fbits |= NODE; else if (*fields == 'a')/* name */ fbits |= NAME; else return(NULL); /* n? */ break; case 'd': /* device or directory */ if (*++fields == 'e') /* device */ fbits |= DEV; else if (*fields == 'i')/* directory */ fbits |= DIR; else return(NULL); /* d? */ break; case 't': /* type */ fbits |= TYPE; break; case 'v': /* version */ fbits |= VER; break; default: /* Unknown */ return(NULL); } while (*fields != ',' && *fields != EOS) fields++; if (*fields != EOS) fields++; } } /* * Scan options and set the appropriate RMS bits. */ if (options != NULL) { while (*options != EOS) { switch(*options) { case 'n': /* noconceal */ nam.nam$b_nop |= NAM$M_NOCONCEAL; break; case 'o': /* ofp */ fab.fab$l_fop |= FAB$M_OFP; break; case 'p': /* pwd */ nam.nam$b_nop |= NAM$M_PWD; break; case 's': /* synchk */ nam.nam$b_nop |= NAM$M_SYNCHK; break; default: /* unknown */ return(NULL); } while (*options != ',' && *options != EOS) options++; if (*options != EOS) options++; } } /* * call SYS$PARSE to parse the file name */ fparse_sts = SYS$PARSE(&fab); fparse_dev = fab.fab$l_dev; fparse_sdc = fab.fab$l_sdc; fparse_stv = fab.fab$l_stv; fparse_fnb = nam.nam$l_fnb; if (fparse_sts != RMS$_NORMAL) return(NULL); /* * Calculate the total length of the string we will be returning. * In the process, zero out the lengths of the fields we are not * returning to simplify the later copy code. */ len = 0; if (fbits & NODE) len += nam.nam$b_node; else nam.nam$b_node = 0; if (fbits & DEV) len += nam.nam$b_dev; else nam.nam$b_dev = 0; if (fbits & DIR) len += nam.nam$b_dir; else nam.nam$b_dir = 0; if (fbits & NAME) len += nam.nam$b_name; else nam.nam$b_name = 0; if (fbits & TYPE) len += nam.nam$b_type; else nam.nam$b_type = 0; if (fbits & VER) len += nam.nam$b_ver; else nam.nam$b_ver = 0; /* * Construct the expanded file name */ if ((result = malloc(len + 1)) != NULL) { char *p; p = copyn(result,nam.nam$l_node,nam.nam$b_node); p = copyn(p,nam.nam$l_dev,nam.nam$b_dev); p = copyn(p,nam.nam$l_dir,nam.nam$b_dir); p = copyn(p,nam.nam$l_name,nam.nam$b_name); p = copyn(p,nam.nam$l_type,nam.nam$b_type); p = copyn(p,nam.nam$l_ver,nam.nam$b_ver); *p++ = EOS; } /* * Release any space reserved by RMS for wild-card expansion */ nam.nam$b_nop = NAM$M_SYNCHK; fab.fab$b_dns = 0; nam.nam$l_rlf = NULL; SYS$PARSE(&fab); return(result); } /* * copyn n >= 0 characters from in to out; return (out + n), i.e., the next * free character after the characters copied. */ static char * copyn(out,in,n) char *out; char *in; int n; { while (n-- > 0) *out++ = *in++; return(out); } #ifdef TESTING /* * Simple test program */ main() { char a[200],b[200],c[200],d[200],e[200]; char *r; for (;;) { printf("file name: "); gets(a); printf("default name: "); gets(b); printf("related name: "); gets(c); printf("fields: "); gets(d); printf("options: "); gets(e); r = fparse(a,b,c,d,e); if (r == NULL) printf("[Null]\n"); else { puts(r); free(r); } printf("sts %X, stv %X, dev %X, sdc %X, fnb %X\n", fparse_sts, fparse_stv, fparse_dev, fparse_sdc, fparse_fnb); if (fparse_sts != RMS$_NORMAL) { printf("sts: %s\n",vms_etext(fparse_sts)); if (fparse_stv != 0) printf("stv: %s\n",vms_etext(fparse_stv)); } } } #endif -------