1
0
mirror of https://github.com/Interlisp/maiko.git synced 2026-01-15 15:57:13 +00:00

4414 lines
129 KiB
C

/* $Id: dsk.c,v 1.4 2001/12/24 01:09:01 sybalsky Exp $ (C) Copyright Venue, All Rights Reserved */
/************************************************************************/
/* */
/* (C) Copyright 1989-1995 Venue. All Rights Reserved. */
/* Manufactured in the United States of America. */
/* */
/************************************************************************/
#include "version.h"
#include <errno.h>
#include <fcntl.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef DOS
#include <dirent.h>
#include <pwd.h>
#include <sys/param.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#else /* DOS */
#include <direct.h>
#include <dos.h>
#include <time.h>
#include <io.h>
#define MAXPATHLEN _MAX_PATH
#define MAXNAMLEM _MAX_PATH
#define alarm(x) 0
#endif /* DOS */
#include "lispemul.h"
#include "lispmap.h"
#include "adr68k.h"
#include "lsptypes.h"
#include "lspglob.h"
#include "arith.h"
#include "stream.h"
#include "timeout.h"
#include "locfile.h"
#include "osmsg.h"
#include "dbprint.h"
#include "dskdefs.h"
#include "byteswapdefs.h"
#include "car-cdrdefs.h"
#include "commondefs.h"
#include "ufsdefs.h"
extern int *Lisp_errno;
extern int Dummy_errno;
typedef struct filename_entry {
char name[MAXPATHLEN]; /* With version, foo.~3~ or foo */
int version_no;
} FileName;
typedef struct current_varray {
char path[MAXPATHLEN]; /* pathname of directory */
char file[MAXPATHLEN]; /* file name (down cased name) */
time_t mtime;
} CurrentVArray;
FileName VersionArray[VERSIONARRAYLENGTH];
CurrentVArray VArrayInfo;
static int locate_file(char *dir, char *name);
static int make_directory(register char *dir);
static int maintain_version(char *file, FileName *varray, int forcep);
static int get_versionless(FileName *varray, char *file, char *dir);
static int check_vless_link(char *vless, FileName *varray, char *to_file, int *highest_p);
static int get_old(char *dir, FileName *varray, char *afile, char *vfile);
static int get_oldest(char *dir, FileName *varray, char *afile, char *vfile);
static int get_new(char *dir, FileName *varray, char *afile, char *vfile);
static int get_old_new(char *dir, FileName *varray, char *afile, char *vfile);
static int get_version_array(char *dir, char *file, FileName *varray, CurrentVArray *cache);
#ifdef DOS
static void separate_drive(char *lfname, char *drive)
{
register char *cp;
cp = lfname;
/* Check if there's a drive specified. */
if (*(cp + 1) == DRIVESEP) {
*drive = *cp; /* copy the drive letter, if there is one */
cp++;
cp++; /* Move to the real `<`/ */
while (*cp) /* Move the rest to the left to cover. */
{
*(cp - 2) = *cp;
cp++;
}
*(cp - 2) = '\0';
} else
*drive = '\0'; /* no drive */
}
#endif /* DOS */
/*
* Name: separate_host
*
* Argument: char *lfname Lisp full file name including host field.
* char *host The place where host field will be stored.
*
* Value: void
*
* Side Effect: lfname will be replaced with the file name except host field,
* and host will be replaced with the host field.
*
* Description:
*
* Accepts a Lisp full file name from Lisp code, and separate the host field
* from other components. The result will be appropriate form to pass to
* unixpathname. For convenience to unixpathname, the initial directory
* delimiter will be removed from lfname except the case lfname specifies the
* very root directory. And if the lfname is regarded as other directory,
* the trail directory delimiter is also removed.
*
*/
#ifdef DOS
void separate_host(char *lfname, char *host, char *drive)
#else
void separate_host(char *lfname, char *host)
#endif /* DOS */
{
register char *cp;
register size_t diff;
cp = lfname + 1; /* Skip the initial "{". */
while (*cp != '}') *host++ = *cp++;
*host = '\0';
cp++; /* Now, *cp == '<' or drive letter. */
#ifdef DOS
/* Check if there's a drive specified. */
if (*(cp + 1) == DRIVESEP) {
*drive = *cp; /* copy the drive letter, if there is one */
cp++;
cp++; /* Move to the real `<`/ */
} else
*drive = '\0'; /* no drive */
#endif /* DOS */
if (*(cp + 1) == '\0') {
/* Root directory is specified. */
*lfname = '<';
*(lfname + 1) = '\0';
} else {
diff = cp - lfname;
if (*cp == '<' || *cp == DIRSEP
#ifdef DOS
|| *cp == UNIXDIRSEP
#endif /* DOS */
) {
/*
* Skip the initial directory delimiter.
*/
cp++;
diff++;
}
while (*cp) {
*(cp - diff) = *cp;
cp++;
}
if (*(cp - 1) == '>' && *(cp - 2) != '\'') {
/*
* The last character is a not quoted directory
* delimiter. We have to remove it from the result
* lfname for the convenience of unixpathname.
*/
*(cp - diff - 1) = '\0';
} else {
*(cp - diff) = '\0';
}
}
}
/*
* Name: COM_openfile
*
* Argument: LispPTR *args args[0]
* Full file name which is following the Xerox
* Lisp file naming convention.
* args[1] Recognition mode. See IRM.
* args[2] Access mode. See IRM.
* args[3] The place where the creation date of the
* opened file should be stored.
* args[4] The place where the size of the opened file
* should be stored.
* args[5] The place where the error number should be
* stored.
*
* Value: If succeed, returns the Lisp smallp which represents the open
* file descriptor, otherwise Lisp NIL.
*
* Side Effect: If succeed, cdate(args[3]) and size(args[4]) will hold the
* creation date and file size respectively.
*
* Description:
*
* The implementation of OPENFILE FDEV method for DSK and UNIX device. Try to
* open a specified file.
*/
LispPTR COM_openfile(register LispPTR *args)
{
char lfname[MAXPATHLEN + 5], file[MAXPATHLEN], host[MAXNAMLEN];
char dir[MAXPATHLEN], name[MAXNAMLEN], ver[VERSIONLEN];
register int fatp, dskp, rval, fd, link_check_flg, flags, *bufp;
struct stat sbuf;
#ifdef DOS
char drive[1]; /* Drive designator */
int extlen; /* length of the raw file extension */
char rawname[MAXNAMLEN];
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[5]));
LispStringLength(args[0], rval, fatp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = fatp ? rval + 4 + 1 : rval + 2 + 1;
/* Add five for the host name field in Lisp format. */
if (rval > MAXPATHLEN + 5) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
#ifdef DOS
separate_host(lfname, host, drive);
#else
separate_host(lfname, host);
#endif /* DOS */
UPCASE(host);
if (strcmp(host, "DSK") == 0)
dskp = 1;
else if (strcmp(host, "UNIX") == 0)
dskp = 0;
else
return (NIL);
/*
* Convert a Lisp file name to UNIX one. If host is DSK, we also have to
* convert a version field.
*/
#ifdef DOS
unixpathname(lfname, file, dskp, 0, drive, &extlen, rawname);
#else
unixpathname(lfname, file, dskp, 0);
#endif
/*
* Set up the flags argument for open system call.
* And we have to handle the non existing directory case if the device is
* DSK.
* link_check_flg is used to determine whether we have to check a hard-link
* based version control after opening a file.
*/
link_check_flg = 0;
switch (args[1]) {
case RECOG_OLD:
case RECOG_OLDEST:
switch (args[2]) {
case ACCESS_INPUT: flags = O_RDONLY; break;
case ACCESS_OUTPUT:
/*
* The current implementation of Lisp page mapped device requires
* that the output stream being "readable"!
*/
flags = O_RDWR | O_TRUNC;
break;
case ACCESS_BOTH: flags = O_RDWR; break;
case ACCESS_APPEND:
/*
* Should be O_WRONLY | O_APPEND. But Lisp needs it.
*/
flags = O_RDWR;
break;
}
break;
case RECOG_NEW:
case RECOG_OLD_NEW:
/*
* In DSK device, the not existing yet file can be recognized. In this
* case, if there is a chance to create a new file, we have to make
* sure that all directory to reach the recognized file exists.
* Also we have to check the versionless file is correctly maintained
* or not when we have a chance to create a new file.
*/
switch (args[2]) {
case ACCESS_INPUT:
if (args[1] == RECOG_NEW) {
/*
* Opening a input stream to a new, not yet
* existing, file does not make sense.
*/
return (NIL);
} else {
/*
* Even if OLD/NEW recognition, opening a input
* stream never try to create a new file. Thus,
* without O_CREAT.
*/
flags = O_RDONLY;
}
break;
case ACCESS_OUTPUT:
flags = O_RDWR | O_TRUNC | O_CREAT;
if (dskp) {
unpack_filename(file, dir, name, ver, 1);
if (make_directory(dir) == 0) return (NIL);
link_check_flg = 1;
}
break;
case ACCESS_BOTH:
flags = O_RDWR | O_CREAT;
if (dskp) {
unpack_filename(file, dir, name, ver, 1);
if (make_directory(dir) == 0) return (NIL);
link_check_flg = 1;
}
break;
case ACCESS_APPEND:
flags = O_RDWR | O_CREAT;
if (dskp) {
unpack_filename(file, dir, name, ver, 1);
if (make_directory(dir) == 0) return (NIL);
link_check_flg = 1;
}
break;
}
break;
default: return (NIL);
}
/*
* The file name which has been passed from Lisp is sometimes different
* from the actual file name on DSK, even after the Lisp name is converted
* to UNIX form with unixpathname. Lisp always recognize a file on DSK
* with version. If the versionless file exists and it is not correctly
* maintained, that is it is not hard linked to the existing highest
* versioned file, Lisp regards such link missing versionless file as
* the highest versioned file, but the actual name on the file system
* is still versionless.
* get_old, get_oldest, get_new, get_old_new routines handle all of the
* complicated cases correctly and let us know the "Lisp recognizing"
* name and "Real" name. Both of them are UNIX format.
* At this point, we will use one of the four routines and get the
* real name. We can use it to open a file which is requested from Lisp
* with the "Lisp recognizing" name.
*/
if (dskp) {
if (unpack_filename(file, dir, name, ver, 1) == 0) return (NIL);
if (true_name(dir) != -1) return (0);
if (get_version_array(dir, name, VersionArray, &VArrayInfo) == 0) return (NIL);
ConcNameAndVersion(name, ver, file);
switch (args[1]) {
case RECOG_OLD:
if (get_old(dir, VersionArray, file, name) == 0) return (NIL);
break;
case RECOG_OLDEST:
if (get_oldest(dir, VersionArray, file, name) == 0) return (NIL);
break;
case RECOG_NEW:
if (get_new(dir, VersionArray, file, name) == 0) return (NIL);
break;
case RECOG_OLD_NEW:
if (get_old_new(dir, VersionArray, file, name) == 0) return (NIL);
break;
default: return (NIL);
}
}
/*
* DSK device only allow to open a regular file.
*/
if (dskp) {
TIMEOUT(rval = stat(file, &sbuf));
if (rval == 0) {
if ((sbuf.st_mode & S_IFMT) != S_IFREG) {
/*
* The Lisp code handles this case as same as "file table
* overflow" error. Final error message is "File won't
* open".
*/
*Lisp_errno = ENFILE;
return (NIL);
}
} else {
/*
* When stat failed, only if the reason is "file does not
* exist" and we are trying to open a file with a mode we can
* create a new file, we can proceed.
*/
if (errno != ENOENT || !link_check_flg) {
*Lisp_errno = errno;
return (NIL);
}
}
}
if (dskp && link_check_flg) {
/*
* When we are opening a file with a mode we might create a new file,
* we have to make sure that versionless file is maintained
* correctly before we actually creating a new file, because a
* created new file will change the status and the recognition on
* the same file with the same recognition mode will return the
* different result.
* At this point, the third argument for maintain_version, forcep is
* 1, because a lonly versionless file should be linked to version 1.
* If we are opening a file recognized with new mode, version 2,
* without pre-linking a versionless to version 1, the final
* clean up maintain_version will link the versionless to version 3.
*/
TIMEOUT(rval = access(file, F_OK));
if (rval == -1) {
if (errno == ENOENT) {
/*
* Actually we are creating a new file. We have to
* maintain a version status.
*/
if (maintain_version(file, (FileName *)NULL, 1) == 0) {
TIMEOUT(rval = close(fd));
*Lisp_errno = errno;
return (NIL);
}
} else {
/*
* Because of other reason, access call failed.
*/
*Lisp_errno = errno;
return (NIL);
}
} else {
/*
* The subjective file has already existed. We don't need
* to maintain a version.
*/
#ifdef DOS
if (args[1] == RECOG_NEW) {
char old[MAXPATHLEN];
make_old_version(old, file);
unlink(old);
rename(file, old); /* make old version */
}
#endif /* DOS */
}
}
/*
* If a new file is created, its actual mode is computed from the
* third argument for open and the process's umask. I'm pretty sure
* 0666 would be most appropriate mode to specify here.
*/
TIMEOUT(fd = open(file, flags, 0666));
if (fd == -1) {
*Lisp_errno = errno;
return (NIL);
}
if (dskp && link_check_flg) {
/*
* Again we have to maintain version to clean up the directory.
* This time we invoke maintain_version with forcep off, because
* the entirely newly created file, versionless file, should not
* be linked to any file.
*/
if (maintain_version(file, (FileName *)NULL, 0) == 0) {
TIMEOUT(close(fd));
*Lisp_errno = errno;
return (NIL);
}
}
TIMEOUT(rval = fstat(fd, &sbuf));
if (rval == -1) {
TIMEOUT(close(fd));
*Lisp_errno = errno;
return (NIL);
}
bufp = (int *)(Addr68k_from_LADDR(args[3]));
*bufp = ToLispTime(sbuf.st_mtime);
bufp = (int *)(Addr68k_from_LADDR(args[4]));
if (!dskp && ((sbuf.st_mode & S_IFMT) != S_IFREG) && ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
/*
* Not a regular file or directory file. Put on a marker.
*/
*bufp = SPECIALFILEMARK;
} else {
*bufp = sbuf.st_size;
}
return (GetSmallp(fd));
}
/*
* Name: COM_closefile
*
* Argument: LispPTR *args args[0]
* Full file name which is following the Xerox
* Lisp file naming convention. Including a host
* field.
* args[1]
* The Lisp integer representing a file descriptor
* of the file being closed.
* args[2]
* The creation date of the file.
* args[3]
* The place where the error number should be
* stored.
*
* Value: If succeed, returns the Lisp T otherwise, Lisp NIL.
*
* Side Effect: None.
*
* Description:
*
* The implementation of CLOSEFILE FDEV method for DSK and UNIX device. Try to
* close a specified file.
* The creation date file attribute in Lisp sense is kept in st_mdate field of
* UNIX structure. To keep the creation date of the file, it is maintained by
* Lisp and it is passed to COM_closefile.
*/
LispPTR COM_closefile(register LispPTR *args)
{
#ifdef DOS
register int fd, dskp, rval;
time_t cdate;
char lfname[MAXPATHLEN + 5], host[MAXNAMLEN];
char file[MAXPATHLEN], dir[MAXPATHLEN], name[MAXNAMLEN + 1];
char ver[VERSIONLEN], drive[1];
struct find_t dirp;
int dp;
struct stat sbuf;
ino_t ino;
int extlen;
char rawname[MAXNAMLEN];
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[3]));
LispStringLength(args[0], rval, dskp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = dskp ? rval + 4 + 1 : rval + 2 + 1;
/* Add five for the host name field in Lisp format. */
if (rval > MAXPATHLEN + 5) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
separate_host(lfname, host, drive);
UPCASE(host);
if (strcmp(host, "DSK") == 0)
dskp = 1;
else if (strcmp(host, "UNIX") == 0)
dskp = 0;
else
return (NIL);
/*
* Convert a Lisp file name to UNIX one. If host is DSK, we also have to
* convert a version field.
*/
dskp ? unixpathname(lfname, file, 1, 0, drive, &extlen, rawname)
: unixpathname(lfname, file, 0, 0, drive, &extlen, rawname);
fd = LispNumToCInt(args[1]);
cdate = (time_t)LispNumToCInt(args[2]);
if (!dskp) {
TIMEOUT(rval = fstat(fd, &sbuf));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
}
if (cdate == 0) {
/* Just close. */
TIMEOUT(rval = close(fd));
if (rval == -1) {
if (!dskp && errno == EPERM && (sbuf.st_mode & S_IFREG) == 0) {
/*
* On {UNIX} device, closing a special file we are not
* the owner of it. Although I don't think close fails
* because of EPERM, in honor of Medley 1.1 code, I put
* this segment here.
*/
return (ATOM_T);
} else {
*Lisp_errno = errno;
return (NIL);
}
} else {
return (ATOM_T);
}
}
if (!unpack_filename(file, dir, name, ver, 1)) return (NIL);
if (dskp) {
/*
* On {DSK}, we have to make sure dir is case sensitively existing
* directory.
*/
if (true_name(dir) != -1) return (NIL);
/*
* There is a very troublesome problem here. The file name Lisp
* recognizes is not always the same as the name which COM_openfile
* used to open the file. Sometimes COM_openfile uses the versionless
* file name to open a file, although Lisp always recognizes with
* *versioned* file name.
* Thus, we compare i-node number of the requested file with ones of all
* of files on the directory. This is time spending implementation.
* More clean up work is needed.
*/
TIMEOUT(rval = fstat(fd, &sbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
ino = sbuf.st_ino;
TIMEOUT(rval = _dos_findfirst(dir, _A_SUBDIR, &dirp));
if (rval < 0) {
*Lisp_errno = errno;
return (NIL);
}
for (; rval == 0; S_TOUT(rval = _dos_findnext(&dirp))) {
sprintf(file, "%s\\%s", dir, dirp.name);
}
}
#ifndef DOS /* effectively NEVER, since we're in an ifdef DOS */
time[0].tv_sec = (long)sbuf.st_atime;
time[0].tv_usec = 0L;
time[1].tv_sec = (long)ToUnixTime(cdate);
time[1].tv_usec = 0L;
#endif /* DOS */
TIMEOUT(rval = close(fd));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
#ifndef DOS
TIMEOUT(rval = utimes(file, time));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
#endif /* DOS, internal */
#else /* UNIX version of CLOSEFILE */
register int fd, fatp, dskp, rval;
time_t cdate;
char lfname[MAXPATHLEN + 5], host[MAXNAMLEN];
char file[MAXPATHLEN], dir[MAXPATHLEN], name[MAXNAMLEN + 1];
char ver[VERSIONLEN];
register DIR *dirp;
register struct dirent *dp;
struct stat sbuf;
struct timeval time[2];
ino_t ino;
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[3]));
LispStringLength(args[0], rval, fatp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = fatp ? rval + 4 + 1 : rval + 2 + 1;
/* Add five for the host name field in Lisp format. */
if (rval > MAXPATHLEN + 5) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
separate_host(lfname, host);
UPCASE(host);
if (strcmp(host, "DSK") == 0)
dskp = 1;
else if (strcmp(host, "UNIX") == 0)
dskp = 0;
else
return (NIL);
/*
* Convert a Lisp file name to UNIX one. If host is DSK, we also have to
* convert a version field.
*/
dskp ? unixpathname(lfname, file, 1, 0) : unixpathname(lfname, file, 0, 0);
fd = LispNumToCInt(args[1]);
cdate = (time_t)LispNumToCInt(args[2]);
if (!dskp) {
TIMEOUT(rval = fstat(fd, &sbuf));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
}
if (cdate == 0) {
/* Just close. */
TIMEOUT(rval = close(fd));
if (rval == -1) {
if (!dskp && errno == EPERM && (sbuf.st_mode & S_IFREG) == 0) {
/*
* On {UNIX} device, closing a special file we are not
* the owner of it. Although I don't think close fails
* because of EPERM, in honor of Medley 1.1 code, I put
* this segment here.
*/
return (ATOM_T);
} else {
*Lisp_errno = errno;
return (NIL);
}
} else {
return (ATOM_T);
}
}
if (!unpack_filename(file, dir, name, ver, 1)) return (NIL);
if (dskp) {
/*
* On {DSK}, we have to make sure dir is case sensitively existing
* directory.
*/
if (true_name(dir) != -1) return (NIL);
/*
* There is a very troublesome problem here. The file name Lisp
* recognizes is not always the same as the name which COM_openfile
* used to open the file. Sometimes COM_openfile uses the versionless
* file name to open a file, although Lisp always recognizes with
* *versioned* file name.
* Thus, we compare i-node number of the requested file with ones of all
* of files on the directory. This is time spending implementation.
* More clean up work is needed.
*/
TIMEOUT(rval = fstat(fd, &sbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
ino = sbuf.st_ino;
errno = 0;
TIMEOUT0(dirp = opendir(dir));
if (dirp == (DIR *)NULL) {
*Lisp_errno = errno;
return (NIL);
}
for (S_TOUT(dp = readdir(dirp)); dp != (struct dirent *)NULL || errno == EINTR;
errno = 0, S_TOUT(dp = readdir(dirp)))
if (dp) {
if (ino == (ino_t)dp->d_ino) sprintf(file, "%s/%s", dir, dp->d_name);
}
TIMEOUT(closedir(dirp));
}
time[0].tv_sec = (long)sbuf.st_atime;
time[0].tv_usec = 0L;
time[1].tv_sec = (long)ToUnixTime(cdate);
time[1].tv_usec = 0L;
TIMEOUT(rval = close(fd));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
TIMEOUT(rval = utimes(file, time));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
#endif /* DOS */
return (ATOM_T);
}
/*
* Name: DSK_getfilename
*
* Argument: LispPTR *args args[0] Full file name in Lisp format.
* args[1] Recognition mode. See IRM.
* args[2] Name area where the recognized full file name
* will be stored.
* args[3] The place where the error number should be
* stored.
*
* Value: If succeed, returns the Lisp smallp which represents the length
* of the recognized full file name, otherwise Lisp NIL.
*
* Side Effect: If succeed, name area (args[2]) will be replaced with the
* recognized full file name.
*
* Description:
*
* The implementation of GETFILENAME FDEV method for DSK device. Performs the
* recognition on the specified name. Does not check if OPENFILE actually
* can open the file with the specified mode or not.
*/
LispPTR DSK_getfilename(register LispPTR *args)
{
register char *base;
register int len, dirp, rval;
int fatp;
char lfname[MAXPATHLEN];
char aname[MAXNAMLEN];
char vname[MAXPATHLEN];
char file[MAXPATHLEN];
char dir[MAXPATHLEN];
char name[MAXNAMLEN];
char ver[VERSIONLEN];
#ifdef DOS
char drive[1], rawname[MAXNAMLEN];
int extlen; /* len of extension, for making backup filename */
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[3]));
LispStringLength(args[0], len, fatp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
len = fatp ? len + 4 + 1 : len + 2 + 1;
if (len > MAXPATHLEN) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
#ifdef DOS
separate_drive(lfname, drive);
#endif
/*
* Convert a Lisp file name to UNIX one. This is a DSK device method.
* Thus we have to convert a version field too. Third argument for
* unixpathname specifies it.
*/
#ifdef DOS
if (unixpathname(lfname, file, 1, 0, drive, &extlen, rawname) == 0) return (NIL);
#else
if (unixpathname(lfname, file, 1, 0) == 0) return (NIL);
#endif
if (unpack_filename(file, dir, name, ver, 1) == 0) return (NIL);
switch (args[1]) {
case RECOG_OLD:
/*
* "Old" file means the "newest existing" file. Thus, we have to
* check dir is an existing directory or not. The search has to
* be done in case insensitive manner. true_name does this work.
*/
if (true_name(dir) != -1) {
/* No such directory. */
return (NIL);
}
/*
* At this point, true_name has converted dir to the "true" name
* of the directory.
*/
if (strcmp(name, "") == 0) {
/*
* The file name is specified with a trail directory delimiter.
* We should recognize it as a directory.
*/
strcpy(aname, dir);
strcpy(vname, dir);
dirp = 1;
} else {
/*
* Recognizing a file on DSK device needs the version information.
* We gather version information in a version array first.
*/
if (get_version_array(dir, name, VersionArray, &VArrayInfo) == 0) return (NIL);
ConcNameAndVersion(name, ver, aname);
if (get_old(dir, VersionArray, aname, vname) == 0) return (NIL);
if ((rval = true_name(aname)) == 0) return (NIL);
if (rval == -1) {
/*
* The specified file is a directory file.
*/
strcpy(vname, aname);
dirp = 1;
} else {
#ifdef DOS
strcpy(vname, aname);
#endif
dirp = 0;
}
}
break;
case RECOG_OLDEST:
/*
* "Oldest" file means the "oldest existing" file. Thus, we have to
* check dir is an existing directory or not.
*/
if (true_name(dir) != -1) {
/* No such directory. */
return (NIL);
}
if (strcmp(name, "") == 0) {
/*
* The file name is specified with a trail directory delimiter.
* We should recognize it as a directory.
*/
strcpy(aname, dir);
strcpy(vname, dir);
dirp = 1;
} else {
if (get_version_array(dir, name, VersionArray, &VArrayInfo) == 0) return (NIL);
ConcNameAndVersion(name, ver, aname);
if (get_oldest(dir, VersionArray, aname, vname) == 0) return (NIL);
if ((rval = true_name(aname)) == 0) return (NIL);
if (rval == -1) {
/*
* The specified file is a directory file.
*/
strcpy(vname, aname);
dirp = 1;
} else {
#ifdef DOS
strcpy(vname, aname);
#endif
dirp = 0;
}
}
break;
case RECOG_NEW:
/*
* "New" file means the "not existing" file. Thus it is not
* necessary that dir is an existing directory. If dir is not
* an existing directory, we returns the specified file name
* as if, the subsequent OPENFILE will find the truth.
*/
if (true_name(dir) != -1) {
strcpy(vname, file);
dirp = 0;
} else if (strcmp(name, "") == 0) {
/*
* The file name is specified with a trail directory delimiter.
* We should recognize it as a directory.
*/
strcpy(aname, dir);
strcpy(vname, dir);
dirp = 1;
} else {
ConcDirAndName(dir, name, aname);
if ((rval = true_name(aname)) == -1) {
strcpy(vname, aname);
dirp = 1;
} else {
/*
* Here, dir is an existing directory. We have to perform
* "new" recognition with the version information.
*/
if (get_version_array(dir, name, VersionArray, &VArrayInfo) == 0) return (NIL);
ConcNameAndVersion(name, ver, aname);
if (get_new(dir, VersionArray, aname, vname) == 0) return (NIL);
dirp = 0;
}
}
break;
case RECOG_OLD_NEW:
/*
* "Old/new" file means the "newest existing" or "not existing" file.
* Thus, if dir is not an existing directory, we can return the
* specified file name. If it is an existing one, we have to
* try "old" recognition on the directory first. If the recognition
* fails, we try "new" recognition.
*/
if (true_name(dir) != -1) {
strcpy(vname, file);
dirp = 0;
} else {
ConcDirAndName(dir, name, aname);
if ((rval = true_name(aname)) == -1) {
strcpy(vname, aname);
dirp = 1;
} else {
if (get_version_array(dir, name, VersionArray, &VArrayInfo) == 0) return (NIL);
ConcNameAndVersion(name, ver, aname);
if (get_old_new(dir, VersionArray, aname, vname) == 0) return (NIL);
dirp = 0;
}
}
break;
case RECOG_NON:
/*
* "Non" recognition is used to recognize a sysout file. The sysout
* file is dealt with specially, it does not have any version, even
* if it is on {DSK} device. Only we have to do here is to make
* sure the path to reach to the specified file is an existing
* directories. The file name itself is recognized as if.
*/
if (true_name(dir) != -1) return (NIL);
ConcDirAndName(dir, name, vname);
strcpy(aname, vname);
if (true_name(aname) == -1) {
strcpy(vname, aname);
dirp = 1;
} else {
dirp = 0;
}
if (lisppathname(vname, lfname, dirp, 0) == 0) return (NIL);
STRING_BASE(args[2], base);
len = strlen(lfname);
#ifndef BYTESWAP
strncpy(base, lfname, len + 1);
#else
StrNCpyFromCToLisp(base, lfname, len + 1);
#endif /* BYTESWAP */
return (GetSmallp(len));
}
/*
* DSK device does not recognize a directory file as a file. Thus we should
* return NIL when the recognized file is a directory.
*/
if (dirp) return (NIL);
/*
* Now, vname holds the "versioned" full name of the recognized file in UNIX
* format. We have to convert it back to Lisp format. The version field
* have to be converted. The fourth argument for lisppathname specifies it.
*/
#ifdef DOS
/* For DOS, have to assure we use the name asked for, not the */
/* faked-up oversion-0 name, so reported names match. */
{
char dver[VERSIONLEN];
separate_version(vname, dver, 0);
ConcDirAndName(dir, name, aname);
ConcNameAndVersion(aname, dver, vname);
}
#endif /* DOS */
if (lisppathname(vname, lfname, dirp, (dirp ? 0 : 1)) == 0) return (NIL);
STRING_BASE(args[2], base);
len = strlen(lfname);
#ifndef BYTESWAP
strncpy(base, lfname, len + 1);
#else
StrNCpyFromCToLisp(base, lfname, len + 1);
#endif /* BYTESWAP */
return (GetSmallp(len));
}
/*
* Name: DSK_deletefile
*
* Argument: LispPTR *args args[0]
* Full file name in Lisp format.
* args[1]
* The place where the error number should be
* stored.
*
* Value: If succeed, returns the Lisp symbol T, otherwise Lisp NIL.
*
* Side Effect: If succeed, the specified file is unlinked.
*
* Description:
*
* The implementation of DELETEFILE FDEV method for DSK device. Try to delete
* a specified file.
*/
LispPTR DSK_deletefile(register LispPTR *args)
{
char file[MAXPATHLEN], fbuf[MAXPATHLEN], vless[MAXPATHLEN];
char dir[MAXPATHLEN], ver[VERSIONLEN];
int rval, fatp;
register FileName *varray;
#ifdef DOS
char drive[1], rawname[MAXNAMLEN];
int extlen; /* len of extension, for making backup filename */
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[1]));
LispStringLength(args[0], rval, fatp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = fatp ? rval + 4 + 1 : rval + 2 + 1;
if (rval > MAXPATHLEN) FileNameTooLong(NIL);
LispStringToCString(args[0], fbuf, MAXPATHLEN);
#ifdef DOS
separate_drive(fbuf, drive);
unixpathname(fbuf, file, 1, 0, drive, &extlen, rawname);
#else
unixpathname(fbuf, file, 1, 0);
#endif
if (unpack_filename(file, dir, fbuf, ver, 1) == 0) return (NIL);
if (get_version_array(dir, fbuf, VersionArray, &VArrayInfo) == 0) return (NIL);
varray = VersionArray;
if (NoFileP(varray))
return (NIL); /*
* If the specified file is deleted from
* outside of Lisp during the last time
* Lisp recognize it and now, this case
* will occur.
*/
/*
* Although the file should have been recognized with "oldest" mode in Lisp
* code, we have to recognize it again to know the "real" accessible name
* of it.
*/
ConcNameAndVersion(fbuf, ver, file);
if (get_oldest(dir, varray, file, fbuf) == 0) return (NIL);
if (get_versionless(varray, vless, dir) == 0) {
/*
* There is no versionless file. All we have to do is to simply
* try to unlink the specified file.
*/
TIMEOUT(rval = unlink(file));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
return (ATOM_T);
}
/*
* If a versionless file exists, we have to check the link status of it,
* because deleting a versionless file or a file to which a versionless
* file is linked will destroy the consistency of the version status.
*/
if (check_vless_link(vless, varray, fbuf, &rval) == 0) return (NIL);
if (strcmp(file, vless) == 0 || strcmp(file, fbuf) == 0) {
if (*fbuf != '\0') {
/*
* Both of the versionless file and the file to which the
* versionless file is linked have to be unlinked.
*/
TIMEOUT(rval = unlink(vless));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
TIMEOUT(rval = unlink(fbuf));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
/*
* Finally, we have to maintain the version status.
*/
if (maintain_version(vless, (FileName *)NULL, 0) == 0) return (NIL);
return (ATOM_T);
} else {
/*
* Although the versionfile is specified, it is not linked
* to any file in varray. We should not maintain the version
* status after deleting the versionless file, because
* we cannot say whether the versionless file is actually under
* control of the Medley DSK file system or not.
*/
TIMEOUT(rval = unlink(vless));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
return (ATOM_T);
}
} else {
/*
* Just unlink the specified file.
*/
TIMEOUT(rval = unlink(file));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
return (ATOM_T);
}
}
/*
* Name: DSK_renamefile
*
* Argument: LispPTR *args args[0]
* Full file name in Lisp format. The file which
* is being renamed.
* args[1]
* Full file name in Lisp format. The file to which
* args[0] is being renamed.
* args[2]
* The place where the error number should be
* stored.
*
* Value: If succeed, returns the Lisp symbol T, otherwise Lisp NIL.
*
* Side Effect: If succeed, the specified file is unlinked.
*
* Description:
*
* The implementation of RENAMEFILE FDEV method for DSK device. Try to rename
* a specified file.
*/
LispPTR DSK_renamefile(register LispPTR *args)
{
char src[MAXPATHLEN], dst[MAXPATHLEN];
char fbuf[MAXPATHLEN], vless[MAXPATHLEN], svless[MAXPATHLEN];
char dir[MAXPATHLEN], ver[VERSIONLEN];
int rval, fatp;
register int need_maintain_flg;
register FileName *varray;
#ifdef DOS
char drive1[1], drive2[1];
int extlen1, extlen2; /* len of extension */
char rawname1[MAXNAMLEN], rawname2[MAXNAMLEN];
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[2]));
LispStringLength(args[0], rval, fatp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = fatp ? rval + 4 + 1 : rval + 2 + 1;
if (rval > MAXPATHLEN) FileNameTooLong(NIL);
LispStringLength(args[1], rval, fatp);
rval = fatp ? rval + 4 + 1 : rval + 2 + 1;
if (rval > MAXPATHLEN) FileNameTooLong(NIL);
LispStringToCString(args[0], fbuf, MAXPATHLEN);
#ifdef DOS
separate_drive(fbuf, drive1);
unixpathname(fbuf, src, 1, 0, drive1, &extlen1, rawname1);
#else /* DOS */
unixpathname(fbuf, src, 1, 0);
#endif /* DOS */
LispStringToCString(args[1], fbuf, MAXPATHLEN);
#ifdef DOS
separate_drive(fbuf, drive2);
unixpathname(fbuf, dst, 1, 0, drive2, &extlen2, rawname2);
#else /* DOS */
unixpathname(fbuf, dst, 1, 0);
#endif /* DOS */
if (unpack_filename(dst, dir, fbuf, ver, 1) == 0) return (NIL);
/*
* The destination file has been recognized as new file. Thus we have
* to make sure that the directory exists.
*/
if (make_directory(dir) == 0) return (NIL);
/*
* We maintain the destination to handle the link damaged case correctly.
*/
ConcDirAndName(dir, fbuf, dst);
if (maintain_version(dst, (FileName *)NULL, 0) == 0) return (NIL);
if (get_version_array(dir, fbuf, VersionArray, &VArrayInfo) == 0) return (NIL);
varray = VersionArray;
/*
* Although the file should have been recognized with "new" mode in Lisp
* code, we have to recognize it again to know the "real" accessible name
* of it.
*/
ConcNameAndVersion(fbuf, ver, dst);
if (get_new(dir, varray, dst, fbuf) == 0) return (NIL);
/*
* At this point, there are three cases for the destination. If there is
* no member of the destination family, there is nothing to do. If there
* is only a versionless file and, if the "real" destination is not the
* versionless, we have to rename it to version 1. And last case, if the
* "real" destination file is the file to which the versionless file is linked,
* we have to unlink the versionless file.
*/
if (!NoFileP(varray)) {
if (OnlyVersionlessP(varray)) {
get_versionless(varray, vless, dir);
if (strcmp(dst, vless) != 0) {
ConcNameAndVersion(vless, "1", fbuf);
TIMEOUT(rval = rename(vless, fbuf));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
}
} else {
/*
* We are sure that the versionless file is linked to one of
* the higher versioned file here.
*/
get_versionless(varray, vless, dir);
if (check_vless_link(vless, varray, fbuf, &rval) == 0) { return (NIL); }
if (strcmp(dst, fbuf) == 0) {
TIMEOUT(rval = unlink(vless));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
}
}
}
if (unpack_filename(src, dir, fbuf, ver, 1) == 0) return (NIL);
if (get_version_array(dir, fbuf, varray, &VArrayInfo) == 0) return (NIL);
if (NoFileP(varray))
return (NIL); /*
* If the specified file is deleted from
* outside of Lisp during the last time
* Lisp recognize it and now, this case
* will occur.
*/
/*
* Although the file should have been recognized with "old" mode in Lisp
* code, we have to recognize it again to know the "real" accessible name
* of it.
*/
ConcNameAndVersion(fbuf, ver, src);
if (get_old(dir, varray, src, fbuf) == 0) return (NIL);
if (get_versionless(varray, vless, dir) == 0) {
/*
* There is no versionless file. All we have to do is to simply
* try to rename the specified file.
*/
need_maintain_flg = 0;
} else {
/*
* If a versionless file exists, we have to check the link status
* of it, because renaming a versionless file or a file to which a
* versionless file is linked will destroy the consistency of the
* version status.
*/
if (check_vless_link(vless, varray, fbuf, &rval) == 0) return (NIL);
if (strcmp(src, vless) == 0 && *fbuf != '\0') {
/*
* At this point, we only unlink the file to which the
* versionless is linked. The versionless fill will be
* renamed later.
*/
TIMEOUT(rval = unlink(fbuf));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
need_maintain_flg = 1;
} else if (strcmp(src, fbuf) == 0) {
TIMEOUT(rval = unlink(vless));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
need_maintain_flg = 1;
} else {
need_maintain_flg = 0;
}
strcpy(svless, vless);
}
/*
* At this point, src holds the full file name to be renamed, and dst holds
* the full file name to which src will be renamed.
*/
TIMEOUT(rval = rename(src, dst));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
/*
* The destination directory always have to be maintained, because it is
* now under control of DSK device.
* The source directory have to be maintained only if need_maintain_flg
* is on.
*/
if (maintain_version(dst, (FileName *)NULL, 0) == 0) return (NIL);
if (need_maintain_flg) {
if (maintain_version(src, (FileName *)NULL, 0) == 0) return (NIL);
}
return (ATOM_T);
}
/*
* Name: DSK_directorynamep
*
* Argument: LispPTR *args args[0]
* Directory name in Lispformat. Both of the initial
* and trail directory delimiter are stripped by Lisp
* code. Only one exception is a "root directory".
* "Root directory is represented as ">".
* args[1]
* The place where the "true" name of the directory
* in Lisp format will be stored.
* args[2]
* The place where the error number should be stored.
* Not used in the current Lisp code implementation.
*
* Value: If succeed, returns the Lisp smallp which represents the length
* of the "true" name of the directory, otherwise Lisp NIL.
*
* Side Effect: If the directory is recognized as a valid directory representation,
* args[1] is replaced with the "true" directory name.
*
* Description:
*
* The implementation of the DIRECTORYNAMEP FDEV method for DSK device.
* Performs the recognition as well. Accepts the directory representation which
* obeys the Xerox Lisp file naming convention. The "true" name which is stored
* on the area specified with the second argument also follows the Xerox Lisp
* file naming convention, and it includes the initial and trail directory
* delimiter. Thus the Lisp code does not have to worry about the conversion of
* the directory name representation.
*/
LispPTR DSK_directorynamep(register LispPTR *args)
{
char dirname[MAXPATHLEN];
char fullname[MAXPATHLEN];
register int len, fatp;
register char *base;
#ifdef DOS
char drive[1], rawname[MAXNAMLEN];
int extlen; /* len of extension, for making backup filename */
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[2]));
LispStringLength(args[0], len, fatp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
len = fatp ? len + 4 + 1 : len + 2 + 1;
/* -2 for the initial and trail directory delimiter. */
if (len > MAXPATHLEN - 2) FileNameTooLong(NIL);
LispStringToCString(args[0], dirname, MAXPATHLEN);
/* Convert Xerox Lisp file naming convention to Unix one. */
#ifdef DOS
separate_drive(dirname, drive);
if (unixpathname(dirname, fullname, 1, 0, drive, 0, 0) == 0) return (NIL);
#else /* DOS*/
if (unixpathname(dirname, fullname, 1, 0) == 0) return (NIL);
#endif /* DOS */
if (true_name(fullname) != -1) return (NIL);
/* Convert Unix file naming convention to Xerox Lisp one. */
if (lisppathname(fullname, dirname, 1, 0) == 0) return (NIL);
len = strlen(dirname);
STRING_BASE(args[1], base);
#ifndef BYTESWAP
strncpy(base, dirname, len + 1);
#else
StrNCpyFromCToLisp(base, dirname, len + 1);
#endif /* BYTESWAP */
return (GetSmallp(len));
}
/*
* Name: COM_getfileinfo
*
* Argument: LispPTR *args args[0]
* Full file name which is following the Xerox
* Lisp file naming convention. Including a host
* field.
* args[1]
* The Lisp pointer which represents the requested
* file attribute.
* args[2]
* The place where the requested value will be stored.
* args[3]
* The place where the errno will be stored.
*
* Value: If failed, returns Lisp NIL. If succeed, returned value is
* different according to the attribute requested.
* In the case of LENGTH, WDATE, RDATE, and PROTECTION, returns Lisp T.
* In the case of AUTHOR and ALL, returns the length of the author name
* copied into the specified buffer.
*
* Side Effect: The specified buffer will be replaced with the value of the requested
* attribute.
*
* Description:
*
* The implementation of GETFILEINFO FDEV method for DSK and UNIX device. Try to
* fetch a value of a specified file attribute.
* The creation date file attribute in Lisp sense is kept in st_mdate field of
* UNIX structure, and it is treated as same as the write date file attribute in
* Lisp sense.
*/
LispPTR COM_getfileinfo(register LispPTR *args)
{
register int dskp, rval;
register unsigned *bufp;
#ifndef DOS
register struct passwd *pwd;
#endif
register char *base;
char lfname[MAXPATHLEN + 5], file[MAXPATHLEN], host[MAXNAMLEN];
char dir[MAXPATHLEN], name[MAXNAMLEN], ver[VERSIONLEN];
struct stat sbuf;
LispPTR laddr;
#ifdef DOS
char drive[1], rawname[MAXNAMLEN];
int extlen; /* len of extension, for making backup filename */
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[3]));
LispStringLength(args[0], rval, dskp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = dskp ? rval + 4 + 1 : rval + 2 + 1;
/* Add 5 for the host name field in Lisp format. */
if (rval > MAXPATHLEN + 5) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
#ifdef DOS
separate_host(lfname, host, drive);
#else
separate_host(lfname, host);
#endif
UPCASE(host);
if (strcmp(host, "DSK") == 0)
dskp = 1;
else if (strcmp(host, "UNIX") == 0)
dskp = 0;
else
return (NIL);
/*
* Convert a Lisp file name to UNIX one. If host is DSK, we also have to
* convert a version field.
*/
#ifdef DOS
unixpathname(lfname, file, dskp, 0, drive, &extlen, rawname);
#else /* DOS */
unixpathname(lfname, file, dskp, 0);
#endif /* DOS */
/*
* The file name which has been passed from Lisp is sometimes different
* from the actual file name on DSK, even after the Lisp name is converted
* to UNIX form with unixpathname. The Lisp code for GETFILEINFO always
* recognizes a file with old mode. Thus, we recognize it again using
* get_old routine. It will let us know the "real accessible" name of
* the file.
*/
if (dskp) {
if (unpack_filename(file, dir, name, ver, 1) == 0) return (NIL);
if (true_name(dir) != -1) return (0);
if (strcmp(name, "") == 0) {
/*
* The directory is specified.
*/
strcpy(file, dir);
} else {
if (get_version_array(dir, name, VersionArray, &VArrayInfo) == 0) return (NIL);
ConcNameAndVersion(name, ver, file);
if (get_old(dir, VersionArray, file, name) == 0) return (NIL);
}
}
TIMEOUT(rval = stat(file, &sbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
switch (args[1]) {
case LENGTH:
bufp = (unsigned *)(Addr68k_from_LADDR(args[2]));
*bufp = (unsigned)sbuf.st_size;
return (ATOM_T);
case WDATE:
bufp = (unsigned *)(Addr68k_from_LADDR(args[2]));
*bufp = (unsigned)ToLispTime(sbuf.st_mtime);
return (ATOM_T);
case RDATE:
bufp = (unsigned *)(Addr68k_from_LADDR(args[2]));
*bufp = (unsigned)ToLispTime(sbuf.st_atime);
return (ATOM_T);
case PROTECTION:
bufp = (unsigned *)(Addr68k_from_LADDR(args[2]));
*bufp = sbuf.st_mode;
return (ATOM_T);
case AUTHOR:
#ifndef DOS
TIMEOUT(pwd = getpwuid(sbuf.st_uid));
if (pwd == (struct passwd *)NULL) {
/*
* Returns Lisp 0. Lisp code handles this case as author
* unknown. The returned value from Lisp GETFILEINFO
* function would be "".
*/
return (GetSmallp(0));
}
STRING_BASE(args[2], base);
rval = strlen(pwd->pw_name);
#ifndef BYTESWAP
strncpy(base, pwd->pw_name, rval);
#else
StrNCpyFromCToLisp(base, pwd->pw_name, rval);
#endif /* BYTESWAP */
#endif /* DOS */
return (GetSmallp(rval));
case ALL:
/*
* The format of the buffer which has been allocated by Lisp
* is as follows.
* ((LENGTH . fixp)
* (WDATE . fixp)
* (RDATE . fixp)
* (PROTECTION . fixp)
* (AUTHOR . string))
*/
laddr = cdr(car(args[2]));
bufp = (unsigned *)(Addr68k_from_LADDR(laddr));
*bufp = sbuf.st_size;
laddr = cdr(car(cdr(args[2])));
bufp = (unsigned *)(Addr68k_from_LADDR(laddr));
*bufp = ToLispTime(sbuf.st_mtime);
laddr = cdr(car(cdr(cdr(args[2]))));
bufp = (unsigned *)(Addr68k_from_LADDR(laddr));
*bufp = ToLispTime(sbuf.st_atime);
laddr = cdr(car(cdr(cdr(cdr(args[2])))));
bufp = (unsigned *)(Addr68k_from_LADDR(laddr));
*bufp = sbuf.st_mode;
#ifndef DOS
TIMEOUT(pwd = getpwuid(sbuf.st_uid));
if (pwd == (struct passwd *)NULL) { return (GetSmallp(0)); }
laddr = cdr(car(cdr(cdr(cdr(cdr(args[2]))))));
STRING_BASE(laddr, base);
rval = strlen(pwd->pw_name);
#ifndef BYTESWAP
strncpy(base, pwd->pw_name, rval);
#else
StrNCpyFromCToLisp(base, pwd->pw_name, rval);
#endif /* BYTESWAP */
#endif /* DOS */
return (GetSmallp(rval));
default: return (NIL);
}
}
/*
* Name: COM_setfileinfo
*
* Argument: LispPTR *args args[0]
* Full file name which is following the Xerox
* Lisp file naming convention. Including a host
* field.
* args[1]
* The Lisp pointer which represents the requested
* file attribute.
* args[2]
* The value to be stored on the request attribute.
* args[3]
* The place where the error number should be
* stored.
*
* Value: If succeed, returns Lisp T, otherwise Lisp NIL.
*
* Side Effect: The specified attribute of a file will be replaced with the specified
* value.
*
* Description:
*
* The implementation of SETFILEINFO FDEV method for DSK and UNIX device. Try to
* replace a value of a specified file attribute.
* In this implementation, only WDATE(as well as Creation Date) and PROTECTION
* make sense.
*/
LispPTR COM_setfileinfo(register LispPTR *args)
{
register int dskp, rval, date;
char lfname[MAXPATHLEN + 5], file[MAXPATHLEN], host[MAXNAMLEN];
char dir[MAXPATHLEN], name[MAXNAMLEN], ver[VERSIONLEN];
struct stat sbuf;
#ifndef DOS
struct timeval time[2];
#else
char drive[1], rawname[MAXNAMLEN];
int extlen;
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[3]));
LispStringLength(args[0], rval, dskp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = dskp ? rval + 4 + 1 : rval + 2 + 1;
/* Add 5 for the host name field in Lisp format. */
if (rval > MAXPATHLEN + 5) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
#ifdef DOS
separate_host(lfname, host, drive);
#else
separate_host(lfname, host);
#endif /* DOS */
UPCASE(host);
if (strcmp(host, "DSK") == 0)
dskp = 1;
else if (strcmp(host, "UNIX") == 0)
dskp = 0;
else
return (NIL);
/*
* Convert a Lisp file name to UNIX one. If host is DSK, we also have to
* convert a version field.
*/
#ifdef DOS
unixpathname(lfname, file, dskp, 0, drive, &extlen, rawname);
#else /* DOS */
unixpathname(lfname, file, dskp, 0);
#endif /* DOS */
/*
* The file name which has been passed from Lisp is sometimes different
* from the actual file name on DSK, even after the Lisp name is converted
* to UNIX form with unixpathname. The Lisp code for SETFILEINFO always
* recognizes a file with old mode. Thus, we recognize it again using
* get_old routine. It will let us know the "real accessible" name of
* the file.
*/
if (dskp) {
if (unpack_filename(file, dir, name, ver, 1) == 0) return (NIL);
if (true_name(dir) != -1) return (0);
if (get_version_array(dir, name, VersionArray, &VArrayInfo) == 0) return (NIL);
ConcNameAndVersion(name, ver, file);
if (get_old(dir, VersionArray, file, name) == 0) return (NIL);
}
switch (args[1]) {
case WDATE:
TIMEOUT(rval = stat(file, &sbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
#ifndef DOS
date = LispNumToCInt(args[2]);
time[0].tv_sec = (long)sbuf.st_atime;
time[0].tv_usec = 0L;
time[1].tv_sec = (long)ToUnixTime(date);
time[1].tv_usec = 0L;
TIMEOUT(rval = utimes(file, time));
#endif /* DOS */
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
return (ATOM_T);
case PROTECTION:
rval = LispNumToCInt(args[2]);
TIMEOUT(rval = chmod(file, rval));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
return (ATOM_T);
default: return (NIL);
}
}
/*
* Name: COM_readpage
*
* Argument: LispPTR *args args[0]
* The Lisp integer representing a file descriptor
* of the file being read.
* args[1]
* The Lisp integer representing a page number of the
* file being read.
* args[2]
* The place where the contents of the file will be
* stored.
* args[3]
* The place where the error number should be stored.
*
* Value: If succeed, returns a Lisp integer representing a total number of
* bytes read, otherwise Lisp NIL.
*
* Side Effect: The specified buffer will be filled with the specified region of the
* contents of the file.
*
* Description:
*
* The implementation of READPAGES FDEV method for DSK and UNIX device. Try to
* read a page into a buffer.
* If a page being read is a last page in a file, and it is not a full page, the
* remaining region of a buffer will be zero outed. The size of a page is 512 bytes.
*/
LispPTR COM_readpage(register LispPTR *args)
{
register char *bufp;
register char *bp;
register int count, fd, npage, rval;
struct stat sbuf;
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[3]));
fd = LispNumToCInt(args[0]);
npage = LispNumToCInt(args[1]);
bufp = (char *)(Addr68k_from_LADDR(args[2]));
TIMEOUT(rval = fstat(fd, &sbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
if ((sbuf.st_mode & S_IFREG) != 0) {
/*
* The request file is a regular file. We have to make sure that
* next byte read is at the beginning of the requested page of the
* file. If the request file is special file, lseek is not needed.
*/
sklp:
TIMEOUT(rval = lseek(fd, (npage * FDEV_PAGE_SIZE), SEEK_SET));
if (rval == -1) {
if (errno == EINTR) goto sklp; /* interrupted, retry */
*Lisp_errno = errno;
return (NIL);
}
}
rdlp:
TIMEOUT(count = read(fd, bufp, FDEV_PAGE_SIZE));
if (count == -1) {
if (errno == EINTR) goto rdlp; /* interrupted; retry */
*Lisp_errno = errno;
return (NIL);
}
/* O out the remaining part of the buffer. */
for (bp = &bufp[count], rval = count; rval < FDEV_PAGE_SIZE; ++rval) *bp++ = 0;
#ifdef BYTESWAP
word_swap_page((DLword *)bufp, FDEV_PAGE_SIZE / 4);
#endif /* BYTESWAP */
return (GetSmallp(count));
}
/*
* Name: COM_writepage
*
* Argument: LispPTR *args args[0]
* The Lisp integer representing a file descriptor
* of the file being read.
* args[1]
* The Lisp integer representing a page number of the
* file being written.
* args[2]
* The place in where the next date to be written is
* hold.
* args[3]
* The Lisp integer representing a number of bytes
* of data to be written.
* args[4]
* The place where the error number should be stored.
*
* Value: If succeed, returns a Lisp T, otherwise Lisp NIL.
*
* Side Effect: The specified page of the file will be replaced with the contents of
* the buffer.
*
* Description:
*
* The implementation of WRITEPAGES FDEV method for DSK and UNIX device. Try to
* write a page into a buffer.
* The actual size of data written is specified with args[3].
*/
LispPTR COM_writepage(register LispPTR *args)
{
register int fd;
register int npage;
register char *bufp;
register int rval, count;
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[4]));
fd = LispNumToCInt(args[0]);
npage = LispNumToCInt(args[1]);
bufp = (char *)(Addr68k_from_LADDR(args[2]));
count = LispNumToCInt(args[3]);
sklp2:
TIMEOUT(rval = lseek(fd, (npage * FDEV_PAGE_SIZE), SEEK_SET));
if (rval == -1) {
if (errno == EINTR) goto sklp2; /* interrupted; retry */
*Lisp_errno = errno;
return (NIL);
}
/* OK to write the page. */
#ifdef BYTESWAP
word_swap_page((DLword *)bufp, (count + 3) >> 2);
#endif /* BYTESWAP */
wlp:
TIMEOUT(rval = write(fd, bufp, count));
if (rval == -1) {
if (errno == EINTR) goto wlp; /* interrupted; retry */
*Lisp_errno = errno;
#ifdef BYTESWAP
word_swap_page((DLword *)bufp, (count + 3) >> 2);
#endif /* BYTESWAP */
return (NIL);
}
#ifdef BYTESWAP
word_swap_page((DLword *)bufp, (count + 3) >> 2);
#endif /* BYTESWAP */
return (ATOM_T);
}
/*
* Name: COM_truncatefile
*
* Argument: LispPTR *args args[0]
* The Lisp integer representing a file descriptor
* of the file being truncated.
* args[1]
* The Lisp integer representing a requested length to be
* truncated.
* args[2]
* The place where the error number should be stored.
*
* Value: If succeed, returns a Lisp T, otherwise Lisp NIL.
*
* Side Effect: The length of the specified file will be the specified length.
*
* Description:
*
* The implementation of TRUNCATEFILE FDEV method for DSK and UNIX device. Try to
* truncate a file.
*/
LispPTR COM_truncatefile(register LispPTR *args)
{
register int fd;
register int length;
register int rval;
struct stat sbuf;
ERRSETJMP(NIL);
Lisp_errno = (int *)(Addr68k_from_LADDR(args[2]));
fd = LispNumToCInt(args[0]);
length = LispNumToCInt(args[1]);
TIMEOUT(rval = fstat(fd, &sbuf));
if (rval == -1) {
*Lisp_errno = errno;
return (NIL);
}
if ((sbuf.st_mode & S_IFREG) == 0) {
/*
* The request file is not a regular file. We don't need to
* truncate such file.
*/
return (ATOM_T);
}
if ((off_t)length != sbuf.st_size) {
#ifdef DOS
TIMEOUT(rval = chsize(fd, (off_t)length));
#else
TIMEOUT(rval = ftruncate(fd, (off_t)length));
#endif /* DOS */
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
/*
* TRUNCATEFILE FDEV method is invoked from FORCEOUTPUT Lisp function.
* Thus we have to sync the file state here.
*/
#ifndef DOS
TIMEOUT(rval = fsync(fd));
#endif
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
}
return (ATOM_T);
}
/*
* Name: COM_changedir
*
* Argument: LispPTR *args args[0]
* Full file directory name which is following the Xerox
* Lisp file naming convention. Including a host
* field.
*
* Value: If succeed, returns a Lisp T, otherwise Lisp NIL.
*
* Side Effect: If succeed, the current directory of this Lisp process will be changed.
*
* Description:
*
* Change the current directory of the process to the specified directory.
*/
LispPTR COM_changedir(register LispPTR *args)
{
register int dskp, rval;
char lfname[MAXPATHLEN + 5], dir[MAXPATHLEN], host[MAXNAMLEN];
#ifdef DOS
char drive[1], rawname[MAXNAMLEN];
int extlen;
#endif /* DOS */
ERRSETJMP(NIL);
Lisp_errno = &Dummy_errno;
LispStringLength(args[0], rval, dskp);
/*
* Because of the version number convention, Lisp pathname might
* be shorter than UNIX one. For THIN string, the difference
* is 2 bytes, for FAT string, 4 bytes. Add 1 byte for NULL
* terminating character.
*/
rval = dskp ? rval + 4 + 1 : rval + 2 + 1;
/* Add 5 for the host name field in Lisp format. */
if (rval > MAXPATHLEN + 5) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
#ifdef DOS
separate_host(lfname, host, drive);
#else
separate_host(lfname, host);
#endif /* DOS */
UPCASE(host);
if (strcmp(host, "DSK") == 0)
dskp = 1;
else if (strcmp(host, "UNIX") == 0)
dskp = 0;
else
return (NIL);
#ifdef DOS
if (!unixpathname(lfname, dir, 0, 0, drive, 0, 0)) return (NIL);
#else /* DOS */
if (!unixpathname(lfname, dir, 0, 0)) return (NIL);
#endif /* DOS */
if (dskp) {
/*
* If {DSK} device, the directory name can be specified in a case
* insensitive manner. We have to convert it into a right case.
*/
if (true_name(dir) != -1) return (NIL);
}
TIMEOUT(rval = chdir(dir));
if (rval != 0) return (NIL);
#ifdef DOS
if (*drive) {
if (*drive <= 'Z')
rval = _chdrive(*drive - ('A' - 1));
else
rval = _chdrive(*drive - ('a' - 1));
if (rval != 0) return (NIL);
}
#endif /* DOS */
return (ATOM_T);
}
/************************************************************************/
/* */
/* C O M _ g e t f r e e b l o c k */
/* */
/* */
/* */
/************************************************************************/
/*
* Name: COM_getfreeblock
*
* Argument: LispPTR *args args[0]
* Full sysout in Lisp format. Including a host field.
* args[1]
* The place where the available free block size will
* be stored.
*
* Value: If succeed, returns a Lisp T, otherwise Lisp NIL.
*
* Side Effect: None.
*
* Description:
*
* Examines the statistics of the file system on which the specified sysout file
* resides, and returns the number of available free blocks through the specified
* buffer.
* This routine is invoked just before the Lisp flushes its image on a sysout file so
* as to make sure the Lisp image is gracefully written on a sysout file. See
* \MAIKO.CHECKFREESPACE in MOD44IO.
* The version control based on a file naming convention is not applicable to a
* sysout file, even if the sysout file is specified on {DSK} device. The Lisp code
* strips the version field by invoking DSK_getfilename with NON recognition mode.
*/
LispPTR COM_getfreeblock(register LispPTR *args)
{
register int dskp, rval, *buf;
char lfname[MAXPATHLEN + 5], dir[MAXPATHLEN], host[MAXNAMLEN];
char name[MAXNAMLEN + 1], file[MAXPATHLEN], ver[VERSIONLEN];
#ifdef DOS
char drive[2];
struct diskfree_t sfsbuf;
#else
struct statvfs sfsbuf;
#endif
ERRSETJMP(NIL);
Lisp_errno = &Dummy_errno;
LispStringLength(args[0], rval, dskp);
/*
* Add 1 byte for NULL terminating character.
*/
rval += 1;
/* Add 5 for the host name field in Lisp format. */
if (rval > MAXPATHLEN + 5) FileNameTooLong(NIL);
LispStringToCString(args[0], lfname, MAXPATHLEN);
buf = (int *)(Addr68k_from_LADDR(args[1]));
#ifdef DOS
separate_host(lfname, host, drive);
#else
separate_host(lfname, host);
#endif /* DOS */
UPCASE(host);
if (strcmp(host, "DSK") == 0)
dskp = 1;
else if (strcmp(host, "UNIX") == 0)
dskp = 0;
else
return (NIL);
#ifdef DOS
if (!unixpathname(lfname, file, 0, 0, drive, 0, 0)) return (NIL);
#else /* DOS */
if (!unixpathname(lfname, file, 0, 0)) return (NIL);
#endif /* DOS */
if (!unpack_filename(file, dir, name, ver, 0)) return (NIL);
if (dskp) {
/*
* Although Lisp code guarantees the directory is an existing one,
* by calling DSK_getfilename, we check it again for safety.
*/
if (true_name(dir) != -1) return (NIL);
}
/*
* The specified file itself might be a file being created newly. Thus,
* we check the available block size, using the directory on which the file
* will be exist.
*/
#ifdef DOS
/* For DOS, we have to use either the disk drive the file
will be on, or the default drive. */
if (drive[0]) {
drive[1] = 0;
UPCASE(drive);
if (_dos_getdiskfree((unsigned)drive[0] - (int)'@', &sfsbuf))
return (NIL); /* call failed, so name is invalid */
*buf = sfsbuf.avail_clusters * sfsbuf.sectors_per_cluster * sfsbuf.bytes_per_sector;
} else {
if (_dos_getdiskfree(0, &sfsbuf)) return (NIL); /* call failed, so name is invalid */
*buf = sfsbuf.avail_clusters * sfsbuf.sectors_per_cluster * sfsbuf.bytes_per_sector;
}
#else
TIMEOUT(rval = statvfs(dir, &sfsbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (NIL);
}
*buf = sfsbuf.f_bavail;
#endif /* DOS */
return (ATOM_T);
}
/********************************************
Subroutines
********************************************/
/*
* Name: separate_version
*
* Argument: char *name UNIX file name. It is recommended to pass only
* "root file name" (i.e. without directory) as
* name argument, but full file name is also
* acceptable.
* char *ver The place where extracted version will be stored.
* init checkp If 1, whether the version field contains only
* numbers or not is checked. If 0, anything in the
* version field is stored in ver. If GENERATEFILE
* want to contain a wild card character in a version
* field, checkp should be 0.
*
* Value: void
*
* Side Effect: If name contains a valid version field, name and version will
* be set to name and version respectively.
*
* Description:
*
* Check if name contains the valid version field and if so, separate version
* field from name field. After this operation, name contains only name and
* version contains its version. If name does not contain a valid version
* field, version will be NULL string.
* If checkp is 1, the version field which contains only numeric characters are
* regarded valid.
*
*/
void separate_version(char *name, char *ver, int checkp)
{
register char *start, *end, *cp;
register int ver_no;
size_t len;
char ver_buf[VERSIONLEN];
if ((end = (char *)strchr(name, '~')) != (char *)NULL) {
start = end;
cp = end + 1;
while (*cp) {
if (*cp == '~') {
start = end;
end = cp;
}
cp++;
}
if (start != end && *(start - 1) == '.' && end == (cp - 1)) {
/*
* name ends in the form ".~###~". But we have to check
* ### are all numbers or not, if checkp is 1.
*/
len = (end - start) - 1;
strncpy(ver_buf, start + 1, len);
ver_buf[len] = '\0';
if (checkp) {
NumericStringP(ver_buf, YES, NO);
YES:
/*
* name contains a valid version field.
*/
*(start - 1) = '\0';
*end = '\0';
/*
* Use atoi to eliminate leading 0s.
*/
ver_no = atoi(start + 1);
sprintf(ver_buf, "%d", ver_no);
strcpy(ver, ver_buf);
return;
} else {
*(start - 1) = '\0';
strcpy(ver, ver_buf);
return;
}
}
} else if (strchr(name, '%')) {
strcpy(ver, "0");
return;
}
NO:
/* name does not contain a valid version field. */
*ver = '\0';
}
/*
* Name: unpack_filename
*
* Argument: char *file UNIX file name in the UNIX format. It must be an
* absolute path.
* char *dir The place where unpacked directory will be stored.
* char *name The place where unpacked "root file name" will be
* stored. "Root file name" contains the file name
* and the extension.
* char *ver The place where unpacked version will be stored.
* int checkp If 1, whether the version field contains only the
* numeric characters or not will be checked.
*
* Value: If succeed, returns 1, otherwise 0.
*
* Side Effect: dir, name, and ver will be replaced with the unpacked corresponding
* fields.
*
* Description:
*
* Unpack the specified UNIX full pathname to three components. If the file does
* not include a version field, NULL string will be stored as a version.
* unpack_filename assumes the pathname passed is a file, not a directory.
*
*/
int unpack_filename(char *file, char *dir, char *name, char *ver, int checkp)
{
register char *cp;
#ifdef DOS
if ((cp = (char *)max((UNSIGNED)strrchr(file, DIRSEP), (UNSIGNED)strrchr(file, UNIXDIRSEP))) == 0)
return (0);
if (file[1] == DRIVESEP) { /* There's a drive spec; copy it and ignore it from here on. */
*dir++ = *file++;
*dir++ = *file++;
}
#else /* DOS */
if ((cp = (char *)strrchr(file, UNIXDIRSEP)) == NULL) return (0);
#endif /* DOS */
if (cp == file) {
/* File is on a root directory. */
*dir = '/';
*(dir + 1) = '\0';
} else {
while (file != cp) *dir++ = *file++;
*dir = '\0';
}
strcpy(name, cp + 1);
separate_version(name, ver, checkp);
return (1);
}
/*
* Name: true_name
*
* Argument: char *path The pathname which follows the UNIX file naming
* convention and does not include any meta character.
* Whether a tail directory delimiter is included
* in path or not is not a matter. true_name handles
* both case correctly.
*
* Value: If the pathname is recognized as an existing directory, returns
* -1, recognized as an existing file, returns 1, otherwise 0.
*
* Side Effect: If succeed, the contents of path is replaced with the true name.
*
* Description:
*
* Try to find the file or directory specified with path. The search is case
* insensitive.
*
*/
int true_name(register char *path)
{
char dir[MAXPATHLEN];
char name[MAXNAMLEN];
#ifdef DOS
char drive[1];
#endif
register char *sp, *cp;
register int type, c;
if (strcmp(path, "/") == 0) return (-1);
#ifdef DOS
if (*(path + 1) == DRIVESEP) {
drive[0] = *path;
dir[0] = drive[0]; /* but copy it to the working dir string */
dir[1] = DRIVESEP;
dir[2] = '\0';
cp = path + 3; /* skip the drive spec & 1st dir delimiter */
} else {
#endif /* DOS */
*dir = '\0'; /*
* locate_file does not accept the directory with
* the trail delimiter. Thus, for the root
* directory, we have to set the null character
* as directory.
*/
cp = path + 1;
#ifdef DOS
}
#endif /* DOS */
/* If all there was was the root /, succeed easily */
if (strcmp((cp - 1), DIRSEPSTR) == 0) return (-1);
while (*cp) {
/*
* Copy the next subdirectory to name.
* And examine if it is an existing subdirectory or the file on the
* dir.
* At this point dir has been guaranteed to be exist.
*/
sp = name;
for (c = *cp++, *sp++ = c; c != '/' && c != '\0' && c != DIRSEP; c = *cp++, *sp++ = c) {}
if (c == '/') {
/* Remove the trail delimiter */
*(sp - 1) = '\0';
} else {
/* Move back cp to '\0' character to finish the loop. */
cp--;
}
/* Try to locate name on dir*/
if ((type = locate_file(dir, name)) == 0) {
/*
* No directory or file named name has been found on
* dir.
*/
return (0);
}
/*
* Now, the true name including the name has been set
* to dir by locate_file.
*/
}
strcpy(path, dir);
return (type);
}
/*
* Name: locate_file
*
* Argument: char *dir The existing directory name. Does not include
* the trail delimiter.
*
* char *name The name which is searched on dir.
*
* Value: If name is recognized as an existing directory, returns -1,
* recognized as an existing file, returns 1, otherwise 0.
*
* Side Effect: If succeed, the contents of dir is replaced with the true name
* including name.
*
* Description:
*
* Try to find the file or directory specified with name on the directory
* specified with dir. The search is case insensitive.
*
*/
static int locate_file(char *dir, char *name)
{
#ifdef DOS
char path[MAXPATHLEN];
char nb1[MAXNAMLEN], nb2[MAXNAMLEN];
register int type, len;
struct find_t dirp;
struct direct *dp;
/* First of all, recognize as if. */
sprintf(path, "%s\\%s", dir, name);
DIR_OR_FILE_P(path, type);
if (type != 0) {
strcpy(dir, path);
return (type);
}
return (0);
#else /* UNIX code follows */
char path[MAXPATHLEN];
char nb1[MAXNAMLEN], nb2[MAXNAMLEN];
register int type;
size_t len;
DIR *dirp;
struct dirent *dp;
/* First of all, recognize as if. */
sprintf(path, "%s/%s", dir, name);
DIR_OR_FILE_P(path, type);
if (type != 0) {
strcpy(dir, path);
return (type);
}
/* Next try with all lower case name. */
strcpy(nb1, name);
DOWNCASE(nb1);
sprintf(path, "%s/%s", dir, nb1);
DIR_OR_FILE_P(path, type);
if (type != 0) {
strcpy(dir, path);
return (type);
}
/* Next try with all upper case name. */
UPCASE(nb1);
sprintf(path, "%s/%s", dir, nb1);
DIR_OR_FILE_P(path, type);
if (type != 0) {
strcpy(dir, path);
return (type);
}
/* No way. Read dir and compare with name. */
len = strlen(name);
errno = 0;
TIMEOUT0(dirp = opendir(dir));
if (dirp == NULL) {
*Lisp_errno = errno;
return (0);
}
for (S_TOUT(dp = readdir(dirp)); dp != NULL || errno == EINTR;
errno = 0, S_TOUT(dp = readdir(dirp)))
if (dp) {
if (strlen(dp->d_name) == len) {
strcpy(nb2, dp->d_name);
UPCASE(nb2);
if (strcmp(nb1, nb2) == 0) {
sprintf(path, "%s/%s", dir, dp->d_name);
DIR_OR_FILE_P(path, type);
if (type != 0) {
strcpy(dir, path);
TIMEOUT(closedir(dirp));
return (type);
}
}
}
}
TIMEOUT(closedir(dirp));
return (0);
#endif /* DOS */
}
/*
* Name: make_directory
*
* Argument: char *dir The full directory name in UNIX format. It does
* not include a tail delimiter.
*
* Value: If succeed, returns 1, otherwise 0.
*
* Side Effect: The directory specified as dir will be created in the file system.
* If succeed, dir will be replaced with the true name of the directory.
*
* Description:
*
* Try to create a specified directory.
*
*/
static int make_directory(register char *dir)
{
register char *cp, *dp;
register int maked, rval;
char dir_buf[MAXPATHLEN];
maked = 0;
dp = dir_buf;
cp = dir;
#ifdef DOS
if (DRIVESEP == *(cp + 1)) {
*dp++ - *cp++; /* copy the drive letter and colon */
*dp++ = *cp++;
}
#endif /* DOS */
*dp++ = DIRSEP; /* For a root directory. */
cp++; /* Skip a root directory in dir. */
for (;;) {
switch (*cp) {
#ifdef DOS
case DIRSEP:
#endif
case '/':
case '\0':
*dp = '\0';
/*
* Now, dir_buf contains the absolute path to the next
* subdirectory or file. If one of the parent directories
* are created, we have to create a new subdirectory
* anyway. If all of the parent directories are existing
* directories, we have to check this subdirectory is an
* existing or not.
*/
if (maked) {
#ifdef DOS
TIMEOUT(rval = mkdir(dir_buf));
#else
TIMEOUT(rval = mkdir(dir_buf, 0777));
#endif /* DOS */
if (rval == -1) {
*Lisp_errno = errno;
return (0);
}
if (*cp == '\0') {
strcpy(dir, dir_buf);
return (1);
}
*dp++ = DIRSEP;
cp++;
} else {
switch (true_name(dir_buf)) {
case -1: /* Directory */
if (*cp == '\0') {
/* Every subdirectories are examined. */
strcpy(dir, dir_buf);
return (1);
} else {
dp = dir_buf;
while (*dp) dp++;
*dp++ = DIRSEP;
cp++;
}
break;
case 1: /* Regular File */
/*
* UNIX does not allow to make a directory
* and a file in the same name on a directory.
*/
return (0);
default:
/*
* Should handle other cases. (special file).
*/
#ifdef DOS
TIMEOUT(rval = mkdir(dir_buf));
#else
TIMEOUT(rval = mkdir(dir_buf, 0777));
#endif /* DOS */
if (rval == -1) {
*Lisp_errno = errno;
return (0);
}
if (*cp == '\0') return (1);
*dp++ = DIRSEP;
cp++;
maked = 1;
break;
}
}
break;
default: *dp++ = *cp++; break;
}
}
}
/*
* Name: FindHighestVersion
*
* Argument: FileName *varray
* The version array. It has to include at
* least one versioned file.
* FileName *mentry
* The place where an entry in varray corresponding
* to the highest versioned file will be stored.
* int max_no The place where the version number of the highest
* versioned file will be stored.
*
* Value: N/A
*
* Side Effect: mentry and max_no will be replaced with the highest versioned entry
* and highest version number respectively.
*
* Description:
*
* Find the highest versioned entry in varray. Varray has to include at least
* one versioned file, that is varray has to satisfy (!NoFileP(varray) &&
* !OnlyVersionlessP(varray)).
*
*/
#ifdef DOS
#define FindHighestVersion(varray, mentry, max_no) \
{ \
register FileName *centry; \
for (centry = varray, max_no = -1; centry->version_no != LASTVERSIONARRAY; centry++) { \
if (centry->version_no > max_no) { \
max_no = centry->version_no; \
mentry = centry; \
} \
} \
}
#else
#define FindHighestVersion(varray, mentry, max_no) \
{ \
register FileName *centry; \
for (centry = (varray), (max_no) = 0; centry->version_no != LASTVERSIONARRAY; centry++) { \
if (centry->version_no > (max_no)) { \
(max_no) = centry->version_no; \
(mentry) = centry; \
} \
} \
}
#endif /* DOS */
/*
* Name: FindLowestVersion
*
* Argument: FileName *varray
* The version array. It has to include at
* least one versioned file.
* FileName *mentry
* The place where an entry in varray corresponding
* to the lowest versioned file will be stored.
* int min_no The place where the version number of the highest
* versioned file will be stored.
*
* Value: N/A
*
* Side Effect: mentry and min_no will be replaced with the lowest versioned entry
* and lowest version number respectively.
*
* Description:
*
* Find the lowest versioned entry in varray. Varray has to include at least
* one versioned file, that is varray has to satisfy (!NoFileP(varray) &&
* !OnlyVersionlessP(varray)).
*
*/
#ifdef DOS
#define FindLowestVersion(varray, mentry, min_no) \
{ \
register FileName *centry; \
for (centry = varray, min_no = MAXVERSION; centry->version_no != LASTVERSIONARRAY; centry++) { \
if (centry->version_no < min_no) { \
min_no = centry->version_no; \
mentry = centry; \
} \
} \
}
#else
#define FindLowestVersion(varray, mentry, min_no) \
{ \
register FileName *centry; \
for (centry = (varray), (min_no) = MAXVERSION; centry->version_no != LASTVERSIONARRAY; centry++) { \
if (centry->version_no < (min_no) && centry->version_no != 0) { \
(min_no) = centry->version_no; \
(mentry) = centry; \
} \
} \
}
#endif /* DOS */
/*
* Name: FindSpecifiedVersion
*
* Argument: FileName *varray
* The version array. It has to include at
* least one versioned file.
* FileName *sentry
* The place where an entry in varray corresponding
* to the file which has the specified version will
* be stored.
* int ver_no The version number to be found.
*
* Value: N/A
*
* Side Effect: sentry will be replaced with the specified versioned entry.
*
* Description:
*
* Find the specified versioned entry in varray. Varray has to include at least
* one versioned file, that is varray has to satisfy (!NoFileP(varray) &&
* !OnlyVersionlessP(varray)).
*
*/
#define FindSpecifiedVersion(varray, sentry, ver_no) \
{ \
register FileName *centry; \
\
(sentry) = (FileName *)NULL; \
for (centry = varray; centry->version_no != LASTVERSIONARRAY; centry++) \
if (centry->version_no == (ver_no)) { \
(sentry) = centry; \
break; \
} \
}
/************************************************************************/
/* */
/* g e t _ v e r s i o n _ a r r a y */
/* */
/* Given a file, create an array of file/version# pairs for all */
/* versions of that file. Returns 1 on success, 0 on failure. */
/* */
/* dir UNIX directory specified absolutely. Caller must */
/* guarantee that the directory exists. */
/* file File name, optionally including a (unix) version */
/* varray Place to put the version array entries. */
/* cache Place to hold info about the new version array */
/* */
/* Read thru DIR and gather all files that match FILE into */
/* VARRAY. DIR's case must match existing directory's, but */
/* FILE name matching is case-insensitive. For UNIX, the */
/* versionless file is marked with a version# of 0; for DOS, */
/* version 0 is the back-up copy of the file. */
/* */
/************************************************************************/
static int get_version_array(char *dir, char *file, FileName *varray, CurrentVArray *cache)
{
#ifdef DOS
char lcased_file[MAXPATHLEN];
char old_file[MAXPATHLEN];
char name[MAXNAMLEN];
char ver[VERSIONLEN];
register FileName *svarray;
struct find_t dirp;
register struct direct *dp;
register int rval, drive = 0, isslash = 0;
struct stat sbuf;
int res;
/*
* First of all, prepare a lower cased file name for the case insensitive
* search. Also we have to separate file name from its version field.
*/
if (dir[1] == DRIVESEP) drive = dir[0];
if (strcmp(dir, "\\") == 0)
isslash = 1;
else if (drive && (strcmp(dir + 2, "\\") == 0))
isslash = 1;
if (!isslash)
strcpy(lcased_file, dir); /* Only add the dir if it's real */
else if (drive) {
lcased_file[0] = drive;
lcased_file[1] = DRIVESEP;
lcased_file[2] = '\0';
} else
*lcased_file = '\0';
/* strcpy(lcased_file, dir); removed when above code added 3/4/93 */
strcat(lcased_file, DIRSEPSTR);
strcat(lcased_file, file);
separate_version(lcased_file, ver, 1);
DOWNCASE(lcased_file);
/*************************************************/
/* First, look up the backup version of the file */
/*************************************************/
/* First, make the "backup-file-name" for this file */
make_old_version(old_file, lcased_file);
svarray = varray;
TIMEOUT(res = _dos_findfirst(old_file, _A_NORMAL | _A_SUBDIR, &dirp));
if (res == 0) {
strcpy(name, dirp.name);
strcpy(svarray->name, name);
svarray->version_no = 0;
svarray++;
}
/*******************************/
/* Now look up the file itself */
/*******************************/
TIMEOUT(res = _dos_findfirst(lcased_file, _A_NORMAL | _A_SUBDIR, &dirp));
/* if (res != 0)
{
*Lisp_errno = errno;
return(0);
}
*/
for (; res == 0; S_TOUT(res = _dos_findnext(&dirp))) {
strcpy(name, dirp.name);
separate_version(name, ver, 1);
DOWNCASE(name);
strcpy(svarray->name, dirp.name);
if (*ver == '\0') {
/* Versionless file */
svarray->version_no = 1;
} else {
/*
* separator_version guarantees ver is a numeric
* string.
*/
svarray->version_no = atoi(ver);
}
svarray++;
}
/*
* The last entry of varray is indicated by setting LASTVERSIONARRAY into
* version_no field.
*/
svarray->version_no = LASTVERSIONARRAY;
/*
* If more than one files have been stored in varray, we store the name
* without version in the last marker entry.
*/
if (!NoFileP(varray)) {
strcpy(name, varray->name);
separate_version(name, ver, 1);
strcpy(svarray->name, name);
}
return (1);
#else
/* UNIX version-array builder */
char lcased_file[MAXNAMLEN];
char name[MAXNAMLEN];
char ver[VERSIONLEN];
register FileName *svarray;
register DIR *dirp;
register struct dirent *dp;
/* Used in commented out code below:
register int rval;
struct stat sbuf;
*/
/*
* First of all, prepare a lower cased file name for the case insensitive
* search. Also we have to separate file name from its version field.
*/
strcpy(lcased_file, file);
separate_version(lcased_file, ver, 1);
DOWNCASE(lcased_file);
/*
TIMEOUT(rval = stat(dir, &sbuf));
if (rval == -1) {
*Lisp_errno = errno;
return(0);
}
*/
/*
* If the cached version array is still valid, we can return immediately.
*/
/*
* Cache mechanism is not used now, because of the bug of Sun OS.
* Sometimes just after unlinking a file on a directory, the st_mtime
* of the directory does not change. This will make Maiko believe
* cached version array is still valid, although it is already invalid.
* sync(2) has no effect on such case.
*/
/*
if ((sbuf.st_mtime == cache->mtime) && strcmp(dir, cache->path) == 0
&& strcmp(lcased_file, cache->file) == 0) return(1);
*/
errno = 0;
TIMEOUT0(dirp = opendir(dir));
if (dirp == NULL) {
*Lisp_errno = errno;
return (0);
}
for (S_TOUT(dp = readdir(dirp)), svarray = varray; dp != NULL || errno == EINTR;
errno = 0, S_TOUT(dp = readdir(dirp)))
if (dp) {
strcpy(name, dp->d_name);
separate_version(name, ver, 1);
DOWNCASE(name);
if (strcmp(name, lcased_file) == 0) {
/*
* This file can be regarded as a same file in Lisp sense.
*/
strcpy(svarray->name, dp->d_name);
if (*ver == '\0') {
/* Versionless file */
svarray->version_no = 0;
} else {
/*
* separator_version guarantees ver is a numeric
* string.
*/
svarray->version_no = atoi(ver);
}
svarray++;
}
}
/*
* The last entry of varray is indicated by setting LASTVERSIONARRAY into
* version_no field.
*/
svarray->version_no = LASTVERSIONARRAY;
/*
* If more than one files have been stored in varray, we store the name
* without version in the last marker entry.
*/
if (!NoFileP(varray)) {
strcpy(name, varray->name);
separate_version(name, ver, 1);
strcpy(svarray->name, name);
}
/*
* Update cache information.
*/
/*
strcpy(cache->path, dir);
strcpy(cache->file, lcased_file);
cache->mtime = sbuf.st_mtime;
*/
TIMEOUT(closedir(dirp));
return (1);
#endif /* DOS */
}
/*
* Name: maintain_version
*
* Argument: char *file The full file name in UNIX format.
* FileName *varray
* The version array.
* int forcep If 1, a versionless file will be linked to version
* 1 file when there is no other files having the same
* name.
*
* Value: If succeed, returns 1, otherwise 0.
*
* Side Effect: None.
*
* Description:
*
* Try to make sure that the version control based on the name convention
* is well maintained. The versionless file corresponding to file will be
* maintained. The second argument varray is an optional. If it is not
* NULL pointer, maintain_version maintains the version control using the
* version array. If varray is NULL pointer, a version array is re collected
* by this routine.
* Currently, forcep is 1 only if maintain_version is called from COM_openfile
* to maintain the directory on which a file is being created.
*/
static int maintain_version(char *file, FileName *varray, int forcep)
{
char dir[MAXPATHLEN], fname[MAXNAMLEN], ver[VERSIONLEN];
char old_file[MAXPATHLEN], vless[MAXPATHLEN];
int highest_p;
register int rval, max_no;
register FileName *entry;
if (varray == (FileName *)NULL) {
if (unpack_filename(file, dir, fname, ver, 1) == 0) return (0);
/*
* We have to make sure that dir is the existing directory.
*/
if (true_name(dir) != -1) return (0);
if (get_version_array(dir, fname, VersionArray, &VArrayInfo) == 0) return (0);
varray = VersionArray;
}
if (NoFileP(varray)) {
/*
* We don't need to care about such case that there is no such file
* or an only versionless file exists.
*/
return (1);
}
if (OnlyVersionlessP(varray)) {
if (forcep) {
/*
* If forcep, we link the versionless file to the version
* 1 file.
*/
#ifndef DOS
get_versionless(varray, vless, dir);
ConcNameAndVersion(vless, "1", fname);
TIMEOUT(rval = link(vless, fname));
if (rval == -1) {
*Lisp_errno = errno;
return (0);
}
#endif /* DOS */
}
return (1);
}
/*
* At this point, we are sure that at least one file with version number
* exists. Thus, FindHighestVersion works fine from now on.
*/
if (get_versionless(varray, vless, dir) == 0) {
/*
* There is not a versionless file, but at least one versioned file.
* Thus, the thing we have to do is to link a versionless file
* to the existing highest versioned file.
*/
FindHighestVersion(varray, entry, max_no);
ConcDirAndName(dir, entry->name, old_file);
/*
* The versionless file should have the same case name as the old
* file.
*/
#ifndef DOS
strcpy(fname, entry->name);
separate_version(fname, ver, 1);
ConcDirAndName(dir, fname, vless);
TIMEOUT(rval = link(old_file, vless));
if (rval == -1) {
*Lisp_errno = errno;
return (0);
}
#endif /* DOS */
return (1);
}
if (check_vless_link(vless, varray, old_file, &highest_p) == 0) return (0);
if (*old_file == '\0') {
/*
* The versionless file is not linked to any file in varray.
* Thus, we have to link the versionless file to the file which
* is versioned one higher than the existing highest version.
*/
FindHighestVersion(varray, entry, max_no);
sprintf(ver, "%d", max_no + 1);
/*
* The old file should have the same case name as the versionless
* file.
*/
#ifndef DOS
ConcNameAndVersion(vless, ver, old_file);
TIMEOUT(rval = link(vless, old_file));
if (rval == -1) {
*Lisp_errno = errno;
return (0);
}
#endif /* DOS */
return (1);
}
if (highest_p) {
/*
* The versionless file has been already linked to the highest
* versioned file. Thus, there is nothing to do.
*/
return (1);
} else {
/*
* Although the versionless file is linked to a file in varray,
* the file is not the highest versioned file. We have to unlink
* the wrongly linked versionless file, and link the highest versioned
* file to a versionless file.
*/
TIMEOUT(rval = unlink(vless));
if (rval == -1) {
*Lisp_errno = errno;
return (0);
}
FindHighestVersion(varray, entry, max_no);
ConcDirAndName(dir, entry->name, old_file);
/*
* The versionless file should have the same case name as the old
* file.
*/
#ifndef DOS
strcpy(fname, entry->name);
separate_version(fname, ver, 1);
ConcDirAndName(dir, fname, vless);
TIMEOUT(rval = link(old_file, vless));
if (rval == -1) {
*Lisp_errno = errno;
return (0);
}
#endif /* DOS */
return (1);
}
}
/*
* Name: get_versionless
*
* Argument: FileName *varray
* The version array already filled by
* get_version_array routine.
* char *file The place where the full file name of the found
* versionless file will be stored.
* char *dir Directory absolute path following the UNIX
* file naming convention on which varray
* has been gathered.
*
* Value: If a versionless file found, returns 1, otherwise 0.
*
* Side Effect: file will be replaced with the full name of the found versionless
* file when it is found.
*
* Description:
*
* Accepts a version array and try to find a versionless file within it.
*
*/
static int get_versionless(FileName *varray, char *file, char *dir)
{
#ifdef DOS
return (0);
#endif /* DOS */
if (NoFileP(varray)) return (0);
while (varray->version_no != LASTVERSIONARRAY) {
if (varray->version_no == 0) {
ConcDirAndName(dir, varray->name, file);
return (1);
} else
varray++;
}
return (0);
}
/*
* Name: check_vless_link
*
* Argument: char *vless The full file name of the versionless file in
* UNIX format.
* FileName *varray
* The version array already filled by get_version_array.
* char *to_file
* The place where the full file name of the file
* to which the versionless file is hard linked will
* be stored.
* int *highest_p
* If to_file is the highest versioned file in varray,
* highest_p will point to 1, otherwise, 0.
*
* Value: If succeed, returns 1, otherwise, 0.
*
* Side Effect: to_file will be replaced with the full file name to which the
* versionless file is hard linked. highest_p will be replaced with
* 1 or 0.
*
* Description:
*
* Examines the link status of a specified versionless file. If there is a file
* to which the versionless file is hard linked in a version array, it will be
* stored in to_file. If there is no such file, to_file will be NULL string.
* When hard linked file is found, if highest_p is 1, the file is the highest
* versioned file in the version array. This is the such case that a versionless
* file is well maintained by DSK code.
* Notice that even if to_file is NULL, it does not mean that the versionless file
* is not hard linked to any file. It only means that there is no file to which the
* versionless file is hard linked in the version array. The versionless file
* might be hard linked to the other file entirely differently named.
*
*/
static int check_vless_link(char *vless, FileName *varray, char *to_file, int *highest_p)
{
register int rval, max_no, found;
ino_t vless_ino;
struct stat sbuf;
char dir[MAXPATHLEN], name[MAXNAMLEN], ver[VERSIONLEN];
register FileName *max_entry, *linked_entry;
TIMEOUT(rval = stat(vless, &sbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (0);
}
if (sbuf.st_nlink == 1) {
/* There is no file to which vless is hard linked. */
*to_file = '\0';
return (1);
}
vless_ino = sbuf.st_ino;
if (unpack_filename(vless, dir, name, ver, 1) == 0) return (0);
max_no = 0;
found = 0;
max_entry = NULL;
linked_entry = NULL;
while (varray->version_no != LASTVERSIONARRAY) {
if (varray->version_no > max_no) {
max_no = varray->version_no;
max_entry = varray;
}
if (!found && varray->version_no != 0) {
ConcDirAndName(dir, varray->name, name);
TIMEOUT(rval = stat(name, &sbuf));
if (rval != 0) {
*Lisp_errno = errno;
return (0);
}
if (sbuf.st_ino == vless_ino) {
found = 1;
linked_entry = varray;
}
}
varray++;
}
if (linked_entry != NULL) {
if (linked_entry == max_entry) {
*highest_p = 1;
} else {
*highest_p = 0;
}
strcpy(to_file, name);
} else {
*to_file = '\0';
}
return (1);
}
/*
* Name: get_old
*
* Argument: char *dir Directory absolute path following the UNIX
* file naming convention on which varray
* has been gathered.
* FileName *varray
* The version array already filled by
* get_version_array routine.
* char *afile File name. It might include a version field.
* The version field have to be following the
* UNIX convention, that is "name.~##~", not
* Xerox Lisp one. The versionless file is
* also acceptable.
* afile is also used as a place where the true
* name of the recognized file will be stored.
* char *vfile The place where the versioned name of the
* recognized file will be stored.
*
* Value: If succeed, returns 1, otherwise 0.
*
* Side Effect: afile will be replaced with the full name of the recognized file
* when the recognition succeed. vfile will be replaced with the
* versioned full name of the recognized file when the recognition
* succeed.
*
* Description:
*
* Accepts a version array and file name, and recognize a file in the version
* array with the old mode.
* "Old" file means the "newest existing" file.
* Notice that, afile and vfile are not necessary same name, because the versionless
* file is recognized as an old file, afile will hold the true versionless file name,
* although vfile will hold the versioned file name anyway.
* afile could be used as the real UNIX pathname to access the recognized file, on
* the other hand, vfile could be used as file name from which the file name
* which should be returned to Lisp could be produced.
*
*/
static int get_old(char *dir, FileName *varray, char *afile, char *vfile)
{
char name[MAXPATHLEN], vless[MAXPATHLEN], to_file[MAXPATHLEN];
char ver[VERSIONLEN], vbuf[VERSIONLEN];
register int ver_no, max_no;
int highest_p;
register FileName *entry;
/* "Old" file have to be existing, thus varray should not be empty. */
if (NoFileP(varray)) return (0);
strcpy(name, afile);
separate_version(name, ver, 1);
if (get_versionless(varray, vless, dir) == 0) {
/*
* There is no versionless file, but at least one versioned
* file exists.
*/
if (*ver == '\0') {
/*
* No version is specified. The highest versioned file
* is an old file.
*/
FindHighestVersion(varray, entry, max_no);
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else
return (0);
}
} else if (OnlyVersionlessP(varray)) {
/*
* There is only versionless file in varray.
* If the specified version is 1 the versionless file is regarded
* as version 1 file.
*/
if (*ver == '\0') {
/*
* No version is specified. The versionless file is dealt
* with as version 1.
*/
ConcNameAndVersion(vless, "1", vfile);
strcpy(afile, vless);
return (1);
} else {
ver_no = atoi(ver);
if (ver_no == 1) {
/*
* Version 1 is specified. The versionless file is
* dealt with as a version 1 file.
*/
ConcNameAndVersion(name, "1", afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* Other versions than 1 are not recognized as an old
* file.
*/
return (0);
}
}
} else {
if (check_vless_link(vless, varray, to_file, &highest_p) == 0) return (0);
if (*to_file == '\0') {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is not linked
* to any file in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The one higher than an
* existing maximum version is dealt with as the
* old version, and it should be a version of the
* link missing versionless file.
*/
FindHighestVersion(varray, entry, max_no);
sprintf(vbuf, "%d", max_no + 1);
ConcNameAndVersion(vless, vbuf, vfile);
strcpy(afile, vless);
return (1);
} else {
/* A version is specified. */
ver_no = atoi(ver);
FindHighestVersion(varray, entry, max_no);
if (ver_no == max_no + 1) {
/*
* If the version is one higher than the
* existing highest version is specified, it
* is dealt with as a version of the link
* missing versionless file.
*/
sprintf(vbuf, "%d", ver_no);
ConcNameAndVersion(vless, vbuf, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* We have to walk through varray and try
* to find the file with the specified version.
*/
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else
return (0);
}
}
} else {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is linked to
* one of files in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The highest versioned file
* in varray is an old file.
*/
FindHighestVersion(varray, entry, max_no);
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else
return (0);
}
}
}
}
/*
* Name: get_oldeset
*
* Argument: char *dir Directory absolute path following the UNIX
* file naming convention on which varray
* has been gathered.
* FileName *varray
* The version array already filled by
* get_version_array routine.
* char *afile File name. It might include a version field.
* The version field have to be following the
* UNIX convention, that is "name.~##~", not
* Xerox Lisp one. The versionless file is
* also acceptable.
* afile is also used as a place where the true
* name of the recognized file will be stored.
* char *vfile The place where the versioned name of the
* recognized file will be stored.
*
* Value: If succeed, returns 1, otherwise 0.
*
* Side Effect: afile will be replaced with the full name of the recognized file
* when the recognition succeed. vfile will be replaced with the
* versioned full name of the recognized file when the recognition
* succeed.
*
* Description:
*
* Accepts a version array and filename, and recognize a file in the version
* array with oldest mode.
* "Oldest" file means the "oldest existing" file.
* Notice that, afile and vfile are not necessary same name, because the versionless
* file is recognized as an old file, afile will hold the true versionless file name,
* although vfile will hold the versioned file name anyway.
* afile could be used as the real UNIX pathname to access the recognized file, on
* the other hand, vfile could be used as file name from which the file name
* which should be returned to Lisp could be produced.
*
*/
static int get_oldest(char *dir, FileName *varray, char *afile, char *vfile)
{
char name[MAXPATHLEN], vless[MAXPATHLEN], to_file[MAXPATHLEN];
char ver[VERSIONLEN], vbuf[VERSIONLEN];
register int ver_no, min_no;
int highest_p;
register FileName *entry;
/* "Oldest" file have to be existing, thus varray should not be empty. */
if (NoFileP(varray)) return (0);
strcpy(name, afile);
separate_version(name, ver, 1);
if (get_versionless(varray, vless, dir) == 0) {
/*
* There is no versionless file, but at least one versioned
* file exists.
*/
if (*ver == '\0') {
/*
* No version is specified. The lowest versioned file
* is an oldest file.
*/
FindLowestVersion(varray, entry, min_no);
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else
return (0);
}
} else if (OnlyVersionlessP(varray)) {
/*
* There is only versionless file in varray.
* If the specified version is 1 the versionless file is regarded
* as version 1 file.
*/
if (*ver == '\0') {
/*
* No version is specified. The versionless file is dealt
* with as version 1.
*/
ConcNameAndVersion(vless, "1", vfile);
strcpy(afile, vless);
return (1);
} else {
ver_no = atoi(ver);
if (ver_no == 1) {
/*
* Version 1 is specified. The versionless file is
* dealt with as a version 1 file.
*/
ConcNameAndVersion(name, "1", afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* Other versions than 1 are not recognized as an
* oldest file.
*/
return (0);
}
}
} else {
if (check_vless_link(vless, varray, to_file, &highest_p) == 0) return (0);
if (*to_file == '\0') {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is not linked
* to any file in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The lowest version is
* dealt with as the oldest version.
*/
FindLowestVersion(varray, entry, min_no);
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else {
/* A version is specified. */
ver_no = atoi(ver);
FindHighestVersion(varray, entry, min_no);
if (ver_no == min_no + 1) {
/*
* If the version is one higher than the
* existing highest version is specified, it
* is dealt with as a version of the link
* missing versionless file.
*/
sprintf(vbuf, "%d", ver_no);
ConcNameAndVersion(vless, vbuf, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* We have to walk through varray and try
* to find the file with the specified version.
*/
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else
return (0);
}
}
} else {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is linked to
* one of files in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The lowest versioned file
* in varray is an old file.
*/
FindLowestVersion(varray, entry, min_no);
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else
return (0);
}
}
}
}
/*
* Name: get_new
*
* Argument: char *dir Directory absolute path following the UNIX
* file naming convention on which varray
* has been gathered.
* FileName *varray
* The version array already filled by
* get_version_array routine.
* char *afile File name. It might include a version field.
* The version field have to be following the
* UNIX convention, that is "name.~##~", not
* Xerox Lisp one. The versionless file is
* also acceptable.
* afile is also used as a place where the true
* name of the recognized file will be stored.
* char *vfile The place where the versioned name of the
* recognized file will be stored.
*
* Value: If succeed, returns 1, otherwise 0.
*
* Side Effect: afile will be replaced with the full name of the recognized file
* when the recognition succeed. vfile will be replaced with the
* versioned full name of the recognized file when the recognition
* succeed.
*
* Description:
*
* Accepts a version array and filename, and recognize a file in the version
* array with new mode.
* "New" file means the "not yet existing" file with the version which is one
* higher than the current highest version.
* Notice that, afile and vfile are not necessary same name, because the versionless
* file is recognized as an old file, afile will hold the true versionless file name,
* although vfile will hold the versioned file name anyway.
* afile could be used as the real UNIX pathname to access the recognized file, on
* the other hand, vfile could be used as file name from which the file name
* which should be returned to Lisp could be produced.
*
*/
static int get_new(char *dir, FileName *varray, char *afile, char *vfile)
{
char name[MAXPATHLEN], vless[MAXPATHLEN], to_file[MAXPATHLEN];
char ver[VERSIONLEN], vbuf[VERSIONLEN];
register int ver_no, max_no;
int highest_p;
register FileName *entry;
strcpy(name, afile);
separate_version(name, ver, 1);
#ifndef DOS
if (NoFileP(varray)) {
/*
* If there is no file with such name, "new" file is always
* recognized.
*/
if (*ver == '\0' || strcmp(ver, "1") == 0)
#endif /* DOS */
{
/*
* If version is not specified or 1 is specified,
* we can return versionless file as afile.
*/
ConcNameAndVersion(name, "1", afile);
ConcDirAndName(dir, afile, vfile);
ConcDirAndName(dir, name, afile);
return (1);
}
#ifndef DOS
else {
/*
* A version other than 1 is specified. "New" file
* is recognized as if.
*/
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
}
if (get_versionless(varray, vless, dir) == 0) {
/*
* There is no versionless file, but at least one versioned
* file exists.
*/
if (*ver == '\0') {
/*
* No version is specified. The new file is one higher than
* the existing highest version.
*/
FindHighestVersion(varray, entry, max_no);
sprintf(vbuf, "%d", max_no + 1);
/*
* We will use the file name of the existing highest
* versioned file as the name of the new file, so that
* new file is as the same case as old.
*/
strcpy(name, entry->name);
separate_version(name, ver, 1);
ConcDirAndName(dir, name, afile);
ConcNameAndVersion(afile, vbuf, vfile);
strcpy(afile, vfile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
}
/*
* There is not a file with the specified version in varray.
* The specified file can be recognized as if.
* Most user will hope to create a new file in same case as
* old. One of case sensitive names in the files are stored
* in the trail marker entry in varray by get_version_array
* routine.
* We will use it, although we cannot say all of the gathered
* files has the name in same case.
*/
while (varray->version_no != LASTVERSIONARRAY) varray++;
ConcNameAndVersion(varray->name, ver, afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
} else if (OnlyVersionlessP(varray)) {
/*
* There is only versionless file in varray.
* If the specified version is 1 the versionless file is regarded
* as version 1 file.
*/
if (*ver == '\0') {
/*
* No version is specified. The versionless file is dealt
* with as version 1. Thus new version is 2.
*/
ConcNameAndVersion(vless, "2", vfile);
strcpy(afile, vfile);
return (1);
} else {
ver_no = atoi(ver);
if (ver_no == 1) {
/*
* Version 1 is specified. The versionless file is
* dealt with as a version 1 file.
*/
ConcNameAndVersion(name, "1", afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* Other versions than 1 are recognized as if.
*/
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
}
} else {
if (check_vless_link(vless, varray, to_file, &highest_p) == 0) return (0);
if (*to_file == '\0') {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is not linked
* to any file in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The two higher than an
* existing maximum version is dealt with as the
* new version, because the one higher version is
* dealt with as the actual version of the link
* missing versionless file.
*/
FindHighestVersion(varray, entry, max_no);
sprintf(vbuf, "%d", max_no + 2);
ConcNameAndVersion(vless, vbuf, vfile);
strcpy(afile, vfile);
return (1);
} else {
/* A version is specified. */
ver_no = atoi(ver);
FindHighestVersion(varray, entry, max_no);
if (ver_no == max_no + 1) {
/*
* If the version is one higher than the
* existing highest version is specified, it
* is dealt with as a version of the link
* missing versionless file.
*/
sprintf(vbuf, "%d", ver_no);
ConcNameAndVersion(vless, vbuf, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* We have to walk through varray and try
* to find the file with the specified version.
*/
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
}
/*
* There is not a file with the specified
* version in varray. The specified file can
* be recognized as if.
* Most user will hope to create a new file in
* same case as old. One of case sensitive
* names in the files are stored in the trail
* marker entry in varray by get_version_array
* routine.
* We will use it, although we cannot say all
* of the gathered files has the name in same
* case.
*/
while (varray->version_no != LASTVERSIONARRAY) varray++;
ConcNameAndVersion(varray->name, ver, afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
}
} else {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is linked to
* one of files in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The one higher than the
* existing highest versioned file in varray is a
* new file.
*/
FindHighestVersion(varray, entry, max_no);
sprintf(vbuf, "%d", max_no + 1);
/*
* We will use the name of the highest versioned file
* as the name of the new file.
*/
strcpy(vless, entry->name);
separate_version(vless, ver, 1);
ConcDirAndName(dir, vless, afile);
ConcNameAndVersion(afile, vbuf, vfile);
strcpy(afile, vfile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
}
/*
* There is not a file with the specified
* version in varray. The specified file can
* be recognized as if.
* Most user will hope to create a new file in
* same case as old. We will use the name of
* the highest versioned file as the name of the
* new file.
*/
FindHighestVersion(varray, entry, max_no);
strcpy(vless, entry->name);
separate_version(vless, vbuf, 1);
ConcDirAndName(dir, vless, afile);
ConcNameAndVersion(afile, ver, vfile);
strcpy(afile, vfile);
return (1);
}
}
}
#endif /* DOS */
}
/*
* Name: get_old_new
*
* Argument: char *dir Directory absolute path following the UNIX
* file naming convention on which varray
* has been gathered.
* FileName *varray
* The version array already filled by
* get_version_array routine.
* char *afile File name. It might include a version field.
* The version field have to be following the
* UNIX convention, that is "name.~##~", not
* Xerox Lisp one. The versionless file is
* also acceptable.
* afile is also used as a place where the true
* name of the recognized file will be stored.
* char *vfile The place where the versioned name of the
* recognized file will be stored.
*
* Value: If succeed, returns 1, otherwise 0.
*
* Side Effect: afile will be replaced with the full name of the recognized file
* when the recognition succeed. vfile will be replaced with the
* versioned full name of the recognized file when the recognition
* succeed.
*
* Description:
*
* Accepts a version array and filename, and recognize a file in the version
* array with old/new mode.
* "Old/new" file means the "newest existing" or "not yet existing" file with the
* version which is one higher than the current highest version.
* Notice that, afile and vfile are not necessary same name, because the versionless
* file is recognized as an old file, afile will hold the true versionless file name,
* although vfile will hold the versioned file name anyway.
* afile could be used as the real UNIX pathname to access the recognized file, on
* the other hand, vfile could be used as file name from which the file name
* which should be returned to Lisp could be produced.
*
*/
static int get_old_new(char *dir, FileName *varray, char *afile, char *vfile)
{
char name[MAXPATHLEN], vless[MAXPATHLEN], to_file[MAXPATHLEN];
char ver[VERSIONLEN], vbuf[VERSIONLEN];
register int ver_no, max_no;
int highest_p;
register FileName *entry;
strcpy(name, afile);
separate_version(name, ver, 1);
if (NoFileP(varray)) {
/*
* If there is no file with such name, "old/new" file is always
* recognized.
*/
if (*ver == '\0' || strcmp(ver, "1") == 0) {
/*
* If version is not specified or 1 is specified,
* we can return versionless file as afile.
*/
ConcNameAndVersion(name, "1", afile);
ConcDirAndName(dir, afile, vfile);
ConcDirAndName(dir, name, afile);
return (1);
} else {
/*
* A version other than 1 is specified. "New" file
* is recognized as if.
*/
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
}
if (get_versionless(varray, vless, dir) == 0) {
/*
* There is no versionless file, but at least one versioned
* file exists.
*/
if (*ver == '\0') {
/*
* No version is specified. The highest versioned file
* is an old file.
*/
FindHighestVersion(varray, entry, max_no);
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
}
/*
* There is not a file with the specified version in varray.
* The specified file can be recognized as if.
* Most user will hope to create a new file in same case as
* old. One of case sensitive names in the files are stored
* in the trail marker entry in varray by get_version_array
* routine.
* We will use it, although we cannot say all of the gathered
* files has the name in same case.
*/
while (varray->version_no != LASTVERSIONARRAY) varray++;
ConcNameAndVersion(varray->name, ver, afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
} else if (OnlyVersionlessP(varray)) {
/*
* There is only versionless file in varray.
* If the specified version is 1 the versionless file is regarded
* as version 1 file.
*/
if (*ver == '\0') {
/*
* No version is specified. The versionless file is dealt
* with as version 1.
*/
ConcNameAndVersion(vless, "1", vfile);
strcpy(afile, vless);
return (1);
} else {
ver_no = atoi(ver);
if (ver_no == 1) {
/*
* Version 1 is specified. The versionless file is
* dealt with as a version 1 file.
*/
ConcNameAndVersion(name, "1", afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* Other versions than 1 are recognized as if.
*/
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
}
} else {
if (check_vless_link(vless, varray, to_file, &highest_p) == 0) return (0);
if (*to_file == '\0') {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is not linked
* to any file in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The one higher than an
* existing maximum version is dealt with as the
* old version, and it should be a version of the
* link missing versionless file.
*/
FindHighestVersion(varray, entry, max_no);
sprintf(vbuf, "%d", max_no + 1);
ConcNameAndVersion(vless, vbuf, vfile);
strcpy(afile, vless);
return (1);
} else {
/* A version is specified. */
ver_no = atoi(ver);
FindHighestVersion(varray, entry, max_no);
if (ver_no == max_no + 1) {
/*
* If the version is one higher than the
* existing highest version is specified, it
* is dealt with as a version of the link
* missing versionless file.
*/
sprintf(vbuf, "%d", ver_no);
ConcNameAndVersion(vless, vbuf, vfile);
strcpy(afile, vless);
return (1);
} else {
/*
* We have to walk through varray and try
* to find the file with the specified version.
*/
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
}
/*
* There is not a file with the specified
* version in varray. The specified file can
* be recognized as if.
* Most user will hope to create a new file in
* same case as old. One of case sensitive
* names in the files are stored in the trail
* marker entry in varray by get_version_array
* routine.
* We will use it, although we cannot say all
* of the gathered files has the name in same
* case.
*/
while (varray->version_no != LASTVERSIONARRAY) varray++;
ConcNameAndVersion(varray->name, ver, afile);
ConcDirAndName(dir, afile, vfile);
strcpy(afile, vfile);
return (1);
}
}
} else {
/*
* There is a versionless file in varray and at least one
* versioned file exists. The versionless file is linked to
* one of files in varray.
*/
if (*ver == '\0') {
/*
* No version is specified. The highest versioned file
* in varray is an old file.
*/
FindHighestVersion(varray, entry, max_no);
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
} else {
/*
* A version is specified. We have to walk thorough
* varray and try to find the file with the specified
* version.
*/
ver_no = atoi(ver);
FindSpecifiedVersion(varray, entry, ver_no);
if (entry != NULL) {
ConcDirAndName(dir, entry->name, afile);
strcpy(vfile, afile);
return (1);
}
/*
* There is not a file with the specified
* version in varray. The specified file can
* be recognized as if.
* Most user will hope to create a new file in
* same case as old. We will use the name of
* the highest versioned file as the name of the
* new file.
*/
FindHighestVersion(varray, entry, max_no);
strcpy(vless, entry->name);
separate_version(vless, vbuf, 1);
ConcDirAndName(dir, vless, afile);
ConcNameAndVersion(afile, ver, vfile);
strcpy(afile, vfile);
return (1);
}
}
}
}