461 lines
12 KiB
C
461 lines
12 KiB
C
/*
|
|
Copyright (c) 1990-1999 Info-ZIP. All rights reserved.
|
|
|
|
See the accompanying file LICENSE, version 1999-Oct-05 or later
|
|
(the contents of which are also included in zip.h) for terms of use.
|
|
If, for some reason, both of these files are missing, the Info-ZIP license
|
|
also may be found at: ftp://ftp.cdrom.com/pub/infozip/license.html
|
|
*/
|
|
#pragma library
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sc.h>
|
|
#include <peek.h>
|
|
#include <lub.h>
|
|
#include <fdb.h>
|
|
#include <fsa.h>
|
|
#include "theos/stat.h"
|
|
|
|
/* replacement for standard library functions stat and fstat */
|
|
|
|
int _stat_(struct stat* st, struct fdb* fdb);
|
|
int _dstat_(struct stat* st);
|
|
|
|
#define peekucb() peeknuc()
|
|
|
|
/* map THEOS protection code to Unix modes */
|
|
|
|
unsigned short _tm2um_(char protect)
|
|
{
|
|
unsigned short umask = 0;
|
|
|
|
if (!(protect & _FDB_READ_PROTECT))
|
|
umask = S_IRUSR|S_IRGRP;
|
|
|
|
if (!(protect & _FDB_WRITE_PROTECT))
|
|
umask |= S_IWUSR|S_IWGRP;
|
|
|
|
if (!(protect & _FDB_EXECUTE_PROTECT))
|
|
umask |= S_IXUSR|S_IXGRP;
|
|
|
|
if (!(protect & _FDB_ERASE_PROTECT))
|
|
umask |= S_IEUSR|S_IEGRP;
|
|
|
|
if (!(protect & _FDB_SHARED_READ_PROTECT)) {
|
|
if (_osmajor > 3)
|
|
umask |= S_IROTH|S_IXOTH;
|
|
else
|
|
umask |= S_IROTH;
|
|
}
|
|
|
|
if (!(protect & _FDB_SHARED_WRITE_PROTECT))
|
|
umask |= S_IWOTH;
|
|
|
|
if (!(protect & _FDB_MODIFIED)) {
|
|
if (_osmajor > 3)
|
|
umask |= S_IMODF;
|
|
else
|
|
umask |= S_IXOTH;
|
|
}
|
|
|
|
if (protect & _FDB_NOT_HIDDEN)
|
|
umask |= S_INHID;
|
|
|
|
return umask;
|
|
}
|
|
|
|
/* map Unix modes to THEOS protections */
|
|
|
|
char _um2tm_(unsigned short mask)
|
|
{
|
|
char protect = 0;
|
|
|
|
if (!(mask & (S_IRUSR|S_IRGRP)))
|
|
protect |= _FDB_READ_PROTECT;
|
|
|
|
if (!(mask & (S_IWUSR|S_IWGRP)))
|
|
protect |= _FDB_WRITE_PROTECT;
|
|
|
|
if (!(mask & (S_IXUSR|S_IXGRP)))
|
|
protect |= _FDB_EXECUTE_PROTECT;
|
|
|
|
if (!(mask & (S_IEUSR|S_IEGRP)))
|
|
protect |= _FDB_ERASE_PROTECT;
|
|
|
|
if (_osmajor < 4) {
|
|
if (!(mask & S_IROTH))
|
|
protect |= _FDB_SHARED_READ_PROTECT;
|
|
} else {
|
|
if (!(mask & (S_IROTH|S_IXOTH)))
|
|
protect |= _FDB_SHARED_READ_PROTECT;
|
|
}
|
|
|
|
if (!(mask & S_IWOTH))
|
|
protect |= _FDB_SHARED_WRITE_PROTECT;
|
|
|
|
if (mask & S_IMODF && _osmajor > 3)
|
|
protect |= _FDB_MODIFIED;
|
|
|
|
if (mask & S_INHID && _osmajor > 3)
|
|
protect |= _FDB_NOT_HIDDEN;
|
|
|
|
return protect;
|
|
}
|
|
|
|
/* root directory stat */
|
|
|
|
static int rdirstat(const char* fn, struct stat *st)
|
|
{
|
|
register char* p = strchr(fn, ':');
|
|
char drive;
|
|
|
|
drive = p ? p[1] : 'S';
|
|
|
|
if (drive >= 'a' && drive <= 'Z')
|
|
drive -= 0x40;
|
|
|
|
memset(st, 0, sizeof(struct stat));
|
|
|
|
if (getlub(drive - 'A') != 255) {
|
|
st->st_org = _FDB_STAT_DIRECTORY;
|
|
st->st_mode = S_IFDIR|S_IRUSR|S_IWUSR|S_IROTH|S_IWOTH;
|
|
st->st_nlink = 1;
|
|
st->st_dev = st->st_rdev = drive - 'A';
|
|
st->st_uid = st->st_gid = getuid();
|
|
st->st_protect = _FDB_ERASE_PROTECT;
|
|
return 0;
|
|
}
|
|
errno = _errnum = ENOENT;
|
|
_errarg = fn;
|
|
return -1;
|
|
}
|
|
|
|
#ifdef LOCATE_BUG
|
|
|
|
/* locate fails when stating a file in root dir from a directory with a
|
|
* relative path. Workaround by setting directory to root dir
|
|
* getting the file directory block, then restoring the current directory.
|
|
*/
|
|
|
|
struct fdb* __locate(const char* fn, char* buf, short* drv)
|
|
{
|
|
struct fdb* fdb;
|
|
char buf2[FILENAME_MAX];
|
|
char cwd[FILENAME_MAX];
|
|
char drive[3];
|
|
char* p;
|
|
char* q;
|
|
|
|
/* return if file found */
|
|
if (fdb = _locate(fn, buf, drv))
|
|
return fdb;
|
|
|
|
/* if file name does not contain a path delimiter it really does not exist.
|
|
*/
|
|
strcpy(buf2, fn);
|
|
|
|
if ((p = strrchr(buf2, '/')) == NULL)
|
|
return NULL;
|
|
|
|
/* get drive name from file path */
|
|
q = strrchr(buf2, ':');
|
|
|
|
/* cat drive name if any to directory path */
|
|
if (q) {
|
|
strncpy(drive, q, 2);
|
|
drive[2] = '\0';
|
|
strcpy(p, q);
|
|
} else
|
|
*p = '\0';
|
|
/* save current directory */
|
|
getcwd(cwd, FILENAME_MAX);
|
|
/* chdir to directory path */
|
|
chdir(buf2);
|
|
/* get File Directory Block */
|
|
p = strrchr(fn, '/');
|
|
fdb = _locate(p + 1, buf, drv);
|
|
/* restore current directory */
|
|
chdir(cwd);
|
|
return fdb;
|
|
}
|
|
|
|
#undef _locate
|
|
#define _locate() __locate()
|
|
|
|
/* same cause, same consequence for fopen and open.
|
|
*/
|
|
|
|
FILE* _fopen(const char* fn, const char* mode)
|
|
{
|
|
FILE* fp;
|
|
char buf[FILENAME_MAX];
|
|
short drv;
|
|
|
|
/* prepend a path to current dir to avoid use of default library */
|
|
if (*fn != '.' && *fn != '/') {
|
|
strcpy(buf, "./");
|
|
strcat(buf, fn);
|
|
return fopen(buf, mode);
|
|
}
|
|
|
|
if (fp = fopen(fn, mode))
|
|
return fp;
|
|
|
|
/* see comment for _locate */
|
|
if (_locate(fn, buf, &drv)) {
|
|
fn = strrchr(fn, '/');
|
|
return fopen(fn, mode);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#undef open
|
|
int open(const char*, int, ...);
|
|
|
|
int __open(const char* fn, int mode)
|
|
{
|
|
int fd;
|
|
char buf[FILENAME_MAX];
|
|
short drv;
|
|
|
|
/* prepend a path to current dir to avoid use of default library */
|
|
if (*fn != '.' && *fn != '/') {
|
|
strcpy(buf, "./");
|
|
strcat(buf, fn);
|
|
return open(buf, mode);
|
|
}
|
|
|
|
if ((fd = open(fn, mode)) != EOF)
|
|
return fd;
|
|
|
|
/* see comment for _locate */
|
|
if (_locate(fn, buf, &drv)) {
|
|
fn = strrchr(fn, '/');
|
|
if (fn)
|
|
return open(fn, mode);
|
|
}
|
|
return EOF;
|
|
}
|
|
#endif
|
|
|
|
/* replacement for standard file stat */
|
|
|
|
int _stat(const char *_fn, struct stat *st)
|
|
{
|
|
char buf[FILENAME_MAX], buf2[FILENAME_MAX], buf3[FILENAME_MAX];
|
|
register struct fdb* fdb;
|
|
register char* p;
|
|
register char* fn;
|
|
|
|
fn = strcpy(buf3, _fn);
|
|
|
|
if (p = strrchr(fn, ':'))
|
|
*p = 0;
|
|
|
|
/* on current drive ./:d and .:m point to current dir
|
|
* on another drive to root directory, workaround to avoid it */
|
|
|
|
if (! strcmp(fn, "/") || ! strcmp(fn, ".") || ! strcmp(fn, "./")) {
|
|
if (p == NULL) {
|
|
/* current dir on current drive */
|
|
fn = getcwd(buf2, FILENAME_MAX);
|
|
/* getcwd returns NULL on root dir on drive S */
|
|
if (fn == NULL)
|
|
fn = strcpy(buf2, "/:S");
|
|
/* getcwd returns /:d on root dir on any other drive */
|
|
if (fn[1] == ':')
|
|
return rdirstat(fn, st);
|
|
} else {
|
|
*p = ':';
|
|
return rdirstat(fn, st);
|
|
}
|
|
if (p)
|
|
*p = ':';
|
|
} else {
|
|
if (p)
|
|
*p = ':';
|
|
if (*fn != '.' && *fn != '/') {
|
|
strcpy(buf2, "./");
|
|
fn = strcat(buf2, fn);
|
|
}
|
|
}
|
|
|
|
if (buf2 != fn)
|
|
strcpy(buf2, fn);
|
|
/* remove trailing slash before optional disk name */
|
|
if (p = strrchr(buf2, '/')) {
|
|
if (p[1] == ':') {
|
|
*p = p[1];
|
|
p[1] = p[2];
|
|
p[2] = p[3];
|
|
} else if (p[1] == '\0')
|
|
*p = '\0';
|
|
}
|
|
/* if fn is a file get file directory block structure and device */
|
|
if (fdb = _locate(buf2, buf, &st->st_dev)) {
|
|
/* is it a file from another user... */
|
|
if (strchr(buf2, '\\')
|
|
/* a public system file... */
|
|
|| fdb->fileowner == 0
|
|
/* or a file from the current user account ? */
|
|
|| fdb->fileowner == getuid())
|
|
/* yes, return stat */
|
|
return _stat_(st, fdb);
|
|
else {
|
|
/* no, say file doesn't exist */
|
|
errno = _errnum = ENOENT;
|
|
_errarg = fn;
|
|
return -1;
|
|
}
|
|
}
|
|
/* else should be a device, get device number from device name */
|
|
st->st_rdev = st->st_dev = _lub_name(*fn == ':' ? fn+1 : fn);
|
|
/* if it is really a device return device status */
|
|
if (st->st_dev != -1 && getlub(st->st_dev) != 255)
|
|
return _dstat_(st);
|
|
/* neither an existing file or a device name, return EOF */
|
|
st->st_rdev = st->st_dev = 0;
|
|
errno = _errnum = ENOENT;
|
|
_errarg = fn;
|
|
return -1;
|
|
}
|
|
|
|
/* replacement for fstat */
|
|
|
|
int _fstat(int fd, struct stat *st)
|
|
{
|
|
unsigned short fsanum;
|
|
struct fsa fsa;
|
|
register FILE *fp;
|
|
int status;
|
|
register int i;
|
|
register char *p;
|
|
|
|
if (fd < FOPEN_MAX) {
|
|
fp = &stdin[fd];
|
|
/* get File Save Area number */
|
|
if (_fcntl(fp,1,0) & 0x80) {
|
|
fsanum = (unsigned short) _fcntl(fp,83,0);
|
|
st->st_dev = (unsigned short) _fcntl(fp,5,0);
|
|
|
|
if (st->st_dev >= A_DISK && st->st_dev <= Z_DISK) {
|
|
/* if opened file is a disk file */
|
|
/* copy far fsa in protected segment to local fsa */
|
|
for (i = 0, fsanum *= sizeof(fsa), p = (char *) &fsa;
|
|
i < (sizeof(fsa));
|
|
i++, fsanum++, p++)
|
|
*p = _peekfsa((char *) fsanum);
|
|
/* build stat structure from fsa */
|
|
status = _stat_(st, (struct fdb*) &fsa);
|
|
/* get blocksize */
|
|
if ((st->st_blksize = _fcntl(fp,817,0)) == 0)
|
|
st->st_blksize = BUFSIZ;
|
|
return status;
|
|
}
|
|
/* return device status */
|
|
return _dstat_(st);
|
|
}
|
|
}
|
|
errno = _errnum = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
static int _isprt(int dev)
|
|
{
|
|
return IS_PRT_LUB(dev);
|
|
}
|
|
|
|
/* device stat */
|
|
|
|
int _dstat_(st)
|
|
register struct stat* st;
|
|
{
|
|
register struct ucb* ucb;
|
|
|
|
ucb = getucb(st->st_dev);
|
|
st->st_ino = 0;
|
|
if (st->st_dev <= Z_DISK
|
|
|| (st->st_dev >= TAPE1 && st->st_dev <= TAPE4)) {
|
|
st->st_mode = S_IFBLK | S_IWUSR | S_IRUSR;
|
|
if (peekucb(&ucb->devowner) == 255)
|
|
st->st_mode |= S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH;
|
|
} else {
|
|
st->st_mode = S_IFCHR | S_IWUSR;
|
|
if (_isprt(st->st_dev))
|
|
st->st_mode |= S_IRUSR;
|
|
if (peekucb(&ucb->devowner) == 255) {
|
|
st->st_mode |= S_IWGRP | S_IWOTH;
|
|
if (_isprt(st->st_dev))
|
|
st->st_mode |= S_IRGRP | S_IROTH;
|
|
}
|
|
}
|
|
st->st_nlink = 1;
|
|
st->st_uid = st->st_gid = getuid();
|
|
st->st_size = 0;
|
|
st->st_atime = st->st_mtime = st->st_ctime = 0;
|
|
st->st_rlen = 0;
|
|
st->st_klen = 0;
|
|
st->st_grow = 0;
|
|
st->st_blksize = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* regular file stat */
|
|
|
|
int _stat_(st, fdb)
|
|
register struct stat* st;
|
|
register struct fdb* fdb;
|
|
{
|
|
st->st_rdev = st->st_dev;
|
|
st->st_ino = 0;
|
|
st->st_org = fdb->filestat;
|
|
|
|
/* map fdb file status to stat mode */
|
|
switch (fdb->filestat) {
|
|
case _FDB_STAT_LIBRARY: st->st_mode = S_IFLIB; break;
|
|
case _FDB_STAT_DIRECTORY: st->st_mode = S_IFDIR; break;
|
|
case _FDB_STAT_STREAM: st->st_mode = S_IFREG; break;
|
|
case _FDB_STAT_RELATIVE: st->st_mode = S_IFREL; break;
|
|
case _FDB_STAT_KEYED: st->st_mode = S_IFKEY; break;
|
|
case _FDB_STAT_INDEXED: st->st_mode = S_IFIND; break;
|
|
case _FDB_STAT_RANDOM: st->st_mode = S_IFRND; break;
|
|
case _FDB_STAT_PROGRAM: st->st_mode = S_IFR16; break;
|
|
case _FDB_STAT_16_BIT_PROGRAM: st->st_mode = S_IFP16; break;
|
|
case _FDB_STAT_32_BIT_PROGRAM: st->st_mode = S_IFP32; break;
|
|
}
|
|
|
|
/* map theos file protection codes to stat mode */
|
|
st->st_mode |= _tm2um_(st->st_protect = fdb->protect);
|
|
st->st_nlink = 1;
|
|
st->st_uid = st->st_gid = fdb->fileowner;
|
|
st->st_size = fdb->filesize;
|
|
st->st_atime = st->st_mtime = st->st_ctime = getfiledate(fdb);
|
|
st->st_blksize = 0;
|
|
/* specific theos information */
|
|
st->st_rlen = fdb->reclen;
|
|
st->st_klen = fdb->keylen;
|
|
st->st_grow = fdb->filegrow;
|
|
return 0;
|
|
}
|
|
|
|
#include <direct.h>
|
|
|
|
/* standard diropen fails on path endung with a '/', workaround */
|
|
|
|
struct dirent* _opendir(const char* dirpath)
|
|
{
|
|
int l;
|
|
char dirp[FILENAME_MAX];
|
|
struct dirent* dir;
|
|
|
|
if (dirpath && (l = strlen(dirpath))) {
|
|
if (dirpath[l - 1] == '/') {
|
|
strcpy(dirp, dirpath);
|
|
dirp[l - 1] = '\0';
|
|
return opendir(dirp);
|
|
}
|
|
}
|
|
return opendir(dirpath);
|
|
}
|