/*****************************************************************************/ /* Dir.c This module implements a full multi-threaded, AST-driven, asynchronous directory listing. The AST-driven nature makes the code a little more difficult to follow, but creates a powerful, event-driven, multi-threaded server. All of the necessary functions implementing this module are designed to be non-blocking. This module never returns a valid status and ALWAYS calls the supplied next task (AST) function. This should check for a generated error message to determine is there were any problems. This module uses the NetWriteBuffered() function to buffer any output it can into larger chunks before sending it to the client. A directory listing is recognised by the server whenever a wildcard is present in a specification and there is no query string directing another activity (e.g. a query/search). Compliant with other HTTPD implementations, a directory listing is also generated if a URL specifies a directory only and the directory contains no home page. The directory listing is designed to look very much like the default layout of the CERN and NCSA HTTPD implementations, with the exception that all directories are grouped at the top, not dispersed throughout the file names. This looks and functions better than the above (at least in the experience of the author). ACCESS CONTROL -------------- If the server configuration specifies "DirAccessSelective" then the directory must contain the file ".WWW_BROWSABLE" to be listed by this module. If a directory contains the file ".WWW_HIDDEN" it cannot be listed with this module, but individual files are accessable for server access. If a subdirectory contains this file it does not appear in a listing. If a directory contains the file ".WWW_NOWILD" a wildcard directory specification will not work even if allowed by server configuration. If a directory contains the file ".WWW_NOP" it does not show a parent. If a directory contains the file ".WWW_NOS" it does not show subdirectories. If a directory contains the file ".WWW_NOPS" both the above apply. LISTING LAYOUT -------------- The layout of the listing can be controlled from the configuration file or via a server directive. A decimal number may optionally, immediately precede any directive, this becomes the width of that particular field. If a width is not specified an appropriate default is used. Information wider than the field width is truncated, generally without indication. The following layout directives provide: _ (underscore) each underscore provides one space between fields C creation date D content-type description (BEST be the last field specified) D:L content-type description as a link I icon L file anchor (link, including name as link) L:F file anchor, ODS-5 file system name (e.g. spaces, not %20) L:N file anchor, name-only, do not display the file's extension L:U file anchor, force the name to upper-case (these can have multiple specifications, e.g. "__L:U:N__") N file name (without anchor, why I can't imagine :^) O file owner (only when SYSUAF-authenticated and profile accessed) P file protection (only when SYSUAF-authenticated and profile accessed) R revision date S file size S:B file size to be in comma-formatted bytes S:D express file sizes in 1000 (decimal) kilos not 1024 (binary) kilos S:F file size Mb and Kb to be expressed to one significant place S:K file size to be in K-bytes S:M file size to be in M-bytes S:V file size to be in blocks (VMS-ish) U file/directory name in upper case (MUST be the FIRST directive) These may placed in any order ("D" is best last because it does not use a field width) and with any (reasonable) field-width. For example see 'DirDefaultLayout' below. DIRECTORY DIRECTIVES -------------------- These directives may be added to a directory listing URL, or placed into the ".WWW_WASD" file, to modifiy the format and/or behaviour of the resultant listing. Multiple query string directives may be added using the normal query string ampersand syntax. This example forces an ODS-2 volumes file name to upper-case and does not display the file type (extension). ?httpd=index&upper=1¬ype=1 # .WWW_WASD upper=yes notype=yes Precedence is; .WWW_WASD file directives, then if override=yes SSI query string, then if override=yes URI query string; if no .WWW_WASD file then SSI query string and if override=yes URI query string; finally if no SSI query string then URI query string. The following are the directives: autoscript= yes (default), no, true, false, 1, 0 delimit= header, footer, both (default), none expired= yes, no, true, false, 1, 0 (listing pre-expired) ilink= yes, no, true, false, 1, 0 (icon is a plain-text link) layout= see "listing layout" immediately above local= yes, no (default), true, false, 1, 0 (do not propagate) notype= yes, no (default), true, false, 1, 0 (do not display file type) nop= yes, no (default), true, false, 1, 0 (as if .WWW_NOP found) nops= yes, no (default), true, false, 1, 0 (as if .WWW_NOPS found) nos= yes, no (default), true, false, 1, 0 (as if .WWW_NOS found) override= yes, no (default), true, false, 1, 0 (query string may override) query= set this string as the query string for subsequent requests readme= yes, no (default), true, false, 1, 0 script= script path that will be used when a file link is clicked sort= on column (e.g. 'R','S') then '+' for ascending '-' descending style= any of the mapping SET dir=style= target= open a file in this window (e.g. "_blank") these= one or more comma-separated wildcard file name.type(s) type= force file content-type (e.g. "&type=\"text\"/plain") upper= yes, no (default), true, false, 1, 0 versions= up to many of a file or if '*' then all VMS (ODS-2) FILE NAMES ---------------------- Generally directory listings on ODS-2 volumes (VMS disks prior to VMS V7.2) are displayed lower-case. To force an upper-case listing use any of a directory listing format beginning with "U", or the "httpd=index&upper=1" query string. EXTENDED (ODS-5) FILE NAMES --------------------------- On ODS-5 volumes (available on Alpha VMS V7.2 and following) file names are displayed in mixed, on-disk case. These file names may contain characters that need to be escaped. These are displayed as what the escape sequence represents For example; '^_' as ' ' and '^.' as '.'. PATHWORKS AND SRI ENCODED FILE NAMES ------------------------------------ These will be displayed as what the encoding represents. For example; in Pathworks encoding a '$20' sequence represents a ' ' and in SRI encoding the encoding '$7A' also as ' '. SRI encodings representing mixed-case file names are displayed in mixed case. VERSION HISTORY --------------- 27-SEP-2013 MGD ?httpd=index&versions=|* SET dir=versions=|* 18-SEP-2013 MGD refine sortable whitespace in older MSIE and font in all MSIE 10-JUL-2013 MGD .WWW_WASD directive file sortable directory listing ?httpd=index&local=[yes|no] ?httpd=index&override=[yes|no] ?httpd=index&query= ?httpd=index&style= ?httpd=index&sort=[+|-] ?httpd=index&target= ?httpd=index&these=[,] SET dir=delimit= SET dir=style= SET dir=sort=[+|-] SET dir=target= SET dir=these=[,] bugfix; DirFormatSize() bytes 01-JUN-2013 MGD bugfix; non-ODS_EXTENDED platforms (e.g. VAX) must OdsParse() NAM$M_NOCONCEAL before OdsSearchNoConceal() 30-JAN-2012 MGD DirFormatSize() now uses quadword DirFormatSize() adjusts units to fit size width 27-SEP-2011 MGD bugfix; OdsSearchNoConceal() to successfully process concealed, multi-value logical device names 28-APR-2010 MGD bugfix; DirAuthorizationAst() only check access on non-empty expanded file names 24-MAR-2010 MGD bugfix; DirBegin() "httpd=index&" detection (since v9.3.0) bugfix; DirEnd() suppress unless RequestEnd() AST 23-AUG-2009 MGD WasdCss[] and some refinements 11-JUN-2009 MGD "*.*__WASDAV;" files ignored 10-DEC-2007 MGD DirBegin() only use query string if it begins "httpd=index&" 09-APR-2007 MGD bugfix; DirFormatAcpInfoAst() 'S' (size) processing for block totals at the end of a listing 24-MAY-2006 MGD DirFormat() and DirFormatSize() allow in-line layouts to specify size with VMS format listings, as well as adding size specification of 'V' (VMS-ish, in blocks) 06-SEP-2004 MGD change from self-relative to absolute links in "Index of" anchor generation (broke usage in some SSI documents) 05-SEP-2003 MGD bugfix; according to the doco "Index of"s from SSI should not be delimited top or bottom (up to SSI to caption it!) 05-JUN-2003 MGD bugfix; DirFormatLayout() static flags (jpp@esme.fr) 11-MAR-2003 MGD set html= directory listing header and footer, there are now three directory listing styles implementing set dir=style[=default|original|anchor|htdir] 15-OCT-2002 MGD demo mode ignores .WWW_HIDDEN, etc. 05-OCT-2002 MGD refine VMS security profile usage 20-SEP-2002 MGD bale-out early if no access 14-SEP-2002 MGD implement SET dir=charset directory listing charset 09-SEP-2002 MGD for consistency return an (if configured) empty listing if protection on the directory doesn't allow wildcards 04-SEP-2002 MGD bugfix; 'Index of..' string encoding 20-AUG-2002 MGD modify formatting for SRI and PWK ODS encodings, remove 'filesys=' listing modifier - it's now all FILESYS! 10-JUL-2002 MGD 'RealPath' for absoluting the likes of SSI 'index of's 31-MAY-2002 MGD path SET directory access control 27-APR-2002 MGD make SYSPRV enabled ASTs asynchronous 07-APR-2002 MGD bugfix; ODS-5 parent directories with multiple periods 02-FEB-2002 MGD for non-textual content types the icon is now an anchor for a "text/plain" content-type access 04-AUG-2001 MGD support module WATCHing 17-JUL-2001 MGD bugfix; 'layout=U' upper-casing 21-FEB-2001 MGD include ODS-5 "hidden" files beginning '^.' 13-FEB-2001 MGD bugfix; directory specfication length (sys$check_access()) 04-DEC-2000 MGD bugfix; DirSearchFiles() call DirFiles() with FAB 04-MAR-2000 MGD use FaolToBuffer(), et.al. 27-DEC-1999 MGD support ODS-2 and ODS-5 using ODS module 18-SEP-1999 MGD bugfix; sys$parse() NAM$M_NOCONCEAL for search lists 23-MAY-1999 MGD make year 4 digits for creation and revision date 19-SEP-1998 MGD improve granularity with use of directory/file/ACP services (not as much as I would have liked, but some things are just not important enough to be worth the trouble using ASTs!) 08-AUG-1998 MGD configurable implied wildcards 19-JUL-1998 MGD bugfix; MapUrl_Map() pass 'rqptr' for conditionals to work 14-MAY-1998 MGD request-specified content-type ("httpd=index&type=") 16-MAR-1998 MGD bugfix; file bytes incorrect when 'FirstFreeByte' zero 28-FEB-1998 MGD improved icon handling efficiency 19-FEB-1998 MGD size layout allows ":B", ":D", ":F", ":K", ":M" modifiers, description layout allows ":L" to make a link out of it, description may now have an associated field width, allow for directory paths like "/dir1/dir2" 02-NOV-1997 MGD "delimit=", "nop=", "nops=", "nos=" and "readme=" directives changed file sizes back from 1000 to 1024 kilos, megas, etc. 17-AUG-1997 MGD message database, internationalized file date/times, file/directory names moved to lower-case (see 'U' above), SYSUAF-authenticated users security-profile 20-APR-1997 MGD changed file sizes from 1024 to 1000 kilos, megas, etc. 01-FEB-1997 MGD HTTPd version 4 24-APR-1996 MGD chagrin ... just realized I don't need full URLs in links 01-DEC-1995 MGD HTTPd version 3 27-SEP-1995 MGD major revision of file and directory anchor generation (far fewer MapUrl()s yielding greater efficiency); added query string capability; added automatic script capability 07-AUG-1995 MGD include VMS-style directory layout and file sizes 16-JUN-1995 MGD file contents description for non-HTML files (see config.c) 25-MAR-1995 MGD bugfix; error handling with DirReadMe() and DirFileExists() 20-DEC-1994 MGD multi-threaded daemon (it suddenly got very complex!) 20-JUN-1994 MGD single-threaded daemon */ /*****************************************************************************/ #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 /* VMS related header files */ #include #include #include #include #include #include #include #include #include #include /* application related header file */ #include "wasd.h" #define WASD_MODULE "DIR" /**********/ /* macros */ /**********/ #define DEFAULT_FIELD_WIDTH_INTERFIELD 1 #define DEFAULT_FIELD_WIDTH_CDT 17 #define DEFAULT_FIELD_WIDTH_NAME 25 #define DEFAULT_FIELD_WIDTH_OWNER 20 #define DEFAULT_FIELD_WIDTH_PROTECTION 19 #define DEFAULT_FIELD_WIDTH_RDT 17 #define DEFAULT_FIELD_WIDTH_SIZE 6 #define DEFAULT_FIELD_WIDTH_DECIMAL 11 #define LAYOUT_PARAMETER ':' /******************/ /* global storage */ /******************/ char DirBrowsableFileName [] = ".WWW_BROWSABLE", DirDefaultLayout [] = DEFAULT_DIR_LAYOUT, DirHiddenFileName [] = ".WWW_HIDDEN", DirNoWildFileName [] = ".WWW_NOWILD", DirNopFileName [] = ".WWW_NOP", DirNosFileName [] = ".WWW_NOS", DirNopsFileName [] = ".WWW_NOPS", DirWasdFileName [] = ".WWW_WASD"; char *DirBlankIconPtr, *DirDirIconPtr, *DirParentIconPtr; static char DirColSpan [96]; static $DESCRIPTOR (DirColSpanDsc, DirColSpan); /********************/ /* external storage */ /********************/ extern BOOL CliDemo, NaturalLanguageEnglish, OdsExtended; extern int SsiSizeMax; extern int ToLowerCase[], ToUpperCase[]; extern unsigned long SysPrvMask[]; extern char *ConfigBlankIconPtr, *ConfigDirIconPtr, *ConfigParentIconPtr; extern char ConfigContentTypeSsi[], ErrorSanityCheck[], SoftwareID[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern MSG_STRUCT Msgs; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* This function never returns a valid status and ALWAYS calls the supplied next task (AST) function. This should check for a generated error message to determine is there were any problems. */ DirBegin ( REQUEST_STRUCT *rqptr, REQUEST_AST NextTaskFunction, char *DirSpec, char *DirQueryString, char *RealPath, BOOL AuthorizePath ) { int status; char *cptr, *sptr, *zptr; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirBegin() !&A !&Z !&Z !&Z !&B", NextTaskFunction, DirSpec, DirQueryString, RealPath, AuthorizePath); if (ERROR_REPORTED (rqptr)) { /* previous error, cause threaded processing to unravel */ SysDclAst (NextTaskFunction, rqptr); return; } /* network writes are checked for success, fudge the first one! */ rqptr->rqNet.WriteIOsb.Status = SS$_NORMAL; if (!rqptr->AccountingDone++) InstanceGblSecIncrLong (&AccountingPtr->DoDirectoryCount); if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, WATCH_RESPONSE, "INDEXOF !AZ", DirSpec); if ((!Config.cfDir.Access && !Config.cfDir.AccessSelective && !rqptr->rqPathSet.DirAccess && !rqptr->rqPathSet.DirAccessSelective) || rqptr->rqPathSet.DirNoAccess) { rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_DISABLED), FI_LI); SysDclAst (NextTaskFunction, rqptr); return; } /* set up the task structure (possibly multiple concurrent) */ if (rqptr->DirTaskPtr && LIST_HAS_NEXT (rqptr->DirTaskPtr)) { rqptr->DirTaskPtr = tkptr = LIST_GET_NEXT (rqptr->DirTaskPtr); memset (LIST_GET_DATA(tkptr), 0, sizeof(DIR_TASK) - sizeof(LIST_ENTRY)); } else { rqptr->DirTaskPtr = tkptr = VmGetHeap (rqptr, sizeof(DIR_TASK)); ListAddTail (&rqptr->DirTaskList, tkptr); } tkptr->NextTaskFunction = NextTaskFunction; cptr = MapVmsPath (DirSpec, rqptr); if (!cptr[0]) { rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, cptr+1, FI_LI); DirEnd (rqptr); return; } zptr = (sptr = tkptr->DirPath) + sizeof(tkptr->DirPath); while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { ErrorGeneralOverflow (rqptr, FI_LI); DirEnd (rqptr); return; } *sptr = '\0'; /* when the path may be different from base path (e.g. SSI) */ cptr = RealPath; zptr = (sptr = tkptr->RealPath) + sizeof(tkptr->RealPath); while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { ErrorGeneralOverflow (rqptr, FI_LI); DirEnd (rqptr); return; } *sptr = '\0'; while (sptr > tkptr->RealPath && *sptr != '/') sptr--; if (*sptr == '/') sptr++; *sptr = '\0'; cptr = DirSpec; zptr = (sptr = tkptr->DirSpec) + sizeof(tkptr->DirSpec); while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { ErrorGeneralOverflow (rqptr, FI_LI); DirEnd (rqptr); return; } *sptr = '\0'; tkptr->DirSpecLength = sptr - tkptr->DirSpec; rqptr->rqResponse.PreExpired = Config.cfDir.PreExpired; tkptr->AsIfNopFound = tkptr->AsIfNosFound = false; tkptr->AutoScriptEnabled = tkptr->IconLinkEnabled = tkptr->IncludeAnyReadme = true; if (tkptr->RealPath[0]) tkptr->DirDelimit = MAPURL_DIR_DELIMIT_NONE; else if (rqptr->rqPathSet.DirDelimit) tkptr->DirDelimit = rqptr->rqPathSet.DirDelimit; else tkptr->DirDelimit = MAPURL_DIR_DELIMIT_BOTH; if (rqptr->rqPathSet.DirSort[0]) { tkptr->DirSort[0] = rqptr->rqPathSet.DirSort[0]; tkptr->DirSort[1] = rqptr->rqPathSet.DirSort[1]; } if (rqptr->rqPathSet.DirStyle) tkptr->DirStyle = rqptr->rqPathSet.DirStyle; else tkptr->DirStyle = MAPURL_DIR_STYLE_ANCHOR; if (rqptr->rqPathSet.DirTargetPtr) { zptr = (sptr = tkptr->LinkTarget) + sizeof(tkptr->LinkTarget)-2; for (cptr = " target=\""; *cptr; *sptr++ = *cptr++); for (cptr = rqptr->rqPathSet.DirTargetPtr; *cptr && sptr < zptr; cptr++) if (*cptr != '\"') *sptr++ = *cptr; *sptr++ = '\"'; *sptr = '\0'; } if (rqptr->rqPathSet.DirThesePtr) { for (cptr = sptr = rqptr->rqPathSet.DirThesePtr; *sptr; sptr++); tkptr->ThesePtr = sptr = VmGetHeap (rqptr, sptr-cptr); /* null-separated series of strings terminated by empty string */ while (*cptr) { if (*cptr == ',') *sptr++ = '\0'; else *sptr++ = *cptr; cptr++; } } if (rqptr->rqPathSet.DirVersionsOf) /* negative 1 indicates to revert to zero */ if (rqptr->rqPathSet.DirVersionsOf == -1) tkptr->VersionsOf = 0; else tkptr->VersionsOf = rqptr->rqPathSet.DirVersionsOf; /************************/ /* directory directives */ /************************/ /* .WWW_WASD file */ if (VMSnok (DirWwwWasd (rqptr))) tkptr->QueryOverride = true; /* SSI query string */ if (tkptr->QueryOverride) if (cptr = DirQueryString) if (cptr && TOLO(*cptr) == 'h' && strsame (cptr, "httpd=index&", 12)) DirDirectString (rqptr, cptr+12); /* URI query string */ if (tkptr->QueryOverride) if (cptr = tkptr->QueryStringPtr) if (cptr && TOLO(*cptr) == 'h' && strsame (cptr, "httpd=index&", 12)) DirDirectString (rqptr, cptr+12); /* map any default into the current default */ if (tkptr->DirStyle == MAPURL_DIR_STYLE_DEFAULT) tkptr->DirStyle = MAPURL_DIR_STYLE_ANCHOR; else if (tkptr->DirStyle == MAPURL_DIR_STYLE_DEFAULT2) tkptr->DirStyle = MAPURL_DIR_STYLE_ANCHOR2; if (AuthorizePath) { /***********************/ /* check authorization */ /***********************/ cptr = MapVmsPath (tkptr->DirSpec, rqptr); Authorize (rqptr, cptr, -1, NULL, 0, &DirAuthorizationAst); if (VMSnok (rqptr->rqAuth.FinalStatus)) { /* if asynchronous authentication is underway then just wait for it */ if (rqptr->rqAuth.FinalStatus == AUTH_PENDING) return; DirEnd (rqptr); return; } } /* not to-be-authorized, or authorized ... just carry on regardless! */ DirAuthorizationAst (rqptr); } /*****************************************************************************/ /* This function provides an AST target is Authorize()ation ended up being done asynchronously, otherwise it is just called directly to continue the modules processing. */ DirAuthorizationAst (REQUEST_STRUCT *rqptr) { static char JavaScriptWasd [] = "\n\ \n"; static char StyleWasd [] = "\n"; int status; unsigned short Length; unsigned long FaoVector [32]; unsigned long *vecptr; char Scratch [ODS_MAX_FILE_NAME_LENGTH+1]; char *cptr, *sptr, *IndexOfPtr; DIR_TASK *tkptr; REQUEST_AST AstFunction; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirAuthorizationAst() !&F !&S", &DirAuthorizationAst, rqptr->rqAuth.FinalStatus); if (VMSnok (rqptr->rqAuth.FinalStatus)) { DirEnd (rqptr); return; } tkptr = rqptr->DirTaskPtr; /***************************/ /* parse to get components */ /***************************/ if (tkptr->SearchOds.ExpFileNameLength) AuthAccessEnable (rqptr, tkptr->SearchOds.ExpFileName, AUTH_ACCESS_READ); OdsParse (&tkptr->SearchOds, tkptr->DirSpec, tkptr->DirSpecLength, NULL, 0, 0, NULL, rqptr); AuthAccessEnable (rqptr, 0, 0); if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts)) { rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath; rqptr->rqResponse.ErrorOtherTextPtr = tkptr->DirSpec; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } memcpy (tkptr->DirectoryPart, tkptr->SearchOds.NamDevicePtr, Length = tkptr->SearchOds.NamNamePtr - tkptr->SearchOds.NamDevicePtr); tkptr->DirectoryPart[Length] = '\0'; Scratch[0] = '\0'; sptr = MapUrl_Map (Scratch, sizeof(Scratch), tkptr->DirectoryPart, 0, NULL, 0, NULL, 0, NULL, 0, NULL, rqptr, NULL); if (!sptr[0] && sptr[1]) { /* mapping report */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, sptr+1, FI_LI); DirEnd (rqptr); return; } Length = strlen(Scratch) + 1; tkptr->DirectoryPathPtr = VmGetHeap (rqptr, Length); memcpy (tkptr->DirectoryPathPtr, Scratch, Length); if (tkptr->SearchOds.Nam_fnb & NAM$M_WILD_DIR) { rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_NO_WILDCARD), FI_LI); DirEnd (rqptr); return; } /************************/ /* directory access ok? */ /************************/ if (CliDemo || rqptr->rqAuth.SkelKeyAuthenticated || rqptr->rqAuth.VmsUserProfileLength) { /* the VMS security profile allows access, why further prohibit it? */ status = SS$_NORMAL; } else { /* ensure we can read these directory listing "control" files */ sys$setprv (1, &SysPrvMask, 0, 0); if (VMSok (status = OdsFileExists (tkptr->DirectoryPart, DirHiddenFileName))) status = RMS$_DNF; else if ((Config.cfDir.AccessSelective || rqptr->rqPathSet.DirAccessSelective) && !rqptr->rqPathSet.DirAccess) { status = OdsFileExists (tkptr->DirectoryPart, DirBrowsableFileName); if (status == RMS$_FNF) status = SS$_ABORT; } else if (VMSok (status = OdsFileExists (tkptr->DirectoryPart, DirNoWildFileName))) { if (tkptr->SearchOds.Nam_fnb & NAM$M_WILD_NAME || tkptr->SearchOds.Nam_fnb & NAM$M_WILD_TYPE || tkptr->SearchOds.Nam_fnb & NAM$M_WILD_VER) status = SS$_ABORT; else status = SS$_NORMAL; } else status = SS$_NORMAL; sys$setprv (0, &SysPrvMask, 0, 0); } if (VMSnok (status)) { if (status == SS$_ABORT) { /* abort here means the facility is disabled */ rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_DISABLED), FI_LI); } else { /* give some "disinformation" ;^) */ if (status == RMS$_FNF) status = RMS$_DNF; rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath; rqptr->rqResponse.ErrorOtherTextPtr = tkptr->DirSpec; ErrorVmsStatus (rqptr, status, FI_LI); } DirEnd (rqptr); return; } /*********************************/ /* further process specification */ /*********************************/ tkptr->DirSpecIncludedFilePart = tkptr->SearchOds.NamNameLength > 0 || tkptr->SearchOds.NamTypeLength > 1 || tkptr->SearchOds.NamVersionLength > 1; if (!tkptr->DirSpecIncludedFilePart) { /* no name or type was supplied with the request, wildcard it */ AuthAccessEnable (rqptr, tkptr->SearchOds.ExpFileName, AUTH_ACCESS_READ); OdsParse (&tkptr->SearchOds, tkptr->DirSpec, tkptr->DirSpecLength, "*.*", 3, 0, NULL, rqptr); AuthAccessEnable (rqptr, 0, 0); if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts)) { rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath; rqptr->rqResponse.ErrorOtherTextPtr = tkptr->DirSpec; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } } if (tkptr->SearchOds.Nam_fnb & NAM$M_WILD_VER || tkptr->SearchOds.Nam_fnb & NAM$M_EXP_VER) { /* wildcard or explicit version number supplied ... VMS format */ memcpy (tkptr->FilePart, tkptr->SearchOds.NamNamePtr, Length = tkptr->SearchOds.NamVersionPtr - tkptr->SearchOds.NamNamePtr + tkptr->SearchOds.NamVersionLength); tkptr->FormatLikeVms = true; } else { /* name and/or type or implied wildcards */ memcpy (tkptr->FilePart, tkptr->SearchOds.NamNamePtr, Length = tkptr->SearchOds.NamVersionPtr - tkptr->SearchOds.NamNamePtr); } if (tkptr->VersionsOf) { /* ?httpd=index&versions= */ if (!(tkptr->SearchOds.Nam_fnb & NAM$M_WILD_VER || tkptr->SearchOds.Nam_fnb & NAM$M_EXP_VER)) tkptr->FilePart[Length++] = ';'; if (!(tkptr->SearchOds.Nam_fnb & NAM$M_WILD_VER)) tkptr->FilePart[Length++] = '*'; } tkptr->FilePart[Length] = '\0'; /********************/ /* begin processing */ /********************/ tkptr->FileCount = tkptr->DirectoryCount = 0; if (VMSnok (DirFormatLayout (rqptr))) { DirEnd (rqptr); return; } if (tkptr->ResponseHeaderSent = !rqptr->rqResponse.HeaderSent) { /* if directory listing charset has been set impose that on any other */ cptr = rqptr->rqPathSet.CharsetPtr; if (rqptr->rqPathSet.DirCharsetPtr) rqptr->rqPathSet.CharsetPtr = rqptr->rqPathSet.DirCharsetPtr; /* "index of"s can have pre-expiry controlled from the query string */ ResponseHeader200 (rqptr, "text/html", NULL); /* restore any previous (just to be neat) */ rqptr->rqPathSet.CharsetPtr = cptr; if (rqptr->rqHeader.Method == HTTP_METHOD_HEAD) { DirEnd (rqptr); return; } } if (Config.cfDir.ReadMeTop && tkptr->IncludeAnyReadme) AstFunction = &DirReadMeTop; else AstFunction = &DirHeading; if (tkptr->DirDelimit == MAPURL_DIR_DELIMIT_BOTH || tkptr->DirDelimit == MAPURL_DIR_DELIMIT_HEADER) { vecptr = &FaoVector; *vecptr++ = WASD_DOCTYPE; if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) *vecptr++ = JavaScriptWasd; else *vecptr++ = StyleWasd; if (rqptr->rqPathSet.StyleSheetPtr) { *vecptr++ = "\n"; *vecptr++ = rqptr->rqPathSet.StyleSheetPtr; } else *vecptr++ = ""; if (Config.cfDir.MetaInfoEnabled) { *vecptr++ = "!AZ\n"; *vecptr++ = HtmlMetaInfo (rqptr, tkptr->DirSpec); *vecptr++ = tkptr->SizeKilo; } else *vecptr++ = ""; IndexOfPtr = MsgFor(rqptr,MSG_DIR_INDEX_OF); /* Index of */ if (tkptr->FormatLikeVms) { MapOdsUrlToVms (tkptr->DirPath, Scratch, sizeof(Scratch), 0, rqptr->rqPathSet.MapEllipsis, rqptr->PathOds); *vecptr++ = "!AZ !&;AZ"; *vecptr++ = IndexOfPtr; *vecptr++ = Scratch; } else if (tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2) { *vecptr++ = "//!AZ!&;&[AZ"; if (rqptr->rqHeader.HostPtr && rqptr->rqHeader.HostPtr[0]) *vecptr++ = rqptr->rqHeader.HostPtr; else *vecptr++ = rqptr->ServicePtr->ServerHostPort; *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->DirPath; } else { *vecptr++ = "!AZ //!AZ!&;&[AZ"; *vecptr++ = IndexOfPtr; if (rqptr->rqHeader.HostPtr && rqptr->rqHeader.HostPtr[0]) *vecptr++ = rqptr->rqHeader.HostPtr; else *vecptr++ = rqptr->ServicePtr->ServerHostPort; *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->DirPath; } if (rqptr->rqPathSet.HtmlBodyTagPtr) { /* */ if (rqptr->rqPathSet.HtmlBodyTagPtr[0] == '<') *vecptr++ = "!AZ\n
\n"; else *vecptr++ = "\n
\n"; *vecptr++ = rqptr->rqPathSet.HtmlBodyTagPtr; } else { *vecptr++ = "!AZ\n
\n"; *vecptr++ = Config.cfDir.BodyTag; } if (rqptr->rqPathSet.HtmlHeaderPtr || rqptr->rqPathSet.HtmlHeaderTagPtr) { if (rqptr->rqPathSet.HtmlHeaderTagPtr && rqptr->rqPathSet.HtmlHeaderTagPtr[0] == '<') *vecptr++ = "!AZ\n!&/AZ"; else *vecptr++ = "\ \n!&/AZ"; *vecptr++ = rqptr->rqPathSet.HtmlHeaderTagPtr; *vecptr++ = rqptr->rqPathSet.HtmlHeaderPtr; } else *vecptr++ = ""; status = FaolToNet (rqptr, "!AZ\ \n\ \n\ !AZ\ !&@\ !&@\ !&@\n\ \n\ !&@\ !&@", &FaoVector); if (VMSok (status)) status = DirIndexOf (rqptr, IndexOfPtr, tkptr->FormatLikeVms ? Scratch : NULL); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.ErrorTextPtr = "FaolToNet()"; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } NetWriteFullFlush (rqptr, AstFunction); } else if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { vecptr = &FaoVector; *vecptr++ = JavaScriptWasd; if (rqptr->rqPathSet.StyleSheetPtr) { *vecptr++ = "\n"; *vecptr++ = rqptr->rqPathSet.StyleSheetPtr; } else *vecptr++ = ""; FaolToNet (rqptr, "!AZ!&@", &FaoVector); NetWriteFullFlush (rqptr, AstFunction); } else { vecptr = &FaoVector; *vecptr++ = JavaScriptWasd; if (rqptr->rqPathSet.StyleSheetPtr) { *vecptr++ = "\n"; *vecptr++ = rqptr->rqPathSet.StyleSheetPtr; } else *vecptr++ = ""; FaolToNet (rqptr, "!AZ!&@", &FaoVector); NetWriteFullFlush (rqptr, AstFunction); } } /*****************************************************************************/ /* Provide the "Index of" page heading in any of the post-v8.2, traditional WASD, or the "ABI" styles. */ int DirIndexOf ( REQUEST_STRUCT *rqptr, char *IndexOfPtr, char *UrlAsVms ) { int cnt, status, SlashCount; unsigned long FaoVector [32]; unsigned long *vecptr; char *cptr, *sptr, *zptr, *FinalSlashPtr, *ThisSlashPtr; char Scratch [ODS_MAX_FILE_NAME_LENGTH+1]; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirIndexOf() !&Z !&Z", IndexOfPtr, UrlAsVms); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; if (tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL || tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2) { /**************************/ /* traditional WASD style */ /**************************/ vecptr = &FaoVector; *vecptr++ = IndexOfPtr; *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->DirPath; status = FaolToNet (rqptr, "

!AZ  !&;&_&[AZ

\n", &FaoVector); return (status); } /****************/ /* other styles */ /****************/ /* convert the path into a displayable path (might be ODS-5, etc.) */ vecptr = &FaoVector; *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->DirPath; status = FaolToBuffer (Scratch, sizeof(Scratch), NULL, "!&;&_&[AZ", &FaoVector); if (VMSnok (status)) return (status); /* calculate the number of directory elements */ SlashCount = 0; FinalSlashPtr = ""; for (cptr = Scratch; *cptr; cptr++) { if (*cptr == '/') { SlashCount++; FinalSlashPtr = cptr; } } if (SlashCount > 64) return (SS$_BUGCHECK); if (*FinalSlashPtr) FinalSlashPtr++; vecptr = &FaoVector; if (UrlAsVms) { /* VMS format listing (no ABI rendering) */ *vecptr++ = IndexOfPtr; status = FaolToNet (rqptr, "

!AZ  ", &FaoVector); if (VMSnok (status)) return (status); cptr = UrlAsVms; } else { if (tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2) { /* ABI's style begins with the server name as a 'root' anchor */ *vecptr++ = FinalSlashPtr; if (tkptr->QueryStringPtr) { *vecptr++ = "?httpd=index&!AZ"; *vecptr++ = tkptr->QueryStringPtr; } else *vecptr++ = ""; if (rqptr->rqHeader.HostPtr && rqptr->rqHeader.HostPtr[0]) *vecptr++ = rqptr->rqHeader.HostPtr; else *vecptr++ = rqptr->ServicePtr->ServerHostPort; status = FaolToNet (rqptr, "

//!AZ/", &FaoVector); } else { /* WASD style provides an "Index of" heading */ *vecptr++ = IndexOfPtr; *vecptr++ = FinalSlashPtr; if (tkptr->QueryStringPtr) { *vecptr++ = "?httpd=index&!AZ"; *vecptr++ = tkptr->QueryStringPtr; } else *vecptr++ = ""; if (rqptr->rqHeader.HostPtr && rqptr->rqHeader.HostPtr[0]) *vecptr++ = rqptr->rqHeader.HostPtr; else *vecptr++ = rqptr->ServicePtr->ServerHostPort; status = FaolToNet (rqptr, "

!AZ  //!AZ/", &FaoVector); } if (VMSnok (status)) return (status); cptr = Scratch; } /* provide an individual anchor for each directory element */ ThisSlashPtr = tkptr->DirPath; if (*ThisSlashPtr == '/') ThisSlashPtr++; if (SlashCount) SlashCount--; while (SlashCount-- > 0) { while (*ThisSlashPtr && *ThisSlashPtr != '/') ThisSlashPtr++; if (*ThisSlashPtr == '/') ThisSlashPtr++; vecptr = &FaoVector; *vecptr++ = ThisSlashPtr - tkptr->DirPath; *vecptr++ = tkptr->DirPath; *vecptr++ = FinalSlashPtr; if (tkptr->QueryStringPtr) { *vecptr++ = "?httpd=index&!AZ"; *vecptr++ = tkptr->QueryStringPtr; } else *vecptr++ = ""; if (UrlAsVms) { /* VMS format, parse based on VMS directory delimiting characters */ while (*cptr == '.' || *cptr == ':' || *cptr == '[' || *cptr == ']') cptr++; for (sptr = cptr; *sptr && *sptr != '.' && *sptr != ':' && *sptr != ']'; sptr++) if (*sptr == '^') sptr++; *vecptr++ = sptr - cptr; *vecptr++ = cptr; if (*sptr == ':') *vecptr++ = 2; else *vecptr++ = 1; *vecptr++ = sptr; } else { /* URL format, parse based on delimiting slashes */ if (*cptr == '/') cptr++; for (sptr = cptr; *sptr && *sptr != '/'; sptr++); if (tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2) { /* ABI's style, trailing slashes are part of the link */ if (*sptr) sptr++; *vecptr++ = sptr - cptr; *vecptr++ = cptr; *vecptr++ = 0; *vecptr++ = ""; } else { /* WASD style, trailing slashes are not part of the link */ *vecptr++ = sptr - cptr; *vecptr++ = cptr; *vecptr++ = 1; *vecptr++ = sptr; } } if (VMSnok (status)) return (status); status = FaolToNet (rqptr, "!#AZ!#AZ", &FaoVector); cptr = sptr; } if (!UrlAsVms && (tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2)) { /* ABI's style, 'file' name and type element as an anchor */ if (*cptr == '/') cptr++; vecptr = &FaoVector; *vecptr++ = cptr; if (tkptr->QueryStringPtr) { *vecptr++ = "?httpd=index&!AZ"; *vecptr++ = tkptr->QueryStringPtr; } else *vecptr++ = ""; *vecptr++ = cptr; status = FaolToNet (rqptr, "!AZ

\n", &FaoVector); } else { /* WASD style, URL or VMS format, 'file' name and type just displayed */ if (UrlAsVms) while (*cptr == ':' || *cptr == '[' || *cptr == '.' || *cptr == ']') cptr++; else if (*cptr == '/') cptr++; vecptr = &FaoVector; *vecptr++ = cptr; status = FaolToNet (rqptr, "!AZ

\n", &FaoVector); } return (status); } /*****************************************************************************/ /* Release any dynamic memory allocated for parse structures. */ DirEnd (REQUEST_STRUCT *rqptr) { int status; unsigned long FaoVector [16]; unsigned long *vecptr; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirEnd() !&F", &DirEnd); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; /* ensure parse internal data structures are released */ if (tkptr->SearchOds.ParseInUse) OdsParseRelease (&tkptr->SearchOds); if (!ERROR_REPORTED (rqptr)) { vecptr = &FaoVector; if (rqptr->rqPathSet.HtmlFooterPtr || rqptr->rqPathSet.HtmlFooterTagPtr) { if (rqptr->rqPathSet.HtmlFooterTagPtr && rqptr->rqPathSet.HtmlFooterTagPtr[0] == '<') *vecptr++ = "!AZ\n!&@"; else *vecptr++ = "

\ \n!&@"; *vecptr++ = rqptr->rqPathSet.HtmlFooterTagPtr; *vecptr++ = "!&/AZ
\n"; *vecptr++ = rqptr->rqPathSet.HtmlFooterPtr; } else *vecptr++ = ""; if (tkptr->NextTaskFunction == RequestEnd) { status = FaolToNet (rqptr, "!&@
\n\n\n", &FaoVector); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.ErrorTextPtr = "FaolToNet()"; ErrorVmsStatus (rqptr, status, FI_LI); } } } /* restore previous directory task (if any) */ rqptr->DirTaskPtr = LIST_GET_PREV (tkptr); /* explicitly flushed in case it's an SSI activity */ NetWriteFullFlush (rqptr, tkptr->NextTaskFunction); } /*****************************************************************************/ /* AST-driven output of column headings (these have already generated by DirFormatLayout()). */ DirHeading (REQUEST_STRUCT *rqptr) { int status; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirHeading() !&F", DirHeading); /* error in readme file? */ if (ERROR_REPORTED (rqptr)) { DirEnd (rqptr); return; } /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; if (rqptr->rqPathSet.HtmlHeaderPtr || rqptr->rqPathSet.HtmlHeaderTagPtr) { status = FaolToNet (rqptr, "\n", NULL); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath; rqptr->rqResponse.ErrorOtherTextPtr = tkptr->LayoutFaoPtr; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } } if (tkptr->DirDelimit == MAPURL_DIR_DELIMIT_BOTH || tkptr->DirDelimit == MAPURL_DIR_DELIMIT_HEADER || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { /* header and horizontal line at top of page */ NetWriteBuffered (rqptr, &DirBeginDirectories, tkptr->LayoutHeadingPtr, tkptr->LayoutHeadingLength); return; } else if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { /* just start the preformtted text */ NetWriteBuffered (rqptr, &DirBeginDirectories, "\n
", 6);
   }
   else
      SysDclAst (&DirBeginDirectories, rqptr);
}

/*****************************************************************************/
/*
Begin a listing with directories if not expressly forbidden by configuration 
or local directory-specific configuration files.  If required check if there 
is an accessable parent directory and if so generate a formatted entry for it. 
*/ 
 
DirBeginDirectories (REQUEST_STRUCT *rqptr)

{
   int  status;
   char  *cptr;
   DIR_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirBeginDirectories() !&F", &DirBeginDirectories);

   tkptr = rqptr->DirTaskPtr;

   /*
      Determine if any parent directory or subdirectories will be
      displayed by checking for the presence of directory listing
      control files (and a kludge for user directory mapping).
   */

   /* ensure we can read these directory listing "control" files */
   sys$setprv (1, &SysPrvMask, 0, 0);

   cptr = tkptr->DirectoryPart;

   if (tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL ||
       tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2)
      tkptr->ListParentDir = true;
   else
      tkptr->ListParentDir = false;
   tkptr->ListSubDir = true;
   if (tkptr->AsIfNopFound && tkptr->AsIfNosFound)
      tkptr->ListParentDir = tkptr->ListSubDir = false;
   else
   if (OdsFileExists (cptr, DirNopsFileName) != RMS$_FNF)
      tkptr->ListParentDir = tkptr->ListSubDir = false;
   else
   {
      if (tkptr->AsIfNosFound)
         tkptr->ListSubDir = false;
      else
      if (OdsFileExists (cptr, DirNosFileName) != RMS$_FNF)
         tkptr->ListSubDir = false;

      if (tkptr->AsIfNopFound)
         tkptr->ListParentDir = false;
      else
      if (OdsFileExists (cptr, DirNopFileName) != RMS$_FNF)
         tkptr->ListParentDir = false;
      else
      if (SAME2(tkptr->DirPath,'/~'))
      {
         /*
            This is a kludge to better support user directories ("/~username/").
            It prevents a "top-level" user directory from displaying a parent
            directory even if it is not a "top-level" VMS directory.  It does
            this by looking to see if there is any subdirectory to the "top-
            level" user directory (e.g. "/~username/subdirectory/").
         */

         for (cptr = tkptr->DirPath; *cptr && *cptr != '/'; cptr++);
         if (SAME2(cptr,'/\0')) tkptr->ListParentDir = false;
      }
   }

   sys$setprv (0, &SysPrvMask, 0, 0);

   if (tkptr->ListSubDir)
   {
      AuthAccessEnable (rqptr, tkptr->SearchOds.ExpFileName, AUTH_ACCESS_READ);

      /* OdsParse() NAM$M_NOCONCEAL before OdsSearchNoConceal() */
      OdsParse (&tkptr->SearchOds,
                tkptr->DirectoryPart, 0, "*.DIR;", 6, NAM$M_NOCONCEAL,
                &DirBeginDirectoriesParseAst, rqptr);

      AuthAccessEnable (rqptr, 0, 0);

      return;
   }

   DirBeginDirectoriesParseAst (rqptr);
} 

/*****************************************************************************/
/*
AST called from DirDirectoriesBegin() when the asynchronous parse completes,
or when called explicitly if not listing subdirectories.
*/

DirBeginDirectoriesParseAst (REQUEST_STRUCT *rqptr)

{
   int  status;
   char  *cptr, *sptr;
   char  ParentDirPath [ODS_MAX_FILE_NAME_LENGTH+1],
         ParentDirVms [ODS_MAX_FILE_NAME_LENGTH+1];
   DIR_TASK  *tkptr;
   REQUEST_AST AstFunction;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirBeginDirectoriesParseAst() !&F sts:!&S stv:!&S",
                 &DirBeginDirectoriesParseAst,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_sts,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_stv);

   tkptr = rqptr->DirTaskPtr;

   if (tkptr->ListSubDir)
   {
      /* check the status from the actual sys$parse() */
      if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts))
      {
         rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath;
         rqptr->rqResponse.ErrorOtherTextPtr = tkptr->DirSpec;
         ErrorVmsStatus (rqptr, status, FI_LI);
         DirEnd (rqptr);
         return;
      }
      AstFunction = &DirSearchDirectories;
   }
   else
      AstFunction = &DirBeginFiles;

   if (tkptr->ListParentDir &&
       SAME2(tkptr->DirPath,'/~'))
   {
      /* no parent directories for top-level user directories */
      for (cptr = tkptr->DirPath + 2; *cptr && *cptr != '/'; cptr++);
      if (*cptr) cptr++;
      while (*cptr && *cptr != '/') cptr++;
      if (!*cptr) tkptr->ListParentDir = false;
   }

   if (tkptr->ListParentDir)
   {
      cptr = tkptr->DirectoryPart;
      sptr = ParentDirVms;
      while (*cptr && *cptr != '[') *sptr++ = *cptr++;
      if (MATCH8 (cptr, "[000000]"))
      {
         /* in Master-File-Directory, therefore no parent */
         tkptr->ListParentDir = false;
      }
      else
      {
         /*
            Not in a Master-File-Directory, create a parent directory
            (e.g. "DEVICE:[DIR1.DIR2]" from "DEVICE:[DIR1.DIR2.DIR3]",
            and "DEVICE:[000000]" from "DEVICE:[DIR1]", etc.)
         */
         if (MATCH8 (cptr, "[000000."))
         {
            /* skip over the Master-File-Directory in the specification */
            *sptr++ = *cptr++;
            cptr += 7;
         }
         while (*cptr) *sptr++ = *cptr++;
         *sptr = '\0';
         while (*sptr != ']') sptr--;
         while (*sptr != '[')
         {
            while (*sptr != '.' && *sptr != '[') sptr--;
            if (*sptr == '[' || *(sptr-1) != '^') break;
            sptr--;
         }
         if (*sptr == '.')
         {
            *sptr++ = ']';
            *sptr = '\0';
         }
         else
            memcpy (sptr, "[000000]", 9);

         /*
            If access is allowed display the details.
            Otherwise, its as if the directory just didn't exist!
         */

         /* if the VMS profile allows access, why further prohibit it? */
         if (!CliDemo &&
             !rqptr->rqAuth.SkelKeyAuthenticated &&
             !rqptr->rqAuth.VmsUserProfileLength)
         {
            /* ensure we can read these directory listing "control" files */
            sys$setprv (1, &SysPrvMask, 0, 0);

            if (OdsFileExists (ParentDirVms, DirHiddenFileName) != RMS$_FNF)
               tkptr->ListParentDir = false;
            else
            if ((Config.cfDir.AccessSelective ||
                 rqptr->rqPathSet.DirAccessSelective) &&
                 !rqptr->rqPathSet.DirAccess)
            {
               if (VMSnok (OdsFileExists (ParentDirVms, DirBrowsableFileName)))
                  tkptr->ListParentDir = false;
            }

            sys$setprv (0, &SysPrvMask, 0, 0);
         }
      }

      if (tkptr->ListParentDir)
      {
         ParentDirPath[0] = '\0';
         sptr = MapUrl_Map (ParentDirPath, sizeof(ParentDirPath),
                            ParentDirVms, 0, NULL, 0, NULL, 0, NULL, 0,
                            NULL, rqptr, NULL);
         if (sptr[0])
         {
            DirFormat (rqptr, AstFunction, ParentDirPath, false);
            return;
         }
         /*
            Mapping error, most probably access forbidden, we'll assume that.
            This can happen where a subdirectory is mapable but the parent is
            not (e.g. "pass /parent/subd/* /device/parent/subd/*"), making it
            forbidden to request "/parent/file" or "/parent/*.*".
            Hence, for directory listings, it's as if parent does not exist!
         */
         tkptr->ListParentDir = false;
      }
   }

   if (!tkptr->ListParentDir)
   {
      /* parent directory was not output, explicitly drive the search */
      SysDclAst (AstFunction, rqptr);
   }
}

/*****************************************************************************/
/*
AST function to invoke another sys$search() call when listing directories.
*/ 

DirSearchDirectories (REQUEST_STRUCT *rqptr)

{
   DIR_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirSearchDirectories() !&F !&S",
                 &DirSearchDirectories, rqptr->rqNet.WriteIOsb.Status);

   if (VMSnok (rqptr->rqNet.WriteIOsb.Status))
   {
      /* network write has failed (as AST), bail out now */
      DirEnd (rqptr);
      return;
   }

   /* get the pointer to the task structure */
   tkptr = rqptr->DirTaskPtr;

   AuthAccessEnable (rqptr, tkptr->SearchOds.ExpFileName, AUTH_ACCESS_READ);

   OdsSearchNoConceal (&tkptr->SearchOds, &DirDirectories, rqptr);

   AuthAccessEnable (rqptr, 0, 0);
}

