/*****************************************************************************/ /* ODS.c Module supports file system access for both ODS-2 and where appropriate (Alpha VMS v7.2ff) ODS-5. This supports transparent access to ODS-5 file system structure for the serving of data from the file-system. (The WASD package files however must still be supported inside of ODS-2 conventions.) Basically it does this by abstracting the NAM block so that either the standard or long formats may be used without other modules needing to be aware of the underlying structure. The VAX version does not need to know about long NAMs, or extended file specifications in general (excluded for code compactness and minor efficiency reasons using the ODS_EXTENDED macro). Alpha versions prior to 7.2 do not know about NAMLs and so for compilation in these environments a compatible NAML is provided (ENAMEL.H header file), allowing extended file specification compliant code to be compiled and linked on pre-v7.2 systems. Runtime decision structures based on the VMS version, device ACP type, etc., must be used if pre-v7.2 systems are to avoid using the NAML structure with RMS (which would of course result in runtime errors). In this way a single set of base-level Alpha object modules, built in either environment, will support both pre and post 7.2 environments. The essential accessability to and functionality of RMS is not buried too deeply by this wrapping. File operations are still performed using RMS data structures and services wherever possible, only those that process using NAM structures are, and need to be, abstracted in this way (i.e. sys$open(), sys$parse(), sys$search()). ASTs are called using the same conventions as the RMS service counterpart (i.e. with a pointer to the same data structure), and success should be checked using the same mechanisms, etc. Even when not supplying an AST address it is advised to supply the AST parameter (usually a request or task pointer). The reason? Well this is often placed into the FAB or RAB context storage for retrieval by an AST routine subsequently and explicitly called by the code. Experience has shown it's easy to forget to set up this context in the non-AST instance ;^) DISPLAYING FILE SPECIFICATIONS ------------------------------ As there are a number of ways of displaying VMS file names in mixed ODS-2 and ODS-5 environments this module seemed the appropriate place to discuss the WASD approach (although much of this processing is distributed through the MAPURL.C, DIR.C and UPD.C modules). Prior to the extended file specifications of VMS V7.2 and following WASD tended to default all file and directory paths to lower case (as this is commonly considered more aesthetically pleasing - and to the author). This is obvious in directory listings but may also be seen in UPD.C and SSI.C file naming. Optionally then DIR.C module would display specification in upper case (for instance if a semicolon was used in the URL path). Now that extended file specifications exist in Alpha VMS V7.2 and following there exist a number of methods of displaying file names. Files on ODS-2 volumes continue to be treated as described above. Files from ODS-5 volumes become more complex as a number of options are available. As ODS-5 supports a mixed upper and lower case character set WASD no longer forces case, names are displayed as stored on-disk. RMS displays many non-alpha-numeric characters using escape sequences, of which some have multiple, possible representations (e.g. the space, "^ ", "^_", "^20", and other possibles). In addition, a Web URL has it's own escape syntax for forbidden characters, the "%xx" hexadecimal representation. WASD attempts to accept file specifications in either RMS-escape syntax or URL-escape syntax. Presentation is most commonly done using URL-escape syntax, but may in directory listing be optionally displayed in RMS ("^_") and even on-disk format (" ") (see the DIR.C module). 16 BIT (UNICODE) FILE NAMES ARE NOT SUPPORTED! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VERSION HISTORY --------------- 11-AUG-2020 MGD bugfix; OdsDirectSearch() if wildcard specification return RMS$_NMF, otherwise RMS$_FNF (seems so elementary) 30-JUN-2020 MGD expand WASD_ODS_NODIRECT for comma-separated device names 28-NOV-2019 MGD bugfix; OdsDirectSearch() RMS$_FNF not RMS$_NMF (per JPP) 04-AUG-2019 MGD bugfix; OdsDirectSearch() only if not already on the block boundary add one to get to next, otherwise already there! 08-JAN-2019 MGD bugfix; OdsDirectSearch() (uint)0xffff && rlen < 508) 05-MAY-2018 MGD OdsDirectSearch() et.al. directory parse disabled by $ DEFINE /SYSTEM WASD_ODS_NODIRECT * OdsStructInit() formalises initialisation of ODS structure 25-JUN-2014 MGD bugfix; enabling SYSPRV ensure it is only disabled afterwards when it wasn't previously enabled 27-SEP-2013 MGD OdsNamBlockAst() on non-ODS_EXTENDED platforms (i.e. VAX) add Nam.nam$l_ver to Nam.nam$l_type and Nam.nam$l_name 06-JUN-2013 MGD OdsNamBlockAst() on non-ODS_EXTENDED platforms (i.e. VAX) tease-out system file name from Nam.nam$l_name and Nam.nam$l_type into odsptr->SysFileName buffer historically used by ODS-5 and munge for ODS-2 as well bugfix; OdsParseTerminate() on non-ODS_EXTENDED platforms (i.e. VAX) reset .nam$b_esl to changed expanded length or it can generate RMS$_ESL errors - check it out! 30-SEP-2011 MGD bugfix; OdsFileExists() parse NAM$M_NOCONCEAL in case of multi-valued, concealed logical devices and then convert returned status DNF into the functional equivalent FNF 13-MAR-2011 MGD OdsLoadTextFile() transmogrify embedded null char to space 23-JUN-2010 MGD bugfix; OdsNamBlockAst() odsptr->NamFileSysNamePtr always set to odsptr->SysFileName in case RMS$_FNF, etc. 08-JUN-2010 MGD OdsParseTerminate() allow for trailing period(s) bugfix; OdsNameOfDirectoryFile() allow for '^[' and '^]' 13-JUN-2009 MGD OdsNameOfDirectoryFile() delay return argument reset make cached name comparison case-sensitive (WebDAV) 18-JAN-2008 MGD OdsOpen() and OdsCreate() support STR_DSC OdsClose() refine FAB$M_DLT and FAB$M_TEF handling 10-AUG-2007 MGD OdsReallyADir() 25-MAY-2007 MGD OdsNameOfDirectoryFile() no longer mandatory that a directory file actually exists to generate the name OdsFileAcpInfo() add ATR$C_UCHAR to attributes retrieved bugfix; OdsNamBlockAst() deliver AST with 'AstParam' (requiring parameter changes to *lots* of AST functions called by use of OdsParse() and OdsSearch() - bugga!) 24-APR-2007 MGD OdsCreate() to allow explicit file creation bugfix; OdsNameOfDirectoryFile() allow for '^.' 02-OCT-2006 MGD OdsVolumeStructure() originally MapOdsVolumeStruct() 07-DEC-2002 MGD bugfix; in OdsNameOfDirectoryFile() use SYSPRV around sys$parse() to ensure access to directory 05-OCT-2002 MGD make OdsFileExists() a little more flexible 03-JUN-2002 MGD bugfix; ensure when OdsParse() is used successively with the same ODS structure that previous resources are first released (can present a problem unique to search lists) 05-JAN-2002 MGD bugfix; VAX OdsSearchNoConceal() expanded buffer 17-NOV-2001 MGD OdsSearchNoConceal() and modifications to OdsNamBlockAst() to support "charset=(file,charset)" processing 10-OCT-2001 MGD bugfix; sys$close() in OdsLoadTextFile() 04-AUG-2001 MGD OdsCopyStructure(), OdsFreeTextFile(), support module WATCHing, bugfix; NamFileSysNamePtr/Length in OdsFileAcpInfo() 28-FEB-2001 MGD OdsLoadTextFile(), OdsParseTextFile() 09-JUN-2000 MGD search-list processing refined 26-DEC-1999 MGD initial */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "ODS" #define ODS_PARSE_IN_USE_PATTERN 0xa5a55a5a /******************/ /* global storage */ /******************/ #if ODS_DIRECT BOOL OdsDirect, OdsExtended; char *OdsNoDirect; #else BOOL OdsExtended; #endif /********************/ /* external storage */ /********************/ extern int EfnWait, EfnNoWait, OpcomMessages; extern int ToLowerCase[], ToUpperCase[]; extern unsigned long SysPrvMask[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern MSG_STRUCT Msgs; extern SYS_INFO SysInfo; extern WATCH_STRUCT Watch; /****************************************************************************/ /* Set the ODS extended flag (i.e. supports ODS-5) if the architecture is Alpha and the VMS version is 7.2ff and not explicitly disabled at the command line. Check whether any ENAMEL.H NAML structure basically looks ok. */ OdsSetExtended () { char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsSetExtended()"); #ifdef ODS_EXTENDED #ifdef ENAMEL_NAML /* just a check for stupid errors on compilations using ENAMEL.H NAML */ if (sizeof(struct NAML) != NAML$C_BLN) { FaoToStdout ("%HTTPD-W-ENAMEL, NAML problem, \ extended file specifications not supported\n"); if (OpcomMessages & OPCOM_HTTPD) FaoToOpcom ("%HTTPD-W-ENAMEL, NAML problem, \ extended file specifications not supported\n"); OdsExtended = false; return; } #endif /* ENAMEL_NAML */ #endif /* ODS_EXTENDED */ #ifndef __VAX if (SysInfo.VersionInteger >= 720) OdsExtended = true; else #endif OdsExtended = false; FaoToStdout ("%HTTPD-I-ODS5, !AZsupported by !AZ VMS !AZ\n", OdsExtended ? "" : "not ", #ifdef __ALPHA "Alpha", #endif #ifdef __ia64 "IA64", #endif #ifdef __VAX "VAX", #endif SysInfo.Version); #if ODS_DIRECT OdsDirect = true; OdsNoDirect = SysTrnLnm("WASD_ODS_NODIRECT"); if (OdsNoDirect) if (OdsNoDirect[0] == '*') OdsDirect = false; else OdsNoDirect = strdup (OdsNoDirect); FaoToStdout ("%HTTPD-I-ODS, directory parser !AZ\n", OdsDirect ? "enabled" : "disabled"); if (OdsDirect && OdsNoDirect) FaoToStdout ("-HTTPD-I-ODS, exclude !AZ\n", OdsNoDirect); #endif /* ODS_DIRECT */ } /*****************************************************************************/ /* Initialise an ODS structure. If on the stack then zero the memory. Otherwise assume the structure is already in zero-allocated memory or equivalent. */ void OdsStructInit ( ODS_STRUCT *odsptr, BOOL OnStack ) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsStructInit() !&B", OnStack); if (OnStack) memset (odsptr, 0, sizeof(ODS_STRUCT)); } /****************************************************************************/ /* Generic open for read. Intended for configuration files, etc., but also used to read the raw content of a file in block mode. Completely synchronous with a RAB connected for sequential read. */ int OdsOpenReadOnly ( ODS_STRUCT *odsptr, char *FileSpec, BOOL BlockIO ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsOpenReadOnly() !&B !&B !&Z", OdsExtended, BlockIO, FileSpec); OdsStructInit (odsptr, true); odsptr->Fab = cc$rms_fab; if (BlockIO) odsptr->Fab.fab$b_fac = FAB$M_GET | FAB$M_BIO; else odsptr->Fab.fab$b_fac = FAB$M_GET; odsptr->Fab.fab$b_shr = FAB$M_SHRGET; #ifdef ODS_EXTENDED if (OdsExtended) { odsptr->Fab.fab$l_fna = -1; odsptr->Fab.fab$b_fns = 0; odsptr->Fab.fab$l_nam = &odsptr->Naml; odsptr->NamlInUse = true; ENAMEL_RMS_NAML(odsptr->Naml) odsptr->Naml.naml$l_long_filename = FileSpec; odsptr->Naml.naml$l_long_filename_size = strlen(FileSpec); odsptr->Naml.naml$l_long_expand = odsptr->ExpFileName; odsptr->Naml.naml$l_long_expand_alloc = sizeof(odsptr->ExpFileName)-1; odsptr->Naml.naml$l_long_result = odsptr->ResFileName; odsptr->Naml.naml$l_long_result_alloc = sizeof(odsptr->ResFileName)-1; odsptr->Naml.naml$l_filesys_name = odsptr->SysFileName; odsptr->Naml.naml$l_filesys_name_alloc = sizeof(odsptr->SysFileName)-1; } else #endif /* ODS_EXTENDED */ { odsptr->Fab.fab$l_fna = FileSpec; odsptr->Fab.fab$b_fns = strlen(FileSpec); odsptr->Fab.fab$l_nam = &odsptr->Nam; odsptr->NamlInUse = false; odsptr->Nam = cc$rms_nam; odsptr->Nam.nam$l_esa = odsptr->ExpFileName; odsptr->Nam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; odsptr->Nam.nam$l_rsa = odsptr->ResFileName; odsptr->Nam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } /* initialize the date, header and protection extended attribute blocks */ odsptr->Fab.fab$l_xab = &odsptr->XabDat; odsptr->XabDat = cc$rms_xabdat; odsptr->XabDat.xab$l_nxt = &odsptr->XabFhc; odsptr->XabFhc = cc$rms_xabfhc; odsptr->XabFhc.xab$l_nxt = &odsptr->XabPro; odsptr->XabPro = cc$rms_xabpro; odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; status = sys$open (&odsptr->Fab, 0, 0); if (VMSnok (status) || VMSnok (status = odsptr->Fab.fab$l_sts)) return (status); /* set up the generic information from the NAM(L) block */ odsptr->Fab.fab$l_ctx = odsptr; OdsNamBlockAst (&odsptr->Fab); /* record access block */ odsptr->Rab = cc$rms_rab; odsptr->Rab.rab$l_fab = &odsptr->Fab; /* 2 buffers of six blocks each */ odsptr->Rab.rab$b_mbc = 6; odsptr->Rab.rab$b_mbf = 2; /* read ahead performance option */ odsptr->Rab.rab$l_rop = RAB$M_RAH; /* all synchronous (for now anyway) */ odsptr->Rab.rab$l_rop &= ~FAB$M_ASY; if (BlockIO) { /* sequentially by block */ odsptr->Rab.rab$l_rop |= RAB$M_BIO; odsptr->Rab.rab$l_bkt = 0; } status = sys$connect (&odsptr->Rab, 0, 0); if (VMSnok (status) || VMSnok (status = odsptr->Rab.rab$l_sts)) { sys$close (&odsptr->Fab, 0, 0); return (status); } return (status); } /****************************************************************************/ /* RMS open the supplied file specification (using appropriate standard or long NAM structure). OdsNamBlockAst() is always called, which sets a number of pointers and other data from the NAM block so that calling functions do not need to know which was used. */ int OdsOpen ( ODS_STRUCT *odsptr, char *FileSpec, int FileSpecLength, char *DefaultSpec, int DefaultSpecLength, int FabFac, int FabFop, int FabShr, GENERAL_AST AstFunction, unsigned long AstParam ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsOpen() !&B !AZ !AZ !&A(!&X)", OdsExtended, FileSpecLength == -1 ? STR_DSC_PTR((STR_DSC*)FileSpec) : FileSpec, DefaultSpecLength == -1 ? STR_DSC_PTR((STR_DSC*)DefaultSpec) : DefaultSpec, AstFunction, AstParam); OdsStructInit (odsptr, true); odsptr->AstFunction = AstFunction; odsptr->AstParam = AstParam; if (FabFop & FAB$M_DLT) odsptr->DeleteOnClose = true; else odsptr->DeleteOnClose = false; if (FileSpecLength == -1) { /* use the WASD descriptor */ FileSpecLength = STR_DSC_LEN((STR_DSC*)FileSpec); FileSpec = STR_DSC_PTR((STR_DSC*)FileSpec); } else if (!FileSpecLength && FileSpec) FileSpecLength = strlen(FileSpec); if (DefaultSpecLength == -1) { /* use the WASD descriptor */ DefaultSpecLength = STR_DSC_LEN((STR_DSC*)DefaultSpec); DefaultSpec = STR_DSC_PTR((STR_DSC*)DefaultSpec); } else if (!DefaultSpecLength && DefaultSpec) DefaultSpecLength = strlen(DefaultSpec); odsptr->Fab = cc$rms_fab; odsptr->Fab.fab$l_ctx = odsptr; odsptr->Fab.fab$b_fac = FabFac; odsptr->Fab.fab$l_fop = FabFop; odsptr->Fab.fab$b_shr = FabShr; #ifdef ODS_EXTENDED if (OdsExtended) { odsptr->Fab.fab$l_fna = -1; odsptr->Fab.fab$b_fns = 0; odsptr->Fab.fab$l_nam = &odsptr->Naml; odsptr->NamlInUse = true; ENAMEL_RMS_NAML(odsptr->Naml) odsptr->Naml.naml$l_long_defname = DefaultSpec; odsptr->Naml.naml$l_long_defname_size = DefaultSpecLength; odsptr->Naml.naml$l_long_filename = FileSpec; odsptr->Naml.naml$l_long_filename_size = FileSpecLength; odsptr->Naml.naml$l_long_expand = odsptr->ExpFileName; odsptr->Naml.naml$l_long_expand_alloc = sizeof(odsptr->ExpFileName)-1; odsptr->Naml.naml$l_long_result = odsptr->ResFileName; odsptr->Naml.naml$l_long_result_alloc = sizeof(odsptr->ResFileName)-1; odsptr->Naml.naml$l_filesys_name = odsptr->SysFileName; odsptr->Naml.naml$l_filesys_name_alloc = sizeof(odsptr->SysFileName)-1; } else #endif /* ODS_EXTENDED */ { odsptr->Fab.fab$l_dna = DefaultSpec; odsptr->Fab.fab$b_dns = DefaultSpecLength; odsptr->Fab.fab$l_fna = FileSpec; odsptr->Fab.fab$b_fns = FileSpecLength; odsptr->Fab.fab$l_nam = &odsptr->Nam; odsptr->NamlInUse = false; odsptr->Nam = cc$rms_nam; odsptr->Nam.nam$l_esa = odsptr->ExpFileName; odsptr->Nam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; odsptr->Nam.nam$l_rsa = odsptr->ResFileName; odsptr->Nam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } /* initialize the date, header and protection extended attribute blocks */ odsptr->Fab.fab$l_xab = &odsptr->XabDat; odsptr->XabDat = cc$rms_xabdat; odsptr->XabDat.xab$l_nxt = &odsptr->XabFhc; odsptr->XabFhc = cc$rms_xabfhc; odsptr->XabFhc.xab$l_nxt = &odsptr->XabPro; odsptr->XabPro = cc$rms_xabpro; if (!AstFunction) { /* synchronous service */ odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; status = sys$open (&odsptr->Fab, 0, 0); OdsNamBlockAst (&odsptr->Fab); if (VMSok (status)) status = odsptr->Fab.fab$l_sts; return (status); } else { /* asynchronous service */ odsptr->Fab.fab$l_fop |= FAB$M_ASY; status = sys$open (&odsptr->Fab, &OdsNamBlockAst, &OdsNamBlockAst); return (status); } } /****************************************************************************/ /* RMS create the supplied file specification (using appropriate standard or long NAM structure). OdsNamBlockAst() is always called, which sets a number of pointers and other data from the NAM block so that calling functions do not need to know which was used. If 'FabPtr' is supplied this function attempts to clove the file attributes of the pointed to FAB. */ int OdsCreate ( ODS_STRUCT *odsptr, char *FileSpec, int FileSpecLength, char *DefaultSpec, int DefaultSpecLength, int FabFac, int FabFop, int FabShr, int FabRfm, int FabRat, struct FAB *FabPtr, GENERAL_AST AstFunction, unsigned long AstParam ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsCreate() !&B !AZ !AZ !&A(!&X)", OdsExtended, FileSpecLength == -1 ? STR_DSC_PTR((STR_DSC*)FileSpec) : FileSpec, DefaultSpecLength == -1 ? STR_DSC_PTR((STR_DSC*)DefaultSpec) : DefaultSpec, AstFunction, AstParam); OdsStructInit (odsptr, true); odsptr->AstFunction = AstFunction; odsptr->AstParam = AstParam; if (FabFop & FAB$M_DLT) odsptr->DeleteOnClose = true; else odsptr->DeleteOnClose = false; if (FileSpecLength == -1) { /* use the WASD descriptor */ FileSpecLength = STR_DSC_LEN((STR_DSC*)FileSpec); FileSpec = STR_DSC_PTR((STR_DSC*)FileSpec); } else if (!FileSpecLength && FileSpec) FileSpecLength = strlen(FileSpec); if (DefaultSpecLength == -1) { /* use the WASD descriptor */ DefaultSpecLength = STR_DSC_LEN((STR_DSC*)DefaultSpec); DefaultSpec = STR_DSC_PTR((STR_DSC*)DefaultSpec); } else if (!DefaultSpecLength && DefaultSpec) DefaultSpecLength = strlen(DefaultSpec); odsptr->Fab = cc$rms_fab; if (FabPtr) { /* 'clone' from supplied FAB */ odsptr->Fab.fab$l_alq = FabPtr->fab$l_alq; odsptr->Fab.fab$w_deq = FabPtr->fab$w_deq; odsptr->Fab.fab$b_bks = FabPtr->fab$b_bks; odsptr->Fab.fab$b_fac = FabPtr->fab$b_fac; odsptr->Fab.fab$l_fop = FabPtr->fab$l_fop; odsptr->Fab.fab$b_fsz = FabPtr->fab$b_fsz; odsptr->Fab.fab$w_gbc = FabPtr->fab$w_gbc; odsptr->Fab.fab$l_mrn = FabPtr->fab$l_mrn; odsptr->Fab.fab$w_mrs = FabPtr->fab$w_mrs; odsptr->Fab.fab$b_org = FabPtr->fab$b_org; odsptr->Fab.fab$b_rat = FabPtr->fab$b_rat; odsptr->Fab.fab$b_rfm = FabPtr->fab$b_rfm; odsptr->Fab.fab$b_shr = FabPtr->fab$b_shr; } if (FabFac) odsptr->Fab.fab$b_fac = FabFac; if (FabFop) odsptr->Fab.fab$l_fop = FabFop; if (FabShr) odsptr->Fab.fab$b_shr = FabShr; if (FabRfm) odsptr->Fab.fab$b_rfm = FabRfm; if (FabRat) odsptr->Fab.fab$b_rat = FabRat; odsptr->Fab.fab$l_ctx = odsptr; #ifdef ODS_EXTENDED if (OdsExtended) { odsptr->Fab.fab$l_fna = -1; odsptr->Fab.fab$b_fns = 0; odsptr->Fab.fab$l_nam = &odsptr->Naml; odsptr->NamlInUse = true; ENAMEL_RMS_NAML(odsptr->Naml) odsptr->Naml.naml$l_long_defname = DefaultSpec; odsptr->Naml.naml$l_long_defname_size = DefaultSpecLength; odsptr->Naml.naml$l_long_filename = FileSpec; odsptr->Naml.naml$l_long_filename_size = FileSpecLength; odsptr->Naml.naml$l_long_expand = odsptr->ExpFileName; odsptr->Naml.naml$l_long_expand_alloc = sizeof(odsptr->ExpFileName)-1; odsptr->Naml.naml$l_long_result = odsptr->ResFileName; odsptr->Naml.naml$l_long_result_alloc = sizeof(odsptr->ResFileName)-1; odsptr->Naml.naml$l_filesys_name = odsptr->SysFileName; odsptr->Naml.naml$l_filesys_name_alloc = sizeof(odsptr->SysFileName)-1; } else #endif /* ODS_EXTENDED */ { odsptr->Fab.fab$l_dna = DefaultSpec; odsptr->Fab.fab$b_dns = DefaultSpecLength; odsptr->Fab.fab$l_fna = FileSpec; odsptr->Fab.fab$b_fns = FileSpecLength; odsptr->Fab.fab$l_nam = &odsptr->Nam; odsptr->NamlInUse = false; odsptr->Nam = cc$rms_nam; odsptr->Nam.nam$l_esa = odsptr->ExpFileName; odsptr->Nam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; odsptr->Nam.nam$l_rsa = odsptr->ResFileName; odsptr->Nam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } /* initialize the date, header and protection extended attribute blocks */ odsptr->Fab.fab$l_xab = &odsptr->XabDat; odsptr->XabDat = cc$rms_xabdat; odsptr->XabDat.xab$l_nxt = &odsptr->XabFhc; odsptr->XabFhc = cc$rms_xabfhc; odsptr->XabFhc.xab$l_nxt = &odsptr->XabPro; odsptr->XabPro = cc$rms_xabpro; if (!AstFunction) { /* synchronous service */ odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; status = sys$create (&odsptr->Fab, 0, 0); OdsNamBlockAst (&odsptr->Fab); if (VMSok (status)) status = odsptr->Fab.fab$l_sts; return (status); } else { /* asynchronous service */ odsptr->Fab.fab$l_fop |= FAB$M_ASY; status = sys$create (&odsptr->Fab, &OdsNamBlockAst, &OdsNamBlockAst); return (status); } } /****************************************************************************/ /* Close the currently open file (with delete if required). If an AST function is supplied this is called regardless. */ int OdsClose ( ODS_STRUCT *odsptr, REQUEST_AST AstFunction, unsigned long AstParam ) { int status; unsigned long PrvPrv [2]; FAB_AST FabAstFunction; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsClose() !&B !&A(!&X)", odsptr->DeleteOnClose || odsptr->Fab.fab$l_fop & FAB$M_DLT, AstFunction, AstParam); odsptr->Fab.fab$l_ctx = AstParam; if (odsptr->DeleteOnClose || odsptr->Fab.fab$l_fop & FAB$M_DLT) { odsptr->Fab.fab$l_fop |= FAB$M_DLT; /* use SYSPRV to ensure deletion-on-close of file */ sys$setprv (1, &SysPrvMask, 0, &PrvPrv); } else if (odsptr->Fab.fab$l_fop & FAB$M_TEF) { /* use SYSPRV to ensure file truncate */ sys$setprv (1, &SysPrvMask, 0, &PrvPrv); } if (AstFunction) odsptr->Fab.fab$l_fop |= FAB$M_ASY; else odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; status = sys$close (&odsptr->Fab, AstFunction, AstFunction); if (odsptr->Fab.fab$l_fop & FAB$M_DLT || odsptr->Fab.fab$l_fop & FAB$M_TEF) if (!(PrvPrv[0] & PRV$M_SYSPRV)) sys$setprv (0, &SysPrvMask, 0, 0); return (status); } /****************************************************************************/ /* RMS parse the supplied file specification into an appropriate standard or long NAM structure. OdsNamBlockAst() is always called, which sets a number of pointers and other data from the NAM block so that calling functions do not need to know which was used. */ int OdsParse ( ODS_STRUCT *odsptr, char *FileSpec, int FileSpecLength, char *DefaultSpec, int DefaultSpecLength, int NamNop, GENERAL_AST AstFunction, unsigned long AstParam ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsParse() !&B !&Z !&Z !&A(!&X)", OdsExtended, FileSpec, DefaultSpec, AstFunction, AstParam); /* ensure for successive parses any previous resources are released */ OdsParseRelease (odsptr); OdsStructInit (odsptr, true); odsptr->AstFunction = AstFunction; odsptr->AstParam = AstParam; /* a pattern allows for less frequent false releases on uninited structs */ odsptr->ParseInUse = ODS_PARSE_IN_USE_PATTERN; if (!FileSpecLength && FileSpec) FileSpecLength = strlen(FileSpec); if (!DefaultSpecLength && DefaultSpec) DefaultSpecLength = strlen(DefaultSpec); odsptr->Fab = cc$rms_fab; odsptr->Fab.fab$l_ctx = odsptr; #ifdef ODS_EXTENDED if (OdsExtended) { odsptr->Fab.fab$l_fna = odsptr->Fab.fab$l_dna = -1; odsptr->Fab.fab$b_fns = odsptr->Fab.fab$b_dns = 0; odsptr->Fab.fab$l_nam = &odsptr->Naml; odsptr->NamlInUse = true; ENAMEL_RMS_NAML(odsptr->Naml) odsptr->Naml.naml$l_long_filename = FileSpec; odsptr->Naml.naml$l_long_filename_size = FileSpecLength; odsptr->Naml.naml$l_long_defname = DefaultSpec; odsptr->Naml.naml$l_long_defname_size = DefaultSpecLength; odsptr->Naml.naml$l_long_expand = odsptr->ExpFileName; odsptr->Naml.naml$l_long_expand_alloc = sizeof(odsptr->ExpFileName)-1; odsptr->Naml.naml$l_filesys_name = odsptr->SysFileName; odsptr->Naml.naml$l_filesys_name_alloc = sizeof(odsptr->SysFileName)-1; odsptr->Naml.naml$b_nop = NamNop; } else #endif /* ODS_EXTENDED */ { odsptr->Fab.fab$l_fna = FileSpec; odsptr->Fab.fab$b_fns = FileSpecLength; odsptr->Fab.fab$l_dna = DefaultSpec; odsptr->Fab.fab$b_dns = DefaultSpecLength; odsptr->Fab.fab$l_nam = &odsptr->Nam; odsptr->NamlInUse = false; odsptr->Nam = cc$rms_nam; odsptr->Nam.nam$l_esa = odsptr->ExpFileName; odsptr->Nam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; odsptr->Nam.nam$b_nop = NamNop; } if (!AstFunction) { /* synchronous service */ odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; status = sys$parse (&odsptr->Fab, 0, 0); OdsNamBlockAst (&odsptr->Fab); if (VMSok (status)) status = odsptr->Fab.fab$l_sts; return (status); } else { /* asynchronous service */ odsptr->Fab.fab$l_fop |= FAB$M_ASY; status = sys$parse (&odsptr->Fab, &OdsNamBlockAst, &OdsNamBlockAst); return (status); } } /****************************************************************************/ /* Do an RMS sys$search(). OdsNamBlockAst() is always called to set up generic pointers and other data of the various components of interest regardless of whether a standard or long NAM structure is in use. *** THIS FUNCTION IS (NO LONGER) SIGNIFICANTLY BROKEN *** It does not use the 'AstParam' parameter as was intended. It always delivers with a pointer to FAB. It should be fixed one day. (MGD 05-DEC-2000) FIXED 25-MAY-2007!! */ int OdsSearch ( ODS_STRUCT *odsptr, GENERAL_AST AstFunction, unsigned long AstParam ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsSearch() !&B !AZ !&A(!&X)", OdsExtended, odsptr->ExpFileName, AstFunction, AstParam); odsptr->AstFunction = AstFunction; odsptr->AstParam = AstParam; odsptr->Fab.fab$l_ctx = odsptr; #ifdef ODS_EXTENDED if (OdsExtended) { odsptr->Naml.naml$l_long_result = odsptr->ResFileName; odsptr->Naml.naml$l_long_result_alloc = sizeof(odsptr->ResFileName)-1; odsptr->Naml.naml$l_filesys_name = odsptr->SysFileName; odsptr->Naml.naml$l_filesys_name_alloc = sizeof(odsptr->SysFileName)-1; } else #endif /* ODS_EXTENDED */ { odsptr->Nam.nam$l_rsa = odsptr->ResFileName; odsptr->Nam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } if (!AstFunction) { /* synchronous service */ odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; #if ODS_DIRECT if (OdsDirect && !odsptr->DirectRetry) { status = OdsDirectSearch (odsptr); if (status == SS$_RETRY) { /* status to indicate the directory parser declines the call */ status = sys$search (&odsptr->Fab, 0, 0); OdsNamBlockAst (&odsptr->Fab); if (VMSok (status)) status = odsptr->Fab.fab$l_sts; } } else #endif /* ODS_DIRECT */ { status = sys$search (&odsptr->Fab, 0, 0); OdsNamBlockAst (&odsptr->Fab); if (VMSok (status)) status = odsptr->Fab.fab$l_sts; } return (status); } else { /* asynchronous service */ odsptr->Fab.fab$l_fop |= FAB$M_ASY; #if ODS_DIRECT if (OdsDirect && !odsptr->DirectRetry) { status = OdsDirectSearch (odsptr); if (status == SS$_RETRY) status = sys$search (&odsptr->Fab, &OdsNamBlockAst, &OdsNamBlockAst); } else #endif /* ODS_DIRECT */ status = sys$search (&odsptr->Fab, &OdsNamBlockAst, &OdsNamBlockAst); return (status); } } /****************************************************************************/ /* Performs the same functionality as OdsSearch() but using the NOCONCEAL option. This results in a concealed device/search list being resolved into actual file name in the result file name storage, but leave the expanded file untouched. OdsNamBlockAst() detects the absence of expanded file name storage and adjusts behaviour for terminating the result file name only. */ int OdsSearchNoConceal ( ODS_STRUCT *odsptr, GENERAL_AST AstFunction, unsigned long AstParam ) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsSearchNoConceal() !&B !AZ !&A(!&X)", OdsExtended, odsptr->ExpFileName, AstFunction, AstParam); odsptr->AstFunction = AstFunction; odsptr->AstParam = AstParam; odsptr->Fab.fab$l_ctx = odsptr; #ifdef ODS_EXTENDED if (OdsExtended) { odsptr->Naml.naml$b_nop |= NAM$M_NOCONCEAL; odsptr->Naml.naml$l_long_result = odsptr->ResFileName; odsptr->Naml.naml$l_long_result_alloc = sizeof(odsptr->ResFileName)-1; odsptr->Naml.naml$l_filesys_name = odsptr->SysFileName; odsptr->Naml.naml$l_filesys_name_alloc = sizeof(odsptr->SysFileName)-1; } else #endif /* ODS_EXTENDED */ { odsptr->Nam.nam$b_nop |= NAM$M_NOCONCEAL; odsptr->Nam.nam$l_rsa = odsptr->ResFileName; odsptr->Nam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } if (!AstFunction) { /* synchronous service */ odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; #if ODS_DIRECT if (OdsDirect && !odsptr->DirectRetry) { status = OdsDirectSearch (odsptr); if (status == SS$_RETRY) { /* status to indicate the directory parser declines the call */ status = sys$search (&odsptr->Fab, 0, 0); OdsNamBlockAst (&odsptr->Fab); if (VMSok (status)) status = odsptr->Fab.fab$l_sts; } } else #endif /* ODS_DIRECT */ { status = sys$search (&odsptr->Fab, 0, 0); OdsNamBlockAst (&odsptr->Fab); if (VMSok (status)) status = odsptr->Fab.fab$l_sts; } return (status); } else { /* asynchronous service */ odsptr->Fab.fab$l_fop |= FAB$M_ASY; #if ODS_DIRECT if (OdsDirect && !odsptr->DirectRetry) { status = OdsDirectSearch (odsptr); if (status == SS$_RETRY) status = sys$search (&odsptr->Fab, &OdsNamBlockAst, &OdsNamBlockAst); } else #endif /* ODS_DIRECT */ status = sys$search (&odsptr->Fab, &OdsNamBlockAst, &OdsNamBlockAst); return (status); } } /****************************************************************************/ /* Open/Parse/Search is complete. If successful set the generic pointers to the various NAM components of interest. If an AST has been provided declare that (can be called as an AST or directly). */ void OdsNamBlockAst (struct FAB *FabPtr) { int status; FAB_AST AstFunction; ODS_STRUCT *odsptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsNamBlockAst() !&F !&B !&S", &OdsNamBlockAst, OdsExtended, FabPtr->fab$l_sts); odsptr = (ODS_STRUCT*)FabPtr->fab$l_ctx; FabPtr->fab$l_ctx = odsptr->AstParam; if (VMSok (FabPtr->fab$l_sts) || FabPtr->fab$l_sts == RMS$_FNF || FabPtr->fab$l_sts == RMS$_DNF || FabPtr->fab$l_sts == RMS$_DEV) { /* file name has been parsed under these status */ #ifdef ODS_EXTENDED if (OdsExtended) { if (odsptr->Naml.naml$l_filesys_name && odsptr->Naml.naml$l_filesys_name != odsptr->SysFileName) ErrorNoticed (NULL, FabPtr->fab$l_sts, "naml$l_filesys_name", FI_LI); odsptr->NamNodePtr = odsptr->Naml.naml$l_long_node; odsptr->NamNodeLength = odsptr->Naml.naml$l_long_node_size; odsptr->NamDevicePtr = odsptr->Naml.naml$l_long_dev; odsptr->NamDeviceLength = odsptr->Naml.naml$l_long_dev_size; odsptr->NamDirectoryPtr = odsptr->Naml.naml$l_long_dir; odsptr->NamDirectoryLength = odsptr->Naml.naml$l_long_dir_size; odsptr->NamNamePtr = odsptr->Naml.naml$l_long_name; odsptr->NamNameLength = odsptr->Naml.naml$l_long_name_size; odsptr->NamTypePtr = odsptr->Naml.naml$l_long_type; odsptr->NamTypeLength = odsptr->Naml.naml$l_long_type_size; odsptr->NamVersionPtr = odsptr->Naml.naml$l_long_ver; odsptr->NamVersionLength = odsptr->Naml.naml$l_long_ver_size; odsptr->ExpFileNameLength = odsptr->Naml.naml$l_long_expand_size; odsptr->ResFileNameLength = odsptr->Naml.naml$l_long_result_size; odsptr->Nam_fnb = odsptr->Naml.naml$l_fnb; odsptr->NamFileSysNamePtr = odsptr->SysFileName; odsptr->NamFileSysNameLength = odsptr->Naml.naml$l_filesys_name_size; odsptr->NamFileSysNamePtr[odsptr->NamFileSysNameLength] = '\0'; odsptr->ExpFileName[odsptr->ExpFileNameLength] = '\0'; odsptr->ResFileName[odsptr->ResFileNameLength] = '\0'; } else #endif /* ODS_EXTENDED */ { odsptr->NamNodePtr = odsptr->Nam.nam$l_node; odsptr->NamNodeLength = odsptr->Nam.nam$b_node; odsptr->NamDevicePtr = odsptr->Nam.nam$l_dev; odsptr->NamDeviceLength = odsptr->Nam.nam$b_dev; odsptr->NamDirectoryPtr = odsptr->Nam.nam$l_dir; odsptr->NamDirectoryLength = odsptr->Nam.nam$b_dir; odsptr->NamNamePtr = odsptr->Nam.nam$l_name; odsptr->NamNameLength = odsptr->Nam.nam$b_name; odsptr->NamTypePtr = odsptr->Nam.nam$l_type; odsptr->NamTypeLength = odsptr->Nam.nam$b_type; odsptr->NamVersionPtr = odsptr->Nam.nam$l_ver; odsptr->NamVersionLength = odsptr->Nam.nam$b_ver; odsptr->ExpFileNameLength = odsptr->Nam.nam$b_esl; odsptr->ResFileNameLength = odsptr->Nam.nam$b_rsl; odsptr->Nam_fnb = odsptr->Nam.nam$l_fnb; odsptr->NamFileSysNamePtr = odsptr->SysFileName; odsptr->NamFileSysNameLength = odsptr->Nam.nam$b_name + odsptr->Nam.nam$b_type + odsptr->Nam.nam$b_ver; memcpy (odsptr->NamFileSysNamePtr, odsptr->Nam.nam$l_name, odsptr->NamFileSysNameLength); odsptr->NamFileSysNamePtr[odsptr->NamFileSysNameLength] = '\0'; odsptr->ExpFileName[odsptr->ExpFileNameLength] = '\0'; odsptr->ResFileName[odsptr->ResFileNameLength] = '\0'; } } if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!&Z !&Z !&Z", odsptr->ExpFileName, odsptr->ResFileName, odsptr->NamFileSysNamePtr); if (!odsptr->AstFunction) return; AstFunction = odsptr->AstFunction; odsptr->AstFunction = NULL; (AstFunction)(odsptr->AstParam); } /****************************************************************************/ /* Ensure parse internal data structures are released. */ OdsParseRelease (ODS_STRUCT *odsptr) { int status; char ExpFileName [16]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsParseRelease() !&B !8XL", OdsExtended, odsptr->ParseInUse); if (odsptr->ParseInUse != ODS_PARSE_IN_USE_PATTERN) return; odsptr->ParseInUse = 0; /* synchronous service */ odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; odsptr->Fab.fab$l_fna = "a:[b]c.d;"; odsptr->Fab.fab$b_fns = 9; #ifdef ODS_EXTENDED if (OdsExtended) { odsptr->Naml.naml$l_long_defname_size = 0; odsptr->Naml.naml$b_nop = NAM$M_SYNCHK; } else #endif /* ODS_EXTENDED */ { odsptr->Fab.fab$b_dns = 0; odsptr->Nam.nam$b_nop = NAM$M_SYNCHK; } status = sys$parse (&odsptr->Fab, 0, 0); if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!&S", status); #if ODS_DIRECT /* release any directory file (global) memory */ if (odsptr->DataPtr) { if (WATCH_CATEGORY(WATCH_INTERNAL)) WatchThis (WATCHALL, WATCH_INTERNAL, "DIRECTory called:!UL", odsptr->DirectCallCount); VmFree (odsptr->DataPtr, FI_LI); odsptr->DataPtr = NULL; odsptr->DataLength = 0; } #endif /* ODS_DIRECT */ } /****************************************************************************/ /* Place a terminating null in the 'ExpFileName' appropriate to whether it is a directory specific, file specification without version, or with version, and adjust '...Length's as necessary. This function can only be used after parse using OdsParse() or open using OdsOpen(). */ int OdsParseTerminate (ODS_STRUCT *odsptr) { char *cptr, *sptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsParseTerminate() !&B !&Z !&Z", OdsExtended, odsptr->ExpFileName, odsptr->NamFileSysNamePtr); if (!(odsptr->ExpFileNameLength || odsptr->ResFileNameLength)) return (SS$_ABORT); if (odsptr->NamNamePtr == odsptr->NamTypePtr && odsptr->NamTypeLength <= 1) { /* no name, no type; a directory specification (with possible version) */ if (odsptr->Nam_fnb & NAM$M_EXP_VER || odsptr->Nam_fnb & NAM$M_WILD_VER) { /* with version, remove that annoying single period */ sptr = (cptr = odsptr->NamNamePtr) + 1; while (*sptr) *cptr++ = *sptr++; *cptr = '\0'; odsptr->NamVersionPtr = odsptr->NamNamePtr; odsptr->NamTypeLength = 0; if (odsptr->NamFileSysNameLength) { sptr = (cptr = odsptr->NamFileSysNamePtr) + 1; while (*sptr) *cptr++ = *sptr++; *cptr = '\0'; odsptr->NamFileSysNameLength--; } } else { /* just lop it off at the directory terminator */ odsptr->NamNamePtr[0] = '\0'; odsptr->NamFileSysNamePtr = ""; odsptr->NamTypeLength = odsptr->NamVersionLength = odsptr->NamFileSysNameLength = 0; } } else if (odsptr->NamTypeLength <= 1) { /* name but no type (with possible version) */ if (odsptr->Nam_fnb & NAM$M_EXP_VER || odsptr->Nam_fnb & NAM$M_WILD_VER) { /* remove that annoying single period */ sptr = (cptr = odsptr->NamTypePtr) + 1; while (*sptr) *cptr++ = *sptr++; *cptr = '\0'; odsptr->NamVersionPtr = odsptr->NamTypePtr; odsptr->NamTypeLength = 0; if (odsptr->NamFileSysNameLength) { cptr = odsptr->NamFileSysNamePtr + odsptr->NamFileSysNameLength; while (cptr > odsptr->NamFileSysNamePtr && *cptr != '.' && !SAME2(cptr,'^.')) cptr--; if (*cptr == '.') { sptr = cptr + 1; while (*sptr) *cptr++ = *sptr++; *cptr = '\0'; odsptr->NamFileSysNameLength = cptr - odsptr->NamFileSysNamePtr; } } } else { if (!(odsptr->Nam_fnb & NAM$M_EXP_TYPE || odsptr->Nam_fnb & NAM$M_WILD_TYPE)) { /* no type and no version supplied */ (odsptr->NamVersionPtr = odsptr->NamTypePtr)[0] = '\0'; odsptr->NamTypeLength = odsptr->NamVersionLength = 0; } else { /* type is a period only! */ odsptr->NamVersionPtr[0] = '\0'; odsptr->NamVersionLength = 0; } if (odsptr->NamFileSysNameLength) { cptr = odsptr->NamFileSysNamePtr + odsptr->NamFileSysNameLength; while (cptr > odsptr->NamFileSysNamePtr && *cptr != '.' && !SAME2(cptr-1,'^.')) cptr--; if (*cptr == '.') { *cptr = '\0'; odsptr->NamFileSysNameLength = cptr - odsptr->NamFileSysNamePtr; } } } } else if (odsptr->Nam_fnb & NAM$M_EXP_VER || odsptr->Nam_fnb & NAM$M_WILD_VER) { /* a type and a version supplied */ odsptr->NamVersionPtr[odsptr->NamVersionLength] = '\0'; } else { /* a type but no version supplied */ odsptr->NamVersionPtr[0] = '\0'; odsptr->NamVersionLength = 0; if (odsptr->NamFileSysNameLength) { cptr = odsptr->NamFileSysNamePtr + odsptr->NamFileSysNameLength; while (cptr > odsptr->NamFileSysNamePtr && *cptr != ';' && !SAME2(cptr-1,'^;')) cptr--; if (*cptr == ';') { *cptr = '\0'; odsptr->NamFileSysNameLength = cptr - odsptr->NamFileSysNamePtr; } } } if (odsptr->ResFileNameLength) odsptr->ResFileNameLength = odsptr->NamVersionPtr - odsptr->ResFileName; else odsptr->ExpFileNameLength = odsptr->NamVersionPtr - odsptr->ExpFileName; #ifndef ODS_EXTENDED /* 06-JUN-2013 MGD VAX VMS V7.3 seems to require this (these) adjusted or it generates RMS$_ESL errors (!?) */ if (odsptr->ResFileNameLength) odsptr->Nam.nam$b_rsl = odsptr->ResFileNameLength; else odsptr->Nam.nam$b_esl = odsptr->ExpFileNameLength; #endif if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!&Z !&Z", odsptr->ExpFileName, odsptr->NamFileSysNamePtr); return (SS$_NORMAL); } /****************************************************************************/ /* Using the information in the ODS structure NAM field queue an ACP I/O to retrieve file information. Of course, this function can only be used after parse using OdsParse(). For simplicity retrieves all information of interest to all users of the function regardless of whether an individual user is interested in all information! Any AST routine *MUST* deassign the channel after use and before continuing. This function uses the ACP-QIO interface detailed in the "OpenVMS I/O User's Reference Manual", and is probably as fast as we can get for this type of file system functionality! */ int OdsFileAcpInfo ( ODS_STRUCT *odsptr, GENERAL_AST AstFunction, unsigned long AstParam ) { static $DESCRIPTOR (DeviceDsc, ""); int status; FILE_QIO *fqptr; ATRDEF *atptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsFileAcpInfo() !&B !&Z !&A(!&X)", OdsExtended, odsptr->NamDevicePtr, AstFunction, AstParam); fqptr = &odsptr->FileQio; /* assign a channel to the disk device containing the file */ DeviceDsc.dsc$a_pointer = odsptr->NamDevicePtr; DeviceDsc.dsc$w_length = odsptr->NamDeviceLength; status = sys$assign (&DeviceDsc, &fqptr->AcpChannel, 0, 0, 0); if (VMSnok (status)) { fqptr->IOsb.Status = status; if (!AstFunction) return (status); SysDclAst (AstFunction, AstParam); return (status); } /* set up the File Information Block for the ACP interface */ fqptr->FibDsc.dsc$w_length = sizeof(fqptr->Fib); fqptr->FibDsc.dsc$a_pointer = &fqptr->Fib; memset (&fqptr->Fib, 0, sizeof(fqptr->Fib)); fqptr->FileNameDsc.dsc$a_pointer = odsptr->NamFileSysNamePtr; fqptr->FileNameDsc.dsc$w_length = odsptr->NamFileSysNameLength; #ifdef ODS_EXTENDED if (OdsExtended) { memcpy (&fqptr->Fib.fib$w_did, &odsptr->Naml.naml$w_did, 6); fqptr->Fib.fib$b_name_format_in = FIB$C_ISO_LATIN; fqptr->Fib.fib$b_name_format_out = FIB$C_ISO_LATIN; fqptr->Fib.fib$w_nmctl = FIB$M_NAMES_8BIT; } else #endif /* ODS_EXTENDED */ memcpy (&fqptr->Fib.fib$w_did, &odsptr->Nam.nam$w_did, 6); atptr = &fqptr->FileAtr; atptr->atr$w_size = sizeof(fqptr->CdtTime64); atptr->atr$w_type = ATR$C_CREDATE; atptr->atr$l_addr = &fqptr->CdtTime64; atptr++; atptr->atr$w_size = sizeof(fqptr->RdtTime64); atptr->atr$w_type = ATR$C_REVDATE; atptr->atr$l_addr = &fqptr->RdtTime64; atptr++; atptr->atr$w_size = sizeof(fqptr->AtrUic); atptr->atr$w_type = ATR$C_UIC; atptr->atr$l_addr = &fqptr->AtrUic; atptr++; atptr->atr$w_size = sizeof(fqptr->AtrUchar); atptr->atr$w_type = ATR$C_UCHAR; atptr->atr$l_addr = &fqptr->AtrUchar; atptr++; atptr->atr$w_size = sizeof(fqptr->AtrFpro); atptr->atr$w_type = ATR$C_FPRO; atptr->atr$l_addr = &fqptr->AtrFpro; atptr++; atptr->atr$w_size = sizeof(fqptr->RecAttr); atptr->atr$w_type = ATR$C_RECATTR; atptr->atr$l_addr = &fqptr->RecAttr; atptr++; atptr->atr$w_size = atptr->atr$w_type = atptr->atr$l_addr = 0; if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!#AZ did:!UL,!UL,!UL {!UL}!-!#AZ", DeviceDsc.dsc$w_length, DeviceDsc.dsc$a_pointer, fqptr->Fib.fib$w_did[0], fqptr->Fib.fib$w_did[1], fqptr->Fib.fib$w_did[2], fqptr->FileNameDsc.dsc$w_length, fqptr->FileNameDsc.dsc$a_pointer); if (!AstFunction) { status = sys$qiow (EfnWait, fqptr->AcpChannel, IO$_ACCESS, &fqptr->IOsb, 0, 0, &fqptr->FibDsc, &fqptr->FileNameDsc, 0, 0, &fqptr->FileAtr, 0); sys$dassgn (fqptr->AcpChannel); fqptr->AcpChannel = 0; if (VMSok (status)) status = fqptr->IOsb.Status; } else status = sys$qio (EfnNoWait, fqptr->AcpChannel, IO$_ACCESS, &fqptr->IOsb, AstFunction, AstParam, &fqptr->FibDsc, &fqptr->FileNameDsc, 0, 0, &fqptr->FileAtr, 0); if (VMSnok (status)) { fqptr->IOsb.Status = status; if (AstFunction) SysDclAst (AstFunction, AstParam); } return (status); } /****************************************************************************/ /* Using the information in the ODS structure NAM field queue an ACP I/O to modify selected file information. This function can only be used after parse using OdsParse(). Any AST *MUST* deassign the channel after use and before continuing. This function uses the ACP-QIO interface detailed in the "OpenVMS I/O User's Reference Manual", and is probably as fast as we can get for this type of file system functionality! */ int OdsFileAcpModify ( ODS_STRUCT *odsptr, unsigned short *ProtectionMaskPtr, unsigned short *VersionLimitPtr, GENERAL_AST AstFunction, unsigned long AstParam ) { static $DESCRIPTOR (DeviceDsc, ""); int status; FILE_QIO *fqptr; ATRDEF *atptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsFileAcpModify() !&B !&X:!4XL !&X:!UL !&A(!&X)", OdsExtended, ProtectionMaskPtr, !ProtectionMaskPtr ? 0 : *ProtectionMaskPtr, VersionLimitPtr, !VersionLimitPtr ? 0 : *VersionLimitPtr, AstFunction, AstParam); fqptr = &odsptr->FileQio; /* assign a channel to the disk device containing the file */ DeviceDsc.dsc$a_pointer = odsptr->NamDevicePtr; DeviceDsc.dsc$w_length = odsptr->NamDeviceLength; status = sys$assign (&DeviceDsc, &fqptr->AcpChannel, 0, 0, 0); if (VMSnok (status)) { fqptr->IOsb.Status = status; if (!AstFunction) return (status); SysDclAst (AstFunction, AstParam); return (status); } /* set up the File Information Block for the ACP interface */ fqptr->FibDsc.dsc$w_length = sizeof(fqptr->Fib); fqptr->FibDsc.dsc$a_pointer = &fqptr->Fib; memset (&fqptr->Fib, 0, sizeof(fqptr->Fib)); #ifdef ODS_EXTENDED if (OdsExtended) { fqptr->FileNameDsc.dsc$a_pointer = odsptr->Naml.naml$l_filesys_name; fqptr->FileNameDsc.dsc$w_length = odsptr->Naml.naml$l_filesys_name_size; memcpy (&fqptr->Fib.fib$w_did, &odsptr->Naml.naml$w_did, 6); fqptr->Fib.fib$b_name_format_in = FIB$C_ISO_LATIN; fqptr->Fib.fib$b_name_format_out = FIB$C_ISO_LATIN; fqptr->Fib.fib$w_nmctl = FIB$M_NAMES_8BIT; } else #endif /* ODS_EXTENDED */ { fqptr->FileNameDsc.dsc$a_pointer = odsptr->NamNamePtr; fqptr->FileNameDsc.dsc$w_length = odsptr->NamNameLength + odsptr->NamTypeLength + odsptr->NamVersionLength; memcpy (&fqptr->Fib.fib$w_did, &odsptr->Nam.nam$w_did, 6); } if (VersionLimitPtr) fqptr->Fib.fib$w_verlimit = *VersionLimitPtr; atptr = &fqptr->FileAtr; if (ProtectionMaskPtr) { atptr->atr$w_size = sizeof(*ProtectionMaskPtr); atptr->atr$w_type = ATR$C_FPRO; atptr->atr$l_addr = ProtectionMaskPtr; atptr++; } atptr->atr$w_size = atptr->atr$w_type = atptr->atr$l_addr = 0; if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!#AZ did:!UL,!UL,!UL {!UL}!-!#AZ", DeviceDsc.dsc$w_length, DeviceDsc.dsc$a_pointer, fqptr->Fib.fib$w_did[0], fqptr->Fib.fib$w_did[1], fqptr->Fib.fib$w_did[2], fqptr->FileNameDsc.dsc$w_length, fqptr->FileNameDsc.dsc$a_pointer); if (!AstFunction) { status = sys$qiow (EfnWait, fqptr->AcpChannel, IO$_MODIFY, &fqptr->IOsb, 0, 0, &fqptr->FibDsc, &fqptr->FileNameDsc, 0, 0, &fqptr->FileAtr, 0); sys$dassgn (fqptr->AcpChannel); fqptr->AcpChannel = 0; if (VMSok (status)) status = fqptr->IOsb.Status; } else status = sys$qio (EfnNoWait, fqptr->AcpChannel, IO$_MODIFY, &fqptr->IOsb, AstFunction, AstParam, &fqptr->FibDsc, &fqptr->FileNameDsc, 0, 0, &fqptr->FileAtr, 0); if (VMSnok (status)) { fqptr->IOsb.Status = status; if (AstFunction) SysDclAst (AstFunction, AstParam); } return (status); } /*****************************************************************************/ /* Return success status if the specified file name exists in the specified directory, error otherwise. This function completes synchronously! */ int OdsFileExists ( char *Directory, char *FileName ) { int status, DirectoryLength, FileNameLength; char ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1]; struct FAB SearchFab; struct NAM SearchNam; #ifdef ODS_EXTENDED struct NAML SearchNaml; #endif /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsFileExists() !&B !&Z !&Z", OdsExtended, Directory, FileName); if (Directory) DirectoryLength = strlen(Directory); else DirectoryLength = 0; if (FileName) FileNameLength = strlen(FileName); else FileNameLength = 0; SearchFab = cc$rms_fab; SearchFab.fab$l_fop = FAB$M_NAM; /* synchronous service */ SearchFab.fab$l_fop &= ~FAB$M_ASY; #ifdef ODS_EXTENDED if (OdsExtended) { SearchFab.fab$l_fna = SearchFab.fab$l_dna = -1; SearchFab.fab$b_fns = SearchFab.fab$b_dns = 0; SearchFab.fab$l_nam = &SearchNaml; ENAMEL_RMS_NAML(SearchNaml) SearchNaml.naml$b_nop = NAM$M_NOCONCEAL; SearchNaml.naml$l_long_defname = Directory; SearchNaml.naml$l_long_defname_size = DirectoryLength; SearchNaml.naml$l_long_filename = FileName; SearchNaml.naml$l_long_filename_size = FileNameLength; SearchNaml.naml$l_long_expand = ExpFileName; SearchNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1; } else #endif /* ODS_EXTENDED */ { SearchFab.fab$l_dna = Directory; SearchFab.fab$b_dns = DirectoryLength; SearchFab.fab$l_fna = FileName; SearchFab.fab$b_fns = FileNameLength; SearchFab.fab$l_nam = &SearchNam; SearchNam = cc$rms_nam; SearchNam.nam$b_nop = NAM$M_NOCONCEAL; SearchNam.nam$l_esa = ExpFileName; SearchNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; } status = sys$parse (&SearchFab, 0, 0); if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "sys$parse() !&S", status); if (VMSok (status)) { status = sys$search (&SearchFab, 0, 0); if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "sys$search() !&S", status); if (status == RMS$_DNF) status = RMS$_FNF; } /* release parse and search internal data structures */ SearchFab.fab$l_fna = "a:[b]c.d;"; SearchFab.fab$b_fns = 9; SearchFab.fab$b_dns = 0; #ifdef ODS_EXTENDED if (OdsExtended) { SearchNaml.naml$l_long_result = 0; SearchNaml.naml$b_nop = NAM$M_SYNCHK; } else #endif /* ODS_EXTENDED */ { SearchNam.nam$l_rlf = 0; SearchNam.nam$b_nop = NAM$M_SYNCHK; } sys$parse (&SearchFab, 0, 0); return (status); } /*****************************************************************************/ /* Given a file or directory specification in 'FileName' generate the file name of the directory file. (e.g. "DEVICE:[DIR1]DIR2.DIR" from "DEVICE:[DIR1.DIR2]FILE.EXT", "DEVICE:[DIR1]DIR2.DIR" from "DEVICE:[DIR1.DIR2]", and "DEVICE:[000000]DIR1.DIR" from "DEVICE:[DIR1]", etc.) Attempts to improve the perfomance of this function by storing the previous file specification processed and the previous directory file name result. If this file specification directory part is the same as the previous directory part then just return the previous result! */ int OdsNameOfDirectoryFile ( char *FileName, int FileNameLength, char *DirFileNamePtr, int *DirFileLengthPtr ) { static int DirFileNameBufferLength = 0; static char FileNameBuffer [ODS_MAX_FILE_NAME_LENGTH+1], DirFileNameBuffer [ODS_MAX_FILE_NAME_LENGTH+1]; int status; unsigned long Nam_fnb; unsigned long PrvPrv [2]; char *cptr, *fptr, *sptr, *zptr; char ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1]; struct FAB ParseFab; struct NAM ParseNam; #ifdef ODS_EXTENDED struct NAML ParseNaml; #endif /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsNameOfDirectoryFile() !&B !&Z !&Z !&Z", OdsExtended, FileName, FileNameBuffer, DirFileNameBuffer); if (!FileNameLength) FileNameLength = strlen(FileName); /* check if this has the same directory components as the last one */ sptr = FileNameBuffer; cptr = FileName; while (*cptr == *sptr && (*sptr != ']' || SAME2(sptr-1,'^]')) && (*cptr != ']' || SAME2(cptr-1,'^]'))) { *sptr++; *cptr++; } if (*cptr == ']' && *sptr == ']' && *(sptr-1) != '.' && *(cptr-1) != '.') { /* it does! return the result of the last one */ strcpy (DirFileNamePtr, DirFileNameBuffer); *DirFileLengthPtr = DirFileNameBufferLength; return (SS$_NORMAL); } ParseFab = cc$rms_fab; #ifdef ODS_EXTENDED if (OdsExtended) { ParseFab.fab$l_fna = -1; ParseFab.fab$b_fns = 0; ParseFab.fab$l_nam = &ParseNaml; ENAMEL_RMS_NAML(ParseNaml) ParseNaml.naml$l_long_filename = FileName; ParseNaml.naml$l_long_filename_size = FileNameLength; ParseNaml.naml$l_long_expand = ExpFileName; ParseNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1; ParseNaml.naml$b_nop = NAM$M_NOCONCEAL | NAM$M_SYNCHK; /* use SYSPRV to ensure access to directory */ sys$setprv (1, &SysPrvMask, 0, &PrvPrv); status = sys$parse (&ParseFab, 0, 0); if (!(PrvPrv[0] & PRV$M_SYSPRV)) sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { DirFileNamePtr[*DirFileLengthPtr = 0] = '\0'; return (status); } ParseNaml.naml$l_long_ver[ParseNaml.naml$l_long_ver_size] = '\0'; fptr = ParseNaml.naml$l_long_name - 1; } else #endif /* ODS_EXTENDED */ { ParseFab.fab$l_fna = FileName; ParseFab.fab$b_fns = FileNameLength; ParseFab.fab$l_nam = &ParseNam; ParseNam = cc$rms_nam; ParseNam.nam$l_esa = ExpFileName; ParseNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; ParseNam.nam$b_nop = NAM$M_NOCONCEAL | NAM$M_SYNCHK; /* use SYSPRV to ensure access to directory */ sys$setprv (1, &SysPrvMask, 0, &PrvPrv); status = sys$parse (&ParseFab, 0, 0); if (!(PrvPrv[0] & PRV$M_SYSPRV)) sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { DirFileNamePtr[*DirFileLengthPtr = 0] = '\0'; return (status); } ParseNam.nam$l_ver[ParseNam.nam$b_ver] = '\0'; fptr = ParseNam.nam$l_name - 1; } #ifdef ODS_EXTENDED if (OdsExtended) Nam_fnb = ParseNaml.naml$l_fnb; else #endif /* ODS_EXTENDED */ Nam_fnb = ParseNam.nam$l_fnb; if (Nam_fnb & NAM$M_SEARCH_LIST) { /* search list, look for the actual file/directory */ status = sys$search (&ParseFab, 0, 0); if (VMSok (status)) status = ParseFab.fab$l_sts; /* if the directory doesn't actually exist then that's OK too! */ if (status == RMS$_FNF) status = SS$_NORMAL; if (VMSnok (status)) { /* release parse and search internal data structures */ ParseFab.fab$l_fna = "a:[b]c.d;"; ParseFab.fab$b_fns = 9; ParseFab.fab$b_dns = 0; #ifdef ODS_EXTENDED if (OdsExtended) { ParseNaml.naml$l_long_result = 0; ParseNaml.naml$b_nop = NAM$M_SYNCHK; } else #endif /* ODS_EXTENDED */ { ParseNam.nam$l_rlf = 0; ParseNam.nam$b_nop = NAM$M_SYNCHK; } sys$parse (&ParseFab, 0, 0); return (status); } } if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!&Z", ExpFileName); while (fptr > ExpFileName) { if (*fptr == '[' && !SAME2(fptr-1,'^[')) break; if (*fptr == '.' && !SAME2(fptr-1,'^.')) break; fptr--; } if (fptr > ExpFileName && fptr[-1] == ']') { /* concealed, logical device */ fptr -= 2; if (MATCH10 (fptr, ".][000000]")) { fptr--; while (fptr > ExpFileName && *fptr != '[' && *fptr != '.' && fptr[-1] != '^') fptr--; } } zptr = (sptr = DirFileNameBuffer) + sizeof(DirFileNameBuffer); if (MATCH8 (fptr, "[000000]") || MATCH0 (fptr, "[000000.]", 9)) { #ifdef ODS_EXTENDED if (OdsExtended) { for (cptr = ParseNaml.naml$l_long_dev; cptr < fptr && sptr < zptr; *sptr++ = *cptr++); } else #endif /* ODS_EXTENDED */ { for (cptr = ParseNam.nam$l_dev; cptr < fptr && sptr < zptr; *sptr++ = *cptr++); } for (cptr = "[000000]000000.DIR"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr >= zptr) sptr--; *sptr = '\0'; } else if (*fptr == '[') { #ifdef ODS_EXTENDED if (OdsExtended) { for (cptr = ParseNaml.naml$l_long_dev; cptr < fptr && sptr < zptr; *sptr++ = *cptr++); } else #endif /* ODS_EXTENDED */ { for (cptr = ParseNam.nam$l_dev; cptr < fptr && sptr < zptr; *sptr++ = *cptr++); } for (cptr = "[000000]"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (fptr[0] == '.' && fptr[1] == ']') fptr += 3; else fptr++; while (*fptr && *fptr != '.' && *fptr != ']' && sptr < zptr) { if (*fptr == '^') *sptr++ = *fptr++; if (sptr < zptr) *sptr++ = *fptr++; } for (cptr = ".DIR"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr >= zptr) sptr--; *sptr = '\0'; } else { #ifdef ODS_EXTENDED if (OdsExtended) { for (cptr = ParseNaml.naml$l_long_dev; cptr < fptr && sptr < zptr; *sptr++ = *cptr++); } else #endif /* ODS_EXTENDED */ { for (cptr = ParseNam.nam$l_dev; cptr < fptr && sptr < zptr; *sptr++ = *cptr++); } if (sptr < zptr) *sptr++ = ']'; if (fptr[0] == '.' && fptr[1] == ']') fptr += 3; else fptr++; while (*fptr && *fptr != ']' && *fptr != '.' && sptr < zptr) { if (*fptr == '^') *sptr++ = *fptr++; if (sptr < zptr) *sptr++ = *fptr++; } for (cptr = ".DIR"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr >= zptr) sptr--; *sptr = '\0'; } /* buffer this (soon to be the previous) file name */ strcpy (FileNameBuffer, FileName); /* copy out the generated directory file name */ strcpy (DirFileNamePtr, DirFileNameBuffer); *DirFileLengthPtr = DirFileNameBufferLength = sptr - DirFileNameBuffer; if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "{!UL}!-!#AZ", *DirFileLengthPtr, DirFileNamePtr); /* release parse and search internal data structures */ ParseFab.fab$l_fna = "a:[b]c.d;"; ParseFab.fab$b_fns = 9; ParseFab.fab$b_dns = 0; #ifdef ODS_EXTENDED if (OdsExtended) { ParseNaml.naml$l_long_result = 0; ParseNaml.naml$b_nop = NAM$M_SYNCHK; } else #endif /* ODS_EXTENDED */ { ParseNam.nam$l_rlf = 0; ParseNam.nam$b_nop = NAM$M_SYNCHK; } sys$parse (&ParseFab, 0, 0); if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!AZ !AZ", FileNameBuffer, DirFileNameBuffer); return (SS$_NORMAL); } /*****************************************************************************/ /* ODS (FILES-11) directory parser. Two orders of magnitude faster than $SEARCH and NO repeated elevation to EXEC mode. At the first call takes an in-memory snapshot of the directory file content and retains that, parsing the content, for the duration of the search. The memory is released by OdsParseRelease(). Successive calls parse the next directory name from the directory file and return as would OdsSearch(). Returns SS$_RETRY to indicate the parser declines to process the initial call and the caller should return to using $SEARCH (reasons obvious in code). Otherwise returns a VMS success or non-success status code. Always allow for the extended specification escape character when copying file specification elements. This is not a syntax check function and will present no issue with a non-extended file specification. THE FOLLOWING ARE NOT MY WORDS: A files-11 directory file holds a sequence of records. There is one record for each filename in the directory. Each record consists of a HEADER, followed by one or more SUBRECORDs - one SUBRECORD for each version of that file. The HEADER is laid out as follows: word:size size of this record, excluding itself word:verlimit version limit for this file byte:flags flags : 0 = DIR$C_FID = usual case byte:namecount # of characters in next field byte:name[namecount] filename byte:pad null byte, if reqd, to pad to next word boundary The SUBRECORDs are laid out as follows: word:version version number six-byte:fid fid of this version The SUBRECORDs are ordered by decreasing version number. Records are dense within each 512-byte block of the directory file (ie, there are no 'gaps' between records). However, not all blocks of the directory file need be full. The 'end-of-data' for a block is marked by a HEADER whose size field is 0xFFFF. Finally, a file may well have so many versions that there is not enough room within a 512-byte block to hold the entire record for that filename. Say the first block had enough room for versions 1000 down to 950 for filename "A.A". The Files-11 XQP inserts a 0xFFFF 'end-of-block', at that point, then starts afresh in the next block with a HEADER, for the same filename, "A.A", but with SUBRECORDs continuing for version 944 and downwards. [put another way, records don't span block boundaries] Very finally, a HEADER record must have at least one following VERSION subrecord in that block. Backup code assumes this to hold true. If it doesn't hold true, then typical symptoms are failures in trying to access FOO.BAR;-1 when there's only one version of that file. Here's a tiny example, of a directory which holds just 3 files, called A.A;5, A.A;2 and MINE.DAT;6 ... record HEADER word:size 0018 word:verlimit 7fff byte:flags 00 = DIR$C_FID = regular case byte:namecount 03 byte:name[namecount] 412e41 = 'A.A' byte:pad 00 SUBRECORD-1 word:version 0005 six-byte:fid 1524-0049-0000 = (5412,73,0) SUBRECORD-2 word:version 0002 six-byte:fid 16bc-003f-0000 = (5820,63,0) record HEADER word:size 0014 word:verlimit 7fff byte:flags 00 = DIR$C_FID = regular case byte:namecount 08 byte:name[namecount] 4d494e452e444154 = 'MINE.DAT' byte:pad none required SUBRECORD-1 word:version 0006 six-byte:fid 190f-0057-0000 = (6415,87,0) record HEADER word:size ffff => end-of-block */ #if ODS_DIRECT int OdsDirectSearch (ODS_STRUCT *odsptr) { static char VersionString [16]; static $DESCRIPTOR (VersionStringDsc, VersionString); static $DESCRIPTOR (VersionFaoDsc, ";!UL\0"); BOOL WildMatch; uint status, flags, length, nlen, rlen, vlim, vnum, EmptyBlockCount; char ch; char *cptr, *czptr, *dzptr, *fptr, *rptr, *rzptr, *sptr, *zptr; uchar *nptr; uchar fid [6]; char DeviceName [64+1]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsDirectSearch() !&X", odsptr); /* this bogus status indicates the parser declines the call */ if (!OdsDirect) { odsptr->DirectRetry = true; return (SS$_RETRY); } InstanceGblSecIncrLong (&AccountingPtr->OdsDirectCallCount); odsptr->DirectCallCount++; /* this function only handles single, flat directories */ if (((odsptr->Nam_fnb & NAM$M_SEARCH_LIST) != 0) || ((odsptr->Nam_fnb & NAM$M_WILD_DIR) != 0)) { InstanceGblSecIncrLong (&AccountingPtr->OdsDirectRetryCount); /* only do this the once when the initial call declined */ odsptr->DirectRetry = true; return (SS$_RETRY); } if (!odsptr->DataPtr) { /**************/ /* initialise */ /**************/ if (OdsNoDirect) { zptr = (sptr = DeviceName) + sizeof(DeviceName)-1; czptr = (cptr = odsptr->ExpFileName) + odsptr->ExpFileNameLength; while (cptr < czptr && sptr < zptr && *cptr != ':') *sptr++ = *cptr++; if (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!&Z !&Z", DeviceName, OdsNoDirect); /* list of comma-separated device names */ cptr = OdsNoDirect; while (*cptr) { sptr = cptr; while (*cptr && *cptr != ',') cptr++; ch = *cptr; *cptr = '\0'; if (!strcasecmp (DeviceName, sptr)) { *cptr = ch; sptr = NULL; break; } if (*cptr = ch) cptr++; } if (!sptr) { /* hit one of the device names in the logical name value */ InstanceGblSecIncrLong (&AccountingPtr->OdsDirectRetryCount); odsptr->DirectRetry = true; return (SS$_RETRY); } } InstanceGblSecIncrLong (&AccountingPtr->OdsDirectLoadCount); /* pre-fill result file name up to and including directory */ zptr = (sptr = odsptr->ResFileName) + sizeof(odsptr->ResFileName)-1; czptr = (cptr = odsptr->ExpFileName) + odsptr->ExpFileNameLength; while (cptr < czptr && sptr < zptr && *cptr != ']') { if (MATCH10(cptr,".][000000]")) { cptr += 9; continue; } if (SAME2(cptr,'.]')) { *sptr++ = *cptr++; cptr += 2; continue; } if (*cptr == '^') *sptr++ = *cptr++; if (cptr < czptr && sptr < zptr) *sptr++ = *cptr++; } if (cptr < czptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (cptr < czptr && sptr < zptr) { /* fill various pointers and lengths */ length = sptr - odsptr->ResFileName; czptr = (cptr = odsptr->ResFileName) + length; odsptr->NamNodePtr = cptr; odsptr->NamNodeLength = 0; odsptr->NamDevicePtr = cptr; while (cptr < czptr && *cptr != ':') cptr++; while (cptr < czptr) cptr++; odsptr->NamDeviceLength = cptr - odsptr->NamDevicePtr; odsptr->NamDirectoryPtr = cptr; while (cptr < czptr && *cptr != ']') { if (*cptr == '^') cptr++; if (cptr < czptr) cptr++; } while (cptr < czptr) cptr++; odsptr->NamDirectoryLength = cptr - odsptr->NamDirectoryPtr; /* the parsed file name will be appended to this pointer */ odsptr->NamNamePtr = cptr; /* reproduce the pre-fill in the expanded file name */ zptr = (sptr = odsptr->ExpFileName) + sizeof(odsptr->ExpFileName)-1; czptr = (cptr = odsptr->ResFileName) + (int)odsptr->NamNamePtr; for (cptr = odsptr->ResFileName; cptr < czptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; /* searches for highest version and *.DIR may be optimised */ odsptr->DirectDotDir = odsptr->DirectVersion0 = odsptr->DirectVersionHit0 = false; odsptr->DirectWildcard[0] = '\0'; zptr = (sptr = odsptr->DirectWildcard) + sizeof(odsptr->DirectWildcard)-1; for (cptr = odsptr->NamFileSysNamePtr; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (!odsptr->DirectWildcard[0]) odsptr->DirectVersion0 = true; else if (MATCH7 (odsptr->DirectWildcard, "*.DIR;")) { /* optimise WASD's "Index of" DirBeginDirectories() */ odsptr->DirectDotDir = odsptr->DirectVersion0 = true; odsptr->DirectWildcard[0] = '\0'; } else if (MATCH5 (odsptr->DirectWildcard, "*.*;") || MATCH6 (odsptr->DirectWildcard, "*.*;0")) { odsptr->DirectVersion0 = true; odsptr->DirectWildcard[0] = '\0'; } else if (MATCH6 (odsptr->DirectWildcard, "*.*;*")) odsptr->DirectWildcard[0] = '\0'; else if (*(sptr-1) == ';') { odsptr->DirectVersion0 = true; strcat (odsptr->DirectWildcard, "*"); } else if (*(USHORTPTR)(sptr-2) == ';0') odsptr->DirectVersion0 = true; else if (!odsptr->NamVersionLength) { odsptr->DirectVersion0 = true; strcat (odsptr->DirectWildcard, ";*"); } if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "dotdir:!&B version0:!&B dotdir:!&B wildcard:!&Z", odsptr->DirectDotDir, odsptr->DirectVersion0, odsptr->DirectDotDir, odsptr->DirectWildcard); /* read the directory file into (global) memory */ status = OdsLoadDirect (odsptr, odsptr->NamDevicePtr); if (VMSnok (status)) { if (status != SS$_NOSUCHFILE) ErrorNoticed (NULL, status, odsptr->NamDevicePtr, FI_LI); return (SS$_RETRY); } odsptr->DirectTheLength = odsptr->DirectTheRecord = odsptr->DirectSubRecord = 0; if (WATCH_CATEGORY(WATCH_INTERNAL)) WatchThis (WATCHALL, WATCH_INTERNAL, "DIRECTory !UL blocks !UL bytes version0:!&B dotdir:!&B !&Z !&Z", (odsptr->DataLength / 512) + 1, odsptr->DataLength, odsptr->DirectVersion0, odsptr->DirectDotDir, odsptr->NamDevicePtr, odsptr->DirectWildcard); } else status = SS$_RESULTOVF; } else status = SS$_NORMAL; /* loop(s) just to allow convenient /break;/s from the processing */ for (;;) { /*******************/ /* parse directory */ /*******************/ /* if an error from the initialisation */ if (VMSnok (status)) break; EmptyBlockCount = 0; if (odsptr->DirectSubRecord) rptr = odsptr->DataPtr + odsptr->DirectSubRecord; else rptr = odsptr->DataPtr + odsptr->DirectTheRecord; rzptr = odsptr->DataPtr + odsptr->DirectTheRecord + odsptr->DirectTheLength; dzptr = odsptr->DataPtr + odsptr->DataLength; if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!UL !UL !UL !UL !UL !UL !UL !UL", odsptr->DataPtr, odsptr->DataLength, odsptr->DirectTheRecord, odsptr->DirectTheLength, odsptr->DirectSubRecord, rptr, rzptr, dzptr); for (;;) { /* if just parsing subrecords (versions) */ if (odsptr->DirectSubRecord) break; for (;;) { rlen = (uint)*(USHORTPTR)rptr; rptr += sizeof(ushort); if (WATCH_MODULE(WATCH_MOD_ODS)) { if (rlen == (uint)0xffff) WatchThis (WATCHALL, WATCH_MOD_ODS, "0xFFFF end of record"); else { WatchThis (WATCHALL, WATCH_MOD_ODS, "RECORD block:!UL byte:!UL rlen:!UL", ((rptr - odsptr->DataPtr) / 512) + 1, ((rptr - odsptr->DataPtr) % 512), rlen); WatchDataDump (rptr, rlen); } } /* if not end of records (in this block) sentinal */ if (rlen && rlen != (uint)0xffff && rlen < 508) { /* records are always on even byte (word) boundaries */ rzptr = rptr + rlen; if ((int)rzptr & 1) rzptr++; /* break from loop to process record */ EmptyBlockCount = 0; break; } else if (rlen && rlen != (uint)0xffff && rlen > 508) { status = SS$_BUGCHECK; ErrorNoticed (NULL, status, "block:!UL byte:!UL length:!UL !&Z", FI_LI, ((rptr - odsptr->DataPtr) / 512) + 1, ((rptr - odsptr->DataPtr) % 512), rlen, odsptr->NamDevicePtr); InstanceGblSecIncrLong (&AccountingPtr->OdsDirectBugcheckCount); break; } else if (EmptyBlockCount++ >= 32) { status = SS$_BUGCHECK; ErrorNoticed (NULL, status, odsptr->NamDevicePtr, FI_LI); InstanceGblSecIncrLong (&AccountingPtr->OdsDirectBugcheckCount); break; } /* step the record pointer to the next block */ rptr -= odsptr->DataPtr; /* if not already on the block boundary add one to get to next */ if ((ulong)rptr % 512) rptr = (((ulong)rptr / 512) + 1) * 512; else rptr = ((ulong)rptr / 512) * 512; rptr += (ulong)odsptr->DataPtr; if (WATCH_MODULE(WATCH_MOD_ODS)) { WatchThis (WATCHALL, WATCH_MOD_ODS, "DISK block:!UL byte:!UL", ((rptr - odsptr->DataPtr) / 512) + 1, ((rptr - odsptr->DataPtr) % 512)); WatchDataDump (rptr, 512); } /* continue to parse if not at end of content */ if (rptr < dzptr) continue; /* end of records (file names) */ odsptr->DirectTheLength = odsptr->DirectTheRecord = odsptr->DirectSubRecord = 0; if (odsptr->DirectWildcard[0]) status = RMS$_NMF; else status = RMS$_FNF; break; } if (VMSnok (status)) break; /****************/ /* got a record */ /****************/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "rlen:!UL", rlen); vlim = *(USHORTPTR)rptr; rptr += sizeof(ushort); flags = *(uchar*)rptr++; flags &= 0x07; nlen = *(uchar*)rptr++; nptr = rptr; rptr += nlen; if ((int)rptr & 1) rptr++; if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "flags:!2XL !UL !#AZ", flags, nlen, nlen, nptr); if (flags) { rptr = rzptr; continue; } /** Andrew C. Goldstein VAX/VMS Software Devlopment 11-Jan-1985 https://web-docs.gsi.de/~kraemer/COLLECTION/VMS/ods2.txt 4.2.3 DIR$B_FLAGS - 1 Byte Flags This byte contains the type code of the directory entry and assorted flag bits. It contains the following subfields and status bits: DIR$V_TYPE The type code is contained in the three low bits of the flags byte. It is one of the following values: DIR$C_FID The value field is a list of version numbers and 48 bit File Id's. DIR$C_LINKNAME The value field is a symbolic link string (i.e., an alphabetic file specification). [Symbolic links are not yet supported by any implementation of Files-11.] [No status bits are defined at present.] DIR$C_FID = 0; ! normal file ID DIR$C_LINKNAME = 1; ! symbolic name ! Filename encoding codes. DIR$C_ODS2 = 0; ! ODS-2 legal ASCII DIR$C_ISL1 = 1; ! ODS-2 illegal ASCII or ISO LATIN-1 DIR$$_TYPE_RESERVED = 2; DIR$C_UCS2 = 3; ! Unicode USC-2 (16 bit characters) **/ /* note the current record */ odsptr->DirectTheRecord = rptr - odsptr->DataPtr; odsptr->DirectTheLength = rzptr - rptr; odsptr->DirectSubRecord = 0; /* buffer the on-disk file name */ zptr = (sptr = odsptr->SysFileName) + sizeof(odsptr->SysFileName)-1; while (nlen-- && sptr < zptr) *sptr++ = *nptr++; *sptr = '\0'; odsptr->NamFileSysNamePtr = odsptr->SysFileName; odsptr->NamFileSysNameLength = sptr - odsptr->SysFileName; /* note where the version begins */ odsptr->DirectSysFileNameLength = odsptr->NamFileSysNameLength; if (sptr >= zptr) { status = SS$_RESULTOVF; ErrorNoticed (NULL, status, odsptr->SysFileName, FI_LI); InstanceGblSecIncrLong (&AccountingPtr->OdsDirectBugcheckCount); } break; } if (VMSnok (status)) break; if (odsptr->DirectVersion0 && !odsptr->DirectSubRecord) { /****************************/ /* only most-recent version */ /****************************/ /* note current file name in case spans multiple blocks */ if (odsptr->NamFileSysNameLength != odsptr->DirectCurrentLength || !strsame (odsptr->NamFileSysNamePtr, odsptr->DirectCurrent, -1)) { zptr = (sptr = odsptr->DirectCurrent) + sizeof(odsptr->DirectCurrent)-1; nlen = odsptr->NamFileSysNameLength; nptr = odsptr->NamFileSysNamePtr; while (nlen-- && sptr < zptr) *sptr++ = *nptr++; *sptr = '\0'; if (sptr >= zptr) { status = SS$_RESULTOVF; ErrorNoticed (NULL, status, odsptr->DirectCurrent, FI_LI); InstanceGblSecIncrLong (&AccountingPtr->OdsDirectBugcheckCount); } odsptr->DirectCurrentLength = sptr - odsptr->DirectCurrent; odsptr->DirectVersionHit0 = false; } else if (odsptr->DirectVersionHit0) { /* same name and already hit version zero so skip this record */ odsptr->DirectTheRecord = rzptr - odsptr->DataPtr; odsptr->DirectTheLength = odsptr->DirectSubRecord = 0; continue; } else odsptr->DirectVersionHit0 = true; } if (VMSok (status)) { /*******************/ /* version and FID */ /*******************/ vnum = *(USHORTPTR)rptr; rptr += sizeof(ushort); memcpy (fid, rptr, sizeof(fid)); rptr += sizeof(fid); if (rptr < rzptr) { /* next sub-record (version) */ odsptr->DirectSubRecord = rptr - odsptr->DataPtr; } else { /* subrecords exhausted, next record please Mr Music */ odsptr->DirectTheLength = odsptr->DirectSubRecord = 0; odsptr->DirectTheRecord = rptr - odsptr->DataPtr; } } if (VMSok (status)) { /**************************/ /* version 0 already hit? */ /**************************/ if (odsptr->DirectVersionHit0) continue; } if (VMSok (status)) { /***********************/ /* report this version */ /***********************/ #ifdef ODS_EXTENDED if (OdsExtended) memcpy (&odsptr->Naml.naml$w_fid, &fid, sizeof(fid)); else #endif /* ODS_EXTENDED */ memcpy (&odsptr->Nam.nam$w_fid, &fid, sizeof(fid)); sys$fao (&VersionFaoDsc, 0, &VersionStringDsc, vnum); /* append the version number */ zptr = (sptr = odsptr->SysFileName) + sizeof(odsptr->SysFileName)-1; sptr += odsptr->DirectSysFileNameLength; for (cptr = VersionString; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; odsptr->NamFileSysNameLength = sptr - odsptr->SysFileName; if (sptr >= zptr) { status = SS$_RESULTOVF; ErrorNoticed (NULL, status, odsptr->SysFileName, FI_LI); InstanceGblSecIncrLong (&AccountingPtr->OdsDirectBugcheckCount); } } if (VMSnok (status)) break; if (odsptr->DirectDotDir) { /* small optimisation when looking only for directories */ if (*(sptr-1) != '1' || *(sptr-2) != ';' || TOUP(*(sptr-3)) != 'R' || TOUP(*(sptr-4)) != 'I' || TOUP(*(sptr-5)) != 'D' || *(sptr-6) != '.') continue; } else if (odsptr->DirectWildcard[0]) { /******************/ /* match wildcard */ /******************/ WildMatch = StringMatchAndRegex (NULL, odsptr->SysFileName, odsptr->DirectWildcard, SMATCH_GREEDY, NULL, NULL); if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "WILDCARD !&Z !&Z !AZ", odsptr->SysFileName, odsptr->DirectWildcard, WildMatch ? "YES" : "NO"); if (!WildMatch) continue; } break; } if (VMSok (status)) { /************************/ /* complete result name */ /************************/ /* append the pre-filled elements with the name.type;version */ zptr = odsptr->ResFileName + sizeof(odsptr->ResFileName)-1; sptr = odsptr->NamNamePtr; odsptr->NamVersionPtr = odsptr->NamTypePtr = NULL; /* escape non-ODS-2 characters (after all, if not there not ODS-5!) */ nptr = odsptr->NamFileSysNamePtr; for (nlen = odsptr->NamFileSysNameLength; nlen && sptr < zptr; nlen--) { if ((*nptr >= 'A' && *nptr <= 'Z') || (*nptr >= 'a' && *nptr <= 'z') || (*nptr >= '0' && *nptr <= '9') || (*nptr == '$' || *nptr == '-' || *nptr == '_') || (*nptr >= 0xa0 && *nptr <= 0xff)) *(uchar*)sptr++ = *nptr++; else { if (*nptr == '.') { /* look ahead for a following period */ czptr = nptr + nlen; for (cptr = nptr+1; cptr < czptr && *cptr != '.'; cptr++); if (cptr < czptr) { /* found a following period so escape this one */ *sptr++ = '^'; } else { /* no following period so end of name and begining of type */ odsptr->NamNameLength = sptr - odsptr->NamNamePtr; odsptr->NamTypePtr = sptr; } } else /* semicolon permitted in names so only after a type period */ if (*nptr == ';' && odsptr->NamTypePtr) { /* version delimiting semicolon so end of type */ if (!odsptr->NamTypePtr) odsptr->NamTypePtr = sptr; odsptr->NamTypeLength = sptr - odsptr->NamTypePtr; odsptr->NamVersionPtr = sptr; } else *sptr++ = '^'; if (sptr < zptr) *sptr++ = *nptr++; } } *sptr = '\0'; if (!odsptr->NamTypePtr) { odsptr->NamTypePtr = sptr; odsptr->NamTypeLength = sptr - odsptr->NamTypePtr; } if (!odsptr->NamVersionPtr) odsptr->NamVersionPtr = sptr; odsptr->NamVersionLength = sptr - odsptr->NamVersionPtr; odsptr->ResFileNameLength = sptr - odsptr->ResFileName; /* also append the resultant file name to the pre-filled expanded name */ zptr = (sptr = odsptr->ExpFileName) + sizeof(odsptr->ExpFileName)-1; sptr += (odsptr->NamNamePtr - odsptr->ResFileName); czptr = odsptr->ResFileName + odsptr->ResFileNameLength; for (cptr = odsptr->ResFileName; cptr < czptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; odsptr->ExpFileNameLength = sptr - odsptr->ExpFileName; if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "fid:(!UL,!UL,!UL) (!UL)!AZ (!UL)!AZ (!UL)!#AZ", (fid[0] + (fid[1] << 8) + (fid[5] << 16)), (fid[2] + (fid[3] << 8)), (fid[4]), odsptr->ExpFileNameLength, odsptr->ExpFileName, odsptr->ResFileNameLength, odsptr->ResFileName, odsptr->NamFileSysNameLength, odsptr->NamFileSysNameLength, odsptr->NamFileSysNamePtr); if (odsptr->DirectVersion0) odsptr->DirectVersionHit0 = true; } if (VMSnok (status)) { if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!&S", status); /* release any directory file (global) memory */ if (odsptr->DataPtr) { if (WATCH_CATEGORY(WATCH_INTERNAL)) WatchThis (WATCHALL, WATCH_INTERNAL, "DIRECTory called:!UL", odsptr->DirectCallCount); VmFree (odsptr->DataPtr, FI_LI); odsptr->DataPtr = NULL; odsptr->DataLength = 0; } } /*******************/ /* deliver any AST */ /*******************/ odsptr->Fab.fab$l_sts = status; if (odsptr->AstFunction) { SysDclAst (odsptr->AstFunction, odsptr->AstParam); odsptr->AstFunction = odsptr->AstParam = NULL; } return (status); } #endif /* ODS_DIRECT */ /*****************************************************************************/ /* Called by AdminReportServerStats(). */ #if ODS_DIRECT char* OdsDirectReport (REQUEST_STRUCT* rqptr) { static buf [96]; int average; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsDirectReport()"); if (AccountingPtr->OdsDirectLoadCount) average = (AccountingPtr->OdsDirectCallCount - AccountingPtr->OdsDirectRetryCount) / AccountingPtr->OdsDirectLoadCount; else average = 0; FaoToBuffer (buf, sizeof(buf), NULL, "ODS directory parser:\ !&L ÷ !&L = !&L  (!&L, !&L)\n", AccountingPtr->OdsDirectCallCount, AccountingPtr->OdsDirectLoadCount, average, AccountingPtr->OdsDirectRetryCount, AccountingPtr->OdsDirectBugcheckCount); return (buf); } #endif /* ODS_DIRECT */ /*****************************************************************************/ /* Load the specified directory name into dynamically allocated memory. 'DataPtr' and 'DataLength' are set to reflect the file contents. 'DataPtr' needs to be freed when finished with. */ #if ODS_DIRECT int OdsLoadDirect ( ODS_STRUCT *odsptr, char *DirectName ) { int status, DirFileNameLength; char DirFileName [ODS_MAX_FILE_NAME_LENGTH+1]; ODS_STRUCT LoadOds; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsLoadDirect() !&Z", DirectName); /* release any directory file (global) memory */ if (odsptr->DataPtr) { VmFree (odsptr->DataPtr, FI_LI); odsptr->DataPtr = NULL; odsptr->DataLength = 0; } status = OdsNameOfDirectoryFile (DirectName, strlen(DirectName), DirFileName, &DirFileNameLength); if (VMSnok (status)) return (status); OdsStructInit (&LoadOds, true); /* ensure we can read this directory */ sys$setprv (1, &SysPrvMask, 0, 0); /* OdsParse() NAM$M_NOCONCEAL before OdsSearchNoConceal() */ OdsParse (&LoadOds, NULL, 0, DirFileName, DirFileNameLength, NAM$M_NOCONCEAL, NULL, NULL); if (VMSok (status = OdsReallyADir (NULL, &LoadOds))) { if (VMSok (status = OdsLoadContent (&LoadOds, DirFileName))) { /* purloin the loaded directory content */ odsptr->DataPtr = LoadOds.DataPtr; odsptr->DataLength = LoadOds.DataLength; LoadOds.DataPtr = NULL; LoadOds.DataLength = 0; OdsParseRelease (&LoadOds); if (0 && WATCH_MODULE(WATCH_MOD_ODS)) WatchDataDump (odsptr->DataPtr, odsptr->DataLength); } } sys$setprv (0, &SysPrvMask, 0, 0); return (status); } #endif /* ODS_DIRECT */ /*****************************************************************************/ /* Load the specified file name raw content into dynamically allocated memory. 'DataPtr' and 'DataLength' are set to reflect the file contents. 'DataPtr' needs to be freed when finished with. */ int OdsLoadContent ( ODS_STRUCT *odsptr, char *FileName ) { int status, SizeInBytes; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsLoadContent() !AZ", FileName); status = OdsOpenReadOnly (odsptr, FileName, true); if (VMSnok (status)) return (status); if (odsptr->XabFhc.xab$l_ebk <= 1) SizeInBytes = odsptr->XabFhc.xab$w_ffb; else SizeInBytes = ((odsptr->XabFhc.xab$l_ebk-1) << 9) + odsptr->XabFhc.xab$w_ffb; /* round-up to ensure we have a full complement of blocks allocated */ odsptr->DataPtr = (char*)VmGet (((SizeInBytes/512)+1)*512); odsptr->DataLength = 0; odsptr->Rab.rab$l_ubf = odsptr->DataPtr; for (;;) { if (SizeInBytes > 512*127) odsptr->Rab.rab$w_usz = 512*127; else if (SizeInBytes > 0) odsptr->Rab.rab$w_usz = SizeInBytes; else break; status = sys$read (&odsptr->Rab, 0, 0); if (VMSnok (status)) break; odsptr->DataLength += odsptr->Rab.rab$w_rsz; odsptr->Rab.rab$l_ubf += odsptr->Rab.rab$w_rsz; SizeInBytes -= odsptr->Rab.rab$w_rsz; } sys$close (&odsptr->Fab, 0, 0); if (status == RMS$_EOF) status = SS$_NORMAL; if (VMSnok (status)) { VmFree (odsptr->DataPtr, FI_LI); odsptr->DataPtr = NULL; odsptr->DataLength = 0; } return (status); } /*****************************************************************************/ /* Load the specified file name into dynamically allocated memory. 'DataPtr' and 'DataLength' are set to reflect the file contents. Records are delimited by newline characters and the file is terminated with a null character. Other file related data remains set in the ODS structure. A VMS status code is returned. This is a fully synchronous function mainly designed for loading configuration files, etc. 'DataPtr' needs to be freed when finished with. */ int OdsLoadTextFile ( ODS_STRUCT *odsptr, char *FileName ) { int status, SizeInBytes; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsLoadTextFile() !AZ", FileName); status = OdsOpenReadOnly (odsptr, FileName, false); if (VMSnok (status)) return (status); if (odsptr->XabFhc.xab$l_ebk <= 1) SizeInBytes = odsptr->XabFhc.xab$w_ffb; else SizeInBytes = ((odsptr->XabFhc.xab$l_ebk-1) << 9) + odsptr->XabFhc.xab$w_ffb; odsptr->DataPtr = odsptr->DataParsePtr = cptr = (char*)VmGet(SizeInBytes+1); odsptr->DataLength = odsptr->DataLineLength = 0; for (;;) { odsptr->Rab.rab$l_ubf = cptr; if (SizeInBytes > 32767) odsptr->Rab.rab$w_usz = 32767; else odsptr->Rab.rab$w_usz = SizeInBytes; status = sys$get (&odsptr->Rab, 0, 0); if (VMSnok (status)) break; cptr[odsptr->Rab.rab$w_rsz] = '\n'; sptr = cptr; cptr += odsptr->Rab.rab$w_rsz + 1; while (sptr < cptr) { if (!*sptr) *sptr = ' '; sptr++; } odsptr->DataLength += odsptr->Rab.rab$w_rsz + 1; SizeInBytes -= odsptr->Rab.rab$w_rsz + 1; } sys$close (&odsptr->Fab, 0, 0); if (status == RMS$_EOF) status = SS$_NORMAL; if (VMSok (status)) *cptr = '\0'; else { VmFree (odsptr->DataPtr, FI_LI); odsptr->DataPtr = odsptr->DataLinePtr = odsptr->DataParsePtr = NULL; odsptr->DataLength = odsptr->DataLineLength = 0; } return (status); } /*****************************************************************************/ /* Free any memory allocated to contain the loaded text file. Reset associated pointers and storage to empty. */ void OdsFreeTextFile (ODS_STRUCT *odsptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsFreeTextFile()"); if (odsptr->DataPtr) { VmFree (odsptr->DataPtr, FI_LI); odsptr->DataPtr = odsptr->DataLinePtr = odsptr->DataParsePtr = NULL; odsptr->DataLength = odsptr->DataLineLength = 0; } } /*****************************************************************************/ /* Parses each newline-delimited 'line' from a text file pointed to by 'DataPtr'. Stores each of these lines between calls pointed to by 'DataLinePtr' and 'DataLineLength'. Current position in text file pointed to by 'DataParsePtr' Designed for configuration files where a trailing backslash is used to continue a line. 'DataLinePtr' needs to be freed if the file content is not read to completion. 'BackslashMeans' can be '\\' meaning the backslash/newline are absorbed, can be '\n' meaning a single newline is substituted, or anything else the backslash is just part of the line. */ char* OdsParseTextFile ( ODS_STRUCT *odsptr, char BackslashMeans ) { int DataLineSize; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsParseTextFile() !UL", BackslashMeans); if (!odsptr->DataParsePtr) return (NULL); if (!*(cptr = sptr = odsptr->DataParsePtr)) { if (odsptr->DataLinePtr) { VmFree (odsptr->DataLinePtr, FI_LI); odsptr->DataLinePtr = NULL; odsptr->DataLineLength = odsptr->DataLineSize = 0; } return (NULL); } while (*sptr && *sptr != '\n') { sptr = odsptr->DataParsePtr; while (*sptr && *sptr != '\n' && !SAME2(sptr,'\\\n')) sptr++; if (*sptr == '\\') sptr += 2; odsptr->DataParsePtr = sptr; } if (sptr - cptr + 1 > odsptr->DataLineSize) { if (sptr - cptr + 1 < 512) DataLineSize = 512; else DataLineSize = sptr - cptr + 1; if (DataLineSize > odsptr->DataLineSize) { if (odsptr->DataLinePtr) VmFree (odsptr->DataLinePtr, FI_LI); odsptr->DataLinePtr = VmGet (odsptr->DataLineSize = DataLineSize); } } if (*sptr) sptr++; odsptr->DataParsePtr = sptr; sptr = odsptr->DataLinePtr; while (*cptr && *cptr != '\n') { while (*cptr && *cptr != '\n' && !SAME2(cptr,'\\\n')) *sptr++ = *cptr++; if (*cptr == '\\') { odsptr->DataLineNumber++; switch (BackslashMeans) { case '\\' : cptr += 2; break; case '\n' : *sptr++ = '\n'; cptr += 2; break; default : *sptr++ = *cptr++; } } } *sptr = '\0'; odsptr->DataLineLength = sptr - odsptr->DataLinePtr; odsptr->DataLineNumber++; return (odsptr->DataLinePtr); } /*****************************************************************************/ /* Synchronous check on the file characteristics directory bit to confirm a file ending in '.DIR' is really a directory. The file must have been parsed or searched-for. Returns SS$_NORMAL if it is, SS$_ABORT if it is not, or another error status for whatever reason. */ int OdsReallyADir ( REQUEST_STRUCT *rqptr, ODS_STRUCT *odsptr ) { #define FCH$M_DIRECTORY 0x2000 int status; unsigned long PrvPrv [2]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsReallyADir()"); /* use SYSPRV to ensure access to perform this check */ sys$setprv (1, &SysPrvMask, 0, &PrvPrv); status = OdsFileAcpInfo (odsptr, NULL, 0); if (!(PrvPrv[0] & PRV$M_SYSPRV)) sys$setprv (0, &SysPrvMask, 0, 0); if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "!&S !&B", status, odsptr->FileQio.AtrUchar & FCH$M_DIRECTORY); if (VMSnok (status)) return (status); if (odsptr->FileQio.AtrUchar & FCH$M_DIRECTORY) return (SS$_NORMAL); return (SS$_ABORT); } /*****************************************************************************/ /* Copies ODS structure 2 to structure 1 adjusting the various pointers. */ OdsCopyStructure ( ODS_STRUCT *odsptr1, ODS_STRUCT *odsptr2 ) { char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsCopyStructure() !&B", OdsExtended); /* copy the entire on-disk structure from file to cache */ memcpy (odsptr1, odsptr2, sizeof(ODS_STRUCT)); /* now adjust the pointers in this new structure */ odsptr1->NamNodePtr = cptr = odsptr1->ExpFileName; odsptr1->NamDevicePtr = (cptr += odsptr1->NamNodeLength); odsptr1->NamDirectoryPtr = (cptr += odsptr1->NamDeviceLength); odsptr1->NamNamePtr = (cptr += odsptr1->NamDirectoryLength); odsptr1->NamTypePtr = (cptr += odsptr1->NamNameLength); odsptr1->NamVersionPtr = (cptr += odsptr1->NamTypeLength); odsptr1->NamFileSysNamePtr = odsptr1->SysFileName; } /****************************************************************************/ /* Get the volume's underlying file system (ODS-2 or ODS-5), by sys$getdvi() the ACPTYPE for the device. Return the appropriate WASD value for detected file system or zero to indicate some other condition. Return 0 if it cannot be determined for some reason, or 2 or 5 representing each of the underlying file-system structures. */ /* if pre-7.2 Alpha then define this */ #ifndef DVI$C_ACP_F11V5 #define DVI$C_ACP_F11V5 11 #endif int OdsVolumeStructure (char *DeviceName) { static unsigned long DevAcpType, DevChar; static $DESCRIPTOR (DevNameDsc, ""); static struct { short buf_len; short item; char *buf_addr; short *ret_len; } AcpTypeItemList [] = { { sizeof(DevAcpType), DVI$_ACPTYPE, &DevAcpType, 0 }, { sizeof(DevChar), DVI$_DEVCHAR, &DevChar, 0 }, { 0, 0, 0, 0 } }; int status; char *cptr; struct { unsigned short Status; unsigned short Count; unsigned long Unused; } IOsb; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsVolumeStructure() !&Z", DeviceName); for (cptr = DeviceName; *cptr && *cptr != ':'; cptr++); /* if we can't find a device in it then assume it's ODS-2 */ if (!*cptr) return (0); DevNameDsc.dsc$a_pointer = DeviceName; DevNameDsc.dsc$w_length = cptr - DeviceName; DevChar = DevAcpType = 0; status = sys$getdviw (EfnWait, 0, &DevNameDsc, &AcpTypeItemList, &IOsb, 0, 0, 0); if (WATCH_MODULE(WATCH_MOD_ODS)) WatchDataFormatted ("sys$getdviw() !#AZ !&S !&S !UL\n", DevNameDsc.dsc$w_length, DevNameDsc.dsc$a_pointer, status, IOsb.Status, DevAcpType); if (VMSok (status)) status = IOsb.Status; /* if error */ if (VMSnok(status)) return (0); /* if volume not mounted */ if (!(DevChar & DEV$M_MNT)) return (0); if (DevAcpType == DVI$C_ACP_F11V2) return (2); if (DevAcpType == DVI$C_ACP_F11V5) return (5); return (0); } /*****************************************************************************/ /* Performance of OdsDirectSearch() compared to OdsSearch(). Compile with module WATCHing. $ HTTPD /TESTS ODS=SEARCH= !use OdsSearch() i.e. $SEARCH $ HTTPD /TESTS ODS=DIRECT= !use OdsSearchNext() Adding an ACP file access provides a bit more of a realistic load. $ HTTPD /TESTS ODS=SEARCH=ACP= $ HTTPD /TESTS ODS=DIRECT=ACP= */ #if ODS_DIRECT void OdsDirectPerfOut ( struct dsc$descriptor_s *dscptr, int fcount ) { fprintf (stdout, "%d files\t%*.*s\n", fcount, dscptr->dsc$w_length, dscptr->dsc$w_length, dscptr->dsc$a_pointer); } void OdsDirectPerf (char *param) { BOOL DoAcp, DoShow; int dlen, fcount, flen, status; char *dptr, *fptr; ODS_STRUCT SearchOds; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_ODS)) WatchThis (WATCHALL, WATCH_MOD_ODS, "OdsDirectPerf() !AZ", param); if (strsame (param, "DIRECT=", 7)) { OdsDirect = true; param += 7; } else if (strsame (param, "SEARCH=", 7)) param += 7; else exit (SS$_ABORT); if (strsame (param, "ACP=", 4)) { DoAcp = true; param += 4; } else DoAcp = false; if (*param == '+') { DoShow = true; param++; } else DoShow = false; dlen = flen = 0; for (dptr = fptr = param; *fptr; fptr++); while (fptr > dptr && *fptr != ']') fptr--; if (fptr > dptr) fptr++; flen = strlen(fptr); dlen = fptr - dptr; status = OdsParse (&SearchOds, fptr, flen, dptr, dlen, NAM$M_NOCONCEAL, 0, 0); if (VMSnok (status)) exit (status); fcount = 0; lib$init_timer(); while (VMSok (status = OdsSearchNoConceal (&SearchOds, 0, 0))) { if (DoShow) fprintf (stdout, "%s\n", SearchOds.ResFileName); if (DoAcp) if (VMSnok (status = OdsFileAcpInfo (&SearchOds, NULL, 0))) break; if (!(++fcount % 1000)) lib$show_timer(0, 0, OdsDirectPerfOut, fcount); } lib$show_timer(0, 0, OdsDirectPerfOut, fcount); exit (status); } #endif /* ODS_DIRECT */ /****************************************************************************/