1
0
mirror of https://github.com/Interlisp/maiko.git synced 2026-01-25 11:36:31 +00:00
Files
Interlisp.maiko/src/dsk.c
Bruce Mitchener 1c28005420 Remove support for Ultrix. (#28)
Ultrix hasn't seen an update since 1995 and the DEC3100 hardware
that this was originally written for was released in 1989.

It isn't likely that anyone has Ultrix running on hardware where
they would also want to run this.
2020-12-12 18:15:22 -08:00

4559 lines
132 KiB
C

/* $Id: dsk.c,v 1.4 2001/12/24 01:09:01 sybalsky Exp $ (C) Copyright Venue, All Rights Reserved */
static char *id = "$Id: dsk.c,v 1.4 2001/12/24 01:09:01 sybalsky Exp $ Copyright (C) Venue";
/************************************************************************/
/* */
/* (C) Copyright 1989-1995 Venue. All Rights Reserved. */
/* Manufactured in the United States of America. */
/* */
/************************************************************************/
#include "version.h"
#if defined(MACOSX) || defined(FREEBSD) || defined(LINUX) || defined(OS5)
#include <string.h>
#endif
#include <sys/types.h>
#ifndef DOS
#include <sys/param.h>
#include <sys/file.h>
#ifndef OS5
#include <strings.h>
#endif /* OS5 Solaris doesn't need strings.h */
#ifndef SYSVONLY
#ifndef MACOSX
#ifndef FREEBSD
#ifndef OS5
#include <sys/dir.h>
#endif /* OS5 */
#endif /* FREEBSD */
#endif /* MACOSX */
#endif /* SYSVONLY */
#include <sys/stat.h>
#include <sys/time.h>
#ifdef sun
#include <sys/vfs.h>
#endif /* sun */
#ifdef HPUX
#include <sys/vfs.h>
#endif /* HPUX */
#else /* DOS */
#include <string.h>
#include <direct.h>
#include <stdlib.h>
#include <unistd.h> /* get R_OK, F_OK symbols */
#include <dos.h>
#include <sys/stat.h>
#include <time.h>
#include <io.h>
#define index strchr
#define rindex strrchr
#define MAXPATHLEN _MAX_PATH
#define MAXNAMLEM _MAX_PATH
#define L_SET SEEK_SET
#define alarm(x) 0
#endif /* DOS */
#ifdef RISCOS
#include <dirent.h>
#include <unistd.h>
#define direct dirent
#define d_namlen d_reclen
#endif /* RISCOS */
#ifdef OS5
#define index strchr
#define rindex strrchr
#define L_SET SEEK_SET
#endif /* OS5 */
#ifdef ISC
#include <dirent.h>
#include <unistd.h>
#define direct dirent
#define d_namlen d_reclen
#define L_SET SEEK_SET
#endif /* ISC */
#if defined(SYSVONLY) || defined(MACOSX) || defined(FREEBSD) || defined(OS5)
#include <dirent.h>
#include <unistd.h>
#define direct dirent
#define d_namlen d_reclen
#define d_fileno d_ino
#ifndef LINUX
#define L_SET SEEK_SET
#endif
#endif /* SYSVONLY */
#ifndef DOS
#include <pwd.h>
#endif /* DOS */
#include <setjmp.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#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"
#if defined(OSF1) || defined(MACOSX) || defined(FREEBSD)
#include <sys/mount.h>
#else
#ifdef AIX
#if (!defined(AIXPS2) && !defined(HPUX))
#ifdef LINUX
#include <sys/vfs.h>
#else
#include <sys/statfs.h>
#endif
#endif /* AIXPS2 | HPUX */
#define d_fileno d_ino
#endif /* AIX */
#endif /* OSF1 | MACOSX | FREEBSD */
#ifdef GCC386
#include "inlnPS2.h"
#endif /* GCC386 */
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 int 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 = (int)cp - (int)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 direct *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 direct *)NULL || errno == EINTR;
errno = 0, S_TOUT(dp = readdir(dirp)))
if (dp) {
if (ino == (ino_t)dp->d_fileno) 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);
}
#ifdef USE_UTIME
TIMEOUT(rval = utime(file, time));
#else
TIMEOUT(rval = utimes(file, time));
#endif
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;
#ifdef USE_UTIME
TIMEOUT(rval = utime(file, time));
#else
TIMEOUT(rval = utimes(file, time));
#endif /* HPUX */
#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), L_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), L_SET));
if (rval == -1) {
if (errno == EINTR) goto sklp2; /* interrupted; retry */
*Lisp_errno = errno;
return (NIL);
}
#ifdef DEMO
/* Do nothing if this is a demo emulator--not allowed to write files. */
#else
/* 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 */
#endif /* DEMO */
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) {
#ifndef ISC /* ISC has no ftruncate; sigh */
#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);
}
#endif /* ISC */
/*
* TRUNCATEFILE FDEV method is invoked from FORCEOUTPUT Lisp function.
* Thus we have to sync the file state here.
*/
#ifdef INDIGO
TIMEOUT(rval = fsync(fd));
#elif OS5
TIMEOUT(rval = fsync(fd));
#elif DOS
#elif SYSVONLY
TIMEOUT(rval = fsync(fd));
#else
TIMEOUT(rval = fsync(fd));
#endif /* INDIGO */
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];
char drive[2];
#if defined(OS5)
struct statvfs sfsbuf;
#else
#ifndef AIXPS2
#ifndef DOS
struct statfs sfsbuf;
#endif /* DOS */
#endif /* AIXPS2 */
#endif /* OS5 */
#ifdef DOS
struct diskfree_t sfsbuf;
#endif /* DOS */
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
#if OSF1
TIMEOUT(rval = statfs(dir, &sfsbuf, sizeof(struct statfs)));
if (rval <= 0) {
#elif defined(ISC)
TIMEOUT(rval = statfs(dir, &sfsbuf, sizeof(struct statfs), 0));
if (rval != 0) {
#elif defined(LINUX)
TIMEOUT(rval = statfs(dir, &sfsbuf));
if (rval != 0) {
#elif defined(MACOSX) || defined(FREEBSD)
TIMEOUT(rval = statfs(dir, &sfsbuf));
if (rval != 0) {
#elif HPUX
TIMEOUT(rval = statfs(dir, &sfsbuf));
if (rval != 0) {
#elif INDIGO
TIMEOUT(rval = statfs(dir, &sfsbuf, sizeof(struct statfs), 0));
if (rval != 0) {
#elif defined(SYSVONLY)
TIMEOUT(rval = statfs(dir, &sfsbuf, sizeof(struct statfs), 0));
if (rval != 0) {
#elif defined(OS5)
TIMEOUT(rval = statvfs(dir, &sfsbuf));
if (rval != 0) {
#else
#ifndef AIXPS2
TIMEOUT(rval = statfs(dir, &sfsbuf));
#endif /* AIXPS2 */
if (rval != 0) {
#endif /* OSF1 */
*Lisp_errno = errno;
return (NIL);
}
#if defined(RS6000)
*buf = (sfsbuf.f_bavail) * 4; /* AIX 3.1 returns no. of 4K blocks */
#elif defined(ISC)
*buf = (sfsbuf.f_bfree) / 2; /* ISC claims 1K blocks, but it's really 512b */
#elif defined(SYSVONLY) || defined(OS5)
*buf = sfsbuf.f_bfree;
#elif (!defined(AIXPS2))
*buf = sfsbuf.f_bavail;
#else
*buf = 200000; /* FAKE - pretend we have 200,000 blocks free! */
#endif /* RS6000 */
#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 len, ver_no;
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 = (int)end - (int)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)rindex(file, DIRSEP), (UNSIGNED)rindex(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 *)rindex(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], drive[1];
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, len;
DIR *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);
}
/* 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 (dp->d_namlen == 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 highset 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 direct *dp;
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 varrray,
* 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);
}
}
}
}