/*****************************************************************************/
/*
AST completion routine called each time sys$search() completes.  It will 
either point to another file name found or have "no more files found" status 
(or an error!).  If a file (directory) has been found check if its a "hidden" 
directory and if not format its details for the listing.
*/ 

DirDirectories (REQUEST_STRUCT  *rqptr)

{
   int  status;
   char  LocalDirectory [ODS_MAX_FILE_NAME_LENGTH+1];
   char  *cptr, *sptr;
   DIR_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD
   HttpdCheckPriv (FI_LI);
#endif /* WATCH_MOD */

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirDirectories() !&F sts:!&S stv:!&S",
                 &DirDirectories,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_sts,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_stv);

   tkptr = rqptr->DirTaskPtr;

   if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts))
   {
      /* if its a search list treat directory not found as if file not found */
      if ((tkptr->SearchOds.Nam_fnb & NAM$M_SEARCH_LIST) && status == RMS$_DNF)
         status = RMS$_FNF;
      
      if (status == RMS$_FNF || status == RMS$_NMF)
      {
         /***************************/
         /* end of directory search */
         /***************************/

         tkptr->SearchOds.ParseInUse = false;
         DirBeginFiles (rqptr);
         return;
      }

      /*************************/
      /* protection violation? */
      /*************************/

      if ((status == SS$_NOPRIV || status == RMS$_PRV) &&
          Config.cfDir.NoPrivIgnore)
      {
         tkptr->SearchOds.ParseInUse = false;
         if (tkptr->FormatLikeVms)
         {
            DirFormat (rqptr, &DirEndOutput, "", true);
            return;
         }
         else
         {
            DirEndOutput (rqptr);
            return;
         }
      }

      /**********************/
      /* sys$search() error */
      /**********************/

      rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath;
      rqptr->rqResponse.ErrorOtherTextPtr = tkptr->SearchOds.NamDevicePtr;
      ErrorVmsStatus (rqptr, status, FI_LI);
      DirEnd (rqptr);
      return;
   }

   /* check if the .DIR is really a directory */
   status = OdsReallyADir (rqptr, &tkptr->SearchOds);
   if (VMSnok (status))
   {
      DirSearchDirectories (rqptr);
      return;
   }

   /* terminate following the last character in the version number */
   tkptr->SearchOds.NamVersionPtr[tkptr->SearchOds.NamVersionLength] = '\0';

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "!&Z", tkptr->SearchOds.NamDevicePtr);

   /************************/
   /* directory access ok? */
   /************************/

   /*
      Create a directory specification from a directory file name.
      (e.g. "DEVICE:[DIR1.DIR2]" from "DEVICE:[DIR1]DIR2.DIR")
   */
   sptr = LocalDirectory;
   cptr = tkptr->SearchOds.NamDevicePtr;
   tkptr->SearchOds.NamTypePtr[0] = '\0';
   while (*cptr) *sptr++ = *cptr++;
   tkptr->SearchOds.NamTypePtr[0] = '.';
   *sptr++ = ']';
   *sptr = '\0';
   sptr -= 2;
   while (*sptr != ']') sptr--;
   *sptr = '.';

   /*
      If access is allowed display the details.
      Otherwise, its as if the directory just didn't exist!
      Access may also be denied if file protections prevent checking!
   */

   if (CliDemo ||
       rqptr->rqAuth.SkelKeyAuthenticated ||
       rqptr->rqAuth.VmsUserProfileLength)
   {
      /* the VMS security profile allows access, why further prohibit it? */
      status = SS$_NORMAL;
   }
   else
   if (VMSok (status = OdsFileExists (LocalDirectory, DirHiddenFileName)))
      status = RMS$_DNF;
   else
   if ((Config.cfDir.AccessSelective ||
        rqptr->rqPathSet.DirAccessSelective) &&
        !rqptr->rqPathSet.DirAccess)
      status = OdsFileExists (LocalDirectory, DirBrowsableFileName);
   else
      status = SS$_NORMAL;

   if (VMSnok (status))
   {
      /* directory shouldn't/couldn't be accessed */
      DirSearchDirectories (rqptr);
      return;
   }

   tkptr->Description[0] = '\0';
   ConfigContentType (&tkptr->ContentInfo, tkptr->SearchOds.NamTypePtr);
   DirFormat (rqptr, &DirSearchDirectories, "", false);
}

