earlybrowserreborn - Blame information for rev 4

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 roytam 1 /*                      File Access                             HTFile.c
2 **                      ===========
3 **
4 **      This is unix-specific code in general, with some VMS bits.
5 **      These are routines for file access used by browsers.
6 **
7 ** History:
8 **         Feb 91       Written Tim Berners-Lee CERN/CN
9 **         Apr 91       vms-vms access included using DECnet syntax
10 **      26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
11 **                      Fixed access bug for relative names on VMS.
12 **         Sep 93 (MD)  Access to VMS files allows sharing.
13 **      15 Nov 93 (MD)  Moved HTVMSname to HTVMSUTILS.C
14 **
15 ** Bugs:
16 **      FTP: Cannot access VMS files from a unix machine.
17 **      How can we know that the
18 **      target machine runs VMS?
19 */
20  
21 #include "HTFile.h"             /* Implemented here */
22  
23  
24 #define INFINITY 512            /* file name length @@ FIXME */
25 #define MULTI_SUFFIX ".multi"   /* Extension for scanning formats */
26  
27 #include "HTUtils.h"
28  
29 #ifdef VMS
30 #include "HTVMSUtils.h"
31 #endif /* VMS */
32  
33 #include "HTParse.h"
34 #include "tcp.h"
35 #include "HTTCP.h"
36 #ifndef DECNET
37 #include "HTFTP.h"
38 #endif
39 #include "HTAnchor.h"
40 #include "HTAtom.h"
41 #include "HTWriter.h"
42 #include "HTFWriter.h"
43 #include "HTInit.h"
44 #include "HTBTree.h"
45  
46 typedef struct _HTSuffix {
47         char *          suffix;
48         HTAtom *        rep;
49         HTAtom *        encoding;
50         float           quality;
51 } HTSuffix;
52  
53  
54 #ifdef USE_DIRENT               /* Set this for Sys V systems */
55 #define STRUCT_DIRENT struct dirent
56 #else
57 #define STRUCT_DIRENT struct direct
58 #endif
59  
60 #include "HTML.h"               /* For directory object building */
61  
62 #define PUTC(c) (*target->isa->put_character)(target, c)
63 #define PUTS(s) (*target->isa->put_string)(target, s)
64 #define START(e) (*target->isa->start_element)(target, e, 0, 0,\
65                                                &HTML_dtd.tags[e]/*PYW*/)
66 #define END(e) (*target->isa->end_element)(target, e)
67 #define FREE_TARGET (*target->isa->free)(target)
68 struct _HTStructured {
69         CONST HTStructuredClass *       isa;
70         /* ... */
71 };
72  
73  
74 /*                   Controlling globals
75 **
76 */
77  
78 PUBLIC int HTDirAccess = HT_DIR_OK;
79 PUBLIC int HTDirReadme = HT_DIR_README_TOP;
80  
81 PRIVATE char *HTMountRoot = "/Net/";            /* Where to find mounts */
82 #ifdef VMS
83 PRIVATE char *HTCacheRoot = "/WWW$SCRATCH";   /* Where to cache things */
84 #else
85 PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
86 #endif
87  
88 /* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
89  
90  
91 /*      Suffix registration
92 */
93  
94 PRIVATE HTList * HTSuffixes = 0;
95 PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 };
96 PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0};
97  
98  
99 /*      Define the representation associated with a file suffix
100 **      -------------------------------------------------------
101 **
102 **      Calling this with suffix set to "*" will set the default
103 **      representation.
104 **      Calling this with suffix set to "*.*" will set the default
105 **      representation for unknown suffix files which contain a ".".
106 **
107 **      If filename suffix is already defined its previous
108 **      definition is overridden.
109 */
110 PUBLIC void HTSetSuffix ARGS4(
111         CONST char *,   suffix,
112         CONST char *,   representation,
113         CONST char *,   encoding,
114         float,          value)
115 {
116  
117     HTSuffix * suff;
118  
119     if (strcmp(suffix, "*")==0) suff = &no_suffix;
120     else if (strcmp(suffix, "*.*")==0) suff = &unknown_suffix;
121     else {
122         HTList *cur = HTSuffixes;
123  
124         while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) {
125             if (suff->suffix && 0==strcmp(suff->suffix, suffix))
126                 break;
127         }
128         if (!suff) { /* Not found -- create a new node */
129             suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
130             if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
131  
132             if (!HTSuffixes) HTSuffixes = HTList_new();
133             HTList_addObject(HTSuffixes, suff);
134  
135             StrAllocCopy(suff->suffix, suffix);
136         }
137     }
138  
139     suff->rep = HTAtom_for(representation);
140  
141     {
142         char * enc = NULL;
143         char * p;
144         StrAllocCopy(enc, encoding);
145         for (p=enc; *p; p++) *p = TOLOWER(*p);
146         suff->encoding = HTAtom_for(encoding);
147         free(enc);
148     }
149  
150     suff->quality = value;
151 }
152  
153  
154  
155  
156  
157  
158  
159 /*      Send README file
160 **
161 **  If a README file exists, then it is inserted into the document here.
162 */
163  
164 #ifdef GOT_READ_DIR
165 PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
166 {
167     FILE * fp;
168     char * readme_file_name =
169         malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
170     strcpy(readme_file_name, localname);
171     strcat(readme_file_name, "/");
172     strcat(readme_file_name, HT_DIR_README_FILE);
173  
174     fp = fopen(readme_file_name,  "r");
175  
176     if (fp) {
177         HTStructuredClass targetClass;
178  
179         targetClass =  *target->isa;    /* (Can't init agregate in K&R) */
180         START(HTML_PRE);
181         for(;;){
182             char c = fgetc(fp);
183             if (c == (char)EOF) break;
184             switch (c) {
185                 case '&':
186                 case '<':
187                 case '>':
188                         PUTC('&');
189                         PUTC('#');
190                         PUTC((char)(c / 10));
191                         PUTC((char) (c % 10));
192                         PUTC(';');
193                         break;
194 /*              case '\n':
195                         PUTC('\r');    
196 Bug removed thanks to joe@athena.mit.edu */                    
197                 default:
198                         PUTC(c);
199             }
200         }
201         END(HTML_PRE);
202         fclose(fp);
203     }
204 }
205 #endif
206  
207  
208 /*      Make the cache file name for a W3 document
209 **      ------------------------------------------
210 **      Make up a suitable name for saving the node in
211 **
212 **      E.g.    /tmp/WWW_Cache_news/1234@cernvax.cern.ch
213 **              /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
214 **
215 ** On exit,
216 **      returns a malloc'ed string which must be freed by the caller.
217 */
218 PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
219 {
220     char * access = HTParse(name, "", PARSE_ACCESS);
221     char * host = HTParse(name, "", PARSE_HOST);
222     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
223  
224     char * result;
225     result = (char *)malloc(
226             strlen(HTCacheRoot)+strlen(access)
227             +strlen(host)+strlen(path)+6+1);
228     if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
229     sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
230     free(path);
231     free(access);
232     free(host);
233     return result;
234 }
235  
236  
237 /*      Open a file for write, creating the path
238 **      ----------------------------------------
239 */
240 #ifdef NOT_IMPLEMENTED
241 PRIVATE int HTCreatePath ARGS1(CONST char *,path)
242 {
243     return -1;
244 }
245 #endif
246  
247 /*      Convert filenames between local and WWW formats
248 **      -----------------------------------------------
249 **      Make up a suitable name for saving the node in
250 **
251 **      E.g.    $(HOME)/WWW/news/1234@cernvax.cern.ch
252 **              $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
253 **
254 ** On exit,
255 **      returns a malloc'ed string which must be freed by the caller.
256 */
257 PUBLIC char * HTLocalName ARGS1(CONST char *,name)
258 {
259     char * access = HTParse(name, "", PARSE_ACCESS);
260     char * host = HTParse(name, "", PARSE_HOST);
261     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
262  
263     HTUnEscape(path);   /* Interpret % signs */
264  
265     if (0==strcmp(access, "file")) { /* local file */
266         free(access);  
267         if ((0==strcasecomp(host, HTHostName())) ||
268             (0==strcasecomp(host, "localhost")) || !*host) {
269             free(host);
270             if (TRACE) fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
271             return(path);
272         } else {
273             char * result = (char *)malloc(
274                                 strlen("/Net/")+strlen(host)+strlen(path)+1);
275               if (result == NULL) outofmem(__FILE__, "HTLocalName");
276             sprintf(result, "%s%s%s", "/Net/", host, path);
277             free(host);
278             free(path);
279             if (TRACE) fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
280             return result;
281         }
282     } else {  /* other access */
283         char * result;
284         CONST char * home =  (CONST char*)getenv("HOME");
285 #ifdef VMS
286         if (!home)
287             home = HTCacheRoot;
288         else
289             home = HTVMS_wwwName(home);
290 #else /* not VMS */
291         if (!home) home = "/tmp";
292 #endif /* not VMS */
293         result = (char *)malloc(
294                 strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
295       if (result == NULL) outofmem(__FILE__, "HTLocalName");
296         sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
297         free(path);
298         free(access);
299         free(host);
300         return result;
301     }
302 }
303  
304  
305 /*      Make a WWW name from a full local path name
306 **
307 ** Bugs:
308 **      At present, only the names of two network root nodes are hand-coded
309 **      in and valid for the NeXT only. This should be configurable in
310 **      the general case.
311 */
312  
313 PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
314 {
315     char * result;
316 #ifdef NeXT
317     if (0==strncmp("/private/Net/", name, 13)) {
318         result = (char *)malloc(7+strlen(name+13)+1);
319         if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
320         sprintf(result, "file://%s", name+13);
321     } else
322 #endif
323     if (0==strncmp(HTMountRoot, name, 5)) {
324         result = (char *)malloc(7+strlen(name+5)+1);
325         if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
326         sprintf(result, "file://%s", name+5);
327     } else {
328         result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
329         if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
330         sprintf(result, "file://%s%s", HTHostName(), name);
331     }
332     if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
333     return result;
334 }
335  
336  
337 /*      Determine a suitable suffix, given the representation
338 **      -----------------------------------------------------
339 **
340 ** On entry,
341 **      rep     is the atomized MIME style representation
342 **
343 ** On exit,
344 **      returns a pointer to a suitable suffix string if one has been
345 **              found, else "".
346 */
347 PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
348 {
349     HTSuffix * suff;
350     int n;
351     int i;
352  
353 #ifndef NO_INIT    
354     if (!HTSuffixes) HTFileInit();
355 #endif
356     n = HTList_count(HTSuffixes);
357     for(i=0; i<n; i++) {
358         suff = HTList_objectAt(HTSuffixes, i);
359         if (suff->rep == rep) {
360             return suff->suffix;                /* OK -- found */
361         }
362     }
363     return "";          /* Dunno */
364 }
365  
366  
367 /*      Determine file format from file name
368 **      ------------------------------------
369 **
370 **      This version will return the representation and also set
371 **      a variable for the encoding.
372 **
373 **      It will handle for example  x.txt, x.txt,Z, x.Z
374 */
375  
376 PUBLIC HTFormat HTFileFormat ARGS2 (
377                         CONST char *,   filename,
378                         HTAtom **,      pencoding)
379  
380 {
381     HTSuffix * suff;
382     int n;
383     int i;
384     int lf = strlen(filename);
385  
386 #ifndef NO_INIT    
387     if (!HTSuffixes) HTFileInit();
388 #endif
389     *pencoding = NULL;
390     n = HTList_count(HTSuffixes);
391     for(i=0; i<n; i++) {
392         int ls;
393         suff = HTList_objectAt(HTSuffixes, i);
394         ls = strlen(suff->suffix);
395         if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
396             int j;
397             *pencoding = suff->encoding;
398             if (suff->rep) return suff->rep;            /* OK -- found */
399  
400             for(j=0; j<n; j++) {  /* Got encoding, need representation */
401                 int ls2;
402                 suff = HTList_objectAt(HTSuffixes, j);
403                 ls2 = strlen(suff->suffix);
404                 if ((ls <= lf) && 0==strncmp(
405                         suff->suffix, filename + lf - ls -ls2, ls2)) {
406                     if (suff->rep) return suff->rep;
407                 }
408             }
409  
410         }
411     }
412  
413     /* defaults tree */
414  
415     suff = strchr(filename, '.') ?      /* Unknown suffix */
416          ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
417          : &no_suffix;
418  
419     /* set default encoding unless found with suffix already */
420     if (!*pencoding) *pencoding = suff->encoding ? suff->encoding
421                                     : HTAtom_for("binary");
422     return suff->rep ? suff->rep : WWW_BINARY;
423 }
424  
425  
426 /*      Determine value from file name
427 **      ------------------------------
428 **
429 */
430  
431 PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
432  
433 {
434     HTSuffix * suff;
435     int n;
436     int i;
437     int lf = strlen(filename);
438  
439 #ifndef NO_INIT    
440     if (!HTSuffixes) HTFileInit();
441 #endif
442     n = HTList_count(HTSuffixes);
443     for(i=0; i<n; i++) {
444         int ls;
445         suff = HTList_objectAt(HTSuffixes, i);
446         ls = strlen(suff->suffix);
447         if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
448             if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
449                                filename, suff->quality);
450             return suff->quality;               /* OK -- found */
451         }
452     }
453     return 0.3;         /* Dunno! */
454 }
455  
456  
457 /*      Determine write access to a file
458 **      --------------------------------
459 **
460 ** On exit,
461 **      return value    YES if file can be accessed and can be written to.
462 **
463 ** Bugs:
464 **      1.      No code for non-unix systems.
465 **      2.      Isn't there a quicker way?
466 */
467  
468 #ifdef VMS
469 #define NO_GROUPS
470 #endif
471 #ifdef NO_UNIX_IO
472 #define NO_GROUPS
473 #endif
474 #ifdef PCNFS
475 #define NO_GROUPS
476 #endif
477  
478 PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
479 {
480 #ifdef NO_GROUPS
481     return NO;          /* Safe answer till we find the correct algorithm */
482 #else
483     int         groups[NGROUPS];       
484     uid_t       myUid;
485     int         ngroups;                        /* The number of groups  */
486     struct stat fileStatus;
487     int         i;
488  
489     if (stat(filename, &fileStatus))            /* Get details of filename */
490         return NO;                              /* Can't even access file! */
491  
492     ngroups = getgroups(NGROUPS, groups);       /* Groups to which I belong  */
493     myUid = geteuid();                          /* Get my user identifier */
494  
495     if (TRACE) {
496         int i;
497         fprintf(stderr,
498             "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
499             (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
500             fileStatus.st_gid,
501             myUid, ngroups);
502         for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
503         fprintf(stderr, ")\n");
504     }
505  
506     if (fileStatus.st_mode & 0002)              /* I can write anyway? */
507         return YES;
508  
509     if ((fileStatus.st_mode & 0200)             /* I can write my own file? */
510      && (fileStatus.st_uid == myUid))
511         return YES;
512  
513     if (fileStatus.st_mode & 0020)              /* Group I am in can write? */
514     {
515         for (i=0; i<ngroups; i++) {
516             if (groups[i] == fileStatus.st_gid)
517                 return YES;
518         }
519     }
520     if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
521     return NO;                                  /* If no excuse, can't do */
522 #endif
523 }
524  
525  
526 /*      Make a save stream
527 **      ------------------
528 **
529 **      The stream must be used for writing back the file.
530 **      @@@ no backup done
531 */
532 PUBLIC HTStream * HTFileSaveStream ARGS1(HTParentAnchor *, anchor)
533 {
534  
535     CONST char * addr = HTAnchor_address((HTAnchor*)anchor);
536     char *  localname = HTLocalName(addr);
537  
538     FILE* fp = fopen(localname, "w");
539     if (!fp) return NULL;
540  
541     return HTFWriter_new(fp);
542  
543 }
544  
545 /*      Output one directory entry
546 **
547 */
548 PUBLIC void HTDirEntry ARGS3(HTStructured *, target,
549                  CONST char * , tail,
550                  CONST char *,  entry)
551 {
552     char * relative;
553     char * escaped = HTEscape(entry, URL_XPALPHAS);
554  
555     /* If empty tail, gives absolute ref below */
556     relative = (char*) malloc(
557                               strlen(tail) + strlen(escaped)+2);
558     if (relative == NULL) outofmem(__FILE__, "DirRead");
559     sprintf(relative, "%s/%s", tail, escaped);
560     HTStartAnchor(target, NULL, relative);
561     free(escaped);
562     free(relative);
563     PUTS(entry);
564     END(HTML_A);
565 }
566  
567 /*      Output parent directory entry
568 **
569 **    This gives the TITLE and H1 header, and also a link
570 **    to the parent directory if appropriate.
571 */
572 PUBLIC void HTDirTitles ARGS2(HTStructured *, target,
573                  HTAnchor * , anchor)
574  
575 {
576     char * logical = HTAnchor_address(anchor);
577     char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
578     char * current;
579  
580     current = strrchr(path, '/');       /* last part or "" */
581     free(logical);
582  
583     {
584       char * printable = NULL;
585       StrAllocCopy(printable, (current + 1));
586       HTUnEscape(printable);
587       START(HTML_TITLE);
588       PUTS(*printable ? printable : "Welcome ");
589       PUTS(" directory");
590       END(HTML_TITLE);    
591  
592       START(HTML_H1);
593       PUTS(*printable ? printable : "Welcome");
594       END(HTML_H1);
595       free(printable);
596     }
597  
598     /*  Make link back to parent directory
599      */
600  
601     if (current && current[1]) {   /* was a slash AND something else too */
602         char * parent;
603         char * relative;
604         *current++ = 0;
605       parent = strrchr(path, '/');  /* penultimate slash */
606  
607         relative = (char*) malloc(strlen(current) + 4);
608         if (relative == NULL) outofmem(__FILE__, "DirRead");
609         sprintf(relative, "%s/..", current);
610         HTStartAnchor(target, "", relative);
611         free(relative);
612  
613         PUTS("Up to ");
614         if (parent) {
615           char * printable = NULL;
616           StrAllocCopy(printable, parent + 1);
617           HTUnEscape(printable);
618           PUTS(printable);
619           free(printable);
620         } else {
621           PUTS("/");
622         }
623  
624         END(HTML_A);
625  
626     }
627     free(path);
628 }
629  
630  
631  
632 /*      Load a document
633 **      ---------------
634 **
635 ** On entry,
636 **      addr            must point to the fully qualified hypertext reference.
637 **                      This is the physsical address of the file
638 **
639 ** On exit,
640 **      returns         <0              Error has occured.
641 **                      HTLOADED        OK
642 **
643 */
644 PUBLIC int HTLoadFile ARGS4 (
645         CONST char *,           addr,
646         HTParentAnchor *,       anchor,
647         HTFormat,               format_out,
648         HTStream *,             sink
649 )
650 {
651     char * filename;
652     HTFormat format;
653     char * nodename = 0;
654     char * newname=0;   /* Simplified name of file */
655     HTAtom * encoding;  /* @@ not used yet */
656  
657 /*      Reduce the filename to a basic form (hopefully unique!)
658 */
659     StrAllocCopy(newname, addr);
660     filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
661     nodename=HTParse(newname, "", PARSE_HOST);
662     free(newname);
663  
664     format = HTFileFormat(filename, &encoding);
665  
666  
667 #ifdef VMS
668 /* Assume that the file is in Unix-style syntax if it contains a '/'
669    after the leading one @@ */
670     {
671         FILE * fp;
672         char * vmsname = strchr(filename + 1, '/') ?
673           HTVMS_name(nodename, filename) : filename + 1;
674         fp = fopen(vmsname, "r", "shr=put", "shr=upd");
675  
676 /*      If the file wasn't VMS syntax, then perhaps it is ultrix
677 */
678         if (!fp) {
679             char ultrixname[INFINITY];
680             if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
681             sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
682             fp = fopen(ultrixname, "r", "shr=put", "shr=upd");
683             if (!fp) {
684                 if (TRACE) fprintf(stderr,
685                                    "HTFile: Can't open as %s\n", ultrixname);
686             }
687         }
688         if (fp)
689         {
690             if (HTEditable(vmsname)) {
691                 HTAtom * put = HTAtom_for("PUT");
692                 HTList * methods = HTAnchor_methods(anchor);
693                 if (HTList_indexOf(methods, put) == (-1)) {
694                     HTList_addObject(methods, put);
695                 }
696             }
697             HTParseFile(format, format_out, anchor, fp, sink);
698             fclose(fp);
699             return HT_LOADED;
700         }  /* If successfull open */
701     }
702 #else
703  
704     free(filename);
705  
706 /*      For unix, we try to translate the name into the name of a transparently
707 **      mounted file.
708 **
709 **      Not allowed in secure (HTClienntHost) situations TBL 921019
710 */
711 #ifndef NO_UNIX_IO
712     /*  Need protection here for telnet server but not httpd server */
713  
714     if (!HTSecure) {            /* try local file system */
715         char * localname = HTLocalName(addr);
716         struct stat dir_info;
717  
718 #ifdef GOT_READ_DIR
719  
720 /*                        Multiformat handling
721 **
722 **      If needed, scan directory to find a good file.
723 **  Bug:  we don't stat the file to find the length
724 */
725         if ( (strlen(localname) > strlen(MULTI_SUFFIX))
726            && (0==strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
727                           MULTI_SUFFIX))) {
728             DIR *dp;
729  
730             STRUCT_DIRENT * dirbuf;
731             float best = NO_VALUE_FOUND;        /* So far best is bad */
732             HTFormat best_rep = NULL;   /* Set when rep found */
733             STRUCT_DIRENT best_dirbuf;  /* Best dir entry so far */
734  
735             char * base = strrchr(localname, '/');
736             int baselen;
737  
738             if (!base || base == localname) goto forget_multi;
739             *base++ = 0;                /* Just got directory name */
740             baselen = strlen(base)- strlen(MULTI_SUFFIX);
741             base[baselen] = 0;  /* Chop off suffix */
742  
743             dp = opendir(localname);
744             if (!dp) {
745 forget_multi:
746                 free(localname);
747                 return HTLoadError(sink, 500,
748                         "Multiformat: directory scan failed.");
749             }
750  
751             while ((dirbuf = readdir(dp))!=0) {
752                         /* while there are directory entries to be read */
753                 if (dirbuf->d_ino == 0) continue;
754                                 /* if the entry is not being used, skip it */
755  
4 roytam 756                 if (dirbuf->d_reclen > baselen &&      /* Match? */
1 roytam 757                     !strncmp(dirbuf->d_name, base, baselen)) { 
758                     HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding);
759                     float value = HTStackValue(rep, format_out,
760                                                 HTFileValue(dirbuf->d_name),
761                                                 0.0  /* @@@@@@ */);
762                     if (value != NO_VALUE_FOUND) {
763                         if (TRACE) fprintf(stderr,
764                                 "HTFile: value of presenting %s is %f\n",
765                                 HTAtom_name(rep), value);
766                         if  (value > best) {
767                             best_rep = rep;
768                             best = value;
769                             best_dirbuf = *dirbuf;
770                        }
771                     }   /* if best so far */               
772                  } /* if match */  
773  
774             } /* end while directory entries left to read */
775             closedir(dp);
776  
777             if (best_rep) {
778                 format = best_rep;
779                 base[-1] = '/';         /* Restore directory name */
780                 base[0] = 0;
781                 StrAllocCat(localname, best_dirbuf.d_name);
782                 goto open_file;
783  
784             } else {                    /* If not found suitable file */
785                 free(localname);
786                 return HTLoadError(sink, 403,   /* List formats? */
787                    "Could not find suitable representation for transmission.");
788             }
789             /*NOTREACHED*/
790         } /* if multi suffix */
791 /*
792 **      Check to see if the 'localname' is in fact a directory.  If it is
793 **      create a new hypertext object containing a list of files and
794 **      subdirectories contained in the directory.  All of these are links
795 **      to the directories or files listed.
796 **      NB This assumes the existance of a type 'STRUCT_DIRENT', which will
797 **      hold the directory entry, and a type 'DIR' which is used to point to
798 **      the current directory being read.
799 */
800  
801  
802         if (stat(localname,&dir_info) == -1) {     /* get file information */
803                                        /* if can't read file information */
804             if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
805  
806         }  else {               /* Stat was OK */
807  
808  
809             if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
810                 /* if localname is a directory */      
811  
812                 HTStructured* target;           /* HTML object */
813                 HTStructuredClass targetClass;
814  
815                 DIR *dp;
816                 STRUCT_DIRENT * dirbuf;
817  
818                 char * logical;
819                 char * tail;
820  
821                 BOOL present[HTML_A_ATTRIBUTES];
822  
823                 char * tmpfilename = NULL;
824                 struct stat file_info;
825  
826                 if (TRACE)
827                     fprintf(stderr,"%s is a directory\n",localname);
828  
829 /*      Check directory access.
830 **      Selective access means only those directories containing a
831 **      marker file can be browsed
832 */
833                 if (HTDirAccess == HT_DIR_FORBID) {
834                     free(localname);
835                     return HTLoadError(sink, 403,
836                     "Directory browsing is not allowed.");
837                 }
838  
839  
840                 if (HTDirAccess == HT_DIR_SELECTIVE) {
841                     char * enable_file_name =
842                         malloc(strlen(localname)+ 1 +
843                          strlen(HT_DIR_ENABLE_FILE) + 1);
844                     strcpy(enable_file_name, localname);
845                     strcat(enable_file_name, "/");
846                     strcat(enable_file_name, HT_DIR_ENABLE_FILE);
847                     if (stat(enable_file_name, &file_info) != 0) {
848                         free(localname);
849                         return HTLoadError(sink, 403,
850                         "Selective access is not enabled for this directory");
851                     }
852                 }
853  
854  
855                 dp = opendir(localname);
856                 if (!dp) {
857                     free(localname);
858                     return HTLoadError(sink, 403, "This directory is not readable.");
859                 }
860  
861  
862  /*     Directory access is allowed and possible
863  */
864                 logical = HTAnchor_address((HTAnchor*)anchor);
865                 tail = strrchr(logical, '/') +1;        /* last part or "" */
866  
867                 target = HTML_new(anchor, format_out, sink);
868                 targetClass = *target->isa;     /* Copy routine entry points */
869  
870                 { int i;
871                         for(i=0; i<HTML_A_ATTRIBUTES; i++)
872                                 present[i] = (i==HTML_A_HREF);
873                 }
874  
875                 HTDirTitles(target, (HTAnchor *)anchor);
876  
877                 if (HTDirReadme == HT_DIR_README_TOP)
878                     do_readme(target, localname);
879                 {
880                     HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
881  
882                     while ((dirbuf = readdir(dp))!=0)
883                     {
884                         HTBTElement * dirname = NULL;
885  
886                             /* while there are directory entries to be read */
887                         if (dirbuf->d_ino == 0)
888                                   /* if the entry is not being used, skip it */
889                             continue;
890  
891  
892                                 /* if the current entry is parent directory */
893                         if ((*(dirbuf->d_name)=='.') ||
894                                 (*(dirbuf->d_name)==','))
895                             continue;    /* skip those files whose name begins
896                                             with '.' or ',' */
897  
898                         dirname = (HTBTElement *)malloc(
899                                         strlen(dirbuf->d_name) + 2);
900                         if (dirname == NULL) outofmem(__FILE__,"DirRead");
901                         StrAllocCopy(tmpfilename,localname);
902                         if (strcmp(localname,"/"))
903  
904                                         /* if filename is not root directory */
905                             StrAllocCat(tmpfilename,"/");
906  
907  
908                         StrAllocCat(tmpfilename,dirbuf->d_name);
909                         stat(tmpfilename, &file_info);
910                         if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
911                                 sprintf((char *)dirname,"D%s",dirbuf->d_name);
912                         else sprintf((char *)dirname,"F%s",dirbuf->d_name);
913                             /* D & F to have first directories, then files */
914                         HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
915                     }
916  
917                     /*    Run through tree printing out in order
918                      */
919                     {
920                         HTBTElement * next_element = HTBTree_next(bt,NULL);
921                             /* pick up the first element of the list */
922                         char state;
923                             /* I for initial (.. file),
924                                D for directory file,
925                                F for file */
926  
927                         state = 'I';
928  
929                         while (next_element != NULL)
930                         {
931                             StrAllocCopy(tmpfilename,localname);
932                             if (strcmp(localname,"/"))
933  
934                                         /* if filename is not root directory */
935                                 StrAllocCat(tmpfilename,"/");
936  
937                             StrAllocCat(tmpfilename,
938                                         (char *)HTBTree_object(next_element)+1);
939                             /* append the current entry's filename to the path */
940                             HTSimplify(tmpfilename);
941                             /* Output the directory entry */
942                             if (strcmp((char *)
943                                              (HTBTree_object(next_element)),"D.."))
944                             {                      
945                                 if (state != *(char *)(HTBTree_object(next_element)))
946                                 {
947                                     if (state == 'D')
948                                         END(HTML_DIR);
949                                     state = *(char *)
950                                         (HTBTree_object(next_element))=='D'?'D':'F';
951                                     START(HTML_H2);
952                                     PUTS(state == 'D'?"Subdirectories:":"Files");
953                                     END(HTML_H2);
954                                     START(HTML_DIR);
955                                 }
956                                 START(HTML_LI);
957                             }
958                             HTDirEntry(target, tail,
959                                        (char*)HTBTree_object(next_element) +1);
960  
961                             END(HTML_LI);/*PYW*/
962  
963                             next_element = HTBTree_next(bt,next_element);
964                                 /* pick up the next element of the list;
965                                  if none, return NULL*/
966                         }
967                         if (state == 'I')
968                         {
969                             START(HTML_P);
970                             PUTS("Empty Directory");
971                         }
972                         else
973                             END(HTML_DIR);
974                     }
975  
976                         /* end while directory entries left to read */
977                     closedir(dp);
978                     free(logical);
979                     free(tmpfilename);
980                     HTBTreeAndObject_free(bt);
981  
982                     if (HTDirReadme == HT_DIR_README_BOTTOM)
983                           do_readme(target, localname);
984                     FREE_TARGET;
985                     free(localname);
986                     return HT_LOADED;   /* document loaded */
987                 }
988  
989             } /* end if localname is directory */
990  
991         } /* end if file stat worked */
992  
993 /* End of directory reading section
994 */
995 #endif
996 open_file:
997         {
998             FILE * fp = fopen(localname,"r");
999             extern int http_progress_expected_total_bytes;  /*PYW*/
1000             extern int http_progress_subtotal_bytes;
1001             extern int http_progress_total_bytes;
1002             extern int http_progress_reporter_level;
1003  
1004             if(TRACE) fprintf (stderr, "HTFile: Opening `%s' gives %p\n",
1005                                 localname, (void*)fp);
1006             if (fp) {           /* Good! */
1007                 if (HTEditable(localname)) {
1008                     HTAtom * put = HTAtom_for("PUT");
1009                     HTList * methods = HTAnchor_methods(anchor);
1010                     if (HTList_indexOf(methods, put) == (-1)) {
1011                         HTList_addObject(methods, put);
1012                     }
1013                 }
1014                 free(localname);
1015  
1016                 http_progress_subtotal_bytes = 0;
1017                 if (http_progress_reporter_level == 0) {/*PYW*/
1018                   http_progress_total_bytes = 0;
1019                   http_progress_expected_total_bytes = 0;
1020                 }
1021                 ++http_progress_reporter_level;
1022  
1023                 HTParseFile(format, format_out, anchor, fp, sink);
1024  
1025                 --http_progress_reporter_level;
1026                 if (http_progress_reporter_level <= 0) {/*PYW*/
1027                   http_progress_total_bytes = 0;
1028                   http_progress_expected_total_bytes = 0;
1029                 }
1030                 http_progress_subtotal_bytes = 0;
1031  
1032                 fclose(fp);
1033                 return HT_LOADED;
1034             }  /* If succesfull open */
1035         }    /* scope of fp */
1036     }  /* local unix file system */    
1037 #endif
1038 #endif
1039  
1040 #ifndef DECNET
1041 /*      Now, as transparently mounted access has failed, we try FTP.
1042 */
1043     {
1044         if (strcmp(nodename, HTHostName())!=0)
1045             return HTFTPLoad(addr, anchor, format_out, sink);
1046     }
1047 #endif
1048  
1049 /*      All attempts have failed.
1050 */
1051     {
1052         if (TRACE)
1053             printf("Can't open `%s', errno=%d\n", addr, errno);
1054  
1055         return HTLoadError(sink, 403, "Can't access requested file.");
1056     }
1057  
1058  
1059 }
1060  
1061 /*              Protocol descriptors
1062 */
1063 GLOBALDEF PUBLIC HTProtocol HTFTP  = { "ftp", HTLoadFile, 0 };
1064 GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream };