diff options
Diffstat (limited to 'WWW/Library/Implementation/HTFile.c')
-rw-r--r-- | WWW/Library/Implementation/HTFile.c | 1871 |
1 files changed, 1871 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTFile.c b/WWW/Library/Implementation/HTFile.c new file mode 100644 index 00000000..9148f5d1 --- /dev/null +++ b/WWW/Library/Implementation/HTFile.c @@ -0,0 +1,1871 @@ +/* File Access HTFile.c +** =========== +** +** This is unix-specific code in general, with some VMS bits. +** These are routines for file access used by browsers. +** Development of this module for Unix DIRED_SUPPORT in Lynx +** regrettably has has been conducted in a manner with now +** creates a major impediment for hopes of adapting Lynx to +** a newer version of the library. +** +** History: +** Feb 91 Written Tim Berners-Lee CERN/CN +** Apr 91 vms-vms access included using DECnet syntax +** 26 Jun 92 (JFG) When running over DECnet, suppressed FTP. +** Fixed access bug for relative names on VMS. +** Sep 93 (MD) Access to VMS files allows sharing. +** 15 Nov 93 (MD) Moved HTVMSname to HTVMSUTILS.C +** 27 Dec 93 (FM) FTP now works with VMS hosts. +** FTP path must be Unix-style and cannot include +** the device or top directory. +*/ + +#ifndef VMS +/* #define LONG_LIST */ /* Define this for long style unix listings (ls -l) */ +/* #define NO_PARENT_DIR_REFERENCE */ /* Define this for no parent links */ +#endif /* !VMS */ + +#include "HTUtils.h" +#include "tcp.h" +#include "HTFile.h" /* Implemented here */ +#ifdef VMS +#include <stat.h> +#endif /* VMS */ + +#ifndef VMS +#ifdef LONG_LIST +#include <pwd.h> +#include <grp.h> +#endif /* LONG_LIST */ +#endif /* !VMS */ + +#define INFINITY 512 /* file name length @@ FIXME */ +#define MULTI_SUFFIX ".multi" /* Extension for scanning formats */ + +#define HT_EM_SPACE ((char)2) + +#define FREE(x) if (x) {free(x); x = NULL;} + +#ifdef VMS +#include "HTVMSUtils.h" +#endif /* VMS */ + +#include "HTParse.h" +#include "HTTCP.h" +#ifndef DECNET +#include "HTFTP.h" +#endif /* !DECNET */ +#include "HTAnchor.h" +#include "HTAtom.h" +#include "HTWriter.h" +#include "HTFWriter.h" +#include "HTInit.h" +#include "HTBTree.h" +#include "HTAlert.h" +#include "HTCJK.h" + +#include "LYexit.h" +#include "LYLeaks.h" + +typedef struct _HTSuffix { + char * suffix; + HTAtom * rep; + HTAtom * encoding; + float quality; +} HTSuffix; + +#ifndef NGROUPS +#ifdef NGROUPS_MAX +#define NGROUPS NGROUPS_MAX +#else +#define NGROUPS 32 +#endif /* NGROUPS_MAX */ +#endif /* NGROUPS */ + + +#ifdef USE_DIRENT /* Set this for Sys V systems */ +#define STRUCT_DIRENT struct dirent +#else +#define STRUCT_DIRENT struct direct +#endif /* USE_DIRENT */ + +#include "HTML.h" /* For directory object building */ + +#define PUTC(c) (*target->isa->put_character)(target, c) +#define PUTS(s) (*target->isa->put_string)(target, s) +#define START(e) (*target->isa->start_element)(target, e, 0, 0, 0) +#define END(e) (*target->isa->end_element)(target, e, 0) +#define FREE_TARGET (*target->isa->_free)(target) +struct _HTStructured { + CONST HTStructuredClass * isa; + /* ... */ +}; + + +/* Controlling globals +** +*/ + +PUBLIC int HTDirAccess = HT_DIR_OK; + +#ifdef DIRED_SUPPORT +PUBLIC int HTDirReadme = HT_DIR_README_NONE; +#define FILES_FIRST 1 +#define MIXED_STYLE 2 +extern BOOLEAN lynx_edit_mode; +extern BOOLEAN dir_list_style; +#else +PUBLIC int HTDirReadme = HT_DIR_README_TOP; +#endif /* DIRED_SUPPORT */ + +extern int current_char_set; +extern char *LYchar_set_names[]; +extern BOOL HTPassEightBitRaw; +extern HTCJKlang HTCJK; + +PRIVATE char *HTMountRoot = "/Net/"; /* Where to find mounts */ +#ifdef VMS +PRIVATE char *HTCacheRoot = "/WWW$SCRATCH"; /* Where to cache things */ +#else +PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_"; /* Where to cache things */ +#endif /* VMS */ + +/* PRIVATE char *HTSaveRoot = "$(HOME)/WWW/";*/ /* Where to save things */ + + +/* Suffix registration +*/ + +PRIVATE HTList * HTSuffixes = 0; +PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 }; +PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0}; + +/* + * To free up the suffixes at program exit. + */ +PRIVATE void free_suffixes NOPARAMS; + +#ifdef LONG_LIST +PRIVATE void LYListFmtParse ARGS5( + char *, fmtstr, + char *, file, + HTStructured *, target, + char *, entry, + char *, tail) +{ + char c; + char *s; + char *end; + char *start; + char *str = NULL; + struct stat st; + char buf[512]; + char fmt[512]; + char type; + struct passwd *p; + struct group *g; + time_t now; + char *datestr; + int len; +#define SEC_PER_YEAR (60 * 60 * 24 * 365) + static char *pbits[] = { "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx", 0 }; + static char *psbits[] = { "--S", "--s", "-wS", "-ws", + "r-S", "r-s", "rwS", "rws", 0 }; +#define PBIT(a, n, s) (s) ? psbits[((a) >> (n)) & 0x7] : \ + pbits[((a) >> (n)) & 0x7] + + if (lstat(file, &st) < 0) + fmtstr = "%a"; /* can't stat so just do anchor */ + + StrAllocCopy(str, fmtstr); + s = str; + end = str + strlen(str); + START(HTML_PRE); + while (*s) { + start = s; + while (*s) { + if (*s == '%') { + if (*(s+1) == '%') /* literal % */ + s++; + else + break; + } + s++; + } + /* s is positioned either at a % or at \0 */ + *s = '\0'; + if (s > start) { /* some literal chars. */ + PUTS(start); + } + if (s == end) + break; + start = ++s; + while (isdigit(*s) || *s == '.' || *s == '-') + s++; + c = *s; /* the format char. or \0 */ + *s = '\0'; + + switch (c) { + case '\0': + break; + + case 'A': + case 'a': /* anchor */ + HTDirEntry(target, tail, entry); + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, entry); + PUTS(buf); + END(HTML_A); + if (c != 'A' && (st.st_mode & S_IFMT) == S_IFLNK && + (len = readlink(file, buf, sizeof(buf))) >= 0) { + PUTS(" -> "); + buf[len] = '\0'; + PUTS(buf); + } + *buf = '\0'; + break; + + case 'd': /* date */ + now = time(0); + datestr = ctime(&st.st_mtime); + if ((now - st.st_mtime) < SEC_PER_YEAR/2) + /* MMM DD HH:MM */ + sprintf(buf, "%.12s", datestr + 4); + else + /* MMM DD YYYY */ + sprintf(buf, "%.7s %.4s ", datestr + 4, + datestr + 20); + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, buf); + break; + + case 's': /* size in bytes */ + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_size); + break; + + case 'K': /* size in Kilobytes but not for directories */ + if ((st.st_mode & S_IFMT) == S_IFDIR) { + sprintf(fmt, "%%%ss ", start); + sprintf(buf, fmt, ""); + break; + } + /* FALL THROUGH */ + case 'k': /* size in Kilobytes */ + sprintf(fmt, "%%%sdK", start); + sprintf(buf, fmt, (st.st_size+1023)/1024); + break; + + case 'p': /* unix-style permission bits */ + switch(st.st_mode & S_IFMT) { + case S_IFIFO: type = 'p'; break; + case S_IFCHR: type = 'c'; break; + case S_IFDIR: type = 'd'; break; + case S_IFBLK: type = 'b'; break; + case S_IFREG: type = '-'; break; + case S_IFLNK: type = 'l'; break; +#ifdef S_IFSOCK + case S_IFSOCK: type = 's'; break; +#endif /* S_IFSOCK */ + default: type = '?'; break; + } + sprintf(buf, "%c%s%s%s", type, + PBIT(st.st_mode, 6, st.st_mode & S_ISUID), + PBIT(st.st_mode, 3, st.st_mode & S_ISGID), + PBIT(st.st_mode, 0, 0)); + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, buf); + break; + + case 'o': /* owner */ + sprintf(fmt, "%%%ss", start); + p = getpwuid(st.st_uid); + if (p) { + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, p->pw_name); + } else { + + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_uid); + } + break; + + case 'g': /* group */ + g = getgrgid(st.st_gid); + if (g) { + sprintf(fmt, "%%%ss", start); + sprintf(buf, fmt, g->gr_name); + } else { + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_gid); + } + break; + + case 'l': /* link count */ + sprintf(fmt, "%%%sd", start); + sprintf(buf, fmt, st.st_nlink); + break; + + default: + fprintf(stderr, + "Unknown format character `%c' in list format\n", c); + break; + } + PUTS(buf); + + s++; + } + END(HTML_PRE); + PUTS("\n"); + FREE(str); +} +#endif /* LONG_LIST */ + +/* Define the representation associated with a file suffix +** ------------------------------------------------------- +** +** Calling this with suffix set to "*" will set the default +** representation. +** Calling this with suffix set to "*.*" will set the default +** representation for unknown suffix files which contain a ".". +** +** If filename suffix is already defined its previous +** definition is overridden. +*/ +PUBLIC void HTSetSuffix ARGS4( + CONST char *, suffix, + CONST char *, representation, + CONST char *, encoding, + float, value) +{ + HTSuffix * suff; + + if (strcmp(suffix, "*") == 0) + suff = &no_suffix; + else if (strcmp(suffix, "*.*") == 0) + suff = &unknown_suffix; + else { + HTList *cur = HTSuffixes; + + while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) { + if (suff->suffix && 0 == strcmp(suff->suffix, suffix)) + break; + } + if (!suff) { /* Not found -- create a new node */ + suff = (HTSuffix *) calloc(1, sizeof(HTSuffix)); + if (suff == NULL) + outofmem(__FILE__, "HTSetSuffix"); + + /* + * Memory leak fixed. + * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe + */ + if (!HTSuffixes) { + HTSuffixes = HTList_new(); + atexit(free_suffixes); + } + + HTList_addObject(HTSuffixes, suff); + + StrAllocCopy(suff->suffix, suffix); + } + } + + suff->rep = HTAtom_for(representation); + + /* + * Memory leak fixed. + * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe + * Invariant code removed. + */ + suff->encoding = HTAtom_for(encoding); + + suff->quality = value; +} + +/* +** Purpose: Free all added suffixes. +** Arguments: void +** Return Value: void +** Remarks/Portability/Dependencies/Restrictions: +** To be used at program exit. +** Revision History: +** 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe +*/ +PRIVATE void free_suffixes NOARGS +{ + HTSuffix * suff = NULL; + + /* + * Loop through all suffixes. + */ + while (!HTList_isEmpty(HTSuffixes)) { + /* + * Free off each item and its members if need be. + */ + suff = (HTSuffix *)HTList_removeLastObject(HTSuffixes); + FREE(suff->suffix); + FREE(suff); + } + /* + * Free off the list itself. + */ + HTList_delete(HTSuffixes); + HTSuffixes = NULL; +} + + +/* Send README file +** +** If a README file exists, then it is inserted into the document here. +*/ +#ifdef GOT_READ_DIR +PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname) +{ + FILE * fp; + char * readme_file_name = + malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1); + strcpy(readme_file_name, localname); + strcat(readme_file_name, "/"); + strcat(readme_file_name, HT_DIR_README_FILE); + + fp = fopen(readme_file_name, "r"); + + if (fp) { + HTStructuredClass targetClass; + + targetClass = *target->isa; /* (Can't init agregate in K&R) */ + 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; +/* case '\n': + PUTC('\r'); +Bug removed thanks to joe@athena.mit.edu */ + default: + PUTC(c); + } +#else + PUTC(c); +#endif /* NOTDEFINED */ + } + END(HTML_PRE); + fclose(fp); + } +} +#endif /* GOT_READ_DIR */ + + +/* Make the cache file name for a W3 document +** ------------------------------------------ +** Make up a suitable name for saving the node in +** +** E.g. /tmp/WWW_Cache_news/1234@cernvax.cern.ch +** /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx +** +** On exit, +** returns a malloc'ed string which must be freed by the caller. +*/ +PUBLIC char * HTCacheFileName ARGS1( + CONST char *, name) +{ + char * access = HTParse(name, "", PARSE_ACCESS); + char * host = HTParse(name, "", PARSE_HOST); + char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION); + + char * result; + result = (char *)malloc( + strlen(HTCacheRoot)+strlen(access) + +strlen(host)+strlen(path)+6+1); + if (result == NULL) + outofmem(__FILE__, "HTCacheFileName"); + sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path); + FREE(path); + FREE(access); + FREE(host); + return result; +} + + +/* Open a file for write, creating the path +** ---------------------------------------- +*/ +#ifdef NOT_IMPLEMENTED +PRIVATE int HTCreatePath ARGS1(CONST char *,path) +{ + return -1; +} +#endif /* NOT_IMPLEMENTED */ + +/* Convert filenames between local and WWW formats +** ----------------------------------------------- +** Make up a suitable name for saving the node in +** +** E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch +** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx +** +** On exit, +** returns a malloc'ed string which must be freed by the caller. +*/ +PUBLIC char * HTLocalName ARGS1( + CONST char *, name) +{ + char * access = HTParse(name, "", PARSE_ACCESS); + char * host = HTParse(name, "", PARSE_HOST); + char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION); + + HTUnEscape(path); /* Interpret % signs */ + + if (0 == strcmp(access, "file")) { /* local file */ + FREE(access); + if ((0 == strcasecomp(host, HTHostName())) || + (0 == strcasecomp(host, "localhost")) || !*host) { + FREE(host); + if (TRACE) + fprintf(stderr, "Node `%s' means path `%s'\n", name, path); + return(path); + } else { + char * result = (char *)malloc( + strlen("/Net/")+strlen(host)+strlen(path)+1); + if (result == NULL) + outofmem(__FILE__, "HTLocalName"); + sprintf(result, "%s%s%s", "/Net/", host, path); + FREE(host); + FREE(path); + if (TRACE) + fprintf(stderr, "Node `%s' means file `%s'\n", name, result); + return result; + } + } else { /* other access */ + char * result; +#ifdef VMS + char * home = getenv("HOME"); + if (!home) + home = HTCacheRoot; + else + home = HTVMS_wwwName(home); +#else + CONST char * home = (CONST char*)getenv("HOME"); + if (!home) + home = "/tmp"; +#endif /* VMS */ + result = (char *)malloc( + strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1); + if (result == NULL) + outofmem(__FILE__, "HTLocalName"); + sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path); + FREE(path); + FREE(access); + FREE(host); + return result; + } +} + + +/* Make a WWW name from a full local path name +** +** Bugs: +** At present, only the names of two network root nodes are hand-coded +** in and valid for the NeXT only. This should be configurable in +** the general case. +*/ + +PUBLIC char * WWW_nameOfFile ARGS1( + CONST char *, name) +{ + char * result; +#ifdef NeXT + if (0 == strncmp("/private/Net/", name, 13)) { + result = (char *)malloc(7+strlen(name+13)+1); + if (result == NULL) + outofmem(__FILE__, "WWW_nameOfFile"); + sprintf(result, "file://%s", name+13); + } else +#endif /* NeXT */ + if (0 == strncmp(HTMountRoot, name, 5)) { + result = (char *)malloc(7+strlen(name+5)+1); + if (result == NULL) + outofmem(__FILE__, "WWW_nameOfFile"); + sprintf(result, "file://%s", name+5); + } else { + result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1); + if (result == NULL) + outofmem(__FILE__, "WWW_nameOfFile"); + sprintf(result, "file://%s%s", HTHostName(), name); + } + if (TRACE) + fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result); + return result; +} + + +/* Determine a suitable suffix, given the representation +** ----------------------------------------------------- +** +** On entry, +** rep is the atomized MIME style representation +** +** On exit, +** returns a pointer to a suitable suffix string if one has been +** found, else "". +*/ +PUBLIC CONST char * HTFileSuffix ARGS1( + HTAtom*, rep) +{ + HTSuffix * suff; + int n; + int i; + +#define NO_INIT /* dont init anymore since I do it in Lynx at startup */ +#ifndef NO_INIT + if (!HTSuffixes) + HTFileInit(); +#endif /* !NO_INIT */ + n = HTList_count(HTSuffixes); + for (i = 0; i < n; i++) { + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i); + if (suff->rep == rep) { + return suff->suffix; /* OK -- found */ + } + } + return ""; /* Dunno */ +} + + +/* Determine file format from file name +** ------------------------------------ +** +** This version will return the representation and also set +** a variable for the encoding. +** +** It will handle for example x.txt, x.txt,Z, x.Z +*/ + +PUBLIC HTFormat HTFileFormat ARGS2( + CONST char *, filename, + HTAtom **, pencoding) +{ + HTSuffix * suff; + int n; + int i; + int lf; +#ifdef VMS + char *semicolon = NULL; +#endif /* VMS */ + extern char LYforce_HTML_mode; + + if (LYforce_HTML_mode) { + LYforce_HTML_mode = FALSE; + return WWW_HTML; + } + +#ifdef VMS + /* + * Trim at semicolon if a version number was + * included, so it doesn't interfere with the + * code for getting the MIME type. - FM + */ + if ((semicolon = strchr(filename, ';')) != NULL) + *semicolon = '\0'; +#endif /* VMS */ + +#ifndef NO_INIT + if (!HTSuffixes) + HTFileInit(); +#endif /* !NO_INIT */ + *pencoding = NULL; + lf = strlen(filename); + n = HTList_count(HTSuffixes); + for (i = 0; i < n; i++) { + int ls; + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i); + ls = strlen(suff->suffix); + if ((ls <= lf) && 0 == strcasecomp(suff->suffix, filename + lf - ls)) { + int j; + *pencoding = suff->encoding; + if (suff->rep) { +#ifdef VMS + if (semicolon != NULL) + *semicolon = ';'; +#endif /* VMS */ + return suff->rep; /* OK -- found */ + } + for (j = 0; j < n; j++) { /* Got encoding, need representation */ + int ls2; + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, j); + ls2 = strlen(suff->suffix); + if ((ls <= lf) && 0 == strncasecomp( + suff->suffix, filename + lf - ls -ls2, ls2)) { + if (suff->rep) { +#ifdef VMS + if (semicolon != NULL) + *semicolon = ';'; +#endif /* VMS */ + return suff->rep; + } + } + } + + } + } + + /* defaults tree */ + + suff = strchr(filename, '.') ? /* Unknown suffix */ + ( unknown_suffix.rep ? &unknown_suffix : &no_suffix) + : &no_suffix; + + /* set default encoding unless found with suffix already */ + if (!*pencoding) + *pencoding = suff->encoding ? suff->encoding + : HTAtom_for("binary"); +#ifdef VMS + if (semicolon != NULL) + *semicolon = ';'; +#endif /* VMS */ + return suff->rep ? suff->rep : WWW_BINARY; +} + + +/* Revise the file format in relation to the Lynx charset. - FM +** ------------------------------------------------------- +** +** This checks the format associated with an anchor for +** an extended MIME Content-Type, and if a charset is +** indicated, sets Lynx up for proper handling in relation +** to the currently selected character set. - FM +*/ +PUBLIC HTFormat HTCharsetFormat ARGS2( + HTFormat, format, + HTParentAnchor *, anchor) + +{ + char *cp = NULL, *cp1, *cp2; + int i; + + FREE(anchor->charset); + StrAllocCopy(cp, format->name); + for (i = 0; cp[i]; i++) + cp[i] = TOLOWER(cp[i]); + if (((cp1 = strchr(cp, ';')) != NULL) && + (cp2 = strstr(cp1, "charset")) != NULL) { + if (TRACE) + fprintf(stderr, + "HTCharsetFormat: Extended MIME Content-Type is %s\n", + format->name); + cp2 += 7; + while (*cp2 == ' ' || *cp2 == '=') + cp2++; + if (!strncmp(cp2, "us-ascii", 8) || + !strncmp(cp2, "iso-8859-1", 10)) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-8859-1"); + HTCJK = NOCJK; + } else if (!strncmp(cp2, "iso-8859-2", 10) && + !strncmp(LYchar_set_names[current_char_set], + "ISO Latin 2", 11)) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-8859-2"); + HTPassEightBitRaw = TRUE; + } else if (!strncmp(cp2, "iso-8859-", 9) && + !strncmp(LYchar_set_names[current_char_set], + "Other ISO Latin", 15)) { + /* + ** Hope it's a match, for now. - FM + */ + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-8859- "); + anchor->charset[9] = cp2[9]; + HTPassEightBitRaw = TRUE; + HTAlert(anchor->charset); + } else if (!strncmp(cp2, "koi8-r", 6) && + !strncmp(LYchar_set_names[current_char_set], + "KOI8-R character set", 20)) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "koi8-r"); + HTPassEightBitRaw = TRUE; + } else if (!strncmp(cp2, "euc-jp", 6) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "euc-jp"); + } else if (!strncmp(cp2, "shift_jis", 9) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "shift_jis"); + } else if (!strncmp(cp2, "iso-2022-jp", 11) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-jp"); + } else if (!strncmp(cp2, "iso-2022-jp-2", 13) && + HTCJK == JAPANESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-jp-2"); + } else if (!strncmp(cp2, "euc-kr", 6) && + HTCJK == KOREAN) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "euc-kr"); + } else if (!strncmp(cp2, "iso-2022-kr", 11) && + HTCJK == KOREAN) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-kr"); + } else if ((!strncmp(cp2, "big5", 4) || + !strncmp(cp2, "cn-big5", 7)) && + HTCJK == TAIPEI) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "big5"); + } else if (!strncmp(cp2, "euc-cn", 6) && + HTCJK == CHINESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "euc-cn"); + } else if ((!strncmp(cp2, "gb2312", 6) || + !strncmp(cp2, "cn-gb", 5)) && + HTCJK == CHINESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "gb2312"); + } else if (!strncmp(cp2, "iso-2022-cn", 11) && + HTCJK == CHINESE) { + *cp1 = '\0'; + format = HTAtom_for(cp); + StrAllocCopy(anchor->charset, "iso-2022-cn"); + } + } + FREE(cp); + + return format; +} + + +/* Determine value from file name +** ------------------------------ +** +*/ + +PUBLIC float HTFileValue ARGS1( + CONST char *, filename) +{ + HTSuffix * suff; + int n; + int i; + int lf = strlen(filename); + +#ifndef NO_INIT + if (!HTSuffixes) + HTFileInit(); +#endif /* !NO_INIT */ + n = HTList_count(HTSuffixes); + for (i = 0; i < n; i++) { + int ls; + suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i); + ls = strlen(suff->suffix); + if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) { + if (TRACE) + fprintf(stderr, "File: Value of %s is %.3f\n", + filename, suff->quality); + return suff->quality; /* OK -- found */ + } + } + return 0.3; /* Dunno! */ +} + + +/* Determine write access to a file +** -------------------------------- +** +** On exit, +** return value YES if file can be accessed and can be written to. +** +** Bugs: +** 1. No code for non-unix systems. +** 2. Isn't there a quicker way? +*/ + +#ifdef VMS +#define NO_GROUPS +#endif /* VMS */ +#ifdef NO_UNIX_IO +#define NO_GROUPS +#endif /* NO_UNIX_IO */ +#ifdef PCNFS +#define NO_GROUPS +#endif /* PCNFS */ + +PUBLIC BOOL HTEditable ARGS1( + CONST char *, filename) +{ +#ifdef NO_GROUPS + return NO; /* Safe answer till we find the correct algorithm */ +#else +#ifdef NeXT + int groups[NGROUPS]; +#else + gid_t groups[NGROUPS]; +#endif /* NeXT */ + uid_t myUid; + int ngroups; /* The number of groups */ + struct stat fileStatus; + int i; + + if (stat(filename, &fileStatus)) /* Get details of filename */ + return NO; /* Can't even access file! */ + + ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */ + myUid = geteuid(); /* Get my user identifier */ + + if (TRACE) { + int i; + fprintf(stderr, + "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (", + (unsigned int) fileStatus.st_mode, fileStatus.st_uid, + fileStatus.st_gid, + myUid, ngroups); + for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]); + fprintf(stderr, ")\n"); + } + + if (fileStatus.st_mode & 0002) /* I can write anyway? */ + return YES; + + if ((fileStatus.st_mode & 0200) /* I can write my own file? */ + && (fileStatus.st_uid == myUid)) + return YES; + + if (fileStatus.st_mode & 0020) /* Group I am in can write? */ + { + for (i=0; i<ngroups; i++) { + if (groups[i] == fileStatus.st_gid) + return YES; + } + } + if (TRACE) + fprintf(stderr, "\tFile is not editable.\n"); + return NO; /* If no excuse, can't do */ +#endif /* NO_GROUPS */ +} + + +/* Make a save stream +** ------------------ +** +** The stream must be used for writing back the file. +** @@@ no backup done +*/ +PUBLIC HTStream * HTFileSaveStream ARGS1( + HTParentAnchor *, anchor) +{ + + CONST char * addr = HTAnchor_address((HTAnchor*)anchor); + char * localname = HTLocalName(addr); + + FILE* fp = fopen(localname, "w"); + if (!fp) + return NULL; + + return HTFWriter_new(fp); + +} + +/* Output one directory entry +** +*/ +PUBLIC void HTDirEntry ARGS3( + HTStructured *, target, + CONST char *, tail, + CONST char *, entry) +{ + char * relative; + char * escaped = HTEscape(entry, URL_XPALPHAS); + + + if (tail == NULL || *tail == '\0') { + /* handle extra slash at end of path */ + HTStartAnchor(target, NULL, escaped); + } else { + /* If empty tail, gives absolute ref below */ + relative = (char*) malloc(strlen(tail) + strlen(escaped)+2); + if (relative == NULL) + outofmem(__FILE__, "DirRead"); + sprintf(relative, "%s/%s", tail, escaped); + HTStartAnchor(target, NULL, relative); + FREE(relative); + } + FREE(escaped); +} + +/* Output parent directory entry +** +** This gives the TITLE and H1 header, and also a link +** to the parent directory if appropriate. +*/ +PUBLIC void HTDirTitles ARGS2( + HTStructured *, target, + HTAnchor * , anchor) +{ + char * logical = HTAnchor_address(anchor); + char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION); + char * current; + char * cp = NULL; + + /* Trim out the ;type= parameter, if present. - FM */ + if ((cp = strrchr(path, ';')) != NULL) { + if (!strncasecomp((cp+1), "type=", 5)) { + if (TOUPPER(*(cp+6)) == 'D' || + TOUPPER(*(cp+6)) == 'A' || + TOUPPER(*(cp+6)) == 'I') + *cp = '\0'; + } + } + current = strrchr(path, '/'); /* last part or "" */ + + { + char * printable = NULL; + +#ifdef DIRED_SUPPORT + if (0 == strncasecomp(path, "/%2F", 4)) + StrAllocCopy(printable, (path+1)); + else + StrAllocCopy(printable, path); + if (0 == strncasecomp(printable, "/vmsysu%2b", 10) || + 0 == strncasecomp(printable, "/anonymou.", 10)) { + StrAllocCopy(cp, (printable+1)); + StrAllocCopy(printable, cp); + FREE(cp); + } +#else + StrAllocCopy(printable, (current + 1)); +#endif /* DIRED_SUPPORT */ + + START(HTML_HEAD); + PUTS("\n"); + HTUnEscape(printable); + START(HTML_TITLE); + PUTS(*printable ? printable : "Welcome"); + PUTS(" directory"); + END(HTML_TITLE); + PUTS("\n"); + END(HTML_HEAD); + PUTS("\n"); + +#ifdef DIRED_SUPPORT + START(HTML_H2); + PUTS(*printable ? "Current directory is " : ""); + PUTS(*printable ? printable : "Welcome"); + END(HTML_H2); + PUTS("\n"); +#else + START(HTML_H1); + PUTS(*printable ? printable : "Welcome"); + END(HTML_H1); + PUTS("\n"); +#endif /* DIRED_SUPPORT */ + if (((0 == strncasecomp(printable, "vmsysu:", 7)) && + (cp = strchr(printable, '.')) != NULL && + strchr(cp, '/') == NULL) || + (0 == strncasecomp(printable, "anonymou.", 9) && + strchr(printable, '/') == NULL)) { + FREE(printable); + FREE(logical); + FREE(path); + return; + } + FREE(printable); + } + +#ifndef NO_PARENT_DIR_REFERENCE + /* Make link back to parent directory + */ + + if (current && current[1]) { /* was a slash AND something else too */ + char * parent; + char * relative; + *current++ = 0; + parent = strrchr(path, '/'); /* penultimate slash */ + + if ((parent && 0 == strncasecomp(parent, "/%2F", 4)) || + 0 == strncasecomp(current, "%2F", 3)) { + FREE(logical); + FREE(path); + return; + } + + relative = (char*) malloc(strlen(current) + 4); + if (relative == NULL) + outofmem(__FILE__, "DirRead"); + sprintf(relative, "%s/..", current); +#ifndef VMS + { + /* + * On Unix, if it's not ftp and the directory cannot + * be read, don't put out a link. + * + * On VMS, this problem is dealt with internally by + * HTVMSBrowseDir(). + */ + extern BOOLEAN LYisLocalFile PARAMS((char *logical)); + DIR * dp=NULL; + + if (LYisLocalFile(logical)) { + if ((dp = opendir(relative)) == NULL) { + FREE(logical); + FREE(relative); + FREE(path); + return; + } + if (dp) + closedir(dp); + } + } +#endif /* !VMS */ + HTStartAnchor(target, "", relative); + FREE(relative); + +#ifdef DIRED_SUPPORT + if (dir_list_style != MIXED_STYLE) +#endif /* DIRED_SUPPORT */ + PUTS("Up to "); + if (parent) { +#ifdef DIRED_SUPPORT + if (dir_list_style == MIXED_STYLE) { + PUTS("../"); + } else { +#else + { +#endif /* DIRED_SUPPORT */ + char * printable = NULL; + StrAllocCopy(printable, parent + 1); + HTUnEscape(printable); + PUTS(printable); + FREE(printable); + } + } else { + PUTS("/"); + } + + END(HTML_A); + } +#endif /* NO_PARENT_DIR_REFERENCE */ + + FREE(logical); + FREE(path); + return; +} + + + +/* Load a document +** --------------- +** +** On entry, +** addr must point to the fully qualified hypertext reference. +** This is the physical address of the file +** +** On exit, +** returns <0 Error has occured. +** HTLOADED OK +** +*/ +PUBLIC int HTLoadFile ARGS4( + CONST char *, addr, + HTParentAnchor *, anchor, + HTFormat, format_out, + HTStream *, sink) +{ + char * filename = NULL; + char * access = NULL; + HTFormat format; + char * nodename = NULL; + char * newname = NULL; /* Simplified name of file */ + HTAtom * encoding; /* @@ not used yet */ +#ifdef VMS + struct stat stat_info; +#else + extern char *list_format; +#endif /* VMS */ + + /* + ** Reduce the filename to a basic form (hopefully unique!) + */ + StrAllocCopy(newname, addr); + filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION); + nodename=HTParse(newname, "", PARSE_HOST); + + /* + ** If access is ftp, or file is on another host, invoke ftp now. + */ + access = HTParse(newname, "", PARSE_ACCESS); + if (strcmp("ftp", access) == 0 || + (strcmp("localhost", nodename) != 0 && +#ifdef VMS + strcasecomp(nodename, HTHostName()) != 0)) +#else + strcmp(nodename, HTHostName()) != 0)) +#endif /* VMS */ + { + FREE(newname); + FREE(filename); + FREE(nodename); + FREE(access); + return HTFTPLoad(addr, anchor, format_out, sink); + } else { + FREE(newname); + FREE(access); + } +#ifdef VMS + HTUnEscape(filename); +#endif /* VMS */ + + /* + ** Determine the format and encoding mapped to any suffix. + */ + format = HTFileFormat(filename, &encoding); + + /* + ** Check the format for an extended MIME charset value, and + ** act on it if present. Otherwise, assume the ISO-8859-1 + ** character set for local files. If it's actually another + ** charset (e.g., ISO-8859-2 or KOI8-R) and the terminal is + ** using that, Lynx users should make the current character + ** set "ISO Latin 1" so that 8-bit characters are passed raw. + */ + format = HTCharsetFormat(format, anchor); + +#ifdef VMS + /* + ** Check to see if the 'filename' is in fact a directory. If it is + ** create a new hypertext object containing a list of files and + ** subdirectories contained in the directory. All of these are links + ** to the directories or files listed. + */ + if (HTStat(filename, &stat_info) == -1) { + if (TRACE) + fprintf(stderr, "HTLoadFile: Can't stat %s\n", filename); + } else { + if (((stat_info.st_mode) & S_IFMT) == S_IFDIR) { + if (HTDirAccess == HT_DIR_FORBID) { + FREE(filename); + FREE(nodename); + return HTLoadError(sink, 403, + "Directory browsing is not allowed."); + } + + if (HTDirAccess == HT_DIR_SELECTIVE) { + char * enable_file_name = + malloc(strlen(filename)+ 1 + + strlen(HT_DIR_ENABLE_FILE) + 1); + strcpy(enable_file_name, filename); + strcat(enable_file_name, "/"); + strcat(enable_file_name, HT_DIR_ENABLE_FILE); + if (HTStat(enable_file_name, &stat_info) == -1) { + FREE(filename); + FREE(nodename); + return HTLoadError(sink, 403, + "Selective access is not enabled for this directory"); + } + } + + FREE(filename); + FREE(nodename); + return HTVMSBrowseDir(addr, anchor, format_out, sink); + } + } + + /* + ** Assume that the file is in Unix-style syntax if it contains a '/' + ** after the leading one @@ + */ + { + FILE * fp; + char * vmsname = strchr(filename + 1, '/') ? + HTVMS_name(nodename, filename) : filename + 1; + fp = fopen(vmsname, "r", "shr=put", "shr=upd"); + + /* + ** If the file wasn't VMS syntax, then perhaps it is ultrix + */ + if (!fp) { + char ultrixname[INFINITY]; + if (TRACE) + fprintf(stderr, "HTLoadFile: Can't open as %s\n", vmsname); + sprintf(ultrixname, "%s::\"%s\"", nodename, filename); + fp = fopen(ultrixname, "r", "shr=put", "shr=upd"); + if (!fp) { + if (TRACE) + fprintf(stderr, "HTLoadFile: Can't open as %s\n", + ultrixname); + } + } + if (fp) { + int len; + char *cp = NULL; + char *semicolon = NULL; + + if (HTEditable(vmsname)) { + HTAtom * put = HTAtom_for("PUT"); + HTList * methods = HTAnchor_methods(anchor); + if (HTList_indexOf(methods, put) == (-1)) { + HTList_addObject(methods, put); + } + } + /* + * Trim vmsname at semicolon if a version number was + * included, so it doesn't interfere with the check + * for a compressed file. - FM + */ + if ((semicolon = strchr(vmsname, ';')) != NULL) + *semicolon = '\0'; + /* + ** Fake a Content-Encoding for compressed files. - FM + */ + if ((len = strlen(vmsname)) > 2) { + if ((vmsname[len - 1] == 'Z') && + (vmsname[len - 2] == '.' || + vmsname[len - 2] == '-' || + vmsname[len - 2] == '_') && + vmsname[len - 3] != ']' && + vmsname[len - 3] != ':') { + StrAllocCopy(cp, vmsname); + cp[len - 2] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-compress"); + format = HTAtom_for("www/compressed"); + } else if ((len > 3) && + !strcasecomp((char *)&vmsname[len - 2], "gz")) { + if (vmsname[len - 3] == '.' || + vmsname[len - 3] == '-' || + vmsname[len - 3] == '_') { + StrAllocCopy(cp, vmsname); + cp[len - 3] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-gzip"); + format = HTAtom_for("www/compressed"); + } + } + } + if (semicolon != NULL) + *semicolon = ';'; + FREE(filename); + FREE(nodename); + HTParseFile(format, format_out, anchor, fp, sink); + fclose(fp); + return HT_LOADED; + } /* If successfull open */ + FREE(filename); + } + +#else /* Unix: */ + + FREE(filename); + + /* + ** For unix, we try to translate the name into the name of a + ** transparently mounted file. + ** + ** Not allowed in secure (HTClienntHost) situations TBL 921019 + */ +#ifndef NO_UNIX_IO + /* Need protection here for telnet server but not httpd server */ + + if (!HTSecure) { /* try local file system */ + char * localname = HTLocalName(addr); + struct stat dir_info; + +#ifdef GOT_READ_DIR + /* Multiformat handling + ** + ** If needed, scan directory to find a good file. + ** Bug: we don't stat the file to find the length + */ + if ((strlen(localname) > strlen(MULTI_SUFFIX)) && + (0 == strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX), + MULTI_SUFFIX))) { + DIR *dp; + + STRUCT_DIRENT * dirbuf; + float best = NO_VALUE_FOUND; /* So far best is bad */ + HTFormat best_rep = NULL; /* Set when rep found */ + STRUCT_DIRENT best_dirbuf; /* Best dir entry so far */ + + char * base = strrchr(localname, '/'); + int baselen; + + if (!base || base == localname) + goto forget_multi; + *base++ = 0; /* Just got directory name */ + baselen = strlen(base)- strlen(MULTI_SUFFIX); + base[baselen] = 0; /* Chop off suffix */ + + dp = opendir(localname); + if (!dp) { +forget_multi: + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 500, + "Multiformat: directory scan failed."); + } + + while ((dirbuf = readdir(dp))!=0) { + /* while there are directory entries to be read */ + if (dirbuf->d_ino == 0) + continue; /* if the entry is not being used, skip it */ + + if ((int)strlen(dirbuf->d_name) > baselen && /* Match? */ + !strncmp(dirbuf->d_name, base, baselen)) { + HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding); + float value = HTStackValue(rep, format_out, + HTFileValue(dirbuf->d_name), + 0.0 /* @@@@@@ */); + if (value != NO_VALUE_FOUND) { + if (TRACE) + fprintf(stderr, + "HTLoadFile: value of presenting %s is %f\n", + HTAtom_name(rep), value); + if (value > best) { + best_rep = rep; + best = value; + best_dirbuf = *dirbuf; + } + } /* if best so far */ + } /* if match */ + + } /* end while directory entries left to read */ + closedir(dp); + + if (best_rep) { + format = best_rep; + base[-1] = '/'; /* Restore directory name */ + base[0] = 0; + StrAllocCat(localname, best_dirbuf.d_name); + goto open_file; + + } else { /* If not found suitable file */ + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, /* List formats? */ + "Could not find suitable representation for transmission."); + } + /*NOTREACHED*/ + } /* if multi suffix */ + + /* + ** Check to see if the 'localname' is in fact a directory. If it + ** is create a new hypertext object containing a list of files and + ** subdirectories contained in the directory. All of these are + ** links to the directories or files listed. + ** NB This assumes the existance of a type 'STRUCT_DIRENT', which + ** will hold the directory entry, and a type 'DIR' which is used + ** to point to the current directory being read. + */ + if (stat(localname,&dir_info) == -1) { /* get file information */ + /* if can't read file information */ + if (TRACE) + fprintf(stderr, "HTLoadFile: can't stat %s\n", localname); + + } else { /* Stat was OK */ + + if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) { + /* if localname is a directory */ + + HTStructured* target; /* HTML object */ + HTStructuredClass targetClass; + + DIR *dp; + STRUCT_DIRENT * dirbuf; + + char * logical=0; + char * pathname=0; + char * tail=0; + + BOOL present[HTML_A_ATTRIBUTES]; + + char * tmpfilename = NULL; + struct stat file_info; + + if (TRACE) + fprintf(stderr,"%s is a directory\n",localname); + + /* + ** Check directory access. + ** Selective access means only those directories containing + ** a marker file can be browsed + */ + if (HTDirAccess == HT_DIR_FORBID) { + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, + "Directory browsing is not allowed."); + } + + + if (HTDirAccess == HT_DIR_SELECTIVE) { + char * enable_file_name = + malloc(strlen(localname)+ 1 + + strlen(HT_DIR_ENABLE_FILE) + 1); + strcpy(enable_file_name, localname); + strcat(enable_file_name, "/"); + strcat(enable_file_name, HT_DIR_ENABLE_FILE); + if (stat(enable_file_name, &file_info) != 0) { + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, + "Selective access is not enabled for this directory"); + } + } + + + dp = opendir(localname); + if (!dp) { + FREE(localname); + FREE(nodename); + return HTLoadError(sink, 403, "This directory is not readable."); + } + + + /* + ** Directory access is allowed and possible. + */ + logical = HTAnchor_address((HTAnchor*)anchor); + pathname = HTParse(logical, "", + PARSE_PATH + PARSE_PUNCTUATION); + + if (!strcmp(pathname,"/")) /* root path */ + StrAllocCopy (tail, "/foo/.."); + else + { + char * p = strrchr(pathname, '/'); /* find lastslash */ + StrAllocCopy(tail, p+1); /* take slash off the beginning */ + } + FREE(pathname); + + target = HTML_new(anchor, format_out, sink); + targetClass = *target->isa; /* Copy routine entry points */ + + { int i; + for (i = 0; i < HTML_A_ATTRIBUTES; i++) + present[i] = (i==HTML_A_HREF); + } + + HTDirTitles(target, (HTAnchor *)anchor); + +#ifdef DIRED_SUPPORT + HTAnchor_setFormat((HTParentAnchor *) anchor, WWW_DIRED); + lynx_edit_mode = TRUE; +#endif /* DIRED_SUPPORT */ + if (HTDirReadme == HT_DIR_README_TOP) + do_readme(target, localname); + { + HTBTree * bt = HTBTree_new((HTComparer)strcmp); + + while ((dirbuf = readdir(dp))!=0) + { + /* while there are directory entries to be read */ + HTBTElement * dirname = NULL; + extern BOOLEAN no_dotfiles, show_dotfiles; + + if (dirbuf->d_ino == 0) + /* if the entry is not being used, skip it */ + continue; + + if (strcmp(dirbuf->d_name, ".") == 0 /* skip self */ +#ifdef NO_PARENT_DIR_REFERENCE + || strcmp(dirbuf->d_name, "..") == 0 || +#else + || strcmp(dirbuf->d_name, "..") != 0 && +#endif + ((no_dotfiles || !show_dotfiles) && + dirbuf->d_name[0] == '.')) + /* skip those files whose name + * begins with '.' */ + continue; + + dirname = (HTBTElement *)malloc( + strlen(dirbuf->d_name) + 4); + if (dirname == NULL) + outofmem(__FILE__,"DirRead"); + StrAllocCopy(tmpfilename,localname); + if (strcmp(localname,"/")) + /* if filename is not root directory */ + StrAllocCat(tmpfilename,"/"); + + StrAllocCat(tmpfilename,dirbuf->d_name); + stat(tmpfilename, &file_info); + if (((file_info.st_mode) & S_IFMT) == S_IFDIR) +#ifndef DIRED_SUPPORT + sprintf((char *)dirname,"D%s",dirbuf->d_name); + else + sprintf((char *)dirname,"F%s",dirbuf->d_name); + /* D & F to have first directories, then files */ +#else + if (dir_list_style == MIXED_STYLE) + sprintf((char *)dirname," %s/",dirbuf->d_name); + else + sprintf((char *)dirname,"D%s",dirbuf->d_name); + else if (dir_list_style == MIXED_STYLE) + sprintf((char *)dirname," %s",dirbuf->d_name); + else if (dir_list_style == FILES_FIRST) + sprintf((char *)dirname,"C%s",dirbuf->d_name); + /* C & D to have first files, then directories */ + else + sprintf((char *)dirname,"F%s",dirbuf->d_name); +#endif /* !DIRED_SUPPORT */ + HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */ + } + + /* + ** Run through tree printing out in order + */ + { + HTBTElement * next_element = HTBTree_next(bt,NULL); + /* pick up the first element of the list */ + char state; + /* I for initial (.. file), + D for directory file, + F for file */ + +#ifdef DIRED_SUPPORT + char test; +#endif /* DIRED_SUPPORT */ + state = 'I'; + + while (next_element != NULL) + { + char *entry, *file_extra; + + StrAllocCopy(tmpfilename,localname); + if (strcmp(localname,"/")) + + /* if filename is not root directory */ + StrAllocCat(tmpfilename,"/"); + + StrAllocCat(tmpfilename, + (char *)HTBTree_object(next_element)+1); + /* append the current entry's filename to the path */ + HTSimplify(tmpfilename); + /* Output the directory entry */ + if (strcmp((char *) + (HTBTree_object(next_element)),"D..")) + { +#ifdef DIRED_SUPPORT + test = *(char *)(HTBTree_object(next_element))=='D'?'D':'F'; + if (state != test) + { +#ifndef LONG_LIST + if (dir_list_style == FILES_FIRST) { + if (state == 'F') + END(HTML_DIR); + } else if (dir_list_style != MIXED_STYLE) + if (state == 'D') + END(HTML_DIR); +#endif /* !LONG_LIST */ + state = *(char *) + (HTBTree_object(next_element))=='D'?'D':'F'; + START(HTML_H2); + if (dir_list_style != MIXED_STYLE) + PUTS(state == 'D'?"Directories:":"Files"); +#else + if (state != *(char *)(HTBTree_object(next_element))) + { +#ifndef LONG_LIST + if (state == 'D') + END(HTML_DIR); +#endif /* !LONG_LIST */ + state = *(char *) + (HTBTree_object(next_element))=='D'?'D':'F'; + START(HTML_H2); + PUTS(state == 'D'?"Subdirectories:":"Files"); +#endif /* DIRED_SUPPORT */ + END(HTML_H2); +#ifndef LONG_LIST + START(HTML_DIR); +#endif /* !LONG_LIST */ + } +#ifndef LONG_LIST + START(HTML_LI); +#endif /* !LONG_LIST */ + } + entry = (char*)HTBTree_object(next_element)+1; + file_extra = NULL; + +#ifdef LONG_LIST + LYListFmtParse(list_format, tmpfilename, target, + entry, tail); +#else + HTDirEntry(target, tail, entry); + PUTS(entry); + END(HTML_A); + if (file_extra) { + PUTS(file_extra); + FREE(file_extra); + } +#endif /* LONG_LIST */ + + next_element = HTBTree_next(bt,next_element); + /* pick up the next element of the list; + if none, return NULL*/ + } + if (state == 'I') + { + START(HTML_P); + PUTS("Empty Directory"); + } +#ifndef LONG_LIST + else + END(HTML_DIR); +#endif /* !LONG_LIST */ + } + + /* end while directory entries left to read */ + closedir(dp); + FREE(logical); + FREE(tmpfilename); + FREE(tail); + HTBTreeAndObject_free(bt); + + if (HTDirReadme == HT_DIR_README_BOTTOM) + do_readme(target, localname); + FREE_TARGET; + FREE(localname); + FREE(nodename); + return HT_LOADED; /* document loaded */ + } + + } /* end if localname is directory */ + + } /* end if file stat worked */ + +/* End of directory reading section +*/ +#endif /* GOT_READ_DIR */ +open_file: + { + FILE * fp = fopen(localname,"r"); + + if (TRACE) + fprintf (stderr, "HTLoadFile: Opening `%s' gives %p\n", + localname, (void*)fp); + if (fp) { /* Good! */ + int len; + char *cp = NULL; + + if (HTEditable(localname)) { + HTAtom * put = HTAtom_for("PUT"); + HTList * methods = HTAnchor_methods(anchor); + if (HTList_indexOf(methods, put) == (-1)) { + HTList_addObject(methods, put); + } + } + /* + ** Fake a Content-Encoding for compressed files. - FM + */ + if ((len = strlen(localname)) > 2) { + if (localname[len - 1] == 'Z' && + localname[len - 2] == '.') { + StrAllocCopy(cp, localname); + cp[len - 2] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-compress"); + format = HTAtom_for("www/compressed"); + } else if ((len > 3) && + !strcasecomp((char *)&localname[len - 2], + "gz") && + localname[len - 3] == '.') { + StrAllocCopy(cp, localname); + cp[len - 3] = '\0'; + format = HTFileFormat(cp, &encoding); + FREE(cp); + format = HTCharsetFormat(format, anchor); + StrAllocCopy(anchor->content_type, format->name); + StrAllocCopy(anchor->content_encoding, "x-gzip"); + format = HTAtom_for("www/compressed"); + } + } + FREE(localname); + FREE(nodename); + HTParseFile(format, format_out, anchor, fp, sink); + fclose(fp); + return HT_LOADED; + } /* If succesfull open */ + } /* scope of fp */ + } /* local unix file system */ +#endif /* !NO_UNIX_IO */ +#endif /* VMS */ + +#ifndef DECNET +/* Now, as transparently mounted access has failed, we try FTP. +*/ + { + /** Deal with case-sensitivity differences on VMS verus Unix **/ +#ifdef VMS + if (strcasecomp(nodename, HTHostName()) != 0) +#else + if (strcmp(nodename, HTHostName()) != 0) +#endif /* VMS */ + { + FREE(nodename); + if (!strncmp(addr, "file://localhost", 16)) { + return -1; /* never go to ftp site when URL + * is file://localhost + */ + } else { + return HTFTPLoad(addr, anchor, format_out, sink); + } + } + FREE(nodename); + } +#endif /* !DECNET */ + +/* All attempts have failed. +*/ + { + if (TRACE) + fprintf(stderr, "Can't open `%s', errno=%d\n", addr, SOCKET_ERRNO); + + return HTLoadError(sink, 403, "Can't access requested file."); + } + + +} + +/* Protocol descriptors +*/ +#ifdef GLOBALDEF_IS_MACRO +#define _HTFILE_C_1_INIT { "ftp", HTLoadFile, 0 } +GLOBALDEF (HTProtocol,HTFTP,_HTFILE_C_1_INIT); +#define _HTFILE_C_2_INIT { "file", HTLoadFile, HTFileSaveStream } +GLOBALDEF (HTProtocol,HTFile,_HTFILE_C_2_INIT); +#else +GLOBALDEF PUBLIC HTProtocol HTFTP = { "ftp", HTLoadFile, 0 }; +GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream }; +#endif /* GLOBALDEF_IS_MACRO */ |