/*****************************************************************************/
/*
Initiate the listing of non-directory files.
*/ 

DirBeginFiles (REQUEST_STRUCT *rqptr)

{
   DIR_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirBeginFiles() !&F", &DirBeginFiles);

   tkptr = rqptr->DirTaskPtr;

   AuthAccessEnable (rqptr, tkptr->SearchOds.ExpFileName, AUTH_ACCESS_READ);

   /* OdsParse() NAM$M_NOCONCEAL before OdsSearchNoConceal() */
   OdsParse (&tkptr->SearchOds,
             tkptr->FilePart, 0, tkptr->DirectoryPart, 0, NAM$M_NOCONCEAL,
             &DirBeginFilesParseAst, rqptr);

   AuthAccessEnable (rqptr, 0, 0);
}

/*****************************************************************************/
/*
Asynchronous parse from DirBeginFiles() has completed.
*/ 

DirBeginFilesParseAst (REQUEST_STRUCT *rqptr)

{
   int  status;
   DIR_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirBeginFilesParseAst() !&F sts:!&S stv:!&S",
                 &DirBeginFilesParseAst,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_sts,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_stv);

   tkptr = rqptr->DirTaskPtr;

   /* check the status from the actual sys$parse() */
   if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts))
   {
      rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath;
      rqptr->rqResponse.ErrorOtherTextPtr = tkptr->DirSpec;
      ErrorVmsStatus (rqptr, status, FI_LI);
      DirEnd (rqptr);
      return;
   }

   /* fudge this status for the first call */
   rqptr->rqNet.WriteIOsb.Status = SS$_NORMAL;
   DirSearchFiles (rqptr);
}

