summaryrefslogtreecommitdiffstats
path: root/usr.bin/doscmd/cwd.c
diff options
context:
space:
mode:
authordyson <dyson@FreeBSD.org>1997-08-09 01:43:15 +0000
committerdyson <dyson@FreeBSD.org>1997-08-09 01:43:15 +0000
commit305573cb2990c5d329d149cef5a3b5533b1e8fd9 (patch)
treedf06304b637358dbe8a006fdb7a6ea5955fee179 /usr.bin/doscmd/cwd.c
parentdede28832bba6a9de7a428ff58df92439bddbc9c (diff)
downloadFreeBSD-src-305573cb2990c5d329d149cef5a3b5533b1e8fd9.zip
FreeBSD-src-305573cb2990c5d329d149cef5a3b5533b1e8fd9.tar.gz
Add our doscmd to the tree. This is a result of work from BSDI, and
a group of dos emulator developers. Submitted by: Jonathan Lemon <jlemon@americantv.com> Obtained from: BSDI
Diffstat (limited to 'usr.bin/doscmd/cwd.c')
-rw-r--r--usr.bin/doscmd/cwd.c970
1 files changed, 970 insertions, 0 deletions
diff --git a/usr.bin/doscmd/cwd.c b/usr.bin/doscmd/cwd.c
new file mode 100644
index 0000000..df27ff2
--- /dev/null
+++ b/usr.bin/doscmd/cwd.c
@@ -0,0 +1,970 @@
+/*
+ * Copyright (c) 1992, 1993, 1996
+ * Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Berkeley Software
+ * Design, Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * BSDI cwd.c,v 2.2 1996/04/08 19:32:25 bostic Exp
+ *
+ * $Id: cwd.c,v 1.6 1996/09/23 09:59:23 miff Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "doscmd.h"
+
+#define D_REDIR 0x0080000 /* XXX - ack */
+#define D_TRAPS3 0x0200000
+
+typedef struct {
+ u_char *path;
+ u_char *cwd;
+ int len;
+ int maxlen;
+ int read_only:1;
+} Path_t;
+
+typedef struct Name_t {
+ u_char *real;
+ struct Name_t *next;
+ u_char name[9];
+ u_char ext[4];
+} Name_t;
+
+
+#define MAX_DRIVE 26
+
+static Path_t paths[MAX_DRIVE] = { 0, };
+static Name_t *names;
+
+extern int diskdrive;
+
+/*
+ * Initialize the drive to be based at 'base' in the BSD filesystem
+ */
+void
+init_path(int drive, u_char *base, u_char *dir)
+{
+ Path_t *d;
+
+ if (drive < 0 || drive >= MAX_DRIVE)
+ return;
+
+ debug(D_TRAPS3, "init_path(%d, %s, %s)\n", drive, base, dir);
+
+ d = &paths[drive];
+
+ if (d->path)
+ free(d->path);
+
+ if ((d->path = ustrdup(base)) == NULL)
+ fatal("strdup in init_path for %c:%s: %s", drive + 'A', base,
+ strerror(errno));
+
+ if (d->maxlen < 2) {
+ d->maxlen = 128;
+ if ((d->cwd = (u_char *)malloc(d->maxlen)) == NULL)
+ fatal("malloc in init_path for %c:%s: %s", drive + 'A', base,
+ strerror(errno));
+ }
+
+ d->cwd[0] = '\\';
+ d->cwd[1] = 0;
+ d->len = 1;
+ if (dir) {
+ if (ustrncmp(base, dir, ustrlen(base)) == 0)
+ dir += ustrlen(base);
+ while (*dir == '/')
+ ++dir;
+
+ while (*dir) {
+ u_char dosname[15];
+ u_char realname[256];
+ u_char *r = realname;;
+
+ while ((*r = *dir) && *dir++ != '/') {
+ ++r;
+ }
+ *r = 0;
+ while (*dir == '/')
+ ++dir;
+
+ dosname[0] = drive + 'A';
+ dosname[1] = ':';
+ real_to_dos(realname, &dosname[2]);
+
+ if (dos_setcwd(dosname)) {
+ fprintf(stderr, "Failed to CD to directory %s in %s\n",
+ dosname, d->cwd);
+ }
+ }
+ }
+}
+
+/*
+ * Mark this drive as read only
+ */
+void
+dos_makereadonly(int drive)
+{
+
+ if (drive < 0 || drive >= MAX_DRIVE)
+ return;
+ paths[drive].read_only = 1;
+}
+
+/*
+ * Return read-only status of drive
+ */
+int
+dos_readonly(int drive)
+{
+
+ if (drive < 0 || drive >= MAX_DRIVE)
+ return (0);
+ debug(D_REDIR, "dos_readonly(%d) -> %d\n", drive, paths[drive].read_only);
+ return (paths[drive].read_only);
+}
+
+/*
+ * Return DOS's idea of the CWD for drive
+ * Return 0 if the drive specified is not mapped (or bad)
+ */
+u_char *
+dos_getcwd(int drive)
+{
+
+ if (drive < 0 || drive >= MAX_DRIVE)
+ return (0);
+ debug(D_REDIR, "dos_getcwd(%d) -> %s\n", drive, paths[drive].cwd);
+ return (paths[drive].cwd);
+}
+
+/*
+ * Return DOS's idea of the CWD for drive
+ * Return 0 if the drive specified is not mapped (or bad)
+ */
+u_char *
+dos_getpath(int drive)
+{
+
+ if (drive < 0 || drive >= MAX_DRIVE)
+ return (0);
+ debug(D_REDIR, "dos_getpath(%d) -> %s\n", drive, paths[drive].path);
+ return (paths[drive].path);
+}
+
+/*
+ * Fix up a DOS path name. Strip out all '.' and '..' entries, turn
+ * '/' into '\\' and convert all lowercase to uppercase.
+ * Returns 0 on success or DOS errno
+ */
+int
+dos_makepath(u_char *where, u_char *newpath)
+{
+ int drive;
+ u_char **dirs;
+ u_char *np;
+ Path_t *d;
+ u_char tmppath[1024];
+
+ if (where[0] != '\0' && where[1] == ':') {
+ drive = *where - 'A';
+ *newpath++ = *where++;
+ *newpath++ = *where++;
+ } else {
+ drive = diskdrive;
+ *newpath++ = diskdrive + 'A';
+ *newpath++ = ':';
+ }
+
+ if (drive < 0 || drive >= MAX_DRIVE) {
+ debug(D_REDIR,"drive %c invalid\n",drive + 'A');
+ return (DISK_DRIVE_INVALID);
+ }
+
+ d = &paths[drive];
+ if (d->cwd == NULL) {
+ debug(D_REDIR,"no cwd for drive %c\n",drive + 'A');
+ return (DISK_DRIVE_INVALID);
+ }
+
+ debug(D_REDIR, "dos_makepath(%d, %s)\n", drive, where);
+
+ np = newpath;
+ if (*where != '\\' && *where != '/') {
+ ustrcpy(tmppath, d->cwd);
+ if (d->cwd[1])
+ ustrcat(tmppath, (u_char *)"/");
+ ustrcat(tmppath, where);
+ } else {
+ ustrcpy(tmppath, where);
+ }
+
+ dirs = get_entries(tmppath);
+ if (dirs == NULL)
+ return (PATH_NOT_FOUND);
+
+ np = newpath;
+ while (*dirs) {
+ u_char *dir = *dirs++;
+ if (*dir == '/' || *dir == '\\') {
+ np = newpath + 1;
+ newpath[0] = '\\';
+ } else if (dir[0] == '.' && dir[1] == 0) {
+ ;
+ } else if (dir[0] == '.' && dir[1] == '.' && dir[2] == '\0') {
+ while (np[-1] != '/' && np[-1] != '\\')
+ --np;
+ if (np - 1 > newpath)
+ --np;
+ } else {
+ if (np[-1] != '\\')
+ *np++ = '\\';
+ while (*np = *dir++)
+ ++np;
+ }
+ }
+ *np = 0;
+
+ return (0);
+}
+
+/*
+ * Set DOS's idea of the CWD for drive to be where.
+ * Returns DOS errno on failuer.
+ */
+int
+dos_setcwd(u_char *where)
+{
+ u_char newpath[1024];
+ u_char realpath[1024];
+ int drive;
+ struct stat sb;
+ Path_t *d;
+ int error;
+
+ debug(D_REDIR, "dos_setcwd(%s)\n", where);
+
+ error = dos_makepath(where, newpath);
+ if (error)
+ return (error);
+
+ error = dos_to_real_path(newpath, realpath, &drive);
+ if (error)
+ return (error);
+
+ if (ustat(realpath, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ return (PATH_NOT_FOUND);
+ if (uaccess(realpath, R_OK | X_OK))
+ return (PATH_NOT_FOUND);
+
+ d = &paths[drive];
+ d->len = ustrlen(newpath + 2);
+
+ if (d->len + 1 > d->maxlen) {
+ free(d->cwd);
+ d->maxlen = d->len + 1 + 32;
+ d->cwd = (u_char *)malloc(d->maxlen);
+ if (d->cwd == NULL)
+ fatal("malloc in dos_setcwd for %c:%s: %s",
+ drive + 'A', newpath, strerror(errno));
+ }
+ ustrcpy(d->cwd, newpath + 2);
+ return (0);
+}
+
+/*
+ * Given a DOS path dospath and a drive, convert it to a BSD pathname
+ * and store the result in realpath.
+ * Return DOS errno on failure.
+ */
+int
+dos_to_real_path(u_char *dospath, u_char *realpath, int *drivep)
+{
+ Path_t *d;
+ u_char newpath[1024];
+ u_char *rp;
+ int error;
+ u_char **dirs;
+ u_char *dir;
+ int drive;
+
+ debug(D_REDIR, "dos_to_real_path(%s)\n", dospath);
+
+ if (dospath[0] != '\0' && dospath[1] == ':') {
+ drive = *dospath - 'A';
+ dospath++;
+ dospath++;
+ } else {
+ drive = diskdrive;
+ }
+
+ d = &paths[drive];
+ if (d->cwd == NULL)
+ return (DISK_DRIVE_INVALID);
+
+ ustrcpy(realpath, d->path);
+
+ rp = realpath;
+ while (*rp)
+ ++rp;
+
+ ustrcpy(newpath, dospath);
+
+ dirs = get_entries(newpath);
+ if (dirs == NULL)
+ return (PATH_NOT_FOUND);
+
+ /*
+ * Skip the leading /
+ * There are no . or .. entries to worry about either
+ */
+
+ while (dir = *++dirs) {
+ *rp++ = '/';
+ dos_to_real(dir, rp);
+ while (*rp)
+ ++rp;
+ }
+
+ *drivep = drive;
+ return (0);
+}
+
+/*
+ * Provide a few istype() style functions.
+ * isvalid: True if the character is a valid DOS filename character
+ * isdot: True if '.'
+ * isslash: True if '/' or '\'
+ *
+ * 0 - invalid
+ * 1 - okay
+ * 2 - *
+ * 3 - dot
+ * 4 - slash
+ * 5 - colon
+ * 6 - ?
+ * 7 - lowercase
+ */
+u_char cattr[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
+ 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 1, 3, 4, /* 0x20 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 0, 0, 0, 0, 6, /* 0x30 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 0, 1, 1, /* 0x50 */
+ 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 0x60 */
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 0, 1, 1, 0, /* 0x70 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+inline
+isvalid(unsigned c)
+{
+ return (cattr[c & 0xff] == 1);
+}
+
+inline
+isdot(unsigned c)
+{
+ return (cattr[c & 0xff] == 3);
+}
+
+inline
+isslash(unsigned c)
+{
+ return (cattr[c & 0xff] == 4);
+}
+
+/*
+ * Given a real component, compute the DOS component.
+ */
+void
+real_to_dos(u_char *real, u_char *dos)
+{
+ Name_t *n;
+ Name_t *nn;
+ u_char *p;
+ u_char nm[9], ex[4];
+ int ncnt, ecnt;
+ int echar = '0';
+ int nchar = '0';
+
+ if (real[0] == '.' && (real[1] == '\0'
+ || (real[1] == '.' && real[2] == '\0'))) {
+ sprintf((char *)dos, "%.8s", real);
+ return;
+ }
+
+ n = names;
+ while (n) {
+ if (ustrcmp(real, n->real) == 0) {
+ if (n->ext[0])
+ sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
+ else
+ sprintf((char *)dos, "%.8s", n->name);
+ return;
+ }
+ n = n->next;
+ }
+
+ p = real;
+ ncnt = ecnt = 0;
+ while (isvalid(*p) && ncnt < 8) {
+ nm[ncnt] = *p;
+ ++ncnt;
+ ++p;
+ }
+ if (isdot(*p)) {
+ ++p;
+ while (isvalid(*p) && ecnt < 3) {
+ ex[ecnt] = *p;
+ ++ecnt;
+ ++p;
+ }
+ }
+ nm[ncnt] = '\0';
+ ex[ecnt] = '\0';
+
+ if (!*p && ncnt <= 8 && ecnt <= 3) {
+ n = names;
+ while (n) {
+ if (ustrncmp(n->name, nm, 8) == 0 && ustrncmp(n->ext, ex, 3) == 0) {
+ break;
+ }
+ n = n->next;
+ }
+ if (n == 0) {
+ ustrcpy(dos, real);
+ return;
+ }
+ }
+
+ n = (Name_t *)malloc(sizeof(Name_t));
+
+ if (!n)
+ fatal("malloc in real_to_dos: %s\n", strerror(errno));
+
+ n->real = ustrdup(real);
+
+ if (!n->real)
+ fatal("strdup in real_to_dos: %s\n", strerror(errno));
+
+ p = real;
+ ncnt = ecnt = 0;
+ while (*p && ncnt < 8) {
+ if (isvalid(*p))
+ n->name[ncnt] = *p;
+ else if (islower(*p))
+ n->name[ncnt] = toupper(*p);
+ else if (isdot(*p))
+ break;
+ else
+ n->name[ncnt] = (*p |= 0x80);
+ ++ncnt;
+ ++p;
+ }
+ if (isdot(*p)) {
+ ++p;
+ while (*p && ecnt < 3) {
+ if (isvalid(*p))
+ n->ext[ecnt] = *p;
+ else if (islower(*p))
+ n->ext[ecnt] = toupper(*p);
+#if 0
+ else if (isdot(*p))
+ ERROR
+#endif
+ else
+ n->ext[ecnt] = (*p |= 0x80);
+ ++ecnt;
+ ++p;
+ }
+ }
+ n->name[ncnt] = '\0';
+ n->ext[ecnt] = '\0';
+
+ for (;;) {
+ nn = names;
+ while (nn) {
+ if (ustrncmp(n->name, nn->name, 8) == 0 &&
+ ustrncmp(n->ext, nn->ext, 3) == 0) {
+ break;
+ }
+ nn = nn->next;
+ }
+ if (!nn)
+ break;
+ /*
+ * Dang, this name was already in the cache.
+ * Let's munge it a little and try again.
+ */
+ if (ecnt < 3) {
+ n->ext[ecnt] = echar;
+ if (echar == '9') {
+ echar = 'A';
+ } else if (echar == 'Z') {
+ ++ecnt;
+ echar = '0';
+ } else {
+ ++echar;
+ }
+ } else if (ncnt < 8) {
+ n->name[ncnt] = nchar;
+ if (nchar == '9') {
+ nchar = 'A';
+ } else if (nchar == 'Z') {
+ ++ncnt;
+ nchar = '0';
+ } else {
+ ++nchar;
+ }
+ } else if (n->ext[2] < 'Z')
+ n->ext[2]++;
+ else if (n->ext[1] < 'Z')
+ n->ext[1]++;
+ else if (n->ext[0] < 'Z')
+ n->ext[0]++;
+ else if (n->name[7] < 'Z')
+ n->name[7]++;
+ else if (n->name[6] < 'Z')
+ n->name[6]++;
+ else if (n->name[5] < 'Z')
+ n->name[5]++;
+ else if (n->name[4] < 'Z')
+ n->name[4]++;
+ else if (n->name[3] < 'Z')
+ n->name[3]++;
+ else if (n->name[2] < 'Z')
+ n->name[2]++;
+ else if (n->name[1] < 'Z')
+ n->name[1]++;
+ else if (n->name[0] < 'Z')
+ n->name[0]++;
+ else
+ break;
+ }
+
+ if (n->ext[0])
+ sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
+ else
+ sprintf((char *)dos, "%.8s", n->name);
+ n->next = names;
+ names = n;
+}
+
+
+/*
+ * Given a DOS component, compute the REAL component.
+ */
+void
+dos_to_real(u_char *dos, u_char *real)
+{
+ int ncnt = 0;
+ int ecnt = 0;
+ u_char name[8];
+ u_char ext[3];
+ Name_t *n = names;
+
+ while (ncnt < 8 && (isvalid(*dos) || islower(*dos))) {
+ name[ncnt++] = islower(*dos) ? toupper(*dos) : *dos;
+ ++dos;
+ }
+ if (ncnt < 8)
+ name[ncnt] = 0;
+
+ if (isdot(*dos)) {
+ while (ecnt < 3 && (isvalid(*++dos) || islower(*dos))) {
+ ext[ecnt++] = islower(*dos) ? toupper(*dos) : *dos;
+ }
+ }
+ if (ecnt < 3)
+ ext[ecnt] = 0;
+
+ while (n) {
+ if (!ustrncmp(name, n->name, 8) && !ustrncmp(ext, n->ext, 3)) {
+ ustrcpy(real, n->real);
+ return;
+ }
+ n = n->next;
+ }
+
+ if (ext[0])
+ sprintf((char *)real, "%-.8s.%-.3s", name, ext);
+ else
+ sprintf((char *)real, "%-.8s", name);
+
+ while (*real) {
+ if (isupper(*real))
+ *real = tolower(*real);
+ ++real;
+ }
+}
+
+/*
+ * convert a path into an argv[] like vector of components.
+ * If the path starts with a '/' or '\' then the first entry
+ * will be "/" or "\". This is the only case in which a "/"
+ * or "\" may appear in an entry.
+ * Also convert all lowercase to uppercase.
+ * The data returned is in a static area, so a second call will
+ * erase the data of the first.
+ */
+u_char **
+get_entries(u_char *path)
+{
+ static u_char *entries[128]; /* Maximum depth... */
+ static u_char mypath[1024];
+ u_char **e = entries;
+ u_char *p = mypath;
+
+ ustrncpy(mypath+1, path, 1022);
+ p = mypath+1;
+ mypath[1023] = 0;
+ if (path[0] == '/' || path[0] == '\\') {
+ mypath[0] = path[0];
+ *e++ = mypath;
+ *p++ = 0;
+ }
+ while (*p && e < entries + 127) {
+ while (*p && (*p == '/' || *p == '\\')) {
+ ++p;
+ }
+
+ if (!*p)
+ break;
+ *e++ = p;
+ while (*p && (*p != '/' && *p != '\\')) {
+ if (islower(*p))
+ *p = tolower(*p);
+ ++p;
+ }
+ /*
+ * skip over the '/' or '\'
+ */
+ if (*p)
+ *p++ = 0;
+ }
+ *e = 0;
+ return (entries);
+}
+
+/*
+ * Return file system statistics for drive.
+ * Return the DOS errno on failure.
+ */
+get_space(int drive, fsstat_t *fs)
+{
+ Path_t *d;
+ struct statfs *buf;
+ int nfs;
+ int i;
+ struct statfs *me = 0;
+
+ if (drive < 0 || drive >= MAX_DRIVE)
+ return (DISK_DRIVE_INVALID);
+
+ d = &paths[drive];
+
+ if (!d->path)
+ return (DISK_DRIVE_INVALID);
+
+ nfs = getfsstat(0, 0, MNT_WAIT);
+
+ buf = (struct statfs *)malloc(sizeof(struct statfs) * nfs);
+ if (buf == NULL) {
+ perror("get_space");
+ return (DISK_DRIVE_INVALID);
+ }
+ nfs = getfsstat(buf, sizeof(struct statfs) * nfs, MNT_WAIT);
+
+ for (i = 0; i < nfs; ++i) {
+ if (strncmp(buf[i].f_mntonname, (char *)d->path, strlen(buf[i].f_mntonname)))
+ continue;
+ if (me && strlen(me->f_mntonname) > strlen(buf[i].f_mntonname))
+ continue;
+ me = buf + i;
+ }
+ if (!me) {
+ free(buf);
+ return (3);
+ }
+ fs->bytes_sector = 512;
+ fs->sectors_cluster = me->f_bsize / fs->bytes_sector;
+ fs->total_clusters = me->f_blocks / fs->sectors_cluster;
+ while (fs->total_clusters > 0xFFFF) {
+ fs->sectors_cluster *= 2;
+ fs->total_clusters = me->f_blocks / fs->sectors_cluster;
+ }
+ fs->avail_clusters = me->f_bavail / fs->sectors_cluster;
+ free(buf);
+ return (0);
+}
+
+#if 0
+DIR *dp = 0;
+u_char searchdir[1024];
+u_char *searchend;
+#endif
+
+/*
+ * Convert a dos filename into normal form (8.3 format, space padded)
+ */
+void
+to_dos_fcb(u_char *p, u_char *expr)
+{
+ int i;
+
+ if (expr[0] == '.') {
+ p[0] = '.';
+ if (expr[1] == '\0') {
+ for (i = 1; i < 11; i++)
+ p[i] = ' ';
+ return;
+ }
+ if (expr[1] == '.') {
+ p[1] = '.';
+ if (expr[2] == '\0') {
+ for (i = 2; i < 11; i++)
+ p[i] = ' ';
+ return;
+ }
+ }
+ }
+
+ for (i = 8; i > 0; i--) {
+ switch (*expr) {
+ case '\0':
+ case '.':
+ for (; i > 0; i--)
+ *p++ = ' ';
+ break;
+ case '*':
+ for (; i > 0; i--)
+ *p++ = '?';
+ break;
+ default:
+ if (islower(*expr)) {
+ *p++ = toupper(*expr++);
+ break;
+ }
+ case '?':
+ *p++ = *expr++;
+ break;
+ }
+ }
+
+ while (*expr != '\0' && *expr != '.')
+ ++expr;
+ if (*expr)
+ ++expr;
+
+ for (i = 3; i > 0; i--) {
+ switch (*expr) {
+ case '\0':
+ case '.':
+ for (; i > 0; i--)
+ *p++ = ' ';
+ break;
+ case '*':
+ for (; i > 0; i--)
+ *p++ = '?';
+ break;
+ default:
+ if (islower(*expr)) {
+ *p++ = toupper(*expr++);
+ break;
+ }
+ case '?':
+ *p++ = *expr++;
+ break;
+ }
+ }
+}
+
+/*
+** DOS can't handle multiple concurrent searches, and if we leave the
+** search instance in the DTA we get screwed as soon as someone starts lots
+** of searches without finishing them properly.
+** We allocate a single search structure, and recycle it if find_first()
+** is called before a search ends.
+*/
+static search_t dir_search = {dp : NULL};
+
+/*
+ * Find the first file on drive which matches the path with the given
+ * attributes attr.
+ * If found, the result is placed in dir (32 bytes).
+ * The DTA is populated as required by DOS, but the state area is ignored.
+ * Returns DOS errno on failure.
+ */
+find_first(u_char *path, int attr, dosdir_t *dir, find_block_t *dta)
+{
+ u_char newpath[1024], realpath[1024];
+ u_char *expr, *slash;
+ int drive;
+ int error;
+ search_t *search = &dir_search;
+
+ debug(D_REDIR, "find_first(%s, %x, %x)\n", path, attr, dta);
+
+ error = dos_makepath(path, newpath);
+ if (error)
+ return (error);
+
+ expr = newpath;
+ slash = 0;
+ while (*expr != '\0') {
+ if (*expr == '\\' || *expr == '/')
+ slash = expr;
+ expr++;
+ }
+ *slash++ = '\0';
+
+ error = dos_to_real_path(newpath, realpath, &drive);
+ if (error)
+ return (error);
+
+ if (attr == VOLUME_LABEL) /* never find a volume label */
+ return (NO_MORE_FILES);
+
+ if (search->dp) /* stale search? */
+ closedir(search->dp);
+
+ search->dp = opendir(realpath);
+ if (search->dp == NULL)
+ return (PATH_NOT_FOUND);
+
+ ustrcpy(search->searchdir, realpath);
+ search->searchend = search->searchdir;
+ while (*search->searchend)
+ ++search->searchend;
+ *search->searchend++ = '/';
+
+ search->dp->dd_fd = squirrel_fd(search->dp->dd_fd);
+
+ dta->drive = drive | 0x80;
+ to_dos_fcb(dta->pattern, slash);
+ dta->flag = attr;
+
+ return (find_next(dir, dta));
+}
+
+/*
+ * Continue on where find_first left off.
+ * The results will be placed in dir.
+ * DTA state area is ignored.
+ */
+int
+find_next(dosdir_t *dir, find_block_t *dta)
+{
+ search_t *search = &dir_search;
+ struct dirent *d;
+ struct stat sb;
+ u_char name[16];
+
+ if (!search->dp)
+ return (NO_MORE_FILES);
+
+#if 0
+ debug(D_REDIR, "find_next()\n");
+#endif
+
+ while (d = readdir(search->dp)) {
+ real_to_dos((u_char *)d->d_name, name);
+ to_dos_fcb(dir->name, name);
+#if 0
+printf("find_next: |%-11.11s| |%-11.11s| |%s| |%s|\n", dta->pattern, dir->name, d->d_name, name);
+#endif
+ if (dos_match(dta->pattern, dir->name) == 0)
+ continue;
+
+ ustrcpy(search->searchend, (u_char *)d->d_name);
+ if (ustat(search->searchdir, &sb) < 0)
+ continue;
+#if 0
+printf("find_next: %x\n", sb.st_mode);
+#endif
+ if (S_ISDIR(sb.st_mode)) {
+ if (!(dta->flag & DIRECTORY)) {
+ continue;
+ }
+ }
+ dir->attr = (S_ISDIR(sb.st_mode) ? DIRECTORY : 0) |
+ (uaccess(search->searchdir, W_OK) < 0 ? READ_ONLY_FILE : 0);
+ encode_dos_file_time(sb.st_mtime, &dir->date, &dir->time);
+ dir->start = 1;
+ dir->size = sb.st_size;
+#if 0
+printf("find_next: found %s\n",name);
+#endif
+ return (0);
+ }
+ closedir(search->dp);
+ search->dp = NULL;
+ return (NO_MORE_FILES);
+}
+
+/*
+ * perfrom hokey DOS pattern matching. pattern may contain the wild cards
+ * '*' and '?' only. Follow the DOS convention that '?*', '*?' and '**' all
+ * are the same as '*'. Also, allow '?' to match the blank padding in a
+ * name (hence, ???? matchs all of "a", "ab", "abc" and "abcd" but not "abcde")
+ * Return 1 if a match is found, 0 if not.
+ *
+ * XXX This appears to be severely busted! (no * handling - normal?)
+ */
+int
+dos_match(u_char *pattern, u_char *string)
+{
+ int i;
+
+ /*
+ * Check the base part first
+ */
+ for (i = 11; i > 0; i--) {
+ if (*pattern != '?' && *string != *pattern)
+ return (0);
+ pattern++, string++;
+ }
+ return (1);
+}
OpenPOWER on IntegriCloud