Bump copyright years. Happy new year!
[dragonfly.git] / usr.bin / doscmd / cwd.c
blob5e6e198d267145f0fb6cb4fd7adbf1cacf55fbc3
1 /*
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
7 * are met:
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
16 * Design, Inc.
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
28 * SUCH DAMAGE.
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>
39 #include <dirent.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <stdio.h>
47 #include "doscmd.h"
48 #include "cwd.h"
50 /* Local functions */
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
59 typedef struct {
60 u_char *path;
61 u_char *cwd;
62 int len;
63 int maxlen;
64 int read_only:1;
65 } Path_t;
67 typedef struct Name_t {
68 u_char *real;
69 struct Name_t *next;
70 u_char name[9];
71 u_char ext[4];
72 } Name_t;
75 #define MAX_DRIVE 26
77 static Path_t paths[MAX_DRIVE];
78 static Name_t *names;
81 * Initialize the drive to be based at 'base' in the BSD filesystem
83 void
84 init_path(int drive, const u_char *base, const u_char *dir)
86 Path_t *d;
88 if (drive < 0 || drive >= MAX_DRIVE)
89 return;
91 debug(D_TRAPS3, "init_path(%d, %s, %s)\n", drive, base, dir);
93 d = &paths[drive];
95 if (d->path)
96 free(d->path);
98 if ((d->path = ustrdup(base)) == NULL)
99 fatal("strdup in init_path for %c:%s: %s", drntol(drive), base,
100 strerror(errno));
102 if (d->maxlen < 2) {
103 d->maxlen = 128;
104 if ((d->cwd = (u_char *)malloc(d->maxlen)) == NULL)
105 fatal("malloc in init_path for %c:%s: %s", drntol(drive), base,
106 strerror(errno));
109 d->cwd[0] = '\\';
110 d->cwd[1] = 0;
111 d->len = 1;
112 if (dir) {
113 if (ustrncmp(base, dir, ustrlen(base)) == 0)
114 dir += ustrlen(base);
115 while (*dir == '/')
116 ++dir;
118 while (*dir) {
119 u_char dosname[15];
120 u_char realname[256];
121 u_char *r = realname;
123 while ((*r = *dir) && *dir++ != '/') {
124 ++r;
126 *r = 0;
127 while (*dir == '/')
128 ++dir;
130 dosname[0] = drntol(drive);
131 dosname[1] = ':';
132 real_to_dos(realname, &dosname[2]);
134 if (dos_setcwd(dosname)) {
135 fprintf(stderr, "Failed to CD to directory %s in %s\n",
136 dosname, d->cwd);
143 * Mark this drive as read only
145 void
146 dos_makereadonly(int drive)
149 if (drive < 0 || drive >= MAX_DRIVE)
150 return;
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)
162 return (0);
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)
171 u_char *
172 dos_getcwd(int drive)
175 if (drive < 0 || drive >= MAX_DRIVE)
176 return (0);
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)
185 u_char *
186 dos_getpath(int drive)
189 if (drive < 0 || drive >= MAX_DRIVE)
190 return (0);
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)
203 int drive;
204 u_char **dirs;
205 u_char *np;
206 Path_t *d;
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++;
214 } else {
215 drive = diskdrive;
216 *newpath++ = drntol(diskdrive);
217 *newpath++ = ':';
220 if (drive < 0 || drive >= MAX_DRIVE) {
221 debug(D_REDIR,"drive %c invalid\n", drntol(drive));
222 return (DISK_DRIVE_INVALID);
225 d = &paths[drive];
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);
233 np = newpath;
234 if (*where != '\\' && *where != '/') {
235 ustrncpy(tmppath, d->cwd, 1024);
236 if (d->cwd[1])
237 ustrncat(tmppath, "/", 1024 - ustrlen(tmppath));
238 ustrncat(tmppath, where, 1024 - ustrlen(tmppath));
239 } else {
240 ustrncpy(tmppath, where, 1024 - ustrlen(tmppath));
243 dirs = get_entries(tmppath);
244 if (dirs == NULL)
245 return (PATH_NOT_FOUND);
247 np = newpath;
248 while (*dirs) {
249 u_char *dir = *dirs++;
250 if (*dir == '/' || *dir == '\\') {
251 np = newpath + 1;
252 newpath[0] = '\\';
253 } else if (dir[0] == '.' && dir[1] == 0) {
255 } else if (dir[0] == '.' && dir[1] == '.' && dir[2] == '\0') {
256 while (np[-1] != '/' && np[-1] != '\\')
257 --np;
258 if (np - 1 > newpath)
259 --np;
260 } else {
261 if (np[-1] != '\\')
262 *np++ = '\\';
263 while ((*np = *dir++) && np - snewpath < 1023)
264 ++np;
267 *np = 0;
269 return (0);
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];
281 int drive;
282 struct stat sb;
283 Path_t *d;
284 int error;
286 debug(D_REDIR, "dos_setcwd(%s)\n", where);
288 error = dos_makepath(where, new_path);
289 if (error)
290 return (error);
292 error = dos_to_real_path(new_path, real_path, &drive);
293 if (error)
294 return (error);
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);
301 d = &paths[drive];
302 d->len = ustrlen(new_path + 2);
304 if (d->len + 1 > d->maxlen) {
305 free(d->cwd);
306 d->maxlen = d->len + 1 + 32;
307 d->cwd = (u_char *)malloc(d->maxlen);
308 if (d->cwd == NULL)
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);
313 return (0);
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)
324 Path_t *d;
325 u_char new_path[1024];
326 u_char *rp;
327 u_char **dirs;
328 u_char *dir;
329 int drive;
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);
335 dos_path++;
336 dos_path++;
337 } else {
338 drive = diskdrive;
341 d = &paths[drive];
342 if (d->cwd == NULL)
343 return (DISK_DRIVE_INVALID);
345 ustrcpy(real_path, d->path);
347 rp = real_path;
348 while (*rp)
349 ++rp;
351 ustrncpy(new_path, dos_path, 1024 - ustrlen(new_path));
353 dirs = get_entries(new_path);
354 if (dirs == NULL)
355 return (PATH_NOT_FOUND);
358 * Skip the leading /
359 * There are no . or .. entries to worry about either
362 while ((dir = *++dirs) != 0) {
363 *rp++ = '/';
364 dos_to_real(dir, rp);
365 while (*rp)
366 ++rp;
369 *drivep = drive;
370 return (0);
374 * Provide a few istype() style functions.
375 * isvalid: True if the character is a valid DOS filename character
376 * isdot: True if '.'
377 * isslash: True if '/' or '\'
379 * 0 - invalid
380 * 1 - okay
381 * 2 - *
382 * 3 - dot
383 * 4 - slash
384 * 5 - colon
385 * 6 - ?
386 * 7 - lowercase
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,
407 static inline int
408 isvalid(unsigned c)
410 return (cattr[c & 0xff] == 1);
413 static inline int
414 isdot(unsigned c)
416 return (cattr[c & 0xff] == 3);
419 static inline int
420 isslash(unsigned c)
422 return (cattr[c & 0xff] == 4);
426 * Given a real component, compute the DOS component.
428 void
429 real_to_dos(u_char *real, u_char *dos)
431 Name_t *n;
432 Name_t *nn;
433 u_char *p;
434 u_char nm[9], ex[4];
435 int ncnt, ecnt;
436 int echar = '0';
437 int nchar = '0';
439 if (real[0] == '.' && (real[1] == '\0'
440 || (real[1] == '.' && real[2] == '\0'))) {
441 sprintf((char *)dos, "%.8s", real);
442 return;
445 n = names;
446 while (n) {
447 if (ustrcmp(real, n->real) == 0) {
448 if (n->ext[0])
449 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
450 else
451 sprintf((char *)dos, "%.8s", n->name);
452 return;
454 n = n->next;
457 p = real;
458 ncnt = ecnt = 0;
459 while (isvalid(*p) && ncnt < 8) {
460 nm[ncnt] = *p;
461 ++ncnt;
462 ++p;
464 if (isdot(*p)) {
465 ++p;
466 while (isvalid(*p) && ecnt < 3) {
467 ex[ecnt] = *p;
468 ++ecnt;
469 ++p;
472 nm[ncnt] = '\0';
473 ex[ecnt] = '\0';
475 if (!*p && ncnt <= 8 && ecnt <= 3) {
476 n = names;
477 while (n) {
478 if (ustrncmp(n->name, nm, 8) == 0 && ustrncmp(n->ext, ex, 3) == 0) {
479 break;
481 n = n->next;
483 if (n == 0) {
484 ustrcpy(dos, real);
485 return;
489 n = (Name_t *)malloc(sizeof(Name_t));
491 if (!n)
492 fatal("malloc in real_to_dos: %s\n", strerror(errno));
494 n->real = ustrdup(real);
496 if (!n->real)
497 fatal("strdup in real_to_dos: %s\n", strerror(errno));
499 p = real;
500 ncnt = ecnt = 0;
501 while (*p && ncnt < 8) {
502 if (isvalid(*p))
503 n->name[ncnt] = *p;
504 else if (islower(*p))
505 n->name[ncnt] = toupper(*p);
506 else if (isdot(*p))
507 break;
508 else
509 n->name[ncnt] = (*p |= 0x80);
510 ++ncnt;
511 ++p;
513 if (isdot(*p)) {
514 ++p;
515 while (*p && ecnt < 3) {
516 if (isvalid(*p))
517 n->ext[ecnt] = *p;
518 else if (islower(*p))
519 n->ext[ecnt] = toupper(*p);
520 #if 0
521 else if (isdot(*p))
522 ERROR
523 #endif
524 else
525 n->ext[ecnt] = (*p |= 0x80);
526 ++ecnt;
527 ++p;
530 n->name[ncnt] = '\0';
531 n->ext[ecnt] = '\0';
533 for (;;) {
534 nn = names;
535 while (nn) {
536 if (ustrncmp(n->name, nn->name, 8) == 0 &&
537 ustrncmp(n->ext, nn->ext, 3) == 0) {
538 break;
540 nn = nn->next;
542 if (!nn)
543 break;
545 * Dang, this name was already in the cache.
546 * Let's munge it a little and try again.
548 if (ecnt < 3) {
549 n->ext[ecnt] = echar;
550 if (echar == '9') {
551 echar = 'A';
552 } else if (echar == 'Z') {
553 ++ecnt;
554 echar = '0';
555 } else {
556 ++echar;
558 } else if (ncnt < 8) {
559 n->name[ncnt] = nchar;
560 if (nchar == '9') {
561 nchar = 'A';
562 } else if (nchar == 'Z') {
563 ++ncnt;
564 nchar = '0';
565 } else {
566 ++nchar;
568 } else if (n->ext[2] < 'Z')
569 n->ext[2]++;
570 else if (n->ext[1] < 'Z')
571 n->ext[1]++;
572 else if (n->ext[0] < 'Z')
573 n->ext[0]++;
574 else if (n->name[7] < 'Z')
575 n->name[7]++;
576 else if (n->name[6] < 'Z')
577 n->name[6]++;
578 else if (n->name[5] < 'Z')
579 n->name[5]++;
580 else if (n->name[4] < 'Z')
581 n->name[4]++;
582 else if (n->name[3] < 'Z')
583 n->name[3]++;
584 else if (n->name[2] < 'Z')
585 n->name[2]++;
586 else if (n->name[1] < 'Z')
587 n->name[1]++;
588 else if (n->name[0] < 'Z')
589 n->name[0]++;
590 else
591 break;
594 if (n->ext[0])
595 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
596 else
597 sprintf((char *)dos, "%.8s", n->name);
598 n->next = names;
599 names = n;
604 * Given a DOS component, compute the REAL component.
606 void
607 dos_to_real(u_char *dos, u_char *real)
609 int ncnt = 0;
610 int ecnt = 0;
611 u_char name[8];
612 u_char ext[3];
613 Name_t *n = names;
615 while (ncnt < 8 && (isvalid(*dos) || islower(*dos))) {
616 name[ncnt++] = islower(*dos) ? toupper(*dos) : *dos;
617 ++dos;
619 if (ncnt < 8)
620 name[ncnt] = 0;
622 if (isdot(*dos)) {
623 while (ecnt < 3 && (isvalid(*++dos) || islower(*dos))) {
624 ext[ecnt++] = islower(*dos) ? toupper(*dos) : *dos;
627 if (ecnt < 3)
628 ext[ecnt] = 0;
630 while (n) {
631 if (!ustrncmp(name, n->name, 8) && !ustrncmp(ext, n->ext, 3)) {
632 ustrcpy(real, n->real);
633 return;
635 n = n->next;
638 if (ext[0])
639 sprintf((char *)real, "%-.8s.%-.3s", name, ext);
640 else
641 sprintf((char *)real, "%-.8s", name);
643 while (*real) {
644 if (isupper(*real))
645 *real = tolower(*real);
646 ++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.
659 u_char **
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;
665 u_char *p = mypath;
667 ustrncpy(mypath+1, path, 1022);
668 p = mypath+1;
669 mypath[1023] = 0;
670 if (path[0] == '/' || path[0] == '\\') {
671 mypath[0] = path[0];
672 *e++ = mypath;
673 *p++ = 0;
675 while (*p && e < entries + 127) {
676 while (*p && (*p == '/' || *p == '\\')) {
677 ++p;
680 if (!*p)
681 break;
682 *e++ = p;
683 while (*p && (*p != '/' && *p != '\\')) {
684 if (islower(*p))
685 *p = tolower(*p);
686 ++p;
689 * skip over the '/' or '\'
691 if (*p)
692 *p++ = 0;
694 *e = 0;
695 return (entries);
699 * Return file system statistics for drive.
700 * Return the DOS errno on failure.
703 get_space(int drive, fsstat_t *fs)
705 Path_t *d;
706 struct statfs *buf;
707 int nfs;
708 int i;
709 struct statfs *me = 0;
711 if (drive < 0 || drive >= MAX_DRIVE)
712 return (DISK_DRIVE_INVALID);
714 d = &paths[drive];
716 if (!d->path)
717 return (DISK_DRIVE_INVALID);
719 nfs = getfsstat(0, 0, MNT_WAIT);
721 buf = (struct statfs *)malloc(sizeof(struct statfs) * nfs);
722 if (buf == NULL) {
723 perror("get_space");
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)))
730 continue;
731 if (me && strlen(me->f_mntonname) > strlen(buf[i].f_mntonname))
732 continue;
733 me = buf + i;
735 if (!me) {
736 free(buf);
737 return (3);
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;
747 free(buf);
748 return (0);
751 #if 0
752 DIR *dp = 0;
753 u_char searchdir[1024];
754 u_char *searchend;
755 #endif
758 * Convert a dos filename into normal form (8.3 format, space padded)
760 static void
761 to_dos_fcb(u_char *p, u_char *expr)
763 int i;
765 if (expr[0] == '.') {
766 p[0] = '.';
767 if (expr[1] == '\0') {
768 for (i = 1; i < 11; i++)
769 p[i] = ' ';
770 return;
772 if (expr[1] == '.') {
773 p[1] = '.';
774 if (expr[2] == '\0') {
775 for (i = 2; i < 11; i++)
776 p[i] = ' ';
777 return;
782 for (i = 8; i > 0; i--) {
783 switch (*expr) {
784 case '\0':
785 case '.':
786 for (; i > 0; i--)
787 *p++ = ' ';
788 break;
789 case '*':
790 for (; i > 0; i--)
791 *p++ = '?';
792 break;
793 default:
794 if (islower(*expr)) {
795 *p++ = toupper(*expr++);
796 break;
798 case '?':
799 *p++ = *expr++;
800 break;
804 while (*expr != '\0' && *expr != '.')
805 ++expr;
806 if (*expr)
807 ++expr;
809 for (i = 3; i > 0; i--) {
810 switch (*expr) {
811 case '\0':
812 case '.':
813 for (; i > 0; i--)
814 *p++ = ' ';
815 break;
816 case '*':
817 for (; i > 0; i--)
818 *p++ = '?';
819 break;
820 default:
821 if (islower(*expr)) {
822 *p++ = toupper(*expr++);
823 break;
825 case '?':
826 *p++ = *expr++;
827 break;
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
843 * attributes attr.
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;
853 int drive;
854 int error;
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);
860 if (error)
861 return (error);
863 expr = new_path;
864 slash = 0;
865 while (*expr != '\0') {
866 if (*expr == '\\' || *expr == '/')
867 slash = expr;
868 expr++;
870 *slash++ = '\0';
872 error = dos_to_real_path(new_path, real_path, &drive);
873 if (error)
874 return (error);
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)
889 ++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);
896 dta->flag = attr;
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;
910 struct dirent *d;
911 struct stat sb;
912 u_char name[16];
914 if (!search->dp)
915 return (NO_MORE_FILES);
917 #if 0
918 debug(D_REDIR, "find_next()\n");
919 #endif
921 while ((d = readdir(search->dp)) != 0) {
922 real_to_dos((u_char *)d->d_name, name);
923 to_dos_fcb(dir->name, name);
924 #if 0
925 printf("find_next: |%-11.11s| |%-11.11s| |%s| |%s|\n", dta->pattern, dir->name, d->d_name, name);
926 #endif
927 if (dos_match(dta->pattern, dir->name) == 0)
928 continue;
930 ustrcpy(search->searchend, (u_char *)d->d_name);
931 if (ustat(search->searchdir, &sb) < 0)
932 continue;
933 #if 0
934 printf("find_next: %x\n", sb.st_mode);
935 #endif
936 if (S_ISDIR(sb.st_mode)) {
937 if (!(dta->flag & DIRECTORY)) {
938 continue;
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);
944 dir->start = 1;
945 dir->size = sb.st_size;
946 #if 0
947 printf("find_next: found %s\n",name);
948 #endif
949 return (0);
951 closedir(search->dp);
952 search->dp = NULL;
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)
968 int i;
971 * Check the base part first
973 for (i = 11; i > 0; i--) {
974 if (*pattern != '?' && *string != *pattern)
975 return (0);
976 pattern++, string++;
978 return (1);