/*****************************************************************************/
/*
AST completion routine called each time sys$search() completes.  It will 
either point to another file found or have "no more files found" status 
(or an error!).
*/ 

DirSearchFiles (REQUEST_STRUCT *rqptr)

{
   int  status;
   DIR_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirSearchFiles() !&F !&S",
                 &DirSearchFiles, rqptr->rqNet.WriteIOsb.Status);

   tkptr = rqptr->DirTaskPtr;

   if (VMSnok (rqptr->rqNet.WriteIOsb.Status))
   {
      /* network write has failed (as AST), bail out now */
      DirEnd (rqptr);
      return;
   }

   AuthAccessEnable (rqptr, tkptr->SearchOds.ExpFileName, AUTH_ACCESS_READ);

   OdsSearchNoConceal (&tkptr->SearchOds, &DirFiles, rqptr);

   AuthAccessEnable (rqptr, 0, 0);
}

/*****************************************************************************/
/*
This is an AST completion routine called each time sys$search() completes.  It 
will either point to another file name found or have "no more files found" 
status (or an error!).  If a file call a function to determine if the file 
will contain a "description".  When that function returns just return to the 
AST interrupted routine.
*/ 

DirFiles (REQUEST_STRUCT *rqptr)

{
   int  status;
   char  *cptr, *sptr;
   REQUEST_AST AstFunction;
   DIR_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD
   HttpdCheckPriv (FI_LI);
#endif /* WATCH_MOD */

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirFiles() !&F sts:!&S stv:!&S",
                 &DirFiles,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_sts,
                 rqptr->DirTaskPtr->SearchOds.Fab.fab$l_stv);

   tkptr = rqptr->DirTaskPtr;

   if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts))
   {
      /* if its a search list treat directory not found as if file not found */
      if ((tkptr->SearchOds.Nam_fnb & NAM$M_SEARCH_LIST) &&
          status == RMS$_DNF)
         status = RMS$_FNF;

      if (status == RMS$_FNF || status == RMS$_NMF)
      {
         /**********************/
         /* end of file search */
         /**********************/

         tkptr->SearchOds.ParseInUse = false;
         if (tkptr->FormatLikeVms)
         {
            DirFormat (rqptr, &DirEndOutput, "", true);
            return;
         }
         else
         {
            DirEndOutput (rqptr);
            return;
         }
      }

      /**********************/
      /* sys$search() error */
      /**********************/

      rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath;
      rqptr->rqResponse.ErrorOtherTextPtr = tkptr->SearchOds.NamDevicePtr;
      ErrorVmsStatus (rqptr, status, FI_LI);
      DirEnd (rqptr);
      return;
   }

   /* terminate following the last character in the version number */
   tkptr->SearchOds.NamVersionPtr[tkptr->SearchOds.NamVersionLength] = '\0';

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "!&Z", tkptr->SearchOds.NamDevicePtr);

   /* we've already output the directories, ignore them */
   if (strsame (tkptr->SearchOds.NamTypePtr, ".DIR;", 5))
   {
      /* check if the .DIR is really a directory */
      status = OdsReallyADir (rqptr, &tkptr->SearchOds);
      if (VMSok (status))
      {
         DirSearchFiles (rqptr);
         return;
      }
   }

   /* in true Unix-style "hidden" files do not appear (those beginning ".") */
   if (SAME1(tkptr->SearchOds.NamNamePtr,'.') ||
       SAME2(tkptr->SearchOds.NamNamePtr,'^.'))
   {
      /* except in demo mode or to VMS authenticated and profiled requests */
      if (!CliDemo &&
          !rqptr->rqAuth.SkelKeyAuthenticated &&
          !rqptr->rqAuth.VmsUserProfileLength)
      {
         if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
            WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, ".FILE (hidden)");
         DirSearchFiles (rqptr);
         return;
      }
   }

   /* look for WASD WebDAV meta-data file type */
   for (cptr = tkptr->SearchOds.NamVersionPtr;
        cptr > tkptr->SearchOds.NamTypePtr && !WEBDAV_WASDAV(cptr);
        cptr--);
   if (cptr > tkptr->SearchOds.NamTypePtr)
   {
      /* found one, next file please */
      DirSearchFiles (rqptr);
      return;
   }

   if (tkptr->ThesePtr)
   {
      if (!DirTheseFiles (rqptr))
      {
         /* nope, next file please */
         DirSearchFiles (rqptr);
         return;
      }
   }

   if (tkptr->VersionsOf)
   {
      if (tkptr->VersionLength)
         if (tkptr->VersionLength != tkptr->SearchOds.NamNameLength +
                                     tkptr->SearchOds.NamTypeLength)
            tkptr->VersionLength = 0;

      if (tkptr->VersionLength)
         if (strncmp (tkptr->VersionName,
                      tkptr->SearchOds.NamNamePtr,
                      tkptr->VersionLength))
            tkptr->VersionLength = 0;

      if (!tkptr->VersionLength)
      {
         tkptr->VersionLength = tkptr->SearchOds.NamNameLength +
                                tkptr->SearchOds.NamTypeLength;
         memcpy (tkptr->VersionName,
                 tkptr->SearchOds.NamNamePtr,
                 tkptr->VersionLength);
         tkptr->VersionName[tkptr->VersionLength] = '\0';
         tkptr->VersionCount = 0;
      }
           
      if (tkptr->VersionCount++ >= tkptr->VersionsOf)
      {
         /* enough is enough! */
         DirSearchFiles (rqptr);
         return;
      }
   }

   if (rqptr->PathOds == MAPURL_PATH_ODS_ADS ||
       rqptr->PathOds == MAPURL_PATH_ODS_SMB)
      cptr = MapOdsAdsFileType (tkptr->SearchOds.NamTypePtr);
   else
   if (rqptr->PathOds == MAPURL_PATH_ODS_SRI)
      cptr = MapOdsSriFileType (tkptr->SearchOds.NamTypePtr);
   else
      cptr = tkptr->SearchOds.NamTypePtr;
   ConfigContentType (&tkptr->ContentInfo, cptr);

   if (Config.cfDir.DescriptionLines &&
       ConfigSameContentType (tkptr->ContentInfo.ContentTypePtr,
                              "text/html", -1))
   {
      /* generate an HTML description */
      Description (rqptr,
                   &DirEndDescription, 
                   tkptr->SearchOds.ResFileName,
                   tkptr->Description,
                   sizeof(tkptr->Description),
                   DESCRIPTION_TEXT_HTML);
      return;
   }
   else
   {
      /* description is the content-type */
      tkptr->Description[0] = '\0';
      DirFormat (rqptr, &DirSearchFiles, "", false);
      return;
   }
}

/*****************************************************************************/
/*
*/ 

DirEndDescription (REQUEST_STRUCT *rqptr)

{
   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR))
      WatchThis (rqptr, FI_LI, WATCH_MOD_DIR,
                 "DirEndDescription() !&F", &DirEndDescription);

   DirFormat (rqptr, &DirSearchFiles, "", false);
}

/*****************************************************************************/
/*
Directories have been listed, files have been listed.  If appropriate provide 
a readme at the bottom, then conclude the HTML and the directory listing.
*/ 

DirEndOutput (REQUEST_STRUCT *rqptr)

