1131 lines
27 KiB
C
1131 lines
27 KiB
C
/*
|
|
* $LynxId: HTVMSUtils.c,v 1.40 2020/01/21 21:58:29 tom Exp $
|
|
*
|
|
* MODULE HTVMSUtil.c
|
|
* VMS Utility Routines
|
|
*
|
|
* AUTHORS:
|
|
* MD Mark Donszelmann duns@vxdeop.cern.ch
|
|
*
|
|
* HISTORY:
|
|
* 14 Nov 93 MD Written
|
|
*
|
|
* BUGS:
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include <HTUtils.h>
|
|
#ifdef VMS
|
|
#include <HTFormat.h>
|
|
#include <HTStream.h>
|
|
#include <UCDefs.h>
|
|
#include <UCMap.h>
|
|
#include <UCAux.h>
|
|
#include <HTFTP.h>
|
|
#include <HTTCP.h>
|
|
#include <HTVMSUtils.h>
|
|
#include <ssdef.h>
|
|
#include <jpidef.h>
|
|
#include <prvdef.h>
|
|
#include <acldef.h>
|
|
#include <chpdef.h>
|
|
#include <descrip.h>
|
|
#include <lib$routines.h>
|
|
#include <starlet.h>
|
|
#include <rmsdef.h>
|
|
|
|
#include <LYGlobalDefs.h>
|
|
#include <LYUtils.h>
|
|
#include <LYLeaks.h>
|
|
#include <LYStrings.h>
|
|
|
|
BOOL HTVMSFileVersions = FALSE; /* Include version numbers in listing? */
|
|
|
|
typedef struct {
|
|
unsigned long BufferLength:16;
|
|
unsigned long ItemCode:16;
|
|
unsigned long BufferAddress:32;
|
|
unsigned long ReturnLengthAddress:32;
|
|
} ItemStruct;
|
|
|
|
/* PUBLIC HTVMS_authSysPrv()
|
|
* CHECKS IF THIS PROCESS IS AUTHORIZED TO ENABLE SYSPRV
|
|
* ON ENTRY:
|
|
* No arguments.
|
|
*
|
|
* ON EXIT:
|
|
* returns YES if SYSPRV is authorized
|
|
*/
|
|
BOOL HTVMS_authSysPrv(void)
|
|
{
|
|
unsigned long Result;
|
|
ItemStruct ItemList[2];
|
|
unsigned long Length;
|
|
unsigned long Buffer[2];
|
|
|
|
/* fill Item */
|
|
ItemList[0].BufferLength = sizeof(Buffer);
|
|
ItemList[0].BufferAddress = (unsigned long) Buffer;
|
|
ItemList[0].ReturnLengthAddress = (unsigned long) &Length;
|
|
ItemList[0].ItemCode = JPI$_AUTHPRIV;
|
|
|
|
/* terminate list */
|
|
ItemList[1].ItemCode = 0;
|
|
ItemList[1].BufferLength = 0;
|
|
|
|
/* call system */
|
|
Result = sys$getjpiw(0, 0, 0, ItemList, 0, 0, 0);
|
|
|
|
if (Result != SS$_NORMAL)
|
|
return (NO);
|
|
|
|
if (Buffer[0] & PRV$M_SYSPRV)
|
|
return (YES);
|
|
|
|
return (NO);
|
|
}
|
|
|
|
/* PUBLIC HTVMS_enableSysPrv()
|
|
* ENABLES SYSPRV
|
|
* ON ENTRY:
|
|
* No arguments.
|
|
*
|
|
* ON EXIT:
|
|
*
|
|
*/
|
|
void HTVMS_enableSysPrv(void)
|
|
{
|
|
unsigned long Result;
|
|
unsigned long Prv[2], PreviousPrv[2];
|
|
|
|
Prv[0] = PRV$M_SYSPRV;
|
|
Prv[1] = 0;
|
|
Result = sys$setprv(1, &Prv, 0, &PreviousPrv);
|
|
|
|
if (Result == SS$_NORMAL) {
|
|
if (!(PreviousPrv[0] & PRV$M_SYSPRV)) {
|
|
CTRACE((tfp, "HTVMS_enableSysPrv: Enabled SYSPRV\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* PUBLIC HTVMS_disableSysPrv()
|
|
* DISABLES SYSPRV
|
|
* ON ENTRY:
|
|
* No arguments.
|
|
*
|
|
* ON EXIT:
|
|
*
|
|
*/
|
|
void HTVMS_disableSysPrv(void)
|
|
{
|
|
unsigned long Result;
|
|
unsigned long Prv[2], PreviousPrv[2];
|
|
|
|
Prv[0] = PRV$M_SYSPRV;
|
|
Prv[1] = 0;
|
|
Result = sys$setprv(0, &Prv, 0, &PreviousPrv);
|
|
|
|
if (Result == SS$_NORMAL) {
|
|
if (PreviousPrv[0] & PRV$M_SYSPRV) {
|
|
CTRACE((tfp, "HTVMS_disableSysPrv: Disabled SYSPRV\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* PUBLIC HTVMS_checkAccess()
|
|
* CHECKS ACCESS TO FILE FOR CERTAIN USER
|
|
* ON ENTRY:
|
|
* FileName The file to be accessed
|
|
* UserName Name of the user to check access for.
|
|
* User nobody, represented by "" is given NO for an answer
|
|
* Method Name of the method to be checked
|
|
*
|
|
* ON EXIT:
|
|
* returns YES if access is allowed
|
|
*
|
|
*/
|
|
BOOL HTVMS_checkAccess(const char *FileName,
|
|
const char *UserName,
|
|
const char *Method)
|
|
{
|
|
unsigned long Result;
|
|
ItemStruct ItemList[2];
|
|
unsigned long Length;
|
|
unsigned long Buffer;
|
|
unsigned long ObjType;
|
|
|
|
char *VmsName;
|
|
|
|
struct dsc$descriptor_s FileNameDesc;
|
|
struct dsc$descriptor_s UserNameDesc;
|
|
|
|
char *colon;
|
|
|
|
/* user nobody should access as from account under which server is running */
|
|
if (0 == strcmp(UserName, ""))
|
|
return (NO);
|
|
|
|
/* check Filename and convert */
|
|
colon = StrChr(FileName, ':');
|
|
if (colon)
|
|
VmsName = HTVMS_name("", colon + 1);
|
|
else
|
|
VmsName = HTVMS_name("", FileName);
|
|
|
|
/* check for GET */
|
|
if (0 == strcmp(Method, "GET")) {
|
|
/* fill Item */
|
|
ItemList[0].BufferLength = sizeof(Buffer);
|
|
ItemList[0].BufferAddress = (unsigned long) &Buffer;
|
|
ItemList[0].ReturnLengthAddress = (unsigned long) &Length;
|
|
ItemList[0].ItemCode = CHP$_FLAGS;
|
|
|
|
/* terminate list */
|
|
ItemList[1].ItemCode = 0;
|
|
ItemList[1].BufferLength = 0;
|
|
|
|
/* fill input */
|
|
ObjType = ACL$C_FILE;
|
|
Buffer = CHP$M_READ;
|
|
UserNameDesc.dsc$w_length = strlen(UserName);
|
|
UserNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
UserNameDesc.dsc$b_class = DSC$K_CLASS_S;
|
|
UserNameDesc.dsc$a_pointer = (char *) UserName;
|
|
FileNameDesc.dsc$w_length = strlen(VmsName);
|
|
FileNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
FileNameDesc.dsc$b_class = DSC$K_CLASS_S;
|
|
FileNameDesc.dsc$a_pointer = VmsName;
|
|
|
|
/* call system */
|
|
Result = sys$check_access(&ObjType, &FileNameDesc, &UserNameDesc, ItemList);
|
|
|
|
if (Result == SS$_NORMAL)
|
|
return (YES);
|
|
else
|
|
return (NO);
|
|
}
|
|
|
|
return (NO);
|
|
}
|
|
|
|
/* PUBLIC HTVMS_wwwName()
|
|
* CONVERTS VMS Name into WWW Name
|
|
* ON ENTRY:
|
|
* vmsname VMS file specification (NO NODE)
|
|
*
|
|
* ON EXIT:
|
|
* returns www file specification
|
|
*
|
|
* EXAMPLES:
|
|
* vmsname wwwname
|
|
* DISK$USER disk$user
|
|
* DISK$USER: /disk$user/
|
|
* DISK$USER:[DUNS] /disk$user/duns
|
|
* DISK$USER:[DUNS.ECHO] /disk$user/duns/echo
|
|
* [DUNS] duns
|
|
* [DUNS.ECHO] duns/echo
|
|
* [DUNS.ECHO.-.TRANS] duns/echo/../trans
|
|
* [DUNS.ECHO.--.TRANS] duns/echo/../../trans
|
|
* [.DUNS] duns
|
|
* [.DUNS.ECHO] duns/echo
|
|
* [.DUNS.ECHO]TEST.COM duns/echo/test.com
|
|
* TEST.COM test.com
|
|
*
|
|
*
|
|
*/
|
|
const char *HTVMS_wwwName(const char *vmsname)
|
|
{
|
|
static char wwwname[LY_MAXPATH];
|
|
const char *src;
|
|
char *dst;
|
|
int dir;
|
|
|
|
dst = wwwname;
|
|
src = vmsname;
|
|
dir = 0;
|
|
if (StrChr(src, ':'))
|
|
*(dst++) = '/';
|
|
for (; *src != '\0'; src++) {
|
|
switch (*src) {
|
|
case ':':
|
|
*(dst++) = '/';
|
|
break;
|
|
case '-':
|
|
if (dir) {
|
|
if ((*(src - 1) == '[' ||
|
|
*(src - 1) == '.' ||
|
|
*(src - 1) == '-') &&
|
|
(*(src + 1) == '.' ||
|
|
*(src + 1) == '-')) {
|
|
*(dst++) = '/';
|
|
*(dst++) = '.';
|
|
*(dst++) = '.';
|
|
} else
|
|
*(dst++) = '-';
|
|
} else {
|
|
if (*(src - 1) == ']')
|
|
*(dst++) = '/';
|
|
*(dst++) = '-';
|
|
}
|
|
break;
|
|
case '.':
|
|
if (dir) {
|
|
if (*(src - 1) != '[')
|
|
*(dst++) = '/';
|
|
} else {
|
|
if (*(src - 1) == ']')
|
|
*(dst++) = '/';
|
|
*(dst++) = '.';
|
|
}
|
|
break;
|
|
case '[':
|
|
dir = 1;
|
|
break;
|
|
case ']':
|
|
dir = 0;
|
|
break;
|
|
default:
|
|
if (*(src - 1) == ']')
|
|
*(dst++) = '/';
|
|
*(dst++) = *src;
|
|
break;
|
|
}
|
|
}
|
|
*(dst++) = '\0';
|
|
return (wwwname);
|
|
}
|
|
|
|
/*
|
|
* The code below is for directory browsing by VMS Curses clients.
|
|
* It is based on the newer WWWLib's HTDirBrw.c. - Foteos Macrides
|
|
*/
|
|
int HTStat(const char *filename,
|
|
struct stat *info)
|
|
{
|
|
/*
|
|
the following stuff does not work in VMS with a normal stat...
|
|
--> /disk$user/duns/www if www is a directory
|
|
is statted like: /disk$user/duns/www.dir
|
|
after a normal stat has failed
|
|
--> /disk$user/duns if duns is a toplevel directory
|
|
is statted like: /disk$user/000000/duns.dir
|
|
--> /disk$user since disk$user is a device
|
|
is statted like: /disk$user/000000/000000.dir
|
|
--> /
|
|
searches all devices, no solution yet...
|
|
--> /vxcern!/disk$cr/wwwteam/login.com
|
|
is not statted but granted with fake information...
|
|
*/
|
|
int Result;
|
|
int Len;
|
|
char *Ptr, *Ptr2;
|
|
static char *Name;
|
|
|
|
/* try normal stat... */
|
|
Result = stat((char *) filename, info);
|
|
if (Result == 0)
|
|
return (Result);
|
|
|
|
/* make local copy */
|
|
StrAllocCopy(Name, filename);
|
|
|
|
/* failed,so do device search in case root is requested */
|
|
if (!strcmp(Name, "/")) { /* root requested */
|
|
return (-1);
|
|
}
|
|
|
|
/* failed so this might be a directory, add '.dir' */
|
|
Len = strlen(Name);
|
|
if (Name[Len - 1] == '/')
|
|
Name[Len - 1] = '\0';
|
|
|
|
/* fail in case of device */
|
|
Ptr = StrChr(Name + 1, '/');
|
|
if ((Ptr == NULL) && (Name[0] == '/')) { /* device only... */
|
|
StrAllocCat(Name, "/000000/000000");
|
|
}
|
|
|
|
if (Ptr != NULL) { /* correct filename in case of toplevel dir */
|
|
Ptr2 = StrChr(Ptr + 1, '/');
|
|
if ((Ptr2 == NULL) && (Name[0] == '/')) {
|
|
char End[256];
|
|
|
|
LYStrNCpy(End, Ptr, sizeof(End) - 1);
|
|
*(Ptr + 1) = '\0';
|
|
StrAllocCat(Name, "000000");
|
|
StrAllocCat(Name, End);
|
|
}
|
|
}
|
|
|
|
/* try in case a file on toplevel directory or .DIR was already specified */
|
|
Result = stat(Name, info);
|
|
if (Result == 0)
|
|
return (Result);
|
|
|
|
/* add .DIR and try again */
|
|
StrAllocCat(Name, ".dir");
|
|
Result = stat(Name, info);
|
|
return (Result);
|
|
}
|
|
|
|
#ifndef _POSIX_SOURCE
|
|
#define d_ino d_fileno /* compatibility */
|
|
#ifndef NULL
|
|
#define NULL 0
|
|
#endif
|
|
#endif /* !_POSIX_SOURCE */
|
|
|
|
typedef struct __dirdesc {
|
|
long context; /* context descriptor for LIB$FIND_FILE calls */
|
|
char dirname[255 + 1]; /* keeps the directory name, including *.* */
|
|
struct dsc$descriptor_s dirname_desc; /* descriptor of dirname */
|
|
} DIR;
|
|
|
|
static DIR *HTVMSopendir(char *dirname);
|
|
static struct dirent *HTVMSreaddir(DIR *dirp);
|
|
static int HTVMSclosedir(DIR *dirp);
|
|
|
|
/*** #include <sys_dirent.h> ***/
|
|
/*** "sys_dirent.h" ***/
|
|
struct dirent {
|
|
unsigned long d_fileno; /* file number of entry */
|
|
unsigned short d_namlen; /* length of string in d_name */
|
|
char d_name[255 + 1]; /* name (up to MAXNAMLEN + 1) */
|
|
};
|
|
|
|
#ifndef _POSIX_SOURCE
|
|
/*
|
|
* It's unlikely to change, but make sure that sizeof d_name above is
|
|
* at least MAXNAMLEN + 1 (more may be added for padding).
|
|
*/
|
|
#define MAXNAMLEN 255
|
|
/*
|
|
* The macro DIRSIZ(dp) gives the minimum amount of space required to represent
|
|
* a directory entry. For any directory entry dp->d_reclen >= DIRSIZ(dp).
|
|
* Specific filesystem types may use this macro to construct the value
|
|
* for d_reclen.
|
|
*/
|
|
#undef DIRSIZ
|
|
#define DIRSIZ(dp) \
|
|
(((sizeof(struct dirent) - (MAXNAMLEN+1) + ((dp)->d_namlen+1)) +3) & ~3)
|
|
|
|
#endif /* !_POSIX_SOURCE */
|
|
|
|
static DIR *HTVMSopendir(char *dirname)
|
|
{
|
|
static DIR dir;
|
|
char *closebracket;
|
|
long status;
|
|
struct dsc$descriptor_s entryname_desc;
|
|
struct dsc$descriptor_s dirname_desc;
|
|
static char *DirEntry;
|
|
char Actual[256];
|
|
char VMSentry[256];
|
|
char UnixEntry[256];
|
|
int index;
|
|
char *dot;
|
|
|
|
/* check if directory exists */
|
|
/* dirname can look like /disk$user/duns/www/test/multi */
|
|
/* or like /disk$user/duns/www/test/multi/ */
|
|
/* DirEntry should look like disk$user:[duns.www.test]multi in both cases */
|
|
/* dir.dirname should look like disk$user:[duns.www.test.multi] */
|
|
sprintf(UnixEntry, "%.*s", sizeof(UnixEntry) - 2, dirname);
|
|
if (UnixEntry[strlen(UnixEntry) - 1] != '/')
|
|
strcat(UnixEntry, "/");
|
|
|
|
StrAllocCopy(DirEntry, HTVMS_name("", UnixEntry));
|
|
if (strlen(DirEntry) > sizeof(dir.dirname) - 1)
|
|
return (NULL);
|
|
strcpy(dir.dirname, DirEntry);
|
|
index = strlen(DirEntry) - 1;
|
|
|
|
if (DirEntry[index] == ']')
|
|
DirEntry[index] = '\0';
|
|
|
|
if ((dot = strrchr(DirEntry, '.')) == NULL) { /* convert disk$user:[duns] into disk$user:[000000]duns.dir */
|
|
char *openbr = strrchr(DirEntry, '[');
|
|
|
|
if (!openbr) { /* convert disk$user: into disk$user:[000000]000000.dir */
|
|
if (strlen(dir.dirname) > sizeof(dir.dirname) - 10)
|
|
return (NULL);
|
|
sprintf(dir.dirname, "%.*s[000000]", sizeof(dir.dirname) - 9, DirEntry);
|
|
StrAllocCat(DirEntry, "[000000]000000.dir");
|
|
} else {
|
|
char End[256];
|
|
|
|
strcpy(End, openbr + 1);
|
|
*(openbr + 1) = '\0';
|
|
StrAllocCat(DirEntry, "000000]");
|
|
StrAllocCat(DirEntry, End);
|
|
StrAllocCat(DirEntry, ".dir");
|
|
}
|
|
} else {
|
|
*dot = ']';
|
|
StrAllocCat(DirEntry, ".dir");
|
|
}
|
|
/* lib$find_file needs a fixed-size buffer */
|
|
LYStrNCpy(Actual, DirEntry, sizeof(Actual) - 1);
|
|
|
|
dir.context = 0;
|
|
dirname_desc.dsc$w_length = strlen(Actual);
|
|
dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
dirname_desc.dsc$b_class = DSC$K_CLASS_S;
|
|
dirname_desc.dsc$a_pointer = (char *) &(Actual);
|
|
|
|
/* look for the directory */
|
|
entryname_desc.dsc$w_length = 255;
|
|
entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
entryname_desc.dsc$b_class = DSC$K_CLASS_S;
|
|
entryname_desc.dsc$a_pointer = VMSentry;
|
|
|
|
status = lib$find_file(&(dirname_desc),
|
|
&entryname_desc,
|
|
&(dir.context),
|
|
0, 0, 0, 0);
|
|
if (!(status & 0x01)) { /* directory not found */
|
|
return (NULL);
|
|
}
|
|
|
|
if (strlen(dir.dirname) > sizeof(dir.dirname) - 10)
|
|
return (NULL);
|
|
if (HTVMSFileVersions)
|
|
strcat(dir.dirname, "*.*;*");
|
|
else
|
|
strcat(dir.dirname, "*.*");
|
|
dir.context = 0;
|
|
dir.dirname_desc.dsc$w_length = strlen(dir.dirname);
|
|
dir.dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
dir.dirname_desc.dsc$b_class = DSC$K_CLASS_S;
|
|
dir.dirname_desc.dsc$a_pointer = (char *) &(dir.dirname);
|
|
return (&dir);
|
|
}
|
|
|
|
static struct dirent *HTVMSreaddir(DIR *dirp)
|
|
{
|
|
static struct dirent entry;
|
|
long status;
|
|
struct dsc$descriptor_s entryname_desc;
|
|
char *space, *slash;
|
|
char VMSentry[256];
|
|
const char *UnixEntry;
|
|
|
|
entryname_desc.dsc$w_length = 255;
|
|
entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
|
|
entryname_desc.dsc$b_class = DSC$K_CLASS_S;
|
|
entryname_desc.dsc$a_pointer = VMSentry;
|
|
|
|
status = lib$find_file(&(dirp->dirname_desc),
|
|
&entryname_desc,
|
|
&(dirp->context),
|
|
0, 0, 0, 0);
|
|
if (status == RMS$_NMF) { /* no more files */
|
|
return (NULL);
|
|
} else { /* ok */
|
|
if (!(status & 0x01))
|
|
return (0);
|
|
if (HTVMSFileVersions)
|
|
space = StrChr(VMSentry, ' ');
|
|
else
|
|
space = StrChr(VMSentry, ';');
|
|
if (space)
|
|
*space = '\0';
|
|
|
|
/* convert to unix style... */
|
|
UnixEntry = HTVMS_wwwName(VMSentry);
|
|
slash = strrchr(UnixEntry, '/') + 1;
|
|
strcpy(entry.d_name, slash);
|
|
entry.d_namlen = strlen(entry.d_name);
|
|
entry.d_fileno = 1;
|
|
return (&entry);
|
|
}
|
|
}
|
|
|
|
static int HTVMSclosedir(DIR *dirp)
|
|
{
|
|
long status;
|
|
|
|
status = lib$find_file_end(&(dirp->context));
|
|
if (!(status & 0x01))
|
|
exit_immediately(status);
|
|
dirp->context = 0;
|
|
return (0);
|
|
}
|
|
|
|
#include <HTAnchor.h>
|
|
#include <HTParse.h>
|
|
#include <HTBTree.h>
|
|
#include <HTFile.h> /* For HTFileFormat() */
|
|
#include <HTAlert.h>
|
|
/*
|
|
* Hypertext object building machinery.
|
|
*/
|
|
#include <HTML.h>
|
|
#define PUTC(c) (*targetClass.put_character)(target, c)
|
|
#define PUTS(s) (*targetClass.put_string)(target, s)
|
|
#define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0)
|
|
#define END(e) (*targetClass.end_element)(target, e, 0)
|
|
#define FREE_TARGET (*targetClass._free)(target)
|
|
#define ABORT_TARGET (*targetClass._free)(target)
|
|
struct _HTStructured {
|
|
const HTStructuredClass *isa;
|
|
/* ... */
|
|
};
|
|
|
|
#define STRUCT_DIRENT struct dirent
|
|
|
|
static char *months[12] =
|
|
{
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
};
|
|
|
|
typedef struct _VMSEntryInfo {
|
|
char *filename;
|
|
char *type;
|
|
char *date;
|
|
unsigned int size;
|
|
BOOLEAN display; /* show this entry? */
|
|
} VMSEntryInfo;
|
|
|
|
static void free_VMSEntryInfo_contents(VMSEntryInfo * entry_info)
|
|
{
|
|
if (entry_info) {
|
|
FREE(entry_info->filename);
|
|
FREE(entry_info->type);
|
|
FREE(entry_info->date);
|
|
}
|
|
/* don't free the struct */
|
|
}
|
|
|
|
int compare_VMSEntryInfo_structs(VMSEntryInfo * entry1, VMSEntryInfo * entry2)
|
|
{
|
|
int i, status;
|
|
char date1[16], date2[16], time1[8], time2[8], month[4];
|
|
|
|
switch (HTfileSortMethod) {
|
|
case FILE_BY_SIZE:
|
|
/* both equal or both 0 */
|
|
if (entry1->size == entry2->size)
|
|
return (strcasecomp(entry1->filename,
|
|
entry2->filename));
|
|
else if (entry1->size > entry2->size)
|
|
return (1);
|
|
else
|
|
return (-1);
|
|
case FILE_BY_TYPE:
|
|
if (entry1->type && entry2->type) {
|
|
status = strcasecomp(entry1->type, entry2->type);
|
|
if (status)
|
|
return (status);
|
|
/* else fall to filename comparison */
|
|
}
|
|
return (strcasecomp(entry1->filename,
|
|
entry2->filename));
|
|
case FILE_BY_DATE:
|
|
if (entry1->date && entry2->date) {
|
|
/*
|
|
* Make sure we have the correct length. - FM
|
|
*/
|
|
if (strlen(entry1->date) != 12 ||
|
|
strlen(entry2->date) != 12) {
|
|
return (strcasecomp(entry1->filename,
|
|
entry2->filename));
|
|
}
|
|
/*
|
|
* Set up for sorting in reverse
|
|
* chronological order. - FM
|
|
*/
|
|
if (entry1->date[7] != ' ') {
|
|
strcpy(date1, "9999");
|
|
strcpy(time1, (char *) &entry1->date[7]);
|
|
} else {
|
|
strcpy(date1, (char *) &entry1->date[8]);
|
|
strcpy(time1, "00:00");
|
|
}
|
|
LYStrNCpy(month, entry1->date, 3);
|
|
for (i = 0; i < 12; i++) {
|
|
if (!strcasecomp(month, months[i])) {
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
sprintf(month, "%02d", i);
|
|
strcat(date1, month);
|
|
StrNCat(date1, (char *) &entry1->date[4], 2);
|
|
date1[8] = '\0';
|
|
if (date1[6] == ' ') {
|
|
date1[6] = '0';
|
|
}
|
|
strcat(date1, time1);
|
|
if (entry2->date[7] != ' ') {
|
|
strcpy(date2, "9999");
|
|
strcpy(time2, (char *) &entry2->date[7]);
|
|
} else {
|
|
strcpy(date2, (char *) &entry2->date[8]);
|
|
strcpy(time2, "00:00");
|
|
}
|
|
LYStrNCpy(month, entry2->date, 3);
|
|
for (i = 0; i < 12; i++) {
|
|
if (!strcasecomp(month, months[i])) {
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
sprintf(month, "%02d", i);
|
|
strcat(date2, month);
|
|
StrNCat(date2, (char *) &entry2->date[4], 2);
|
|
date2[8] = '\0';
|
|
if (date2[6] == ' ') {
|
|
date2[6] = '0';
|
|
}
|
|
strcat(date2, time2);
|
|
/*
|
|
* Do the comparison. - FM
|
|
*/
|
|
status = strcasecomp(date2, date1);
|
|
if (status)
|
|
return (status);
|
|
/* else fall to filename comparison */
|
|
}
|
|
return (strcasecomp(entry1->filename,
|
|
entry2->filename));
|
|
case FILE_BY_NAME:
|
|
default:
|
|
return (strcmp(entry1->filename,
|
|
entry2->filename));
|
|
}
|
|
}
|
|
|
|
/* HTVMSBrowseDir()
|
|
*
|
|
* This function generates a directory listing as an HTML-object
|
|
* for local file URL's. It assumes the first two elements of
|
|
* of the path are a device followed by a directory:
|
|
*
|
|
* file://localhost/device/directory[/[foo]]
|
|
*
|
|
* Will not accept 000000 as a directory name.
|
|
* Will offer links to parent through the top directory, unless
|
|
* a terminal slash was included in the calling URL.
|
|
*
|
|
* Returns HT_LOADED on success, HTLoadError() messages on error.
|
|
*
|
|
* Developed for Lynx by Foteos Macrides (macrides@sci.wfeb.edu).
|
|
*/
|
|
int HTVMSBrowseDir(const char *address,
|
|
HTParentAnchor *anchor,
|
|
HTFormat format_out,
|
|
HTStream *sink)
|
|
{
|
|
HTStructured *target;
|
|
HTStructuredClass targetClass;
|
|
char *pathname = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
|
|
char *tail = NULL;
|
|
char *title = NULL;
|
|
char *header = NULL;
|
|
char *parent = NULL;
|
|
char *relative = NULL;
|
|
char *cp, *cp1;
|
|
int pathend, len;
|
|
DIR *dp;
|
|
struct stat file_info;
|
|
time_t NowTime;
|
|
static char ThisYear[8];
|
|
VMSEntryInfo *entry_info = 0;
|
|
char string_buffer[64];
|
|
|
|
HTUnEscape(pathname);
|
|
CTRACE((tfp, "HTVMSBrowseDir: Browsing `%s\'\n", pathname));
|
|
|
|
/*
|
|
* Require at least two elements (presumably a device and directory) and
|
|
* disallow the device root (000000 directory). Symbolic paths (e.g.,
|
|
* sys$help) should have been translated and expanded (e.g., to
|
|
* /sys$sysroot/syshlp) before calling this routine.
|
|
*/
|
|
if (((*pathname != '/') ||
|
|
(cp = StrChr(pathname + 1, '/')) == NULL ||
|
|
*(cp + 1) == '\0' ||
|
|
0 == StrNCmp((cp + 1), "000000", 6)) ||
|
|
(dp = HTVMSopendir(pathname)) == NULL) {
|
|
FREE(pathname);
|
|
return HTLoadError(sink, 403, COULD_NOT_ACCESS_DIR);
|
|
}
|
|
|
|
/*
|
|
* Set up the output stream.
|
|
*/
|
|
_HTProgress(BUILDING_DIR_LIST);
|
|
if (UCLYhndl_HTFile_for_unspec >= 0) {
|
|
HTAnchor_setUCInfoStage(anchor,
|
|
UCLYhndl_HTFile_for_unspec,
|
|
UCT_STAGE_PARSER,
|
|
UCT_SETBY_DEFAULT);
|
|
}
|
|
target = HTML_new(anchor, format_out, sink);
|
|
targetClass = *(target->isa);
|
|
|
|
/*
|
|
* Set up the offset string of the anchor reference, and strings for the
|
|
* title and header.
|
|
*/
|
|
cp = strrchr(pathname, '/'); /* find lastslash */
|
|
StrAllocCopy(tail, (cp + 1)); /* take slash off the beginning */
|
|
if (*tail != '\0') {
|
|
StrAllocCopy(title, tail);
|
|
*cp = '\0';
|
|
if ((cp1 = strrchr(pathname, '/')) != NULL &&
|
|
cp1 != pathname &&
|
|
StrNCmp((cp1 + 1), "000000", 6))
|
|
StrAllocCopy(parent, (cp1 + 1));
|
|
*cp = '/';
|
|
} else {
|
|
pathname[strlen(pathname) - 1] = '\0';
|
|
cp = strrchr(pathname, '/');
|
|
StrAllocCopy(title, (cp + 1));
|
|
pathname[strlen(pathname)] = '/';
|
|
}
|
|
StrAllocCopy(header, pathname);
|
|
|
|
/*
|
|
* Initialize path name for HTStat().
|
|
*/
|
|
pathend = strlen(pathname);
|
|
if (*(pathname + pathend - 1) != '/') {
|
|
StrAllocCat(pathname, "/");
|
|
pathend++;
|
|
}
|
|
|
|
/*
|
|
* Output the title and header.
|
|
*/
|
|
START(HTML_HTML);
|
|
PUTC('\n');
|
|
START(HTML_HEAD);
|
|
PUTC('\n');
|
|
HTUnEscape(title);
|
|
START(HTML_TITLE);
|
|
PUTS(title);
|
|
PUTS(" directory");
|
|
END(HTML_TITLE);
|
|
PUTC('\n');
|
|
FREE(title);
|
|
END(HTML_HEAD);
|
|
PUTC('\n');
|
|
START(HTML_BODY);
|
|
PUTC('\n');
|
|
HTUnEscape(header);
|
|
START(HTML_H1);
|
|
PUTS(header);
|
|
END(HTML_H1);
|
|
PUTC('\n');
|
|
if (HTDirReadme == HT_DIR_README_TOP) {
|
|
FILE *fp;
|
|
|
|
if (header[strlen(header) - 1] != '/')
|
|
StrAllocCat(header, "/");
|
|
StrAllocCat(header, HT_DIR_README_FILE);
|
|
if ((fp = fopen(header, "r")) != NULL) {
|
|
START(HTML_PRE);
|
|
for (;;) {
|
|
char c = fgetc(fp);
|
|
|
|
if (c == (char) EOF)
|
|
break;
|
|
#ifdef NOTDEFINED
|
|
switch (c) {
|
|
case '&':
|
|
case '<':
|
|
case '>':
|
|
PUTC('&');
|
|
PUTC('#');
|
|
PUTC((char) (c / 10));
|
|
PUTC((char) (c % 10));
|
|
PUTC(';');
|
|
break;
|
|
default:
|
|
PUTC(c);
|
|
}
|
|
#else
|
|
PUTC(c);
|
|
#endif /* NOTDEFINED */
|
|
}
|
|
END(HTML_PRE);
|
|
fclose(fp);
|
|
}
|
|
}
|
|
FREE(header);
|
|
if (parent) {
|
|
HTSprintf0(&relative, "%s/..", tail);
|
|
HTStartAnchor(target, "", relative);
|
|
PUTS("Up to ");
|
|
HTUnEscape(parent);
|
|
PUTS(parent);
|
|
END(HTML_A);
|
|
START(HTML_P);
|
|
PUTC('\n');
|
|
FREE(relative);
|
|
FREE(parent);
|
|
}
|
|
|
|
/*
|
|
* Set up the date comparison.
|
|
*/
|
|
NowTime = time(NULL);
|
|
strcpy(ThisYear, (char *) ctime(&NowTime) + 20);
|
|
ThisYear[4] = '\0';
|
|
|
|
/*
|
|
* Now, generate the Btree and put it out to the output stream.
|
|
*/
|
|
{
|
|
char dottest = 2; /* To avoid two strcmp() each time */
|
|
STRUCT_DIRENT *dirbuf;
|
|
HTBTree *bt;
|
|
|
|
/* Set up sort key and initialize BTree */
|
|
bt = HTBTree_new((HTComparer) compare_VMSEntryInfo_structs);
|
|
|
|
/* Build tree */
|
|
while ((dirbuf = HTVMSreaddir(dp))) {
|
|
HTAtom *encoding = NULL;
|
|
HTFormat format;
|
|
|
|
/* Skip if not used */
|
|
if (!dirbuf->d_ino) {
|
|
continue;
|
|
}
|
|
|
|
/* Current and parent directories are never shown in list */
|
|
if (dottest && (!strcmp(dirbuf->d_name, ".") ||
|
|
!strcmp(dirbuf->d_name, ".."))) {
|
|
dottest--;
|
|
continue;
|
|
}
|
|
|
|
/* Don't show the selective enabling file
|
|
* unless version numbers are included */
|
|
if (!strcasecomp(dirbuf->d_name, HT_DIR_ENABLE_FILE)) {
|
|
continue;
|
|
}
|
|
|
|
/* Skip files beginning with a dot? */
|
|
if ((no_dotfiles || !show_dotfiles) && *dirbuf->d_name == '.') {
|
|
continue;
|
|
}
|
|
|
|
/* OK, make an lstat() and get a key ready. */
|
|
*(pathname + pathend) = '\0';
|
|
StrAllocCat(pathname, dirbuf->d_name);
|
|
if (HTStat(pathname, &file_info)) {
|
|
/* for VMS the failure here means the file is not readable...
|
|
we however continue to browse through the directory... */
|
|
continue;
|
|
}
|
|
entry_info = (VMSEntryInfo *) malloc(sizeof(VMSEntryInfo));
|
|
if (entry_info == NULL)
|
|
outofmem(__FILE__, "HTVMSBrowseDir");
|
|
entry_info->type = 0;
|
|
entry_info->size = 0;
|
|
entry_info->date = 0;
|
|
entry_info->filename = 0;
|
|
entry_info->display = TRUE;
|
|
|
|
/* Get the type */
|
|
format = HTFileFormat(dirbuf->d_name, &encoding,
|
|
(const char **) &cp);
|
|
if (!cp) {
|
|
if (!StrNCmp(HTAtom_name(format), "application", 11)) {
|
|
cp = HTAtom_name(format) + 12;
|
|
if (!StrNCmp(cp, "x-", 2))
|
|
cp += 2;
|
|
} else
|
|
cp = HTAtom_name(format);
|
|
}
|
|
StrAllocCopy(entry_info->type, cp);
|
|
|
|
StrAllocCopy(entry_info->filename, dirbuf->d_name);
|
|
if (S_ISDIR(file_info.st_mode)) {
|
|
/* strip .DIR part... */
|
|
char *dot;
|
|
|
|
dot = strstr(entry_info->filename, ".DIR");
|
|
if (dot)
|
|
*dot = '\0';
|
|
LYLowerCase(entry_info->filename);
|
|
StrAllocCopy(entry_info->type, "Directory");
|
|
} else {
|
|
if ((cp = strstr(entry_info->filename, "READ")) == NULL) {
|
|
cp = entry_info->filename;
|
|
} else {
|
|
cp += 4;
|
|
if (!StrNCmp(cp, "ME", 2)) {
|
|
cp += 2;
|
|
while (cp && *cp && *cp != '.') {
|
|
cp++;
|
|
}
|
|
} else if (!StrNCmp(cp, ".ME", 3)) {
|
|
cp = (entry_info->filename +
|
|
strlen(entry_info->filename));
|
|
} else {
|
|
cp = entry_info->filename;
|
|
}
|
|
}
|
|
LYLowerCase(cp);
|
|
if (((len = strlen(entry_info->filename)) > 2) &&
|
|
entry_info->filename[len - 1] == 'z') {
|
|
if (entry_info->filename[len - 2] == '.' ||
|
|
entry_info->filename[len - 2] == '_')
|
|
entry_info->filename[len - 1] = 'Z';
|
|
}
|
|
}
|
|
|
|
/* Get the date */
|
|
{
|
|
char *t = (char *) ctime((const time_t *) &file_info.st_ctime);
|
|
|
|
*(t + 24) = '\0';
|
|
|
|
StrAllocCopy(entry_info->date, (t + 4));
|
|
*((entry_info->date) + 7) = '\0';
|
|
if ((atoi((t + 19))) < atoi(ThisYear))
|
|
StrAllocCat(entry_info->date, (t + 19));
|
|
else {
|
|
StrAllocCat(entry_info->date, (t + 11));
|
|
*((entry_info->date) + 12) = '\0';
|
|
}
|
|
}
|
|
|
|
/* Get the size */
|
|
if (!S_ISDIR(file_info.st_mode))
|
|
entry_info->size = (unsigned int) file_info.st_size;
|
|
else
|
|
entry_info->size = 0;
|
|
|
|
/* Now, update the BTree etc. */
|
|
if (entry_info->display) {
|
|
CTRACE((tfp, "Adding file to BTree: %s\n",
|
|
entry_info->filename));
|
|
HTBTree_add(bt, entry_info);
|
|
}
|
|
|
|
} /* End while HTVMSreaddir() */
|
|
|
|
FREE(pathname);
|
|
HTVMSclosedir(dp);
|
|
|
|
START(HTML_PRE);
|
|
/*
|
|
* Run through the BTree printing out in order
|
|
*/
|
|
{
|
|
HTBTElement *ele;
|
|
int i;
|
|
|
|
for (ele = HTBTree_next(bt, NULL);
|
|
ele != NULL;
|
|
ele = HTBTree_next(bt, ele)) {
|
|
entry_info = (VMSEntryInfo *) HTBTree_object(ele);
|
|
|
|
/* Output the date */
|
|
if (entry_info->date) {
|
|
PUTS(entry_info->date);
|
|
PUTS(" ");
|
|
} else
|
|
PUTS(" * ");
|
|
|
|
/* Output the type */
|
|
if (entry_info->type) {
|
|
for (i = 0; entry_info->type[i] != '\0' && i < 15; i++)
|
|
PUTC(entry_info->type[i]);
|
|
for (; i < 17; i++)
|
|
PUTC(' ');
|
|
|
|
}
|
|
|
|
/* Output the link for the name */
|
|
HTDirEntry(target, tail, entry_info->filename);
|
|
PUTS(entry_info->filename);
|
|
END(HTML_A);
|
|
|
|
/* Output the size */
|
|
if (entry_info->size) {
|
|
if (entry_info->size < 1024)
|
|
sprintf(string_buffer, " %d bytes",
|
|
entry_info->size);
|
|
else
|
|
sprintf(string_buffer, " %dKb",
|
|
entry_info->size / 1024);
|
|
PUTS(string_buffer);
|
|
}
|
|
|
|
PUTC('\n'); /* end of this entry */
|
|
|
|
free_VMSEntryInfo_contents(entry_info);
|
|
}
|
|
}
|
|
|
|
HTBTreeAndObject_free(bt);
|
|
|
|
} /* End of both BTree loops */
|
|
|
|
/*
|
|
* Complete the output stream.
|
|
*/
|
|
END(HTML_PRE);
|
|
PUTC('\n');
|
|
END(HTML_BODY);
|
|
PUTC('\n');
|
|
END(HTML_HTML);
|
|
PUTC('\n');
|
|
FREE(tail);
|
|
FREE_TARGET;
|
|
|
|
return HT_LOADED;
|
|
|
|
} /* End of directory reading section */
|
|
|
|
/*
|
|
* Remove all versions of the given file. We assume there are no permissions
|
|
* problems, since we do this mainly for removing temporary files.
|
|
*/
|
|
int HTVMS_remove(char *filename)
|
|
{
|
|
int code = remove(filename); /* return the first status code */
|
|
|
|
while (remove(filename) == 0) ;
|
|
return code;
|
|
}
|
|
|
|
/*
|
|
* Remove all older versions of the given file. We may fail to remove some
|
|
* version due to permissions -- the loop stops either at that point, or when
|
|
* we run out of older versions to remove.
|
|
*/
|
|
void HTVMS_purge(char *filename)
|
|
{
|
|
char *older_file = 0;
|
|
char *oldest_file = 0;
|
|
struct stat sb;
|
|
|
|
StrAllocCopy(older_file, filename);
|
|
StrAllocCat(older_file, ";-1");
|
|
|
|
while (remove(older_file) == 0) ;
|
|
/*
|
|
* If we do not have any more older versions, it is safe to rename the
|
|
* current file to version #1.
|
|
*/
|
|
if (stat(older_file, &sb) != 0) {
|
|
StrAllocCopy(oldest_file, filename);
|
|
StrAllocCat(oldest_file, ";1");
|
|
rename(older_file, oldest_file);
|
|
FREE(oldest_file);
|
|
}
|
|
|
|
FREE(older_file);
|
|
}
|
|
#endif /* VMS */
|