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
32 * $FreeBSD: src/usr.bin/doscmd/cwd.c,v 1.6.2.3 2002/04/25 11:04:50 tg Exp $
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/mount.h>
50 static inline int isvalid(unsigned);
51 static inline int isdot(unsigned);
52 static inline int isslash(unsigned);
53 static void to_dos_fcb(u_char *, u_char *);
55 #define D_REDIR 0x0080000 /* XXX - ack */
56 #define D_TRAPS3 0x0200000
66 typedef struct Name_t {
76 static Path_t paths[MAX_DRIVE];
80 * Initialize the drive to be based at 'base' in the BSD filesystem
83 init_path(int drive, const u_char *base, const u_char *dir)
87 if (drive < 0 || drive >= MAX_DRIVE)
90 debug(D_TRAPS3, "init_path(%d, %s, %s)\n", drive, base, dir);
97 if ((d->path = ustrdup(base)) == NULL)
98 fatal("strdup in init_path for %c:%s: %s", drntol(drive), base,
103 if ((d->cwd = (u_char *)malloc(d->maxlen)) == NULL)
104 fatal("malloc in init_path for %c:%s: %s", drntol(drive), base,
112 if (ustrncmp(base, dir, ustrlen(base)) == 0)
113 dir += ustrlen(base);
119 u_char realname[256];
120 u_char *r = realname;
122 while ((*r = *dir) && *dir++ != '/') {
129 dosname[0] = drntol(drive);
131 real_to_dos(realname, &dosname[2]);
133 if (dos_setcwd(dosname)) {
134 fprintf(stderr, "Failed to CD to directory %s in %s\n",
142 * Mark this drive as read only
145 dos_makereadonly(int drive)
148 if (drive < 0 || drive >= MAX_DRIVE)
150 paths[drive].read_only = 1;
154 * Return read-only status of drive
157 dos_readonly(int drive)
160 if (drive < 0 || drive >= MAX_DRIVE)
162 debug(D_REDIR, "dos_readonly(%d) -> %d\n", drive, paths[drive].read_only);
163 return (paths[drive].read_only);
167 * Return DOS's idea of the CWD for drive
168 * Return 0 if the drive specified is not mapped (or bad)
171 dos_getcwd(int drive)
174 if (drive < 0 || drive >= MAX_DRIVE)
176 debug(D_REDIR, "dos_getcwd(%d) -> %s\n", drive, paths[drive].cwd);
177 return (paths[drive].cwd);
181 * Return DOS's idea of the CWD for drive
182 * Return 0 if the drive specified is not mapped (or bad)
185 dos_getpath(int drive)
188 if (drive < 0 || drive >= MAX_DRIVE)
190 debug(D_REDIR, "dos_getpath(%d) -> %s\n", drive, paths[drive].path);
191 return (paths[drive].path);
195 * Fix up a DOS path name. Strip out all '.' and '..' entries, turn
196 * '/' into '\\' and convert all lowercase to uppercase.
197 * Returns 0 on success or DOS errno
200 dos_makepath(u_char *where, u_char *newpath)
206 u_char tmppath[1024];
207 u_char *snewpath = newpath;
209 if (where[0] != '\0' && where[1] == ':') {
210 drive = drlton(*where);
211 *newpath++ = *where++;
212 *newpath++ = *where++;
215 *newpath++ = drntol(diskdrive);
219 if (drive < 0 || drive >= MAX_DRIVE) {
220 debug(D_REDIR,"drive %c invalid\n", drntol(drive));
221 return (DISK_DRIVE_INVALID);
225 if (d->cwd == NULL) {
226 debug(D_REDIR,"no cwd for drive %c\n",drntol(drive));
227 return (DISK_DRIVE_INVALID);
230 debug(D_REDIR, "dos_makepath(%d, %s)\n", drive, where);
233 if (*where != '\\' && *where != '/') {
234 ustrncpy(tmppath, d->cwd, 1024);
236 ustrncat(tmppath, "/", 1024 - ustrlen(tmppath));
237 ustrncat(tmppath, where, 1024 - ustrlen(tmppath));
239 ustrncpy(tmppath, where, 1024 - ustrlen(tmppath));
242 dirs = get_entries(tmppath);
244 return (PATH_NOT_FOUND);
248 u_char *dir = *dirs++;
249 if (*dir == '/' || *dir == '\\') {
252 } else if (dir[0] == '.' && dir[1] == 0) {
254 } else if (dir[0] == '.' && dir[1] == '.' && dir[2] == '\0') {
255 while (np[-1] != '/' && np[-1] != '\\')
257 if (np - 1 > newpath)
262 while ((*np = *dir++) && np - snewpath < 1023)
272 * Set DOS's idea of the CWD for drive to be where.
273 * Returns DOS errno on failuer.
276 dos_setcwd(u_char *where)
278 u_char new_path[1024];
279 u_char real_path[1024];
285 debug(D_REDIR, "dos_setcwd(%s)\n", where);
287 error = dos_makepath(where, new_path);
291 error = dos_to_real_path(new_path, real_path, &drive);
295 if (ustat(real_path, &sb) < 0 || !S_ISDIR(sb.st_mode))
296 return (PATH_NOT_FOUND);
297 if (uaccess(real_path, R_OK | X_OK))
298 return (PATH_NOT_FOUND);
301 d->len = ustrlen(new_path + 2);
303 if (d->len + 1 > d->maxlen) {
305 d->maxlen = d->len + 1 + 32;
306 d->cwd = (u_char *)malloc(d->maxlen);
308 fatal("malloc in dos_setcwd for %c:%s: %s", drntol(drive),
309 new_path, strerror(errno));
311 ustrncpy(d->cwd, new_path + 2, d->maxlen - d->len);
316 * Given a DOS path dos_path and a drive, convert it to a BSD pathname
317 * and store the result in real_path.
318 * Return DOS errno on failure.
321 dos_to_real_path(u_char *dos_path, u_char *real_path, int *drivep)
324 u_char new_path[1024];
330 debug(D_REDIR, "dos_to_real_path(%s)\n", dos_path);
332 if (dos_path[0] != '\0' && dos_path[1] == ':') {
333 drive = drlton(*dos_path);
342 return (DISK_DRIVE_INVALID);
344 ustrcpy(real_path, d->path);
350 ustrncpy(new_path, dos_path, 1024 - ustrlen(new_path));
352 dirs = get_entries(new_path);
354 return (PATH_NOT_FOUND);
358 * There are no . or .. entries to worry about either
361 while ((dir = *++dirs) != NULL) {
363 dos_to_real(dir, rp);
373 * Provide a few istype() style functions.
374 * isvalid: True if the character is a valid DOS filename character
376 * isslash: True if '/' or '\'
387 u_char cattr[256] = {
388 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
389 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
390 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 1, 3, 4, /* 0x20 */
391 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 0, 0, 0, 0, 6, /* 0x30 */
392 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
393 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 0, 1, 1, /* 0x50 */
394 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 0x60 */
395 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 0, 1, 1, 0, /* 0x70 */
396 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */
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,
400 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
401 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
402 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
403 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
409 return (cattr[c & 0xff] == 1);
415 return (cattr[c & 0xff] == 3);
421 return (cattr[c & 0xff] == 4);
425 * Given a real component, compute the DOS component.
428 real_to_dos(u_char *real, u_char *dos)
438 if (real[0] == '.' && (real[1] == '\0'
439 || (real[1] == '.' && real[2] == '\0'))) {
440 sprintf((char *)dos, "%.8s", real);
446 if (ustrcmp(real, n->real) == 0) {
448 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
450 sprintf((char *)dos, "%.8s", n->name);
458 while (isvalid(*p) && ncnt < 8) {
465 while (isvalid(*p) && ecnt < 3) {
474 if (!*p && ncnt <= 8 && ecnt <= 3) {
477 if (ustrncmp(n->name, nm, 8) == 0 && ustrncmp(n->ext, ex, 3) == 0) {
488 n = (Name_t *)malloc(sizeof(Name_t));
491 fatal("malloc in real_to_dos: %s\n", strerror(errno));
493 n->real = ustrdup(real);
496 fatal("strdup in real_to_dos: %s\n", strerror(errno));
500 while (*p && ncnt < 8) {
503 else if (islower(*p))
504 n->name[ncnt] = toupper(*p);
508 n->name[ncnt] = (*p |= 0x80);
514 while (*p && ecnt < 3) {
517 else if (islower(*p))
518 n->ext[ecnt] = toupper(*p);
524 n->ext[ecnt] = (*p |= 0x80);
529 n->name[ncnt] = '\0';
535 if (ustrncmp(n->name, nn->name, 8) == 0 &&
536 ustrncmp(n->ext, nn->ext, 3) == 0) {
544 * Dang, this name was already in the cache.
545 * Let's munge it a little and try again.
548 n->ext[ecnt] = echar;
551 } else if (echar == 'Z') {
557 } else if (ncnt < 8) {
558 n->name[ncnt] = nchar;
561 } else if (nchar == 'Z') {
567 } else if (n->ext[2] < 'Z')
569 else if (n->ext[1] < 'Z')
571 else if (n->ext[0] < 'Z')
573 else if (n->name[7] < 'Z')
575 else if (n->name[6] < 'Z')
577 else if (n->name[5] < 'Z')
579 else if (n->name[4] < 'Z')
581 else if (n->name[3] < 'Z')
583 else if (n->name[2] < 'Z')
585 else if (n->name[1] < 'Z')
587 else if (n->name[0] < 'Z')
594 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
596 sprintf((char *)dos, "%.8s", n->name);
603 * Given a DOS component, compute the REAL component.
606 dos_to_real(u_char *dos, u_char *real)
614 while (ncnt < 8 && (isvalid(*dos) || islower(*dos))) {
615 name[ncnt++] = islower(*dos) ? toupper(*dos) : *dos;
622 while (ecnt < 3 && (isvalid(*++dos) || islower(*dos))) {
623 ext[ecnt++] = islower(*dos) ? toupper(*dos) : *dos;
630 if (!ustrncmp(name, n->name, 8) && !ustrncmp(ext, n->ext, 3)) {
631 ustrcpy(real, n->real);
638 sprintf((char *)real, "%-.8s.%-.3s", name, ext);
640 sprintf((char *)real, "%-.8s", name);
644 *real = tolower(*real);
650 * convert a path into an argv[] like vector of components.
651 * If the path starts with a '/' or '\' then the first entry
652 * will be "/" or "\". This is the only case in which a "/"
653 * or "\" may appear in an entry.
654 * Also convert all lowercase to uppercase.
655 * The data returned is in a static area, so a second call will
656 * erase the data of the first.
659 get_entries(u_char *path)
661 static u_char *entries[128]; /* Maximum depth... */
662 static u_char mypath[1024];
663 u_char **e = entries;
666 ustrncpy(mypath+1, path, 1022);
669 if (path[0] == '/' || path[0] == '\\') {
674 while (*p && e < entries + 127) {
675 while (*p && (*p == '/' || *p == '\\')) {
682 while (*p && (*p != '/' && *p != '\\')) {
688 * skip over the '/' or '\'
698 * Return file system statistics for drive.
699 * Return the DOS errno on failure.
702 get_space(int drive, fsstat_t *fs)
708 struct statfs *me = NULL;
710 if (drive < 0 || drive >= MAX_DRIVE)
711 return (DISK_DRIVE_INVALID);
716 return (DISK_DRIVE_INVALID);
718 nfs = getfsstat(0, 0, MNT_WAIT);
720 buf = (struct statfs *)malloc(sizeof(struct statfs) * nfs);
723 return (DISK_DRIVE_INVALID);
725 nfs = getfsstat(buf, sizeof(struct statfs) * nfs, MNT_WAIT);
727 for (i = 0; i < nfs; ++i) {
728 if (strncmp(buf[i].f_mntonname, (char *)d->path, strlen(buf[i].f_mntonname)))
730 if (me && strlen(me->f_mntonname) > strlen(buf[i].f_mntonname))
738 fs->bytes_sector = 512;
739 fs->sectors_cluster = me->f_bsize / fs->bytes_sector;
740 fs->total_clusters = me->f_blocks / fs->sectors_cluster;
741 while (fs->total_clusters > 0xFFFF) {
742 fs->sectors_cluster *= 2;
743 fs->total_clusters = me->f_blocks / fs->sectors_cluster;
745 fs->avail_clusters = me->f_bavail / fs->sectors_cluster;
752 u_char searchdir[1024];
757 * Convert a dos filename into normal form (8.3 format, space padded)
760 to_dos_fcb(u_char *p, u_char *expr)
764 if (expr[0] == '.') {
766 if (expr[1] == '\0') {
767 for (i = 1; i < 11; i++)
771 if (expr[1] == '.') {
773 if (expr[2] == '\0') {
774 for (i = 2; i < 11; i++)
781 for (i = 8; i > 0; i--) {
793 if (islower(*expr)) {
794 *p++ = toupper(*expr++);
803 while (*expr != '\0' && *expr != '.')
808 for (i = 3; i > 0; i--) {
820 if (islower(*expr)) {
821 *p++ = toupper(*expr++);
832 ** DOS can't handle multiple concurrent searches, and if we leave the
833 ** search instance in the DTA we get screwed as soon as someone starts lots
834 ** of searches without finishing them properly.
835 ** We allocate a single search structure, and recycle it if find_first()
836 ** is called before a search ends.
838 static search_t dir_search;
841 * Find the first file on drive which matches the path with the given
843 * If found, the result is placed in dir (32 bytes).
844 * The DTA is populated as required by DOS, but the state area is ignored.
845 * Returns DOS errno on failure.
848 find_first(u_char *path, int attr, dosdir_t *dir, find_block_t *dta)
850 u_char new_path[1024], real_path[1024];
851 u_char *expr, *slash;
854 search_t *search = &dir_search;
856 debug(D_REDIR, "find_first(%s, %x, %x)\n", path, attr, (int)dta);
858 error = dos_makepath(path, new_path);
864 while (*expr != '\0') {
865 if (*expr == '\\' || *expr == '/')
871 error = dos_to_real_path(new_path, real_path, &drive);
875 if (attr == VOLUME_LABEL) /* never find a volume label */
876 return (NO_MORE_FILES);
878 if (search->dp) /* stale search? */
879 closedir(search->dp);
881 search->dp = opendir(real_path);
882 if (search->dp == NULL)
883 return (PATH_NOT_FOUND);
885 ustrncpy(search->searchdir, real_path, 1024 - ustrlen(real_path));
886 search->searchend = search->searchdir;
887 while (*search->searchend)
889 *search->searchend++ = '/';
891 search->dp->dd_fd = squirrel_fd(search->dp->dd_fd);
893 dta->drive = drive | 0x80;
894 to_dos_fcb(dta->pattern, slash);
897 return (find_next(dir, dta));
901 * Continue on where find_first left off.
902 * The results will be placed in dir.
903 * DTA state area is ignored.
906 find_next(dosdir_t *dir, find_block_t *dta)
908 search_t *search = &dir_search;
914 return (NO_MORE_FILES);
917 debug(D_REDIR, "find_next()\n");
920 while ((d = readdir(search->dp)) != NULL) {
921 real_to_dos((u_char *)d->d_name, name);
922 to_dos_fcb(dir->name, name);
924 printf("find_next: |%-11.11s| |%-11.11s| |%s| |%s|\n", dta->pattern, dir->name, d->d_name, name);
926 if (dos_match(dta->pattern, dir->name) == 0)
929 ustrcpy(search->searchend, (u_char *)d->d_name);
930 if (ustat(search->searchdir, &sb) < 0)
933 printf("find_next: %x\n", sb.st_mode);
935 if (S_ISDIR(sb.st_mode)) {
936 if (!(dta->flag & DIRECTORY)) {
940 dir->attr = (S_ISDIR(sb.st_mode) ? DIRECTORY : 0) |
941 (uaccess(search->searchdir, W_OK) < 0 ? READ_ONLY_FILE : 0);
942 encode_dos_file_time(sb.st_mtime, &dir->date, &dir->time);
944 dir->size = sb.st_size;
946 printf("find_next: found %s\n",name);
950 closedir(search->dp);
952 return (NO_MORE_FILES);
956 * perfrom hokey DOS pattern matching. pattern may contain the wild cards
957 * '*' and '?' only. Follow the DOS convention that '?*', '*?' and '**' all
958 * are the same as '*'. Also, allow '?' to match the blank padding in a
959 * name (hence, ???? matchs all of "a", "ab", "abc" and "abcd" but not "abcde")
960 * Return 1 if a match is found, 0 if not.
962 * XXX This appears to be severely busted! (no * handling - normal?)
965 dos_match(u_char *pattern, u_char *string)
970 * Check the base part first
972 for (i = 11; i > 0; i--) {
973 if (*pattern != '?' && *string != *pattern)