2 * Copyright (c) 1992, 1993, 1996
3 * Berkeley Software Design, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Berkeley Software
18 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * BSDI cwd.c,v 2.2 1996/04/08 19:32:25 bostic Exp
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/mount.h>
49 #define D_REDIR 0x0080000 /* XXX - ack */
50 #define D_TRAPS3 0x0200000
60 typedef struct Name_t {
70 static Path_t paths[MAX_DRIVE];
76 * Initialize the drive to be based at 'base' in the BSD filesystem
79 init_path(int drive, u_char *base, u_char *dir)
83 if (drive < 0 || drive >= MAX_DRIVE)
86 debug(D_TRAPS3, "init_path(%d, %s, %s)\n", drive, base, dir);
93 if ((d->path = ustrdup(base)) == NULL)
94 fatal("strdup in init_path for %c:%s: %s", drntol(drive), base,
99 if ((d->cwd = (u_char *)malloc(d->maxlen)) == NULL)
100 fatal("malloc in init_path for %c:%s: %s", drntol(drive), base,
108 if (ustrncmp(base, dir, ustrlen(base)) == 0)
109 dir += ustrlen(base);
115 u_char realname[256];
116 u_char *r = realname;;
118 while ((*r = *dir) && *dir++ != '/') {
125 dosname[0] = drntol(drive);
127 real_to_dos(realname, &dosname[2]);
129 if (dos_setcwd(dosname)) {
130 fprintf(stderr, "Failed to CD to directory %s in %s\n",
138 * Mark this drive as read only
141 dos_makereadonly(int drive)
144 if (drive < 0 || drive >= MAX_DRIVE)
146 paths[drive].read_only = 1;
150 * Return read-only status of drive
153 dos_readonly(int drive)
156 if (drive < 0 || drive >= MAX_DRIVE)
158 debug(D_REDIR, "dos_readonly(%d) -> %d\n", drive, paths[drive].read_only);
159 return (paths[drive].read_only);
163 * Return DOS's idea of the CWD for drive
164 * Return 0 if the drive specified is not mapped (or bad)
167 dos_getcwd(int drive)
170 if (drive < 0 || drive >= MAX_DRIVE)
172 debug(D_REDIR, "dos_getcwd(%d) -> %s\n", drive, paths[drive].cwd);
173 return (paths[drive].cwd);
177 * Return DOS's idea of the CWD for drive
178 * Return 0 if the drive specified is not mapped (or bad)
181 dos_getpath(int drive)
184 if (drive < 0 || drive >= MAX_DRIVE)
186 debug(D_REDIR, "dos_getpath(%d) -> %s\n", drive, paths[drive].path);
187 return (paths[drive].path);
191 * Fix up a DOS path name. Strip out all '.' and '..' entries, turn
192 * '/' into '\\' and convert all lowercase to uppercase.
193 * Returns 0 on success or DOS errno
196 dos_makepath(u_char *where, u_char *newpath)
202 u_char tmppath[1024];
203 u_char *snewpath = newpath;
205 if (where[0] != '\0' && where[1] == ':') {
206 drive = drlton(*where);
207 *newpath++ = *where++;
208 *newpath++ = *where++;
211 *newpath++ = drntol(diskdrive);
215 if (drive < 0 || drive >= MAX_DRIVE) {
216 debug(D_REDIR,"drive %c invalid\n", drntol(drive));
217 return (DISK_DRIVE_INVALID);
221 if (d->cwd == NULL) {
222 debug(D_REDIR,"no cwd for drive %c\n",drntol(drive));
223 return (DISK_DRIVE_INVALID);
226 debug(D_REDIR, "dos_makepath(%d, %s)\n", drive, where);
229 if (*where != '\\' && *where != '/') {
230 ustrncpy(tmppath, d->cwd, 1024);
232 ustrncat(tmppath, (u_char *)"/", 1024 - ustrlen(tmppath));
233 ustrncat(tmppath, where, 1024 - ustrlen(tmppath));
235 ustrncpy(tmppath, where, 1024 - ustrlen(tmppath));
238 dirs = get_entries(tmppath);
240 return (PATH_NOT_FOUND);
244 u_char *dir = *dirs++;
245 if (*dir == '/' || *dir == '\\') {
248 } else if (dir[0] == '.' && dir[1] == 0) {
250 } else if (dir[0] == '.' && dir[1] == '.' && dir[2] == '\0') {
251 while (np[-1] != '/' && np[-1] != '\\')
253 if (np - 1 > newpath)
258 while ((*np = *dir++) && np - snewpath < 1023)
268 * Set DOS's idea of the CWD for drive to be where.
269 * Returns DOS errno on failuer.
272 dos_setcwd(u_char *where)
274 u_char new_path[1024];
275 u_char real_path[1024];
281 debug(D_REDIR, "dos_setcwd(%s)\n", where);
283 error = dos_makepath(where, new_path);
287 error = dos_to_real_path(new_path, real_path, &drive);
291 if (ustat(real_path, &sb) < 0 || !S_ISDIR(sb.st_mode))
292 return (PATH_NOT_FOUND);
293 if (uaccess(real_path, R_OK | X_OK))
294 return (PATH_NOT_FOUND);
297 d->len = ustrlen(new_path + 2);
299 if (d->len + 1 > d->maxlen) {
301 d->maxlen = d->len + 1 + 32;
302 d->cwd = (u_char *)malloc(d->maxlen);
304 fatal("malloc in dos_setcwd for %c:%s: %s", drntol(drive),
305 new_path, strerror(errno));
307 ustrncpy(d->cwd, new_path + 2, d->maxlen - d->len);
312 * Given a DOS path dos_path and a drive, convert it to a BSD pathname
313 * and store the result in real_path.
314 * Return DOS errno on failure.
317 dos_to_real_path(u_char *dos_path, u_char *real_path, int *drivep)
320 u_char new_path[1024];
326 debug(D_REDIR, "dos_to_real_path(%s)\n", dos_path);
328 if (dos_path[0] != '\0' && dos_path[1] == ':') {
329 drive = drlton(*dos_path);
338 return (DISK_DRIVE_INVALID);
340 ustrcpy(real_path, d->path);
346 ustrncpy(new_path, dos_path, 1024 - ustrlen(new_path));
348 dirs = get_entries(new_path);
350 return (PATH_NOT_FOUND);
354 * There are no . or .. entries to worry about either
357 while ((dir = *++dirs) != 0) {
359 dos_to_real(dir, rp);
369 * Provide a few istype() style functions.
370 * isvalid: True if the character is a valid DOS filename character
372 * isslash: True if '/' or '\'
383 u_char cattr[256] = {
384 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
385 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
386 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 1, 3, 4, /* 0x20 */
387 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 0, 0, 0, 0, 6, /* 0x30 */
388 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
389 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 0, 1, 1, /* 0x50 */
390 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 0x60 */
391 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 0, 1, 1, 0, /* 0x70 */
392 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */
393 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
394 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
395 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
396 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
397 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
398 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
399 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
405 return (cattr[c & 0xff] == 1);
411 return (cattr[c & 0xff] == 3);
417 return (cattr[c & 0xff] == 4);
421 * Given a real component, compute the DOS component.
424 real_to_dos(u_char *real, u_char *dos)
434 if (real[0] == '.' && (real[1] == '\0'
435 || (real[1] == '.' && real[2] == '\0'))) {
436 sprintf((char *)dos, "%.8s", real);
442 if (ustrcmp(real, n->real) == 0) {
444 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
446 sprintf((char *)dos, "%.8s", n->name);
454 while (isvalid(*p) && ncnt < 8) {
461 while (isvalid(*p) && ecnt < 3) {
470 if (!*p && ncnt <= 8 && ecnt <= 3) {
473 if (ustrncmp(n->name, nm, 8) == 0 && ustrncmp(n->ext, ex, 3) == 0) {
484 n = (Name_t *)malloc(sizeof(Name_t));
487 fatal("malloc in real_to_dos: %s\n", strerror(errno));
489 n->real = ustrdup(real);
492 fatal("strdup in real_to_dos: %s\n", strerror(errno));
496 while (*p && ncnt < 8) {
499 else if (islower(*p))
500 n->name[ncnt] = toupper(*p);
504 n->name[ncnt] = (*p |= 0x80);
510 while (*p && ecnt < 3) {
513 else if (islower(*p))
514 n->ext[ecnt] = toupper(*p);
520 n->ext[ecnt] = (*p |= 0x80);
525 n->name[ncnt] = '\0';
531 if (ustrncmp(n->name, nn->name, 8) == 0 &&
532 ustrncmp(n->ext, nn->ext, 3) == 0) {
540 * Dang, this name was already in the cache.
541 * Let's munge it a little and try again.
544 n->ext[ecnt] = echar;
547 } else if (echar == 'Z') {
553 } else if (ncnt < 8) {
554 n->name[ncnt] = nchar;
557 } else if (nchar == 'Z') {
563 } else if (n->ext[2] < 'Z')
565 else if (n->ext[1] < 'Z')
567 else if (n->ext[0] < 'Z')
569 else if (n->name[7] < 'Z')
571 else if (n->name[6] < 'Z')
573 else if (n->name[5] < 'Z')
575 else if (n->name[4] < 'Z')
577 else if (n->name[3] < 'Z')
579 else if (n->name[2] < 'Z')
581 else if (n->name[1] < 'Z')
583 else if (n->name[0] < 'Z')
590 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
592 sprintf((char *)dos, "%.8s", n->name);
599 * Given a DOS component, compute the REAL component.
602 dos_to_real(u_char *dos, u_char *real)
610 while (ncnt < 8 && (isvalid(*dos) || islower(*dos))) {
611 name[ncnt++] = islower(*dos) ? toupper(*dos) : *dos;
618 while (ecnt < 3 && (isvalid(*++dos) || islower(*dos))) {
619 ext[ecnt++] = islower(*dos) ? toupper(*dos) : *dos;
626 if (!ustrncmp(name, n->name, 8) && !ustrncmp(ext, n->ext, 3)) {
627 ustrcpy(real, n->real);
634 sprintf((char *)real, "%-.8s.%-.3s", name, ext);
636 sprintf((char *)real, "%-.8s", name);
640 *real = tolower(*real);
646 * convert a path into an argv[] like vector of components.
647 * If the path starts with a '/' or '\' then the first entry
648 * will be "/" or "\". This is the only case in which a "/"
649 * or "\" may appear in an entry.
650 * Also convert all lowercase to uppercase.
651 * The data returned is in a static area, so a second call will
652 * erase the data of the first.
655 get_entries(u_char *path)
657 static u_char *entries[128]; /* Maximum depth... */
658 static u_char mypath[1024];
659 u_char **e = entries;
662 ustrncpy(mypath+1, path, 1022);
665 if (path[0] == '/' || path[0] == '\\') {
670 while (*p && e < entries + 127) {
671 while (*p && (*p == '/' || *p == '\\')) {
678 while (*p && (*p != '/' && *p != '\\')) {
684 * skip over the '/' or '\'
694 * Return file system statistics for drive.
695 * Return the DOS errno on failure.
698 get_space(int drive, fsstat_t *fs)
704 struct statfs *me = 0;
706 if (drive < 0 || drive >= MAX_DRIVE)
707 return (DISK_DRIVE_INVALID);
712 return (DISK_DRIVE_INVALID);
714 nfs = getfsstat(0, 0, MNT_WAIT);
716 buf = (struct statfs *)malloc(sizeof(struct statfs) * nfs);
719 return (DISK_DRIVE_INVALID);
721 nfs = getfsstat(buf, sizeof(struct statfs) * nfs, MNT_WAIT);
723 for (i = 0; i < nfs; ++i) {
724 if (strncmp(buf[i].f_mntonname, (char *)d->path, strlen(buf[i].f_mntonname)))
726 if (me && strlen(me->f_mntonname) > strlen(buf[i].f_mntonname))
734 fs->bytes_sector = 512;
735 fs->sectors_cluster = me->f_bsize / fs->bytes_sector;
736 fs->total_clusters = me->f_blocks / fs->sectors_cluster;
737 while (fs->total_clusters > 0xFFFF) {
738 fs->sectors_cluster *= 2;
739 fs->total_clusters = me->f_blocks / fs->sectors_cluster;
741 fs->avail_clusters = me->f_bavail / fs->sectors_cluster;
748 u_char searchdir[1024];
753 * Convert a dos filename into normal form (8.3 format, space padded)
756 to_dos_fcb(u_char *p, u_char *expr)
760 if (expr[0] == '.') {
762 if (expr[1] == '\0') {
763 for (i = 1; i < 11; i++)
767 if (expr[1] == '.') {
769 if (expr[2] == '\0') {
770 for (i = 2; i < 11; i++)
777 for (i = 8; i > 0; i--) {
789 if (islower(*expr)) {
790 *p++ = toupper(*expr++);
799 while (*expr != '\0' && *expr != '.')
804 for (i = 3; i > 0; i--) {
816 if (islower(*expr)) {
817 *p++ = toupper(*expr++);
828 ** DOS can't handle multiple concurrent searches, and if we leave the
829 ** search instance in the DTA we get screwed as soon as someone starts lots
830 ** of searches without finishing them properly.
831 ** We allocate a single search structure, and recycle it if find_first()
832 ** is called before a search ends.
834 static search_t dir_search = {dp : NULL};
837 * Find the first file on drive which matches the path with the given
839 * If found, the result is placed in dir (32 bytes).
840 * The DTA is populated as required by DOS, but the state area is ignored.
841 * Returns DOS errno on failure.
844 find_first(u_char *path, int attr, dosdir_t *dir, find_block_t *dta)
846 u_char new_path[1024], real_path[1024];
847 u_char *expr, *slash;
850 search_t *search = &dir_search;
852 debug(D_REDIR, "find_first(%s, %x, %x)\n", path, attr, dta);
854 error = dos_makepath(path, new_path);
860 while (*expr != '\0') {
861 if (*expr == '\\' || *expr == '/')
867 error = dos_to_real_path(new_path, real_path, &drive);
871 if (attr == VOLUME_LABEL) /* never find a volume label */
872 return (NO_MORE_FILES);
874 if (search->dp) /* stale search? */
875 closedir(search->dp);
877 search->dp = opendir(real_path);
878 if (search->dp == NULL)
879 return (PATH_NOT_FOUND);
881 ustrncpy(search->searchdir, real_path, 1024 - ustrlen(real_path));
882 search->searchend = search->searchdir;
883 while (*search->searchend)
885 *search->searchend++ = '/';
887 search->dp->dd_fd = squirrel_fd(search->dp->dd_fd);
889 dta->drive = drive | 0x80;
890 to_dos_fcb(dta->pattern, slash);
893 return (find_next(dir, dta));
897 * Continue on where find_first left off.
898 * The results will be placed in dir.
899 * DTA state area is ignored.
902 find_next(dosdir_t *dir, find_block_t *dta)
904 search_t *search = &dir_search;
910 return (NO_MORE_FILES);
913 debug(D_REDIR, "find_next()\n");
916 while ((d = readdir(search->dp)) != 0) {
917 real_to_dos((u_char *)d->d_name, name);
918 to_dos_fcb(dir->name, name);
920 printf("find_next: |%-11.11s| |%-11.11s| |%s| |%s|\n", dta->pattern, dir->name, d->d_name, name);
922 if (dos_match(dta->pattern, dir->name) == 0)
925 ustrcpy(search->searchend, (u_char *)d->d_name);
926 if (ustat(search->searchdir, &sb) < 0)
929 printf("find_next: %x\n", sb.st_mode);
931 if (S_ISDIR(sb.st_mode)) {
932 if (!(dta->flag & DIRECTORY)) {
936 dir->attr = (S_ISDIR(sb.st_mode) ? DIRECTORY : 0) |
937 (uaccess(search->searchdir, W_OK) < 0 ? READ_ONLY_FILE : 0);
938 encode_dos_file_time(sb.st_mtime, &dir->date, &dir->time);
940 dir->size = sb.st_size;
942 printf("find_next: found %s\n",name);
946 closedir(search->dp);
948 return (NO_MORE_FILES);
952 * perfrom hokey DOS pattern matching. pattern may contain the wild cards
953 * '*' and '?' only. Follow the DOS convention that '?*', '*?' and '**' all
954 * are the same as '*'. Also, allow '?' to match the blank padding in a
955 * name (hence, ???? matchs all of "a", "ab", "abc" and "abcd" but not "abcde")
956 * Return 1 if a match is found, 0 if not.
958 * XXX This appears to be severely busted! (no * handling - normal?)
961 dos_match(u_char *pattern, u_char *string)
966 * Check the base part first
968 for (i = 11; i > 0; i--) {
969 if (*pattern != '?' && *string != *pattern)