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 $
33 * $DragonFly: src/usr.bin/doscmd/cwd.c,v 1.3 2006/08/03 16:40:48 swildner Exp $
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/mount.h>
51 static inline int isvalid(unsigned);
52 static inline int isdot(unsigned);
53 static inline int isslash(unsigned);
54 static void to_dos_fcb(u_char
*, u_char
*);
56 #define D_REDIR 0x0080000 /* XXX - ack */
57 #define D_TRAPS3 0x0200000
67 typedef struct Name_t
{
77 static Path_t paths
[MAX_DRIVE
];
81 * Initialize the drive to be based at 'base' in the BSD filesystem
84 init_path(int drive
, const u_char
*base
, const u_char
*dir
)
88 if (drive
< 0 || drive
>= MAX_DRIVE
)
91 debug(D_TRAPS3
, "init_path(%d, %s, %s)\n", drive
, base
, dir
);
98 if ((d
->path
= ustrdup(base
)) == NULL
)
99 fatal("strdup in init_path for %c:%s: %s", drntol(drive
), base
,
104 if ((d
->cwd
= (u_char
*)malloc(d
->maxlen
)) == NULL
)
105 fatal("malloc in init_path for %c:%s: %s", drntol(drive
), base
,
113 if (ustrncmp(base
, dir
, ustrlen(base
)) == 0)
114 dir
+= ustrlen(base
);
120 u_char realname
[256];
121 u_char
*r
= realname
;
123 while ((*r
= *dir
) && *dir
++ != '/') {
130 dosname
[0] = drntol(drive
);
132 real_to_dos(realname
, &dosname
[2]);
134 if (dos_setcwd(dosname
)) {
135 fprintf(stderr
, "Failed to CD to directory %s in %s\n",
143 * Mark this drive as read only
146 dos_makereadonly(int drive
)
149 if (drive
< 0 || drive
>= MAX_DRIVE
)
151 paths
[drive
].read_only
= 1;
155 * Return read-only status of drive
158 dos_readonly(int drive
)
161 if (drive
< 0 || drive
>= MAX_DRIVE
)
163 debug(D_REDIR
, "dos_readonly(%d) -> %d\n", drive
, paths
[drive
].read_only
);
164 return (paths
[drive
].read_only
);
168 * Return DOS's idea of the CWD for drive
169 * Return 0 if the drive specified is not mapped (or bad)
172 dos_getcwd(int drive
)
175 if (drive
< 0 || drive
>= MAX_DRIVE
)
177 debug(D_REDIR
, "dos_getcwd(%d) -> %s\n", drive
, paths
[drive
].cwd
);
178 return (paths
[drive
].cwd
);
182 * Return DOS's idea of the CWD for drive
183 * Return 0 if the drive specified is not mapped (or bad)
186 dos_getpath(int drive
)
189 if (drive
< 0 || drive
>= MAX_DRIVE
)
191 debug(D_REDIR
, "dos_getpath(%d) -> %s\n", drive
, paths
[drive
].path
);
192 return (paths
[drive
].path
);
196 * Fix up a DOS path name. Strip out all '.' and '..' entries, turn
197 * '/' into '\\' and convert all lowercase to uppercase.
198 * Returns 0 on success or DOS errno
201 dos_makepath(u_char
*where
, u_char
*newpath
)
207 u_char tmppath
[1024];
208 u_char
*snewpath
= newpath
;
210 if (where
[0] != '\0' && where
[1] == ':') {
211 drive
= drlton(*where
);
212 *newpath
++ = *where
++;
213 *newpath
++ = *where
++;
216 *newpath
++ = drntol(diskdrive
);
220 if (drive
< 0 || drive
>= MAX_DRIVE
) {
221 debug(D_REDIR
,"drive %c invalid\n", drntol(drive
));
222 return (DISK_DRIVE_INVALID
);
226 if (d
->cwd
== NULL
) {
227 debug(D_REDIR
,"no cwd for drive %c\n",drntol(drive
));
228 return (DISK_DRIVE_INVALID
);
231 debug(D_REDIR
, "dos_makepath(%d, %s)\n", drive
, where
);
234 if (*where
!= '\\' && *where
!= '/') {
235 ustrncpy(tmppath
, d
->cwd
, 1024);
237 ustrncat(tmppath
, "/", 1024 - ustrlen(tmppath
));
238 ustrncat(tmppath
, where
, 1024 - ustrlen(tmppath
));
240 ustrncpy(tmppath
, where
, 1024 - ustrlen(tmppath
));
243 dirs
= get_entries(tmppath
);
245 return (PATH_NOT_FOUND
);
249 u_char
*dir
= *dirs
++;
250 if (*dir
== '/' || *dir
== '\\') {
253 } else if (dir
[0] == '.' && dir
[1] == 0) {
255 } else if (dir
[0] == '.' && dir
[1] == '.' && dir
[2] == '\0') {
256 while (np
[-1] != '/' && np
[-1] != '\\')
258 if (np
- 1 > newpath
)
263 while ((*np
= *dir
++) && np
- snewpath
< 1023)
273 * Set DOS's idea of the CWD for drive to be where.
274 * Returns DOS errno on failuer.
277 dos_setcwd(u_char
*where
)
279 u_char new_path
[1024];
280 u_char real_path
[1024];
286 debug(D_REDIR
, "dos_setcwd(%s)\n", where
);
288 error
= dos_makepath(where
, new_path
);
292 error
= dos_to_real_path(new_path
, real_path
, &drive
);
296 if (ustat(real_path
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
))
297 return (PATH_NOT_FOUND
);
298 if (uaccess(real_path
, R_OK
| X_OK
))
299 return (PATH_NOT_FOUND
);
302 d
->len
= ustrlen(new_path
+ 2);
304 if (d
->len
+ 1 > d
->maxlen
) {
306 d
->maxlen
= d
->len
+ 1 + 32;
307 d
->cwd
= (u_char
*)malloc(d
->maxlen
);
309 fatal("malloc in dos_setcwd for %c:%s: %s", drntol(drive
),
310 new_path
, strerror(errno
));
312 ustrncpy(d
->cwd
, new_path
+ 2, d
->maxlen
- d
->len
);
317 * Given a DOS path dos_path and a drive, convert it to a BSD pathname
318 * and store the result in real_path.
319 * Return DOS errno on failure.
322 dos_to_real_path(u_char
*dos_path
, u_char
*real_path
, int *drivep
)
325 u_char new_path
[1024];
331 debug(D_REDIR
, "dos_to_real_path(%s)\n", dos_path
);
333 if (dos_path
[0] != '\0' && dos_path
[1] == ':') {
334 drive
= drlton(*dos_path
);
343 return (DISK_DRIVE_INVALID
);
345 ustrcpy(real_path
, d
->path
);
351 ustrncpy(new_path
, dos_path
, 1024 - ustrlen(new_path
));
353 dirs
= get_entries(new_path
);
355 return (PATH_NOT_FOUND
);
359 * There are no . or .. entries to worry about either
362 while ((dir
= *++dirs
) != 0) {
364 dos_to_real(dir
, rp
);
374 * Provide a few istype() style functions.
375 * isvalid: True if the character is a valid DOS filename character
377 * isslash: True if '/' or '\'
388 u_char cattr
[256] = {
389 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
390 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
391 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 1, 3, 4, /* 0x20 */
392 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 0, 0, 0, 0, 6, /* 0x30 */
393 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
394 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 0, 1, 1, /* 0x50 */
395 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 0x60 */
396 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 0, 1, 1, 0, /* 0x70 */
397 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */
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,
404 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
410 return (cattr
[c
& 0xff] == 1);
416 return (cattr
[c
& 0xff] == 3);
422 return (cattr
[c
& 0xff] == 4);
426 * Given a real component, compute the DOS component.
429 real_to_dos(u_char
*real
, u_char
*dos
)
439 if (real
[0] == '.' && (real
[1] == '\0'
440 || (real
[1] == '.' && real
[2] == '\0'))) {
441 sprintf((char *)dos
, "%.8s", real
);
447 if (ustrcmp(real
, n
->real
) == 0) {
449 sprintf((char *)dos
, "%.8s.%.3s", n
->name
, n
->ext
);
451 sprintf((char *)dos
, "%.8s", n
->name
);
459 while (isvalid(*p
) && ncnt
< 8) {
466 while (isvalid(*p
) && ecnt
< 3) {
475 if (!*p
&& ncnt
<= 8 && ecnt
<= 3) {
478 if (ustrncmp(n
->name
, nm
, 8) == 0 && ustrncmp(n
->ext
, ex
, 3) == 0) {
489 n
= (Name_t
*)malloc(sizeof(Name_t
));
492 fatal("malloc in real_to_dos: %s\n", strerror(errno
));
494 n
->real
= ustrdup(real
);
497 fatal("strdup in real_to_dos: %s\n", strerror(errno
));
501 while (*p
&& ncnt
< 8) {
504 else if (islower(*p
))
505 n
->name
[ncnt
] = toupper(*p
);
509 n
->name
[ncnt
] = (*p
|= 0x80);
515 while (*p
&& ecnt
< 3) {
518 else if (islower(*p
))
519 n
->ext
[ecnt
] = toupper(*p
);
525 n
->ext
[ecnt
] = (*p
|= 0x80);
530 n
->name
[ncnt
] = '\0';
536 if (ustrncmp(n
->name
, nn
->name
, 8) == 0 &&
537 ustrncmp(n
->ext
, nn
->ext
, 3) == 0) {
545 * Dang, this name was already in the cache.
546 * Let's munge it a little and try again.
549 n
->ext
[ecnt
] = echar
;
552 } else if (echar
== 'Z') {
558 } else if (ncnt
< 8) {
559 n
->name
[ncnt
] = nchar
;
562 } else if (nchar
== 'Z') {
568 } else if (n
->ext
[2] < 'Z')
570 else if (n
->ext
[1] < 'Z')
572 else if (n
->ext
[0] < 'Z')
574 else if (n
->name
[7] < 'Z')
576 else if (n
->name
[6] < 'Z')
578 else if (n
->name
[5] < 'Z')
580 else if (n
->name
[4] < 'Z')
582 else if (n
->name
[3] < 'Z')
584 else if (n
->name
[2] < 'Z')
586 else if (n
->name
[1] < 'Z')
588 else if (n
->name
[0] < 'Z')
595 sprintf((char *)dos
, "%.8s.%.3s", n
->name
, n
->ext
);
597 sprintf((char *)dos
, "%.8s", n
->name
);
604 * Given a DOS component, compute the REAL component.
607 dos_to_real(u_char
*dos
, u_char
*real
)
615 while (ncnt
< 8 && (isvalid(*dos
) || islower(*dos
))) {
616 name
[ncnt
++] = islower(*dos
) ? toupper(*dos
) : *dos
;
623 while (ecnt
< 3 && (isvalid(*++dos
) || islower(*dos
))) {
624 ext
[ecnt
++] = islower(*dos
) ? toupper(*dos
) : *dos
;
631 if (!ustrncmp(name
, n
->name
, 8) && !ustrncmp(ext
, n
->ext
, 3)) {
632 ustrcpy(real
, n
->real
);
639 sprintf((char *)real
, "%-.8s.%-.3s", name
, ext
);
641 sprintf((char *)real
, "%-.8s", name
);
645 *real
= tolower(*real
);
651 * convert a path into an argv[] like vector of components.
652 * If the path starts with a '/' or '\' then the first entry
653 * will be "/" or "\". This is the only case in which a "/"
654 * or "\" may appear in an entry.
655 * Also convert all lowercase to uppercase.
656 * The data returned is in a static area, so a second call will
657 * erase the data of the first.
660 get_entries(u_char
*path
)
662 static u_char
*entries
[128]; /* Maximum depth... */
663 static u_char mypath
[1024];
664 u_char
**e
= entries
;
667 ustrncpy(mypath
+1, path
, 1022);
670 if (path
[0] == '/' || path
[0] == '\\') {
675 while (*p
&& e
< entries
+ 127) {
676 while (*p
&& (*p
== '/' || *p
== '\\')) {
683 while (*p
&& (*p
!= '/' && *p
!= '\\')) {
689 * skip over the '/' or '\'
699 * Return file system statistics for drive.
700 * Return the DOS errno on failure.
703 get_space(int drive
, fsstat_t
*fs
)
709 struct statfs
*me
= 0;
711 if (drive
< 0 || drive
>= MAX_DRIVE
)
712 return (DISK_DRIVE_INVALID
);
717 return (DISK_DRIVE_INVALID
);
719 nfs
= getfsstat(0, 0, MNT_WAIT
);
721 buf
= (struct statfs
*)malloc(sizeof(struct statfs
) * nfs
);
724 return (DISK_DRIVE_INVALID
);
726 nfs
= getfsstat(buf
, sizeof(struct statfs
) * nfs
, MNT_WAIT
);
728 for (i
= 0; i
< nfs
; ++i
) {
729 if (strncmp(buf
[i
].f_mntonname
, (char *)d
->path
, strlen(buf
[i
].f_mntonname
)))
731 if (me
&& strlen(me
->f_mntonname
) > strlen(buf
[i
].f_mntonname
))
739 fs
->bytes_sector
= 512;
740 fs
->sectors_cluster
= me
->f_bsize
/ fs
->bytes_sector
;
741 fs
->total_clusters
= me
->f_blocks
/ fs
->sectors_cluster
;
742 while (fs
->total_clusters
> 0xFFFF) {
743 fs
->sectors_cluster
*= 2;
744 fs
->total_clusters
= me
->f_blocks
/ fs
->sectors_cluster
;
746 fs
->avail_clusters
= me
->f_bavail
/ fs
->sectors_cluster
;
753 u_char searchdir
[1024];
758 * Convert a dos filename into normal form (8.3 format, space padded)
761 to_dos_fcb(u_char
*p
, u_char
*expr
)
765 if (expr
[0] == '.') {
767 if (expr
[1] == '\0') {
768 for (i
= 1; i
< 11; i
++)
772 if (expr
[1] == '.') {
774 if (expr
[2] == '\0') {
775 for (i
= 2; i
< 11; i
++)
782 for (i
= 8; i
> 0; i
--) {
794 if (islower(*expr
)) {
795 *p
++ = toupper(*expr
++);
804 while (*expr
!= '\0' && *expr
!= '.')
809 for (i
= 3; i
> 0; i
--) {
821 if (islower(*expr
)) {
822 *p
++ = toupper(*expr
++);
833 ** DOS can't handle multiple concurrent searches, and if we leave the
834 ** search instance in the DTA we get screwed as soon as someone starts lots
835 ** of searches without finishing them properly.
836 ** We allocate a single search structure, and recycle it if find_first()
837 ** is called before a search ends.
839 static search_t dir_search
;
842 * Find the first file on drive which matches the path with the given
844 * If found, the result is placed in dir (32 bytes).
845 * The DTA is populated as required by DOS, but the state area is ignored.
846 * Returns DOS errno on failure.
849 find_first(u_char
*path
, int attr
, dosdir_t
*dir
, find_block_t
*dta
)
851 u_char new_path
[1024], real_path
[1024];
852 u_char
*expr
, *slash
;
855 search_t
*search
= &dir_search
;
857 debug(D_REDIR
, "find_first(%s, %x, %x)\n", path
, attr
, (int)dta
);
859 error
= dos_makepath(path
, new_path
);
865 while (*expr
!= '\0') {
866 if (*expr
== '\\' || *expr
== '/')
872 error
= dos_to_real_path(new_path
, real_path
, &drive
);
876 if (attr
== VOLUME_LABEL
) /* never find a volume label */
877 return (NO_MORE_FILES
);
879 if (search
->dp
) /* stale search? */
880 closedir(search
->dp
);
882 search
->dp
= opendir(real_path
);
883 if (search
->dp
== NULL
)
884 return (PATH_NOT_FOUND
);
886 ustrncpy(search
->searchdir
, real_path
, 1024 - ustrlen(real_path
));
887 search
->searchend
= search
->searchdir
;
888 while (*search
->searchend
)
890 *search
->searchend
++ = '/';
892 search
->dp
->dd_fd
= squirrel_fd(search
->dp
->dd_fd
);
894 dta
->drive
= drive
| 0x80;
895 to_dos_fcb(dta
->pattern
, slash
);
898 return (find_next(dir
, dta
));
902 * Continue on where find_first left off.
903 * The results will be placed in dir.
904 * DTA state area is ignored.
907 find_next(dosdir_t
*dir
, find_block_t
*dta
)
909 search_t
*search
= &dir_search
;
915 return (NO_MORE_FILES
);
918 debug(D_REDIR
, "find_next()\n");
921 while ((d
= readdir(search
->dp
)) != 0) {
922 real_to_dos((u_char
*)d
->d_name
, name
);
923 to_dos_fcb(dir
->name
, name
);
925 printf("find_next: |%-11.11s| |%-11.11s| |%s| |%s|\n", dta
->pattern
, dir
->name
, d
->d_name
, name
);
927 if (dos_match(dta
->pattern
, dir
->name
) == 0)
930 ustrcpy(search
->searchend
, (u_char
*)d
->d_name
);
931 if (ustat(search
->searchdir
, &sb
) < 0)
934 printf("find_next: %x\n", sb
.st_mode
);
936 if (S_ISDIR(sb
.st_mode
)) {
937 if (!(dta
->flag
& DIRECTORY
)) {
941 dir
->attr
= (S_ISDIR(sb
.st_mode
) ? DIRECTORY
: 0) |
942 (uaccess(search
->searchdir
, W_OK
) < 0 ? READ_ONLY_FILE
: 0);
943 encode_dos_file_time(sb
.st_mtime
, &dir
->date
, &dir
->time
);
945 dir
->size
= sb
.st_size
;
947 printf("find_next: found %s\n",name
);
951 closedir(search
->dp
);
953 return (NO_MORE_FILES
);
957 * perfrom hokey DOS pattern matching. pattern may contain the wild cards
958 * '*' and '?' only. Follow the DOS convention that '?*', '*?' and '**' all
959 * are the same as '*'. Also, allow '?' to match the blank padding in a
960 * name (hence, ???? matchs all of "a", "ab", "abc" and "abcd" but not "abcde")
961 * Return 1 if a match is found, 0 if not.
963 * XXX This appears to be severely busted! (no * handling - normal?)
966 dos_match(u_char
*pattern
, u_char
*string
)
971 * Check the base part first
973 for (i
= 11; i
> 0; i
--) {
974 if (*pattern
!= '?' && *string
!= *pattern
)