{
   static $DESCRIPTOR (ColSpanFaoDsc,
"!AZ\

\n\ \n\
\n\0"); static $DESCRIPTOR (DirSortFaoDsc, "!AZ\n\0"); static char DirSort [256]; static $DESCRIPTOR (DirSortDsc, DirSort); int cnt, status; unsigned short slen = 0; char *cptr, *sptr; REQUEST_AST AstFunction; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirEndOutput() !&F", &DirEndOutput); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; if (Config.cfDir.ReadMeBottom && tkptr->IncludeAnyReadme) AstFunction = &DirReadMeBottom; else AstFunction = &DirEnd; if (tkptr->DirDelimit == MAPURL_DIR_DELIMIT_BOTH || tkptr->DirDelimit == MAPURL_DIR_DELIMIT_FOOTER) { if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT) { sys$fao (&ColSpanFaoDsc, &slen, &DirColSpanDsc, tkptr->DirFormatBlockTotals ? "" : "\n\n", tkptr->ColSpanCount+1); cptr = DirColSpan; if (tkptr->DirSort[0]) { sys$fao (&DirSortFaoDsc, &slen, &DirSortDsc, cptr, tkptr->DirSort); cptr = DirSort; } } else if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { if (tkptr->DirFormatBlockTotals) cptr = "\n\n"; else cptr = "\n\n"; slen = 24; if (tkptr->DirSort[0]) { sys$fao (&DirSortFaoDsc, &slen, &DirSortDsc, cptr, tkptr->DirSort); cptr = DirSort; } } else if (tkptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2) { cptr = "\n\n"; slen = 8; } else { cptr = "\n
\n"; slen = 29; } } else if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { if (tkptr->DirFormatBlockTotals) cptr = "\n\n"; else cptr = "\n\n"; slen = 24; if (tkptr->DirSort[0]) { sys$fao (&DirSortFaoDsc, &slen, &DirSortDsc, cptr, tkptr->DirSort); cptr = DirSort; } } else { /* footer not required, just tie-off the preformatted text */ cptr = "\n"; slen = 7; } NetWriteBuffered (rqptr, AstFunction, cptr, slen); } /*****************************************************************************/ /* A series of null-terminated strings (wildcarded file names and/or types) are each matched to the name and type of the currently searched-for file. Return true if the file is matched, false if not. Preceding a string with a '!' negates the match (i.e. not these files). First match is considered a hit. */ BOOL DirTheseFiles (REQUEST_STRUCT *rqptr) { BOOL hit = false, negate = false; char *cptr; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirTheseFiles()"); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; *tkptr->SearchOds.NamVersionPtr = '\0'; cptr = tkptr->ThesePtr; while (*cptr) { if (negate = (*cptr == '!')) cptr++; if (hit = StringMatch (NULL, tkptr->SearchOds.NamNamePtr, cptr)) break; while (*cptr) cptr++; if (*(cptr+1)) cptr++; } *tkptr->SearchOds.NamVersionPtr = ';'; if (negate) return (!hit); return (hit); } /*****************************************************************************/ /* Using the directive string pointed to by 'tkptr->LayoutPtr' format the directory listing column headings and sys$fao() directive string used to format the information for each directory/file. Return normal or error status. This function buffers the details of the default generic and VMS directory layout the first time each is required. There-after the buffered layout information is used, reducing overhead. Only if a user-supplied layout is required does any format processing occur again. */ DirFormatLayout (REQUEST_STRUCT *rqptr) { # define LAYOUT_SIZE 512 static BOOL MakeDescriptionLink, ShowNoType, ShowUpperCase; static int FieldWidthCdt, FieldWidthDescription, FieldWidthName, FieldWidthOwner, FieldWidthRdt, FieldWidthSize, HeadingCreatedLength, HeadingDescriptionLength, HeadingNameLength, HeadingOwnerLength, HeadingProtectionLength, HeadingRevisedLength, HeadingSizeLength, LayoutFaoLength, LayoutHeadingLength, LayoutSizeKilo, VmsFieldWidthCdt, VmsFieldWidthDescription, VmsFieldWidthName, VmsFieldWidthOwner, VmsFieldWidthRdt, VmsFieldWidthSize, VmsLayoutFaoLength, VmsLayoutHeadingLength, VmsLayoutSizeKilo; static char LayoutFao [LAYOUT_SIZE/2], LayoutHeading [LAYOUT_SIZE], VmsLayoutFao [LAYOUT_SIZE/2], VmsLayoutHeading [LAYOUT_SIZE]; static char *HeadingCreatedPtr, *HeadingDescriptionPtr, *HeadingNamePtr, *HeadingOwnerPtr, *HeadingProtectionPtr, *HeadingRevisedPtr, *HeadingSizePtr, *ColumnHeadingsPtr; static $DESCRIPTOR (ColSpanFaoDsc, "\n\
\n\ \n\ \n\0"); BOOL ForbiddenLayout, IconInPlace; int cnt, idx, size; char String [LAYOUT_SIZE]; char *aptr, *cptr, *lptr, *sptr, *wptr, *zptr, *LayoutBufferPtr, *LayoutFaoPtr, *LayoutHeadingPtr; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirFormatLayout()"); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; IconInPlace = false; tkptr->MakeDescriptionLink = false; if (!ColumnHeadingsPtr) { /******************************/ /* initialize column headings */ /******************************/ sptr = MsgFor(rqptr,MSG_DIR_COLUMN_HEADINGS); cptr = ColumnHeadingsPtr = VmGet (strlen(sptr)); strcpy (cptr, sptr); HeadingCreatedPtr = cptr; while (*cptr && *cptr != '|') cptr++; if (*cptr) *cptr++ = '\0'; HeadingCreatedLength = strlen(HeadingCreatedPtr); HeadingDescriptionPtr = cptr; while (*cptr && *cptr != '|') cptr++; if (*cptr) *cptr++ = '\0'; HeadingDescriptionLength = strlen(HeadingDescriptionPtr); HeadingNamePtr = cptr; while (*cptr && *cptr != '|') cptr++; if (*cptr) *cptr++ = '\0'; HeadingNameLength = strlen(HeadingNamePtr); HeadingOwnerPtr = cptr; while (*cptr && *cptr != '|') cptr++; if (*cptr) *cptr++ = '\0'; HeadingOwnerLength = strlen(HeadingOwnerPtr); HeadingProtectionPtr = cptr; while (*cptr && *cptr != '|') cptr++; if (*cptr) *cptr++ = '\0'; HeadingProtectionLength = strlen(HeadingProtectionPtr); HeadingRevisedPtr = cptr; while (*cptr && *cptr != '|') cptr++; if (*cptr) *cptr++ = '\0'; HeadingRevisedLength = strlen(HeadingRevisedPtr); HeadingSizePtr = cptr; while (*cptr && *cptr != '|') cptr++; if (*cptr) *cptr++ = '\0'; HeadingSizeLength = strlen(HeadingSizePtr); } if (!tkptr->LayoutPtr) { /******************/ /* default layout */ /******************/ if (Config.cfDir.DefaultLayout[0]) tkptr->LayoutPtr = Config.cfDir.DefaultLayout; else tkptr->LayoutPtr = DirDefaultLayout; if (tkptr->DirStyle <= 0) { /* default or no style */ if (!tkptr->FormatLikeVms && LayoutFaoLength) { /* default generic heading and fao have already been generated */ tkptr->LayoutFaoPtr = LayoutFao; tkptr->LayoutFaoLength = LayoutFaoLength; tkptr->LayoutHeadingPtr = LayoutHeading; tkptr->LayoutHeadingLength = LayoutHeadingLength; tkptr->FieldWidthCdt = FieldWidthCdt; tkptr->FieldWidthDescription = FieldWidthDescription; tkptr->FieldWidthName = FieldWidthName; tkptr->FieldWidthOwner = FieldWidthOwner; tkptr->FieldWidthRdt = FieldWidthRdt; tkptr->FieldWidthSize = FieldWidthSize; tkptr->SizeKilo = LayoutSizeKilo; tkptr->MakeDescriptionLink = MakeDescriptionLink; tkptr->ShowNoType = ShowNoType; tkptr->ShowUpperCase = ShowUpperCase; if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) { WatchData (LayoutHeading, strlen(LayoutHeading)); WatchData (LayoutFao, strlen(LayoutFao)); } return (SS$_NORMAL); } if (tkptr->FormatLikeVms && VmsLayoutFaoLength) { /* default VMS heading and fao have already been generated */ tkptr->LayoutFaoPtr = VmsLayoutFao; tkptr->LayoutFaoLength = VmsLayoutFaoLength; tkptr->LayoutHeadingPtr = VmsLayoutHeading; tkptr->LayoutHeadingLength = VmsLayoutHeadingLength; tkptr->FieldWidthCdt = VmsFieldWidthCdt; tkptr->FieldWidthDescription = VmsFieldWidthDescription; tkptr->FieldWidthName = VmsFieldWidthName; tkptr->FieldWidthOwner = VmsFieldWidthOwner; tkptr->FieldWidthRdt = VmsFieldWidthRdt; tkptr->FieldWidthSize = VmsFieldWidthSize; tkptr->SizeKilo = VmsLayoutSizeKilo; tkptr->MakeDescriptionLink = MakeDescriptionLink; tkptr->ShowNoType = ShowNoType; tkptr->ShowUpperCase = ShowUpperCase; if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) { WatchData (VmsLayoutHeading, strlen(VmsLayoutHeading)); WatchData (VmsLayoutFao, strlen(VmsLayoutFao)); } return (SS$_NORMAL); } /* default headings and layouts have not been generated ... do it! */ if (tkptr->FormatLikeVms) { /* use the function-internal VMS buffers */ LayoutHeadingPtr = VmsLayoutHeading; LayoutFaoPtr = VmsLayoutFao; } else { /* use the function-internal generic buffers */ LayoutHeadingPtr = LayoutHeading; LayoutFaoPtr = LayoutFao; } } else { /* use scratch space to generate the non-default heading and fao */ LayoutHeadingPtr = LayoutFaoPtr = String; tkptr->SizeKilo = 1024; } } else { /**********************/ /* non-default layout */ /**********************/ /* use scratch space to generate the non-default heading and fao */ LayoutHeadingPtr = LayoutFaoPtr = String; tkptr->SizeKilo = 1024; } if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT) for (cptr = tkptr->LayoutPtr; *cptr; cptr++) if (isupper(*cptr)) tkptr->ColSpanCount++; /********************************/ /* assess layout for forbiddens */ /********************************/ if (!Config.cfDir.OwnerEnabled && !rqptr->rqAuth.VmsUserProfileLength) { /* remove OWNER and PROTECTION directives if found */ ForbiddenLayout = false; cptr = tkptr->LayoutPtr; while (*cptr) { if (*cptr == ':') { cptr++; if (*cptr) cptr++; continue; } if (TOUP(*cptr) == 'P' || TOUP(*cptr) == 'O') { ForbiddenLayout = true; cptr++; continue; } cptr++; } if (ForbiddenLayout) { /* remove forbidden directives creating new layout dynamic buffer */ sptr = LayoutBufferPtr = VmGetHeap (rqptr, LAYOUT_SIZE); zptr = sptr + LAYOUT_SIZE; cptr = tkptr->LayoutPtr; while (*cptr) { if (sptr >= zptr) break; if (*cptr == ':') { if (sptr < zptr) *sptr++ = *cptr++; if (*cptr && sptr < zptr) *sptr++ = *cptr++; continue; } if (TOUP(*cptr) == 'P' || TOUP(*cptr) == 'O') { cptr++; while (*cptr == '_') cptr++; continue; } if (sptr < zptr) *sptr++ = *cptr++; } if (sptr >= zptr) { ErrorGeneralOverflow (rqptr, FI_LI); DirEnd (rqptr); return; } *sptr = '\0'; tkptr->LayoutPtr = LayoutBufferPtr; } } /******************************/ /* first, the column headings */ /******************************/ zptr = (sptr = LayoutHeadingPtr) + LAYOUT_SIZE; lptr = tkptr->LayoutPtr; wptr = ""; switch (tkptr->DirStyle) { case MAPURL_DIR_STYLE_ANCHOR : cptr = "
"; break;
      case MAPURL_DIR_STYLE_ANCHOR2 :
         cptr = "
"; break;
      case MAPURL_DIR_STYLE_HTDIR :
         cptr = "
"; break;
      case MAPURL_DIR_STYLE_HTDIR2 :
         cptr = "
"; break;
      case MAPURL_DIR_STYLE_ORIGINAL :
         cptr = "
"; break;
      case MAPURL_DIR_STYLE_ORIGINAL2 :
         cptr = "
"; break;
      case MAPURL_DIR_STYLE_SORT :
         cptr = "

\n\n"; break;
      case MAPURL_DIR_STYLE_SORT2 :
         cptr = "

\n\n"; break; default : cptr = "
";
   }
   while (*cptr && sptr < zptr) *sptr++ = *cptr++;

   while (*lptr && *lptr != '&' && sptr < zptr)
   {
      if (isdigit(*lptr))
      {
         wptr = lptr;
         while (*lptr && isdigit(*lptr)) lptr++;
         continue;
      }

      if (wptr[0])
      {
         cnt = atoi(wptr);
         if (cnt < 0) cnt = 0;
         wptr = "";
      }
      else
         cnt = 0;

      switch (TOUP(*lptr))
      {
         case ' ' :
         case '_' :
            if (!cnt) cnt = DEFAULT_FIELD_WIDTH_INTERFIELD;
            break;

         case 'C' :
            cptr = HeadingCreatedPtr;
            if (!cnt) cnt = DEFAULT_FIELD_WIDTH_CDT;
            if (cnt < (size = HeadingCreatedLength)) cnt = size;
            if (tkptr->FormatLikeVms) cnt += 5;
            tkptr->FieldWidthCdt = cnt;
            break;

         case 'D' :
            /* flush left */
            cptr = HeadingDescriptionPtr;
            if (cnt)
            {
               if (cnt > 255) cnt = 255;
               if (cnt < (size = HeadingDescriptionLength)) cnt = size;
               tkptr->FieldWidthDescription = cnt;
            }
            else
            {
               cnt = size = HeadingDescriptionLength;
               tkptr->FieldWidthDescription = 0;
            }
            if (lptr[1] == LAYOUT_PARAMETER)
            {
               switch (TOUP(lptr[2]))
               {
                  case 'L' :
                     tkptr->MakeDescriptionLink = true;
                     break;
                  default :
                     /* unknown layout directive character */
                     rqptr->rqResponse.HttpStatus = 501;
                     ErrorGeneral (rqptr, MsgFor(rqptr,MSG_DIR_LAYOUT), FI_LI);
                     return (STS$K_ERROR);
               }
            }
            break;

         case 'I' :  /* icon */
            break;

         case 'L' :
         case 'N' :
            cptr = HeadingNamePtr;
            if (!cnt) cnt = DEFAULT_FIELD_WIDTH_NAME;
            if (cnt < (size = HeadingNameLength)) cnt = size;
            tkptr->FieldWidthName = cnt;
            /* this layout parameter can be used like "L:F:U" */
            idx = 0;
            while (lptr[idx+1] == LAYOUT_PARAMETER)
            {
               switch (TOUP(lptr[idx+2]))
               {
                  case 'F' :
                     /* for backward compatibility just ignore this */
                     break;
                  case 'N' :
                     tkptr->ShowNoType = true;
                     break;
                  case 'U' :
                     tkptr->ShowUpperCase = true;
                     break;
                  default :
                     /* unknown layout directive character */
                     rqptr->rqResponse.HttpStatus = 501;
                     ErrorGeneral (rqptr, MsgFor(rqptr,MSG_DIR_LAYOUT), FI_LI);
                     return (STS$K_ERROR);
               }
               idx += 2;
            }
            break;

         case 'O' :
            cptr = HeadingOwnerPtr;
            if (!cnt) cnt = DEFAULT_FIELD_WIDTH_OWNER;
            if (cnt < (size = HeadingOwnerLength)) cnt = size;
            tkptr->FieldWidthOwner = cnt;
            break;

         case 'P' :
            cptr = HeadingProtectionPtr;
            if (!cnt) cnt = DEFAULT_FIELD_WIDTH_PROTECTION;
            if (cnt < (size = HeadingProtectionLength)) cnt = size;
            tkptr->FieldWidthProtection = cnt;
            break;

         case 'R' :
            cptr = HeadingRevisedPtr;
            if (!cnt) cnt = DEFAULT_FIELD_WIDTH_RDT;
            if (cnt < (size = HeadingRevisedLength)) cnt = size;
            if (tkptr->FormatLikeVms) cnt += 5;
            tkptr->FieldWidthRdt = cnt;
            break;

         case 'S' :
            if (lptr[1] == LAYOUT_PARAMETER)
            {
               switch (TOUP(lptr[2]))
               {
                  case 'D' :
                  case 'F' :
                     if (!cnt) cnt = DEFAULT_FIELD_WIDTH_SIZE;
                     if (cnt < (size = DEFAULT_FIELD_WIDTH_SIZE - 2))
                        cnt = size;
                     break;
                  case 'B' :
                  case 'K' :
                  case 'M' :
                     if (!cnt) cnt = DEFAULT_FIELD_WIDTH_DECIMAL;
                     if (cnt < (size = DEFAULT_FIELD_WIDTH_DECIMAL - 7))
                        cnt = size;
                     break;
                  case 'V' :
                     if (!cnt) cnt = DEFAULT_FIELD_WIDTH_SIZE;
                     if (cnt < (size = DEFAULT_FIELD_WIDTH_SIZE - 2))
                        cnt = size;
                     break;
                  default :
                     /* unknown layout directive character */
                     rqptr->rqResponse.HttpStatus = 501;
                     ErrorGeneral (rqptr, MsgFor(rqptr,MSG_DIR_LAYOUT), FI_LI);
                     return (STS$K_ERROR);
               }
               tkptr->SizeKilo = 1000;
            }
            else
            if (tkptr->FormatLikeVms)
            {
               if (!cnt) cnt = DEFAULT_FIELD_WIDTH_SIZE;
               if (cnt < (size = DEFAULT_FIELD_WIDTH_SIZE - 2)) cnt = size;
               /* expand size field width by 7 for blocks allocated/used */
               cnt += 7;
            }
            else
            {
               if (!cnt) cnt = DEFAULT_FIELD_WIDTH_SIZE;
               if (cnt < (size = DEFAULT_FIELD_WIDTH_SIZE - 2)) cnt = size;
               /* expand size field width by 7 for blocks allocated/used */
               if (tkptr->FormatLikeVms) cnt += 7;
            }
            cptr = HeadingSizePtr;
            tkptr->FieldWidthSize = cnt;
            break;

         case 'U' :
            /* force to upper case, just ignore, MUST be first directive */
            tkptr->ShowUpperCase = true;
            break;

         default :
            /* unknown layout directive character */
            rqptr->rqResponse.HttpStatus = 501;
            ErrorGeneral (rqptr, MsgFor(rqptr,MSG_DIR_LAYOUT), FI_LI);
            return (STS$K_ERROR);
      }

      switch (TOUP(*lptr))
      {
         case ' ' :
         case '_' :
            if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT ||
                tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) break;
            while (cnt-- && sptr < zptr) *sptr++ = ' ';
            break;

         case 'C' :
         case 'R' :
         case 'S' :

            if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT ||
                tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2)
            {
               switch (TOUP(*lptr))
               {
                  case 'C' :
                     aptr = "
"; *aptr && sptr < zptr; *sptr++ = *aptr++); break; case 'D' : case 'L' : case 'N' : case 'O' : case 'P' : if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { switch (TOUP(*lptr)) { case 'D' : aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); else { /* flush the column heading left using spaces */ if (cnt) cnt -= size; while (cnt-- && sptr < zptr) *sptr++ = ' '; } break; case 'I' : if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) cptr = ""; else cptr = ConfigBlankIconPtr; while (*cptr && sptr < zptr) *sptr++ = *cptr++; IconInPlace = true; break; } while (lptr[1] == LAYOUT_PARAMETER) lptr += 2; lptr++; } if (tkptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2) cptr = "\n"; else if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT) { sys$fao (&ColSpanFaoDsc, NULL, &DirColSpanDsc, tkptr->ColSpanCount+1); cptr = DirColSpan; } else if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) cptr = "\n\n\n"; else if (tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR) { if (IconInPlace) cptr = "\n
\n"; else /* when an icon not providing appropriate line spacing to the
*/ cptr = "\n\n
\n"; } else { if (IconInPlace) cptr = "\n
\n"; else /* when an icon not providing appropriate line spacing to the
*/ cptr = "\n\n
\n"; } while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { /* should be enough for all but a deliberate formatting hack! */ rqptr->rqResponse.HttpStatus = 501; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_DIR_LAYOUT), FI_LI); return (STS$K_ERROR); } *sptr = '\0'; cnt = sptr - LayoutHeadingPtr; if (LayoutHeadingPtr == LayoutHeading) { /* first time a generic layout has been required, buffer details */ tkptr->LayoutHeadingPtr = LayoutHeading; tkptr->LayoutHeadingLength = LayoutHeadingLength = cnt; FieldWidthCdt = tkptr->FieldWidthCdt; FieldWidthDescription = tkptr->FieldWidthDescription; FieldWidthName = tkptr->FieldWidthName; FieldWidthOwner = tkptr->FieldWidthOwner; FieldWidthRdt = tkptr->FieldWidthRdt; FieldWidthSize = tkptr->FieldWidthSize; MakeDescriptionLink = tkptr->MakeDescriptionLink; ShowNoType = tkptr->ShowNoType; ShowUpperCase = tkptr->ShowUpperCase; } else if (LayoutHeadingPtr == VmsLayoutHeading) { /* first time a VMS layout has been required, buffer details */ tkptr->LayoutHeadingPtr = VmsLayoutHeading; tkptr->LayoutHeadingLength = VmsLayoutHeadingLength = cnt; VmsFieldWidthCdt = tkptr->FieldWidthCdt; VmsFieldWidthDescription = tkptr->FieldWidthDescription; VmsFieldWidthName = tkptr->FieldWidthName; VmsFieldWidthOwner = tkptr->FieldWidthOwner; VmsFieldWidthRdt = tkptr->FieldWidthRdt; VmsFieldWidthSize = tkptr->FieldWidthSize; MakeDescriptionLink = tkptr->MakeDescriptionLink; ShowNoType = tkptr->ShowNoType; ShowUpperCase = tkptr->ShowUpperCase; } else { /* this is a user-supplied layout, place it in the thread's heap */ tkptr->LayoutHeadingPtr = lptr = VmGetHeap (rqptr, cnt+1); memcpy (lptr, LayoutHeadingPtr, cnt+1); tkptr->LayoutHeadingLength = cnt; } /******************************************/ /* second, the sys$fao() directive string */ /******************************************/ zptr = (sptr = LayoutFaoPtr) + LAYOUT_SIZE / 2; lptr = tkptr->LayoutPtr; wptr = ""; /* accomodate an optional leading new-line (for creating a blank line) */ for (cptr = "!AZ"; *cptr && sptr < zptr; *sptr++ = *cptr++); /* table row for sort */ for (cptr = "!AZ"; *cptr && sptr < zptr; *sptr++ = *cptr++); while (*lptr && *lptr != '&' && sptr < zptr) { if (isdigit(*lptr)) { wptr = lptr; while (*lptr && isdigit(*lptr)) lptr++; continue; } if (wptr[0]) { cnt = atoi(wptr); if (cnt < 0) cnt = 0; wptr = ""; } else cnt = 0; switch (TOUP(*lptr)) { case ' ' : case '_' : if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) break; if (!cnt) cnt = DEFAULT_FIELD_WIDTH_INTERFIELD; while (cnt-- && sptr < zptr) *sptr++ = ' '; break; case 'C' : case 'R' : case 'S' : /* right justify, then value */ if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) cptr = ""; else cptr = "!#"; while (*cptr && sptr < zptr) *sptr++ = *cptr++; break; case 'D' : if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { if (tkptr->MakeDescriptionLink) cptr = ""; else cptr = ""; } else { /* nothing fancy (allow for leading/trailing quotes and link) */ if (tkptr->MakeDescriptionLink) if (tkptr->FieldWidthDescription) cptr = "!AZ!AZ!AZ!AZ!AZ!AZ!#"; else cptr = "!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ"; else if (tkptr->FieldWidthDescription) cptr = "!AZ!#"; else cptr = "!AZ!AZ!AZ"; } while (*cptr && sptr < zptr) *sptr++ = *cptr++; break; case 'I' : /* nothing fancy */ if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) cptr = ""; else cptr = "!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ"; while (*cptr && sptr < zptr) *sptr++ = *cptr++; break; case 'L' : if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) cptr = ""; else /* left justify, then link, name, and name overflow */ cptr = "!AZ!AZ!AZ!AZ!AZ!#"; while (*cptr && sptr < zptr) *sptr++ = *cptr++; break; case 'N' : if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) cptr = ""; else /* left justify, string, plus overflow */ cptr = "!#"; while (*cptr && sptr < zptr) *sptr++ = *cptr++; break; case 'O' : case 'P' : if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) cptr = ""; else /* '!#AZ' will left justify value with spaces */ cptr = "!#AZ"; while (*cptr && sptr < zptr) *sptr++ = *cptr++; break; } if (lptr[1] == LAYOUT_PARAMETER) lptr += 2; lptr++; } /* end table row for sort */ for (cptr = "!AZ"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr >= zptr) { /* should be enough for all but a deliberate formatting hack! */ rqptr->rqResponse.HttpStatus = 501; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_DIR_LAYOUT), FI_LI); return (STS$K_ERROR); } *sptr = '\0'; cnt = sptr - LayoutFaoPtr; if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) { WatchData (LayoutHeadingPtr, strlen(LayoutHeadingPtr)); WatchData (LayoutFaoPtr, strlen(LayoutFaoPtr)); } if (LayoutFaoPtr == LayoutFao) { /* first time a generic layout has been required, buffer details */ tkptr->LayoutFaoPtr = LayoutFao; tkptr->LayoutFaoLength = LayoutFaoLength = cnt; LayoutSizeKilo = tkptr->SizeKilo; } else if (LayoutFaoPtr == VmsLayoutFao) { /* first time a VMS layout has been required, buffer details */ tkptr->LayoutFaoPtr = VmsLayoutFao; tkptr->LayoutFaoLength = VmsLayoutFaoLength = cnt; VmsLayoutSizeKilo = 1024; } else { /* this is a user-supplied layout, place it in the thread's heap */ tkptr->LayoutFaoPtr = lptr = VmGetHeap (rqptr, cnt+1); memcpy (lptr, LayoutFaoPtr, cnt+1); tkptr->LayoutFaoLength = cnt; } return (SS$_NORMAL); } /*****************************************************************************/ /* 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! */ DirFormat ( REQUEST_STRUCT *rqptr, REQUEST_AST AstFunction, char *ParentDirPath, BOOL BlockTotals ) { int status; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirFormat() !AZ !UL", ParentDirPath, BlockTotals); tkptr = rqptr->DirTaskPtr; tkptr->DirFormatAstFunction = AstFunction; tkptr->DirFormatParentDirPath = ParentDirPath; tkptr->DirFormatBlockTotals = BlockTotals; if (ParentDirPath[0] || BlockTotals) { /***************************/ /* do not queue an ACP I/O */ /***************************/ /* explicitly call the AST format routine */ tkptr->SearchOds.FileQio.IOsb.Status = SS$_NORMAL; DirFormatAcpInfoAst (rqptr); return; } AuthAccessEnable (rqptr, tkptr->SearchOds.ExpFileName, AUTH_ACCESS_READ); OdsFileAcpInfo (&tkptr->SearchOds, &DirFormatAcpInfoAst, rqptr); AuthAccessEnable (rqptr, 0, 0); } /****************************************************************************/ /* AST called from OdsFileAcpInfo() when ACP QIO completes. This overly-long (sorry) function formats and outputs a line of information for a parent directory, directory file, non-directory file, or for the final line giving used/allocated totals for a VMS format listing. */ DirFormatAcpInfoAst (REQUEST_STRUCT *rqptr) { static unsigned long AddendZero = 0, FiveTwelve = 512, LibDateTimeContext = 0, LibDateTimeSortTableContext = 0, LibDateTimeVmsContext = 0, LibOutputFormat = LIB$K_OUTPUT_FORMAT; static char Cdt [32], CdtSortTable [16], Rdt [32], RdtSortTable [16]; static $DESCRIPTOR (BlocksFaoDsc, "!UL/!UL"); static $DESCRIPTOR (CdtDsc, Cdt); static $DESCRIPTOR (CdtSortTableDsc, CdtSortTable); static $DESCRIPTOR (DeviceDsc, ""); static $DESCRIPTOR (OutputFormatDsc, "|!DB-!MAAC-!Y4|!H04:!M0|"); static $DESCRIPTOR (OutputFormatSortTableDsc, "|!Y4!MN0!D0!H04!M0!S0|"); static $DESCRIPTOR (OutputFormatVmsDsc, "|!DB-!MAAU-!Y4|!H04:!M0:!S0|"); static $DESCRIPTOR (OwnerFaoDsc, "!%I"); static $DESCRIPTOR (OwnerDsc, ""); static $DESCRIPTOR (RdtDsc, Rdt); static $DESCRIPTOR (RdtSortTableDsc, RdtSortTable); static $DESCRIPTOR (SizeDsc, ""); static $DESCRIPTOR (SizeFaoDsc, "!#* !AZ"); static $DESCRIPTOR (TotalFilesDsc, ""); static $DESCRIPTOR (TotalFilesFaoDsc, "!UL files"); BOOL ThisIsADirectory; int idx, status, Count, DateLength, EncodedNameDelta, MaxIdx; unsigned short AcpChannel, Length; unsigned short NumTime [7]; unsigned long AllocatedVbn, EndOfFileVbn; unsigned long QuadBytes [2]; unsigned long FaoVector [64]; char *cptr, *sptr, *zptr, *IconPtr, *AnchorNameOverflowPtr; char AnchorLink [ODS_MAX_FILE_NAME_LENGTH+1], AnchorName [ODS_MAX_FILE_NAME_LENGTH+1], Description [256], IconAnchor [ODS_MAX_FILE_NAME_LENGTH+1], Owner [64], Protection [64], Size [32], SizeSortTable [32], String [2048], TotalFiles [32]; DIR_TASK *tkptr; unsigned long *ctxptr; /*********/ /* begin */ /*********/ #if WATCH_MOD HttpdCheckPriv (FI_LI); #endif /* WATCH_MOD */ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirFormatAcpInfoAst() !&F !&S", &DirFormatAcpInfoAst, rqptr->DirTaskPtr->SearchOds.FileQio.IOsb.Status); tkptr = rqptr->DirTaskPtr; /* first deassign the channel allocated by OdsFileAcpInfo() */ sys$dassgn (tkptr->SearchOds.FileQio.AcpChannel); if ((status = tkptr->SearchOds.FileQio.IOsb.Status) == SS$_NOSUCHFILE) status = RMS$_FNF; if (VMSnok (status)) { /*************************/ /* protection violation? */ /*************************/ if ((status == SS$_NOPRIV || status == RMS$_PRV) && Config.cfDir.NoPrivIgnore) { /* can't return without queueing the next bit of processing! */ SysDclAst (tkptr->DirFormatAstFunction, rqptr); return; } /*******************/ /* something else! */ /*******************/ rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath; rqptr->rqResponse.ErrorOtherTextPtr = tkptr->SearchOds.NamDevicePtr; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } if (tkptr->DirFormatBlockTotals) ThisIsADirectory = false; else ThisIsADirectory = strsame (tkptr->SearchOds.NamTypePtr, ".DIR;", 5); if (ThisIsADirectory) { /******************************/ /* no need to display the MFD */ /******************************/ if (tkptr->SearchOds.NamNamePtr[0] == '0' && strsame (tkptr->SearchOds.NamNamePtr, "000000.DIR;", 11)) { /* can't return without queueing the next bit of processing! */ SysDclAst (tkptr->DirFormatAstFunction, rqptr); return; } } /****************/ /* begin format */ /****************/ if (!LibDateTimeContext) { /* initialize the date/time formats */ status = lib$init_date_time_context (&LibDateTimeContext, &LibOutputFormat, &OutputFormatDsc); if (VMSok (status)) status = lib$init_date_time_context (&LibDateTimeSortTableContext, &LibOutputFormat, &OutputFormatSortTableDsc); if (VMSok (status)) status = lib$init_date_time_context (&LibDateTimeVmsContext, &LibOutputFormat, &OutputFormatVmsDsc); if (VMSnok (status)) ErrorExitVmsStatus (status, "lib$init_date_time_context()", FI_LI); } /********************************/ /* process file name components */ /********************************/ IconAnchor[0] = '\0'; MaxIdx = sizeof(FaoVector) / sizeof(unsigned long); idx = 0; if (tkptr->DirFormatBlockTotals) { /****************/ /* block totals */ /****************/ /* must be block totals, which requires termination of some sort */ if (idx < MaxIdx) if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) FaoVector[idx++] = "\n\n"; else FaoVector[idx++] = "\n"; } else if (tkptr->DirFormatParentDirPath[0]) { /********************/ /* parent directory */ /********************/ unsigned long LocalFaoVector [32]; unsigned long *vecptr; /* no leading newline required for the parent directory */ if (idx < MaxIdx) FaoVector[idx++] = ""; tkptr->DirectoryCount = -1; vecptr = &LocalFaoVector; /* when the path may be different from base path (e.g. SSI) */ if (tkptr->RealPath[0]) *vecptr++ = tkptr->DirFormatParentDirPath; else *vecptr++ = "../"; /* any file name/type/version from the original spec */ if (tkptr->DirSpecIncludedFilePart || (!Config.cfDir.NoImpliedWildcard && !rqptr->rqPathSet.DirNoImpliedWildcard)) { *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->FilePart; } else { *vecptr++ = 0; *vecptr++ = ""; } if (tkptr->QueryStringPtr) { *vecptr++ = "?httpd=index&!AZ"; *vecptr++ = tkptr->QueryStringPtr; } else *vecptr++ = ""; status = FaolToBuffer (AnchorLink, sizeof(AnchorLink), NULL, "!&%AZ!&%&[AZ!&;&@", &LocalFaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); if (tkptr->FormatLikeVms) memcpy (AnchorName, "[-]", 4); else memcpy (AnchorName, "../", 4); } else if (ThisIsADirectory) { /***************/ /* a directory */ /***************/ unsigned long LocalFaoVector [32]; unsigned long *vecptr; /* no leading newline required for directories */ if (idx < MaxIdx) FaoVector[idx++] = ""; if (tkptr->DirectoryCount <= 0) tkptr->DirectoryCount = 1; else tkptr->DirectoryCount++; /* terminate to remove the file type (".DIR") */ tkptr->SearchOds.NamTypePtr[0] = '\0'; vecptr = &LocalFaoVector; /* when the path may be different from base path (e.g. SSI) */ *vecptr++ = tkptr->RealPath; /* the name of this directory */ *vecptr++ = "!&%&[AZ"; *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->SearchOds.NamNamePtr; /* any file name/type/version from the original spec */ if (tkptr->DirSpecIncludedFilePart || (!Config.cfDir.NoImpliedWildcard && !rqptr->rqPathSet.DirNoImpliedWildcard)) { *vecptr++ = "!&%&[AZ"; *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->FilePart; } else *vecptr++ = ""; if (tkptr->QueryStringPtr) { *vecptr++ = "?httpd=index&!AZ"; *vecptr++ = tkptr->QueryStringPtr; } else *vecptr++ = ""; status = FaolToBuffer (AnchorLink, sizeof(AnchorLink), NULL, "!&%AZ!&@/!&@!&;&@", &LocalFaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); vecptr = &LocalFaoVector; if (tkptr->FormatLikeVms) if (!rqptr->PathOds || rqptr->PathOds == MAPURL_PATH_ODS_2) *vecptr++ = "[.!&;&^AZ]"; else *vecptr++ = "[.!&;AZ]"; else { *vecptr++ = "!&;&[AZ/"; *vecptr++ = rqptr->PathOds; } *vecptr++ = tkptr->SearchOds.NamNamePtr; status = FaolToBuffer (AnchorName, sizeof(AnchorName), NULL, "!&@", &LocalFaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); /* restore the type delimitter */ tkptr->SearchOds.NamTypePtr[0] = '.'; if (tkptr->ShowUpperCase) for (cptr = AnchorName; *cptr; cptr++) *cptr = TOUP(*cptr); } else { /**********/ /* a file */ /**********/ unsigned long LocalFaoVector [32]; unsigned long *vecptr; if (tkptr->DirectoryCount && !tkptr->FileCount) { /* leading newline required for first file after directories */ if (idx < MaxIdx) if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) FaoVector[idx++] = "\n"; else FaoVector[idx++] = ""; } else if (idx < MaxIdx) FaoVector[idx++] = ""; tkptr->FileCount++; /* if appropriate, terminate to remove the version number */ if (!tkptr->FormatLikeVms && !tkptr->VersionsOf) { if (tkptr->SearchOds.NamTypeLength <= 1) tkptr->SearchOds.NamTypePtr[0] = '\0'; else tkptr->SearchOds.NamVersionPtr[0] = '\0'; } /***************/ /* anchor link */ /***************/ vecptr = &LocalFaoVector; if (tkptr->ScriptName[0]) { /* prepend the (optional) script component of the path */ *vecptr++ = "!&%AZ!&%&[AZ"; *vecptr++ = tkptr->ScriptName; /* need the directory portion of the URL for scripting */ *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->DirectoryPathPtr; } else { *vecptr++ = "!&%&[AZ"; /* when the path may be different from base path (e.g. SSI) */ *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->RealPath; } /* file name and type */ *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->SearchOds.NamNamePtr; /* disable auto-scripting by appending a (most-recent) version number */ if (!tkptr->FormatLikeVms && !tkptr->AutoScriptEnabled) *vecptr++ = ";0"; else *vecptr++ = ""; if (tkptr->QueryContentTypePtr && tkptr->QueryContentTypePtr[0]) { /* propagate any request-specified content-type as a query string */ *vecptr++ = "?httpd=content&type=!&%AZ"; *vecptr++ = tkptr->QueryContentTypePtr; } else *vecptr++ = ""; status = FaolToBuffer (AnchorLink, sizeof(AnchorLink), NULL, "!&@!&%&[AZ!AZ!&@", &LocalFaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); /*************/ /* icon link */ /*************/ if (tkptr->IconLinkEnabled && !rqptr->rqPathSet.DirNoIconLink) { vecptr = &LocalFaoVector; *vecptr++ = tkptr->LinkTarget; /* when the path may be different from base path (e.g. SSI) */ *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->RealPath; /* file name and type */ *vecptr++ = rqptr->PathOds; *vecptr++ = tkptr->SearchOds.NamNamePtr; /* disable auto-scripting by appending (most-recent) version number */ if (!tkptr->FormatLikeVms && !tkptr->AutoScriptEnabled) *vecptr++ = ";0"; else *vecptr++ = ""; *vecptr++ = ConfigContentType (NULL, ".TXT"); status = FaolToBuffer (IconAnchor, sizeof(IconAnchor), NULL, "", &LocalFaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); } else IconAnchor[0] = '\0'; /***************/ /* anchor name */ /***************/ vecptr = &LocalFaoVector; if (tkptr->FormatLikeVms) if (!rqptr->PathOds || rqptr->PathOds == MAPURL_PATH_ODS_2) *vecptr++ = "!&;&^AZ"; else *vecptr++ = "!&;AZ"; else { *vecptr++ = "!&;&[AZ"; *vecptr++ = rqptr->PathOds; } *vecptr++ = tkptr->SearchOds.NamNamePtr; status = FaolToBuffer (AnchorName, sizeof(AnchorName), NULL, "!&@", &LocalFaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); /* if appropriate, restore the version delimitter */ if (!tkptr->FormatLikeVms) if (tkptr->SearchOds.NamTypeLength <= 1) tkptr->SearchOds.NamTypePtr[0] = '.'; else tkptr->SearchOds.NamVersionPtr[0] = ';'; if (tkptr->ShowUpperCase) for (cptr = AnchorName; *cptr; cptr++) *cptr = TOUP(*cptr); } if (!tkptr->DirFormatBlockTotals) { /************************/ /* name post-processing */ /************************/ /* count the extra characters introduced by escaping/encoding */ EncodedNameDelta = 0; cptr = AnchorName; while (*cptr && cptr - AnchorName <= tkptr->FieldWidthName + EncodedNameDelta) { if (*cptr != '&') { cptr++; continue; } while (*cptr) { EncodedNameDelta++; cptr++; if (*cptr == ';') break; } } if (cptr - AnchorName > tkptr->FieldWidthName + EncodedNameDelta) { AnchorName[tkptr->FieldWidthName+EncodedNameDelta-1] = '\0'; AnchorNameOverflowPtr = "+"; } else AnchorNameOverflowPtr = ""; } /***********************************/ /* build the sys$faol() paremeters */ /***********************************/ if (idx < MaxIdx) if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) FaoVector[idx++] = ""; else FaoVector[idx++] = ""; cptr = tkptr->LayoutPtr; while (*cptr) { if (!isalpha(*cptr)) { cptr++; continue; } if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "!UL \'!1AZ\' !AZ", idx, cptr, cptr); switch (TOUP(*cptr)) { case 'C' : /*****************/ /* creation date */ /*****************/ if (tkptr->DirFormatParentDirPath[0] || tkptr->DirFormatBlockTotals) { /* make it an empty field */ if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthCdt; if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthCdt; } if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; break; } /* format the creation date/time */ if (tkptr->FormatLikeVms) ctxptr = &LibDateTimeVmsContext; else ctxptr = &LibDateTimeContext; if (VMSnok (status = lib$format_date_time (&CdtDsc, &tkptr->SearchOds.FileQio.CdtBinTime, ctxptr, &DateLength, 0))) { rqptr->rqResponse.ErrorTextPtr = "lib$format_date_time()"; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } Cdt[DateLength] = '\0'; if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { /* custom key as a 64 bit number */ FaoToBuffer (CdtSortTable, sizeof(CdtSortTable), 0, "!@SQ", &tkptr->SearchOds.FileQio.CdtBinTime); if (idx < MaxIdx) FaoVector[idx++] = CdtSortTable; } else { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthCdt; if (idx < MaxIdx) { /* next parameter right justifies the field */ if (tkptr->FieldWidthCdt - DateLength > 0) FaoVector[idx++] = tkptr->FieldWidthCdt - DateLength; else FaoVector[idx++] = 0; } if (idx < MaxIdx) FaoVector[idx++] = ""; } if (idx < MaxIdx) FaoVector[idx++] = Cdt; break; case 'D' : /***************/ /* description */ /***************/ if (tkptr->DirFormatParentDirPath[0]) { /********************/ /* parent directory */ /********************/ if (idx < MaxIdx) FaoVector[idx++] = ""; if (tkptr->MakeDescriptionLink) { if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx && tkptr->FieldWidthDescription) { FaoVector[idx++] = tkptr->FieldWidthDescription; FaoVector[idx++] = tkptr->FieldWidthDescription; } } if (idx < MaxIdx) FaoVector[idx++] = MsgFor(rqptr,MSG_DIR_PARENT); if (idx < MaxIdx && tkptr->MakeDescriptionLink) FaoVector[idx++] = ""; if (idx < MaxIdx && tkptr->FieldWidthDescription) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } else if (tkptr->DirFormatBlockTotals) { /***************************/ /* VMS format block totals */ /***************************/ if (tkptr->DirectoryCount < 0) tkptr->DirectoryCount = 0; TotalFilesDsc.dsc$a_pointer = TotalFiles; TotalFilesDsc.dsc$w_length = sizeof(TotalFiles)-1; sys$fao (&TotalFilesFaoDsc, &Length, &TotalFilesDsc, tkptr->DirectoryCount + tkptr->FileCount); TotalFiles[Length] = '\0'; if (idx < MaxIdx) FaoVector[idx++] = ""; if (tkptr->MakeDescriptionLink) { if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx && tkptr->FieldWidthDescription) { FaoVector[idx++] = tkptr->FieldWidthDescription; FaoVector[idx++] = tkptr->FieldWidthDescription; } } if (idx < MaxIdx) FaoVector[idx++] = TotalFiles; if (idx < MaxIdx && tkptr->MakeDescriptionLink) FaoVector[idx++] = ""; if (idx < MaxIdx && tkptr->FieldWidthDescription) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } else if (ThisIsADirectory) { /*************/ /* directory */ /*************/ if (idx < MaxIdx) FaoVector[idx++] = ""; if (tkptr->MakeDescriptionLink) { if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx && tkptr->FieldWidthDescription) { FaoVector[idx++] = tkptr->FieldWidthDescription; FaoVector[idx++] = tkptr->FieldWidthDescription; } } if (idx < MaxIdx) { if (!tkptr->MsgSubDirPtr) FaoVector[idx++] = tkptr->MsgSubDirPtr = MsgFor(rqptr,MSG_DIR_SUB); else FaoVector[idx++] = tkptr->MsgSubDirPtr; } if (idx < MaxIdx && tkptr->MakeDescriptionLink) FaoVector[idx++] = ""; if (idx < MaxIdx && tkptr->FieldWidthDescription) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } else if (tkptr->Description[0] && tkptr->Description[0] != DESCRIPTION_IMPOSSIBLE) { /******************************/ /* file with HTML description */ /******************************/ if (idx < MaxIdx) FaoVector[idx++] = "\""; if (tkptr->MakeDescriptionLink) { if (idx < MaxIdx) FaoVector[idx++] = "LinkTarget; if (idx < MaxIdx) FaoVector[idx++] = " href=\""; if (idx < MaxIdx) FaoVector[idx++] = AnchorLink; if (idx < MaxIdx) FaoVector[idx++] = "\">"; } if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx && tkptr->FieldWidthDescription) { /* the 4 - 1 allows for the leading quote */ if (tkptr->MakeDescriptionLink) FaoVector[idx++] = tkptr->FieldWidthDescription + 3; else FaoVector[idx++] = tkptr->FieldWidthDescription - 1; if ((Count = strlen(tkptr->Description)) > tkptr->FieldWidthDescription - 3) FaoVector[idx++] = tkptr->FieldWidthDescription - 3; else FaoVector[idx++] = Count; } } if (idx < MaxIdx) FaoVector[idx++] = tkptr->Description; if (idx < MaxIdx && tkptr->MakeDescriptionLink) FaoVector[idx++] = ""; if (idx < MaxIdx && tkptr->FieldWidthDescription) { if (Count > tkptr->FieldWidthDescription - 3) FaoVector[idx++] = "+"; else FaoVector[idx++] = ""; } if (idx < MaxIdx) FaoVector[idx++] = "\""; } else { /*********************************/ /* file with content description */ /*********************************/ if (idx < MaxIdx) FaoVector[idx++] = ""; if (tkptr->MakeDescriptionLink) { if (idx < MaxIdx) FaoVector[idx++] = "LinkTarget; if (idx < MaxIdx) FaoVector[idx++] = " href=\""; if (idx < MaxIdx) FaoVector[idx++] = AnchorLink; if (idx < MaxIdx) FaoVector[idx++] = "\">"; } if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx && tkptr->FieldWidthDescription) { if (tkptr->MakeDescriptionLink) FaoVector[idx++] = tkptr->FieldWidthDescription + 4; else FaoVector[idx++] = tkptr->FieldWidthDescription; if ((Count = strlen(tkptr->ContentInfo.DescriptionPtr)) > tkptr->FieldWidthDescription - 1) FaoVector[idx++] = tkptr->FieldWidthDescription - 1; else FaoVector[idx++] = Count; } } if (idx < MaxIdx) FaoVector[idx++] = tkptr->ContentInfo.DescriptionPtr; if (idx < MaxIdx && tkptr->MakeDescriptionLink) FaoVector[idx++] = ""; if (idx < MaxIdx && tkptr->FieldWidthDescription) { if (Count > tkptr->FieldWidthDescription - 3) FaoVector[idx++] = "+"; else FaoVector[idx++] = ""; } if (idx < MaxIdx) FaoVector[idx++] = ""; } break; case 'I' : /********/ /* icon */ /********/ if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) if (idx < MaxIdx) FaoVector[idx++] = tkptr->DirectoryCount + tkptr->FileCount; if (tkptr->DirFormatBlockTotals) { if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } else { if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; } if (idx < MaxIdx) if (IconAnchor[0]) FaoVector[idx++] = IconAnchor; else FaoVector[idx++] = ""; if (tkptr->DirFormatBlockTotals) IconPtr = ConfigBlankIconPtr; else if (ThisIsADirectory) IconPtr = ConfigDirIconPtr; else if (tkptr->DirFormatParentDirPath[0]) IconPtr = ConfigParentIconPtr; else IconPtr = tkptr->ContentInfo.IconPtr; if (idx < MaxIdx) FaoVector[idx++] = IconPtr; if (idx < MaxIdx) if (IconAnchor[0]) FaoVector[idx++] = ""; else FaoVector[idx++] = ""; break; case 'L' : /***************/ /* link/anchor */ /***************/ if (tkptr->DirFormatBlockTotals) { /* make it an empty field */ if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthName; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; break; } if (idx < MaxIdx) FaoVector[idx++] = "LinkTarget; if (idx < MaxIdx) FaoVector[idx++] = " href=\""; if (idx < MaxIdx) FaoVector[idx++] = AnchorLink; if (idx < MaxIdx) FaoVector[idx++] = "\">"; if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthName + EncodedNameDelta + 4; if (idx < MaxIdx) FaoVector[idx++] = AnchorName; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = AnchorNameOverflowPtr; break; case 'N' : /********/ /* name */ /********/ if (tkptr->DirFormatBlockTotals) { /* make it an empty field */ if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthName; if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; break; } if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthName + EncodedNameDelta; if (idx < MaxIdx) FaoVector[idx++] = AnchorName; if (idx < MaxIdx) FaoVector[idx++] = AnchorNameOverflowPtr; break; case 'O' : /*********/ /* owner */ /*********/ if (tkptr->DirFormatParentDirPath[0] || tkptr->DirFormatBlockTotals) { /* make it an empty field */ if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthOwner; if (idx < MaxIdx) FaoVector[idx++] = ""; break; } OwnerDsc.dsc$a_pointer = Owner; OwnerDsc.dsc$w_length = sizeof(Owner)-1; sys$fao (&OwnerFaoDsc, &Length, &OwnerDsc, tkptr->SearchOds.FileQio.AtrUic); Owner[Length] = '\0'; if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthOwner; if (idx < MaxIdx) FaoVector[idx++] = Owner; break; case 'P' : /**************/ /* protection */ /**************/ if (tkptr->DirFormatParentDirPath[0] || tkptr->DirFormatBlockTotals) { /* make it an empty field */ if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthProtection; if (idx < MaxIdx) FaoVector[idx++] = ""; break; } FormatProtection (tkptr->SearchOds.FileQio.AtrFpro, Protection); if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthProtection; if (idx < MaxIdx) FaoVector[idx++] = Protection; break; case 'R' : /*****************/ /* revision date */ /*****************/ if (tkptr->DirFormatParentDirPath[0] || tkptr->DirFormatBlockTotals) { /* make it an empty field */ if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthRdt; if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthRdt; } if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; break; } /* format the revision date/time */ if (tkptr->FormatLikeVms) ctxptr = &LibDateTimeVmsContext; else ctxptr = &LibDateTimeContext; if (VMSnok (status = lib$format_date_time (&RdtDsc, &tkptr->SearchOds.FileQio.RdtBinTime, ctxptr, &DateLength, 0))) { rqptr->rqResponse.ErrorTextPtr = "lib$format_date_time()"; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } Rdt[DateLength] = '\0'; if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { /* custom key as a 64 bit number */ FaoToBuffer (RdtSortTable, sizeof(RdtSortTable), 0, "!@SQ", &tkptr->SearchOds.FileQio.RdtBinTime); if (idx < MaxIdx) FaoVector[idx++] = RdtSortTable; } else { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthRdt; /* next parameter right justifies the field */ if (tkptr->FieldWidthRdt - DateLength > 0) { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthRdt - DateLength; } else if (idx < MaxIdx) FaoVector[idx++] = 0; if (idx < MaxIdx) FaoVector[idx++] = ""; } if (idx < MaxIdx) FaoVector[idx++] = Rdt; break; case 'S' : /********/ /* size */ /********/ if (tkptr->DirFormatParentDirPath[0]) { /* make it an empty field */ if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT && tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthSize; if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthSize; } if (idx < MaxIdx) FaoVector[idx++] = ""; if (idx < MaxIdx) FaoVector[idx++] = ""; break; } SizeDsc.dsc$a_pointer = Size; SizeDsc.dsc$w_length = sizeof(Size)-1; if (tkptr->FieldWidthSize >= sizeof(Size)-1) SizeDsc.dsc$w_length = sizeof(Size)-1; else SizeDsc.dsc$w_length = tkptr->FieldWidthSize; if (tkptr->DirFormatBlockTotals) { if (tkptr->LayoutPtr == Config.cfDir.DefaultLayout || tkptr->LayoutPtr == DirDefaultLayout) sptr = ""; else for (sptr = tkptr->LayoutPtr; *sptr && TOUP(*sptr) != 'S'; sptr++); if (sptr[0] && sptr[1] == LAYOUT_PARAMETER) { unsigned long BlocksUsed; BlocksUsed = tkptr->TotalUsedBlocks; status = lib$emul (&FiveTwelve, &BlocksUsed, &AddendZero, &QuadBytes); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); Length = DirFormatSize (&QuadBytes, sptr[2], &SizeDsc); } else sys$fao (&BlocksFaoDsc, &Length, &SizeDsc, tkptr->TotalUsedBlocks, tkptr->TotalAllocatedBlocks); } else { AllocatedVbn = ((tkptr->SearchOds.FileQio.RecAttr.fat$l_hiblk & 0xffff) << 16) | ((tkptr->SearchOds.FileQio.RecAttr.fat$l_hiblk & 0xffff0000) >> 16); EndOfFileVbn = ((tkptr->SearchOds.FileQio.RecAttr.fat$l_efblk & 0xffff) << 16) | ((tkptr->SearchOds.FileQio.RecAttr.fat$l_efblk & 0xffff0000) >> 16); if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "AllocatedVbn:!UL EndOfFileVbn:!UL FirstFreeByte:!UL", AllocatedVbn, EndOfFileVbn, tkptr->SearchOds.FileQio.RecAttr.fat$w_ffbyte); if (EndOfFileVbn <= 1) { QuadBytes[0] = tkptr->SearchOds.FileQio.RecAttr.fat$w_ffbyte; QuadBytes[1] = 0; } else { unsigned long BlocksUsed, FFbyte; BlocksUsed = EndOfFileVbn - 1; FFbyte = tkptr->SearchOds.FileQio.RecAttr.fat$w_ffbyte; status = lib$emul (&FiveTwelve, &BlocksUsed, &FFbyte, &QuadBytes); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } if (tkptr->FormatLikeVms && (tkptr->LayoutPtr == Config.cfDir.DefaultLayout || tkptr->LayoutPtr == DirDefaultLayout)) sys$fao (&BlocksFaoDsc, &Length, &SizeDsc, EndOfFileVbn, AllocatedVbn); else if (cptr[1] == LAYOUT_PARAMETER) Length = DirFormatSize (&QuadBytes, cptr[2], &SizeDsc); else Length = DirFormatSize (&QuadBytes, 0, &SizeDsc); tkptr->TotalAllocatedBlocks += AllocatedVbn; tkptr->TotalUsedBlocks += EndOfFileVbn; } Size[Length] = '\0'; if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) { /* custom key as 64 bit number */ FaoToBuffer (SizeSortTable, sizeof(SizeSortTable), 0, "!@SQ", &QuadBytes); if (idx < MaxIdx) FaoVector[idx++] = SizeSortTable; } else { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthSize; /* next parameter right justifies the field */ if (tkptr->FieldWidthSize - Length > 0) { if (idx < MaxIdx) FaoVector[idx++] = tkptr->FieldWidthSize - Length; } else if (idx < MaxIdx) FaoVector[idx++] = 0; if (idx < MaxIdx) FaoVector[idx++] = ""; } if (idx < MaxIdx) FaoVector[idx++] = Size; break; } if (cptr[1] == LAYOUT_PARAMETER) cptr += 2; cptr++; if (idx >= MaxIdx) { /* should be enough for all but a deliberate formatting hack! */ rqptr->rqResponse.HttpStatus = 501; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_DIR_LAYOUT), FI_LI); DirEnd (rqptr); return; } } if (idx < MaxIdx) if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) FaoVector[idx++] = "\n"; else FaoVector[idx++] = "\n"; if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "!UL !AZ", idx, tkptr->LayoutFaoPtr); /*********************/ /* format and output */ /*********************/ status = FaolToNet (rqptr, tkptr->LayoutFaoPtr, &FaoVector); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.ErrorTextPtr = tkptr->DirPath; rqptr->rqResponse.ErrorOtherTextPtr = tkptr->LayoutFaoPtr; ErrorVmsStatus (rqptr, status, FI_LI); DirEnd (rqptr); return; } /* empty any file-internal description previously generated */ tkptr->Description[0] = '\0'; NetWritePartFlush (rqptr, tkptr->DirFormatAstFunction); } /*****************************************************************************/ /* Write a string into the supplied descriptor containing the size specified by the 'Bytes' parameter and the format string character 'FormatChar'. Return the number of bytes written. Does not null-terminate the string! Relies on the calling routine to do that!! */ int DirFormatSize ( unsigned long *QuadBytesPtr, char FormatChar, struct dsc$descriptor *SizeDscPtr ) { static $DESCRIPTOR (BytesFaoDsc, "!UL"); static $DESCRIPTOR (BlocksFaoDsc, "!UL"); static $DESCRIPTOR (GFractFaoDsc, "!UL.!3ZLG"); static $DESCRIPTOR (GFaoDsc, "!ULG"); static $DESCRIPTOR (KBytesFaoDsc, "!ULK"); static $DESCRIPTOR (KBytesFractFaoDsc, "!UL.!ULK"); static $DESCRIPTOR (KBytesFullFractFaoDsc, "!UL.!3ZLK"); static $DESCRIPTOR (MBytesFaoDsc, "!ULM"); static $DESCRIPTOR (MBytesFractFaoDsc, "!UL.!ULM"); static $DESCRIPTOR (MBytesFullFractFaoDsc, "!UL.!6ZLM"); static $DESCRIPTOR (MFractFaoDsc, "!UL.!3ZLM"); static $DESCRIPTOR (OverflowFaoDsc, "!256**"); static $DESCRIPTOR (StatusFaoDsc, "%X!8XL"); int status; unsigned short Length; unsigned long EdivDivisor, EdivQuotient, EdivRemainder; char ch; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (NULL, FI_LI, WATCH_MOD_DIR, "DirFormatSize() \'!&C\' !&,@SQ", FormatChar, QuadBytesPtr); Length = 0; switch (TOUP(FormatChar)) { case 'D' : if (QuadBytesPtr[1] || QuadBytesPtr[0] >= 1000000) { EdivDivisor = 1000000; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); if (VMSok (status)) status = sys$fao (&MBytesFaoDsc, &Length, SizeDscPtr, EdivQuotient); } else if (QuadBytesPtr[0] >= 1000) status = sys$fao (&KBytesFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0] / 1000); else status = sys$fao (&BytesFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0]); break; case 'F' : if (QuadBytesPtr[1] || QuadBytesPtr[0] >= 1000000) { EdivDivisor = 1000000; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); if (VMSok (status)) status = sys$fao (&MBytesFractFaoDsc, &Length, SizeDscPtr, EdivQuotient, EdivRemainder); } else if (QuadBytesPtr[0] >= 1000) status = sys$fao (&KBytesFractFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0] / 1000, (QuadBytesPtr[0] % 1000) / 100); else status = sys$fao (&BytesFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0]); break; case 'B' : status = FaoToBuffer (SizeDscPtr->dsc$a_pointer, SizeDscPtr->dsc$w_length, &Length, "!&,@SQ", QuadBytesPtr); break; case 'K' : if (QuadBytesPtr[1]) { EdivDivisor = 1000; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); if (VMSok (status)) status = sys$fao (&KBytesFractFaoDsc, &Length, SizeDscPtr, EdivQuotient, EdivRemainder); } else status = sys$fao (&KBytesFullFractFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0] / 1000, QuadBytesPtr[0] % 1000); break; case 'M' : if (QuadBytesPtr[1]) { EdivDivisor = 1000000; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); if (VMSok (status)) status = sys$fao (&MBytesFullFractFaoDsc, &Length, SizeDscPtr, EdivQuotient, EdivRemainder); } else status = sys$fao (&MBytesFullFractFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0] / 1000000, QuadBytesPtr[0] % 1000000); break; case 'V' : if (QuadBytesPtr[1]) { EdivDivisor = 512; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); if (VMSok (status)) status = sys$fao (&BlocksFaoDsc, &Length, SizeDscPtr, EdivQuotient ? EdivQuotient / 512 + 1 : 0); } else status = sys$fao (&BlocksFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0] ? QuadBytesPtr[0] / 512 + 1 : 0); break; default : if (QuadBytesPtr[1] || QuadBytesPtr[0] >= 1048576) { EdivDivisor = 1048576; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); if (VMSok (status)) status = sys$fao (&MBytesFaoDsc, &Length, SizeDscPtr, EdivQuotient); } else if (QuadBytesPtr[0] >= 1024) status = sys$fao (&KBytesFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0] >> 10); else status = sys$fao (&BytesFaoDsc, &Length, SizeDscPtr, QuadBytesPtr[0]); } /* fallback, first to Mbytes then to Gbytes */ if (status == SS$_BUFFEROVF) { EdivDivisor = 1048576; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); while (EdivRemainder > 999) EdivRemainder /= 10; if (VMSok (status)) status = sys$fao (&MFractFaoDsc, &Length, SizeDscPtr, EdivQuotient, EdivRemainder); } if (status == SS$_BUFFEROVF) { EdivDivisor = 1073741824; status = lib$ediv (&EdivDivisor, QuadBytesPtr, &EdivQuotient, &EdivRemainder); while (EdivRemainder > 999) EdivRemainder /= 10; if (VMSok (status)) status = sys$fao (&GFractFaoDsc, &Length, SizeDscPtr, EdivQuotient, EdivRemainder); } if (status == SS$_BUFFEROVF) status = sys$fao (&GFaoDsc, &Length, SizeDscPtr, EdivQuotient); if (VMSnok(status) && status != SS$_BUFFEROVF) sys$fao (&StatusFaoDsc, &Length, SizeDscPtr, status); if (status == SS$_BUFFEROVF) sys$fao (&OverflowFaoDsc, &Length, SizeDscPtr); return (Length); } /*****************************************************************************/ /* Process a string of directory directives. The directives can be '&' separated (query string) or '\n' separated (.WWW_WASD) file based. */ DirDirectString ( REQUEST_STRUCT *rqptr, char *DirectString ) { BOOL LocalDirect; int WwwWasd; char *aptr, *cptr, *sptr, *zptr; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchData (DirectString, strlen(DirectString)); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; /* override is always false unless the current source allows it */ tkptr->QueryOverride = false; LocalDirect = false; WwwWasd = 0; cptr = DirectString; while (*cptr) { while (*cptr && (*cptr == '&' || *cptr == ' ' || *cptr == '\n')) { if (*cptr == '\n') WwwWasd++; cptr++; } if (!*cptr) break; if (*cptr == '#') { /* .WWW_WASD file can contain comments */ while (*cptr && *cptr != '\n') cptr++; WwwWasd++; continue; } if (TOLO(*cptr) == 'a' && strsame (cptr, "autoscript=", 11)) { cptr += 11; /* if "false", "no" or "0" then turn off auto-scripting */ if (TOLO(*cptr) == 'f' || TOLO(*cptr) == 'n' || *cptr == '0') tkptr->AutoScriptEnabled = false; else tkptr->AutoScriptEnabled = true; } else if (TOLO(*cptr) == 'd' && strsame (cptr, "delimit=", 8)) { cptr += 8; switch (TOLO(*cptr)) { case 'h' : tkptr->DirDelimit = MAPURL_DIR_DELIMIT_HEADER; break; case 'f' : tkptr->DirDelimit = MAPURL_DIR_DELIMIT_FOOTER; break; case 'n' : tkptr->DirDelimit = MAPURL_DIR_DELIMIT_NONE; break; default : tkptr->DirDelimit = MAPURL_DIR_DELIMIT_BOTH; } } else /* experience shows both make it easier! */ if (TOLO(*cptr) == 'e' && (strsame (cptr, "expire=", 7) || strsame (cptr, "expired=", 8))) { if (cptr[7] == '=') cptr += 8; else cptr += 7; /* "true", "yes", "1", "false", "no" or "0" */ if (TOLO(*cptr) == 't' || TOLO(*cptr) == 'y' || *cptr == '1') rqptr->rqResponse.PreExpired = true; else if (TOLO(*cptr) == 'f' || TOLO(*cptr) == 'n' || *cptr == '0') rqptr->rqResponse.PreExpired = false; } else if (TOLO(*cptr) == 'i' && strsame (cptr, "ilink=", 6)) { cptr += 6; /* if "false", "no" or "0" then icon is not a plain-text link */ if (TOLO(*cptr) == 'f' || TOLO(*cptr) == 'n' || *cptr == '0') tkptr->IconLinkEnabled = false; else tkptr->IconLinkEnabled = true; } else if (TOLO(*cptr) == 'l' && strsame (cptr, "layout=", 7)) { cptr += 7; for (sptr = cptr; *sptr && *sptr != '&' && *sptr != '\n'; sptr++); tkptr->LayoutPtr = sptr = VmGetHeap (rqptr, sptr-cptr+1); while (*cptr && *cptr != '&' && *cptr != '\n') *sptr++ = *cptr++; } else if (TOLO(*cptr) == 'l' && strsame (cptr, "local=", 6)) { cptr += 6; /* if "false", "no" or "0" then turn off auto-scripting */ if (TOLO(*cptr) == 'f' || TOLO(*cptr) == 'n' || *cptr == '0') LocalDirect = false; else LocalDirect = true; } else if (TOLO(*cptr) == 'n' && strsame (cptr, "notype=", 7)) { cptr += 7; /* if "true", "yes" or "1" then do not display parent directory */ if (TOLO(*cptr) == 't' || TOLO(*cptr) == 'y' || *cptr == '1') tkptr->ShowNoType = true; else tkptr->ShowNoType = false; } else if (TOLO(*cptr) == 'n' && strsame (cptr, "nop=", 4)) { cptr += 4; /* if "true", "yes" or "1" then do not display parent directory */ if (TOLO(*cptr) == 't' || TOLO(*cptr) == 'y' || *cptr == '1') tkptr->AsIfNopFound = true; else tkptr->AsIfNopFound = false; } else if (TOLO(*cptr) == 'n' && strsame (cptr, "nops=", 5)) { cptr += 5; /* if "true", "yes" or "1" don't display parent or sub directory */ if (TOLO(*cptr) == 't' || TOLO(*cptr) == 'y' || *cptr == '1') tkptr->AsIfNopFound = tkptr->AsIfNosFound = true; else tkptr->AsIfNopFound = tkptr->AsIfNosFound = false; } else if (TOLO(*cptr) == 'n' && strsame (cptr, "nos=", 4)) { cptr += 4; /* if "true", "yes" or "1" then do not display sub directory */ if (TOLO(*cptr) == 't' || TOLO(*cptr) == 'y' || *cptr == '1') tkptr->AsIfNosFound = true; else tkptr->AsIfNosFound = false; } else if (TOLO(*cptr) == 'o' && strsame (cptr, "override=", 9)) { cptr += 9; /* if "true", "yes" or "1" then allow query string to override */ if (TOLO(*cptr) == 't' || TOLO(*cptr) == 'y' || *cptr == '1') tkptr->QueryOverride = true; else tkptr->QueryOverride = false; } else if (TOLO(*cptr) == 'q' && strsame (cptr, "query=", 6)) { cptr += 6; /* this directive is always terminated only by end-of-line */ for (sptr = cptr; *sptr && *sptr != '\n'; sptr++); tkptr->QueryStringPtr = sptr = VmGetHeap (rqptr, sptr-cptr+1); while (*cptr && *cptr != '\n') *sptr++ = *cptr++; } else if (TOLO(*cptr) == 'r' && strsame (cptr, "readme=", 7)) { cptr += 7; /* if "false", "no" or "0" then do not display any readme */ if (TOLO(*cptr) == 'f' || TOLO(*cptr) == 'n' || *cptr == '0') tkptr->IncludeAnyReadme = false; else tkptr->IncludeAnyReadme = true; } else if (TOLO(*cptr) == 's' && strsame (cptr, "script=", 7)) { cptr += 7; zptr = (sptr = tkptr->ScriptName) + sizeof(tkptr->ScriptName)-1; if (*cptr != '/') *sptr++ = '/'; while (*cptr && *cptr != '&' && *cptr != '\n' && sptr < zptr) { if (*cptr != '&' && *cptr != '+') *sptr++ = *cptr; cptr++; } *sptr = '\0'; } else if (TOLO(*cptr) == 's' && strsame (cptr, "sort=", 5)) { cptr += 5; tkptr->DirSort[0] = TOUP(*cptr++); if (*cptr == '+' || *cptr == '-') tkptr->DirSort[1] = *cptr; else tkptr->DirSort[1] = '\0'; /* style is implicit! */ if (tkptr->DirStyle != MAPURL_DIR_STYLE_SORT2) tkptr->DirStyle = MAPURL_DIR_STYLE_SORT; } else if (TOLO(*cptr) == 's' && strsame (cptr, "style=", 6)) { cptr += 6; if (strsame (cptr, "sort2", 5)) tkptr->DirStyle = MAPURL_DIR_STYLE_SORT2; else if (strsame (cptr, "sort", 4)) tkptr->DirStyle = MAPURL_DIR_STYLE_SORT; else if (strsame (cptr, "anchor2", 7)) tkptr->DirStyle = MAPURL_DIR_STYLE_ANCHOR2; else if (strsame (cptr, "anchor", 6)) tkptr->DirStyle = MAPURL_DIR_STYLE_ANCHOR; else if (strsame (cptr, "htdir2", 6)) tkptr->DirStyle = MAPURL_DIR_STYLE_HTDIR2; else if (strsame (cptr, "htdir", 5)) tkptr->DirStyle = MAPURL_DIR_STYLE_HTDIR; else if (strsame (cptr, "original2", 9)) tkptr->DirStyle = MAPURL_DIR_STYLE_ORIGINAL2; else if (strsame (cptr, "original", 8)) tkptr->DirStyle = MAPURL_DIR_STYLE_ORIGINAL; else if (strsame (cptr, "default2", 8)) tkptr->DirStyle = MAPURL_DIR_STYLE_DEFAULT2; else if (strsame (cptr, "default", 7)) tkptr->DirStyle = MAPURL_DIR_STYLE_DEFAULT; } else if (TOLO(*cptr) == 't' && strsame (cptr, "target=", 7)) { cptr += 7; zptr = (sptr = tkptr->LinkTarget) + sizeof(tkptr->LinkTarget)-2; for (aptr = " target=\""; *aptr; *sptr++ = *aptr++); while (*cptr && *cptr != '&' && *cptr != '\n' && sptr < zptr) { if (*cptr != '\"') *sptr++ = *cptr; cptr++; } *sptr++ = '\"'; *sptr = '\0'; } else if (TOLO(*cptr) == 't' && strsame (cptr, "these=", 6)) { cptr += 6; for (sptr = cptr; *sptr && *sptr != '&' && *sptr != '\n'; sptr++); tkptr->ThesePtr = sptr = VmGetHeap (rqptr, sptr-cptr+2); /* null-separated series of strings terminated by empty string */ while (*cptr && *cptr != '&' && *cptr != '\n') { if (*cptr == ',') *sptr++ = '\0'; else *sptr++ = *cptr; cptr++; } } else if (TOLO(*cptr) == 't' && strsame (cptr, "type=", 5)) { cptr += 5; for (sptr = cptr; *sptr && *sptr != '&' && *sptr != '\n'; sptr++); tkptr->QueryContentTypePtr = sptr = VmGetHeap (rqptr, sptr-cptr+1); while (*cptr && *cptr != '&' && *cptr != '\n') *sptr++ = *cptr++; } else if (TOLO(*cptr) == 'u' && strsame (cptr, "upper=", 6)) { cptr += 6; /* if "true", "yes" or "1" then force to upper case */ if (TOLO(*cptr) == 't' || TOLO(*cptr) == 'y' || *cptr == '1') tkptr->ShowUpperCase = true; else tkptr->ShowUpperCase = false; } else if (TOLO(*cptr) == 'v' && strsame (cptr, "versions=", 9)) { cptr += 9; if (*cptr == '*') tkptr->VersionsOf = 65536; else tkptr->VersionsOf = atoi(cptr); } else if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, WATCH_RESPONSE, "ERROR:!AZ", cptr); while (*cptr && *cptr != '&' && *cptr != '\n') cptr++; } if (!WwwWasd && !LocalDirect) { /* propagate URI query string directive(s) */ tkptr->QueryStringPtr = sptr = VmGetHeap (rqptr, cptr-DirectString+1); cptr = DirectString; while (*cptr && *cptr != '\n') *sptr++ = *cptr++; } } /*****************************************************************************/ /* Open, read, and process the contents of the .WWW_WASD file. */ int DirWwwWasd (REQUEST_STRUCT *rqptr) { int status, SizeInBytes; char *sptr; char ReadBuffer [2048]; DIR_TASK *tkptr; ODS_STRUCT OdsStruct; ODS_STRUCT *odsptr = &OdsStruct; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirWwwWasd()"); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; for (sptr = tkptr->DirSpec; *sptr; sptr++); while (sptr > tkptr->DirSpec && (*sptr != ']' || *(sptr-1) == '^]')) sptr--; if (*sptr == ']') sptr++; if (sptr - tkptr->DirSpec <= 0) return (SS$_BUGCHECK); memset (odsptr, 0, sizeof(ODS_STRUCT)); odsptr->Fab = cc$rms_fab; odsptr->Fab.fab$b_fac = FAB$M_GET; odsptr->Fab.fab$b_shr = FAB$M_SHRGET; odsptr->Fab.fab$l_fop &= ~FAB$M_ASY; #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_defname = tkptr->DirSpec; odsptr->Naml.naml$l_long_defname_size = sptr - tkptr->DirSpec; odsptr->Naml.naml$l_long_filename = DirWasdFileName; odsptr->Naml.naml$l_long_filename_size = sizeof(DirWasdFileName)-1; odsptr->Naml.naml$l_long_expand = odsptr->ExpFileName; odsptr->Naml.naml$l_long_expand_alloc = sizeof(odsptr->ExpFileName)-1; } else #endif /* ODS_EXTENDED */ { odsptr->Fab.fab$l_dna = tkptr->DirSpec; odsptr->Fab.fab$b_dns = sptr - tkptr->DirSpec; odsptr->Fab.fab$l_fna = DirWasdFileName; odsptr->Fab.fab$b_fns = sizeof(DirWasdFileName)-1; 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; } /* ensure we can read this directory listing "control" file */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$open (&odsptr->Fab, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status) || VMSnok (status = odsptr->Fab.fab$l_sts)) { if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "!#AZ!AZ !AZ %X!8XL", sptr - tkptr->DirSpec, tkptr->DirSpec, DirWasdFileName, odsptr->ExpFileName, status); return (status); } /* record access block */ odsptr->Rab = cc$rms_rab; odsptr->Rab.rab$l_fab = &odsptr->Fab; /* read ahead performance option, all synchronous */ odsptr->Rab.rab$l_rop = RAB$M_RAH; odsptr->Rab.rab$l_rop &= ~FAB$M_ASY; 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); } sptr = ReadBuffer; SizeInBytes = sizeof(ReadBuffer)-1; for (;;) { odsptr->Rab.rab$l_ubf = sptr; odsptr->Rab.rab$w_usz = SizeInBytes; status = sys$get (&odsptr->Rab, 0, 0); if (VMSnok (status)) break; sptr[odsptr->Rab.rab$w_rsz++] = '\n'; sptr += odsptr->Rab.rab$w_rsz; SizeInBytes -= odsptr->Rab.rab$w_rsz; } *sptr = '\0'; sys$close (&odsptr->Fab, 0, 0); if (status == RMS$_EOF) status = SS$_NORMAL; if (VMSok(status)) DirDirectString (rqptr, ReadBuffer); return (status); } /*****************************************************************************/ /* These comments apply equally to DirReadMeBottom() below. Attempt to send one of possible multiple files ("README.", et. al.) in the specified directory. Then, provided there was not serious error with the send, execute the function specified by the address in 'NextTaskFunction'. The file can be HTML or plain-text. With plain-text the contents will be encapsulated. */ DirReadMeTop (REQUEST_STRUCT *rqptr) { int status; char *cptr, *rptr, *sptr, *TypePtr; char FileName [ODS_MAX_FILE_NAME_LENGTH+1]; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirReadMeTop() !&F", &DirReadMeTop); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; while (*(rptr = ConfigReadMeFile(tkptr->ReadMeFileIndex++))) { sptr = FileName; for (cptr = tkptr->DirectoryPart; *cptr; *sptr++ = *cptr++); TypePtr = NULL; for ( /* 'rptr' initialized above! */ ; *rptr; *sptr++ = *rptr++) if (*rptr == '.') TypePtr = rptr; *sptr = '\0'; if (!TypePtr) TypePtr = rptr; cptr = ConfigContentType (&tkptr->ContentInfo, TypePtr); /* ignore any non-text types! */ if (!ConfigSameContentType (cptr, "text/", 5)) continue; if (ConfigSameContentType (cptr, ConfigContentTypeSsi, -1)) { FileSetContentHandler (rqptr, &SsiBegin, SsiSizeMax); FileSetCacheAllowed (rqptr, true); FileBegin (rqptr, &DirHeading, &DirReadMeTop, NULL, FileName, cptr); } else { if (ConfigSameContentType (cptr, "text/plain", -1)) FileSetPreTag (rqptr, true); FileSetCacheAllowed (rqptr, true); FileBegin (rqptr, &DirHeading, &DirReadMeTop, NULL, FileName, cptr); } return; } DirHeading (rqptr); } /*****************************************************************************/ /* See comment in DirReadMeTop() above. */ DirReadMeBottom (REQUEST_STRUCT *rqptr) { int status; char *cptr, *rptr, *sptr, *TypePtr; char FileName [ODS_MAX_FILE_NAME_LENGTH+1]; DIR_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_DIR)) WatchThis (rqptr, FI_LI, WATCH_MOD_DIR, "DirReadMeBottom() !&F", DirReadMeBottom); /* get the pointer to the task structure */ tkptr = rqptr->DirTaskPtr; while (*(rptr = ConfigReadMeFile(tkptr->ReadMeFileIndex++))) { sptr = FileName; for (cptr = tkptr->DirectoryPart; *cptr; *sptr++ = *cptr++); TypePtr = NULL; for ( /* 'rptr' initialized above! */ ; *rptr; *sptr++ = *rptr++) if (*rptr == '.') TypePtr = rptr; *sptr = '\0'; if (!TypePtr) TypePtr = rptr; cptr = ConfigContentType (&tkptr->ContentInfo, TypePtr); /* ignore any non-text types! */ if (!ConfigSameContentType (cptr, "text/", 5)) continue; if (ConfigSameContentType (cptr, ConfigContentTypeSsi, -1)) { FileSetContentHandler (rqptr, &SsiBegin, SsiSizeMax); FileSetCacheAllowed (rqptr, true); FileBegin (rqptr, &DirHeading, &DirReadMeBottom, NULL, FileName, cptr); } else { if (ConfigSameContentType (cptr, "text/plain", -1)) FileSetPreTag (rqptr, true); FileSetCacheAllowed (rqptr, true); FileBegin (rqptr, &DirHeading, &DirReadMeBottom, NULL, FileName, cptr); } return; } DirEnd (rqptr); } /*****************************************************************************/
"; break; case 'R' : aptr = ""; break; case 'S' : aptr = ""; } while (*aptr && sptr < zptr) *sptr++ = *aptr++; if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); } else { /* flush the column heading right using spaces */ if (cnt) cnt -= size; while (cnt-- && sptr < zptr) *sptr++ = ' '; for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); if (tkptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2) for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); } while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (tkptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2 || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2) for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) for (aptr = ""; break; case 'L' : aptr = ""; break; case 'N' : aptr = ""; break; case 'O' : aptr = ""; break; case 'P' : aptr = ""; } while (*aptr && sptr < zptr) *sptr++ = *aptr++; if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); } else { for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); if (tkptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2) for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); } while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (tkptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2 || tkptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2 || tkptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2) for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); for (aptr = ""; *aptr && sptr < zptr; *sptr++ = *aptr++); if (tkptr->DirStyle == MAPURL_DIR_STYLE_SORT || tkptr->DirStyle == MAPURL_DIR_STYLE_SORT2) for (aptr = "▼▲
!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ!AZ