1 /* File Access HTFile.c
4 ** This is unix-specific code in general, with some VMS bits.
5 ** These are routines for file access used by WWW browsers.
8 ** Feb 91 Written Tim Berners-Lee CERN/CN
9 ** Apr 91 vms-vms access included using DECnet syntax
12 ** Cannot access VMS files from a unix machine. How can we know that the
13 ** target machine runs VMS?
16 #define INFINITY 512 /* file name length @@ FIXME */
20 #ifdef unix /* if this is to compile on a UNIX machine */
21 #include <sys/types.h>
25 #define GOT_READ_DIR 1 /* if directory reading functions are available */
38 PRIVATE char *HTMountRoot = "/Net/"; /* Where to find mounts */
40 PRIVATE char *HTCacheRoot = "/WWW$SCRATCH/"; /* Where to cache things */
42 PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_"; /* Where to cache things */
45 /* PRIVATE char *HTSaveRoot = "$(HOME)/WWW/";*/ /* Where to save things */
48 /* Convert unix-style name into VMS name
49 ** -------------------------------------
51 ** Bug: Returns pointer to static -- non-reentrant
53 PRIVATE char * vms_name(CONST char * nn, CONST char * fn)
56 /* We try converting the filename into Files-11 syntax. That is, we assume
57 ** first that the file is, like us, on a VMS node. We try remote
58 ** (or local) DECnet access. Files-11, VMS, VAX and DECnet
59 ** are trademarks of Digital Equipment Corporation.
60 ** The node is assumed to be local if the hostname WITHOUT DOMAIN
61 ** matches the local one. @@@
63 static char vmsname[INFINITY]; /* returned */
64 char * filename = (char*)malloc(strlen(fn)+1);
65 char * nodename = (char*)malloc(strlen(nn)+2+1); /* Copies to hack */
66 char *second; /* 2nd slash */
67 char *last; /* last slash */
69 char * hostname = HTHostName();
72 strcpy(nodename, ""); /* On same node? Yes if node names match */
75 for (p=hostname, q=nn; *p && *p!='.' && *q && *q!='.'; p++, q++){
76 if (TOUPPER(*p)!=TOUPPER(*q)) {
78 q = strchr(nodename, '.'); /* Mismatch */
79 if (q) *q=0; /* Chop domain */
80 strcat(nodename, "::"); /* Try decnet anyway */
86 second = strchr(filename+1, '/'); /* 2nd slash */
87 last = strrchr(filename, '/'); /* last slash */
89 if (!second) { /* Only one slash */
90 sprintf(vmsname, "%s%s", nodename, filename);
91 } else if(second==last) { /* Exactly two slashes */
92 *second = 0; /* Split filename from disk */
93 sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1);
94 *second = '/'; /* restore */
95 } else { /* More than two slashes */
97 *second = 0; /* Split disk from directories */
98 *last = 0; /* Split dir from filename */
99 sprintf(vmsname, "%s%s:[%s]%s",
100 nodename, filename+1, second+1, last+1);
101 *second = *last = '/'; /* restore filename */
102 for (p=strchr(vmsname, '['); *p!=']'; p++)
103 if (*p=='/') *p='.'; /* Convert dir sep. to dots */
113 /* Make the cache file name for a W3 document
114 ** ------------------------------------------
115 ** Make up a suitable name for saving the node in
117 ** E.g. /tmp/WWW_Cache_news/1234@cernvax.cern.ch
118 ** /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
121 ** returns a malloc'ed string which must be freed by the caller.
123 PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
125 char * access = HTParse(name, "", PARSE_ACCESS);
126 char * host = HTParse(name, "", PARSE_HOST);
127 char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
130 result = (char *)malloc(
131 strlen(HTCacheRoot)+strlen(access)
132 +strlen(host)+strlen(path)+6+1);
133 if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
134 sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
141 /* Open a file for write, creating the path
142 ** ----------------------------------------
144 #ifdef NOT_IMPLEMENTED
145 PRIVATE int HTCreatePath ARGS1(CONST char *,path)
151 /* Convert filenames between local and WWW formats
152 ** -----------------------------------------------
153 ** Make up a suitable name for saving the node in
155 ** E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch
156 ** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
159 ** returns a malloc'ed string which must be freed by the caller.
161 PUBLIC char * HTLocalName ARGS1(CONST char *,name)
163 char * access = HTParse(name, "", PARSE_ACCESS);
164 char * host = HTParse(name, "", PARSE_HOST);
165 char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
167 if (0==strcmp(access, "file")) {
169 if ((0==strcmp(host, HTHostName())) || !*host) {
171 if (TRACE) printf("Node `%s' means path `%s'\n", name, path);
174 char * result = (char *)malloc(
175 strlen("/Net/")+strlen(host)+strlen(path)+1);
176 if (result == NULL) outofmem(__FILE__, "HTLocalName");
177 sprintf(result, "%s%s%s", "/Net/", host, path);
180 if (TRACE) printf("Node `%s' means file `%s'\n", name, result);
183 } else { /* other access */
185 CONST char * home = (CONST char*)getenv("HOME");
186 if (!home) home = "/tmp";
187 result = (char *)malloc(
188 strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
189 if (result == NULL) outofmem(__FILE__, "HTLocalName");
190 sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
199 /* Make a WWW name from a full local path name
202 ** At present, only the names of two network root nodes are hand-coded
203 ** in and valid for the NeXT only. This should be configurable in
207 PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
211 if (0==strncmp("/private/Net/", name, 13)) {
212 result = (char *)malloc(7+strlen(name+13)+1);
213 if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
214 sprintf(result, "file://%s", name+13);
217 if (0==strncmp(HTMountRoot, name, 5)) {
218 result = (char *)malloc(7+strlen(name+5)+1);
219 if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
220 sprintf(result, "file://%s", name+5);
222 result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
223 if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
224 sprintf(result, "file://%s%s", HTHostName(), name);
226 if (TRACE) printf("File `%s'\n\tmeans node `%s'\n", name, result);
231 /* Determine file format from file name
232 ** ------------------------------------
234 ** Note: This function is also in the server file HTRetrieve.c
235 ** If it gets complicated, it should be shared.
239 PUBLIC HTFormat HTFileFormat ARGS1 (CONST char *,filename)
241 CONST char * extension;
242 for (extension=filename+strlen(filename);
243 (extension>filename) &&
244 (*extension != '.') &&
246 extension--) /* search */ ;
248 if (*extension == '.') {
249 return 0==strcmp(".html", extension) ? WWW_HTML
250 : 0==strcmp(".rtf", extension) ? WWW_RICHTEXT
251 : 0==strcmp(".txt", extension) ? WWW_PLAINTEXT
252 : WWW_PLAINTEXT; /* Unrecognised, try plain text */
254 return WWW_PLAINTEXT;
259 /* Determine write access to a file
260 // --------------------------------
263 // return value YES if file can be accessed and can be written to.
266 // 1. No code for non-unix systems.
267 // 2. Isn't there a quicker way?
280 PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
283 return NO; /* Safe answer till we find the correct algorithm */
287 int ngroups; /* The number of groups */
288 struct stat fileStatus;
291 if (stat(filename, &fileStatus)) /* Get details of filename */
292 return NO; /* Can't even access file! */
294 ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */
295 myUid = geteuid(); /* Get my user identifier */
299 printf("File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
300 fileStatus.st_mode, fileStatus.st_uid, fileStatus.st_gid,
302 for (i=0; i<ngroups; i++) printf(" %d", groups[i]);
306 if (fileStatus.st_mode & 0002) /* I can write anyway? */
309 if ((fileStatus.st_mode & 0200) /* I can write my own file? */
310 && (fileStatus.st_uid == myUid))
313 if (fileStatus.st_mode & 0020) /* Group I am in can write? */
315 for (i=0; i<ngroups; i++) {
316 if (groups[i] == fileStatus.st_gid)
320 if (TRACE) printf("\tFile is not editable.\n");
321 return NO; /* If no excuse, can't do */
326 /* Open a file descriptor for a document
327 ** -------------------------------------
330 ** addr must point to the fully qualified hypertext reference.
333 ** returns <0 Error has occured.
334 ** >=0 Value of file descriptor or socket to be used
336 ** *pFormat Set to the format of the file, if known.
345 HTParentAnchor *,anchor
349 int fd = -1; /* Unix file descriptor number = INVALID */
351 char * newname=0; /* Simplified name of file */
353 /* Reduce the filename to a basic form (hopefully unique!)
355 StrAllocCopy(newname, addr);
356 filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
357 nodename=HTParse(newname, "", PARSE_HOST);
360 *pFormat = HTFileFormat(filename);
364 /* Assume that the file is remote vms or ultrix if no domain name @@ */
365 if (!strchr(nodename, '.')) {
366 char * vmsname = vms_name(nodename, filename);
367 fd = open(vmsname, O_RDONLY, 0);
369 /* If the file wasn't VMS syntax, then perhaps it is ultrix
372 char ultrixname[INFINITY];
373 if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
374 sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
375 fd = open(ultrixname, O_RDONLY, 0);
377 if (TRACE) fprintf(stderr,
378 "HTFile: Can't open as %s\n", ultrixname);
384 /* For unix, we try to translate the name into the name of a transparently
389 char * localname = HTLocalName(addr);
394 ** Check to see if the 'localname' is in fact a directory. If it is
395 ** create a new hypertext object containing a list of files and
396 ** subdirectories contained in the directory. All of these are links
397 ** to the directories or files listed.
398 ** NB This assumes the existance of a type 'struct direct', which will
399 ** hold the directory entry, and a type 'DIR' which is used to point to
400 ** the current directory being read.
403 struct stat dir_info;
405 if (stat(localname,&dir_info) == -1) { /* get file information */
406 /* if can't read file information */
407 if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
409 } else { /* Stat was OK */
412 if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
413 /* if localname is a directory */
414 HTChildAnchor * child;
416 extern HTStyleSheet * styleSheet;
417 static HTStyle * DirectoryStyle = 0;
418 static HTStyle * H1Style = 0;
420 struct direct * dirbuf;
422 char * tmpfilename = NULL;
423 char * shortfilename = NULL;
424 struct stat *file_info = malloc(sizeof(struct stat));
427 fprintf(stderr,"%s is a directory\n",localname);
430 H1Style = HTStyleNamed(styleSheet, "Heading1");
433 DirectoryStyle = HTStyleNamed(styleSheet, "Dir");
435 HTAnchor_setTitle(anchor, filename);
437 HT = HText_new(anchor);
438 HText_beginAppend(HT);
440 HText_setStyle(HT,H1Style);
441 shortfilename=strrchr(localname,'/');
442 /* put the last part of the path in shortfilename */
443 shortfilename++; /* get rid of leading '/' */
444 if (*shortfilename=='\0')
447 HText_appendText(HT,shortfilename);
448 HText_setStyle(HT,DirectoryStyle);
450 dp = opendir(localname);
452 /* if the directory file is readable */
453 while (dirbuf = readdir(dp)) {
454 /* while there are directory entries to be read */
455 if (dirbuf->d_ino == 0)
456 /* if the entry is not being used, skip it */
459 if (!strcmp(dirbuf->d_name,"."))
460 continue; /* skip the entry for this directory */
462 if (strcmp(dirbuf->d_name,".."))
463 /* if the current entry is parent directory */
464 if ((*(dirbuf->d_name)=='.') ||
465 (*(dirbuf->d_name)==','))
466 continue; /* skip those files whose name begins
469 StrAllocCopy(tmpfilename,localname);
470 if (strcmp(localname,"/"))
471 /* if filename is not root directory */
472 StrAllocCat(tmpfilename,"/");
474 if (!strcmp(dirbuf->d_name,".."))
476 /* if root directory and current entry is parent
477 directory, skip the current entry */
479 StrAllocCat(tmpfilename,dirbuf->d_name);
480 /* append the current entry's filename to the path */
481 HTSimplify(tmpfilename);
483 child = HTAnchor_findChildAndLink(
484 anchor, 0, tmpfilename, 0);
485 HText_beginAnchor(HT, child);
486 stat(tmpfilename,file_info);
488 if (strcmp(dirbuf->d_name,"..")) {
489 /* if the current entry is not the parent directory then use the file name */
490 HText_appendText(HT,dirbuf->d_name);
491 if (((file_info->st_mode) & S_IFMT) == S_IFDIR)
492 HText_appendCharacter(HT, '/');
495 /* use name of parent directory */
496 char * endbit = strrchr(tmpfilename, '/');
497 HText_appendText(HT,"Up to ");
498 HText_appendText(HT, endbit?endbit+1:tmpfilename);
502 HText_appendCharacter(HT, '\t');
503 } /* end while directory entries left to read */
508 } else { /* Directory is not readable */
510 fprintf(stderr,"HTFile.c: directory %s unreadable\n",
513 "Sorry, can't read the contents of this directory - probably read protected\n");
515 } /* end if directory not readable */
522 return HT_LOADED; /* fd not valid, but document loaded anyway */
524 } /* end if localname is directory */
526 } /* end if file stat worked */
528 /* End of directory reading section
532 fd = open(localname, O_RDONLY, 0);
533 if(TRACE) printf ("HTAccess: Opening `%s' gives %d\n",
540 /* Now, as transparently mounted access has failed, we try FTP.
543 if (strcmp(nodename, HTHostName())!=0) {
545 return HTFTP_open_file_read(addr, anchor);
548 /* All attempts have failed if fd<0.
550 if (fd<0) printf("Can't open `%s', errno=%d\n", filename, errno);