kernel: Use hashdestroy() to free hash tables allocated with hashinit().
[dragonfly.git] / usr.bin / doscmd / cwd.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 1992, 1993, 1996
3 * Berkeley Software Design, Inc. All rights reserved.
4 *
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.
17 *
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.
29 *
30 * BSDI cwd.c,v 2.2 1996/04/08 19:32:25 bostic Exp
1de703da
MD
31 *
32 * $FreeBSD: src/usr.bin/doscmd/cwd.c,v 1.6.2.3 2002/04/25 11:04:50 tg Exp $
984263bc
MD
33 */
34
984263bc
MD
35#include <sys/types.h>
36#include <sys/param.h>
37#include <sys/mount.h>
38#include <dirent.h>
39#include <errno.h>
40#include <unistd.h>
41#include <stdlib.h>
42#include <string.h>
43#include <ctype.h>
44#include <stdio.h>
45
46#include "doscmd.h"
47#include "cwd.h"
48
49/* Local functions */
50static inline int isvalid(unsigned);
51static inline int isdot(unsigned);
52static inline int isslash(unsigned);
53static void to_dos_fcb(u_char *, u_char *);
54
55#define D_REDIR 0x0080000 /* XXX - ack */
56#define D_TRAPS3 0x0200000
57
58typedef struct {
59 u_char *path;
60 u_char *cwd;
61 int len;
62 int maxlen;
63 int read_only:1;
64} Path_t;
65
66typedef struct Name_t {
67 u_char *real;
68 struct Name_t *next;
69 u_char name[9];
70 u_char ext[4];
71} Name_t;
72
73
74#define MAX_DRIVE 26
75
76static Path_t paths[MAX_DRIVE];
77static Name_t *names;
78
79/*
80 * Initialize the drive to be based at 'base' in the BSD filesystem
81 */
82void
83init_path(int drive, const u_char *base, const u_char *dir)
84{
85 Path_t *d;
86
87 if (drive < 0 || drive >= MAX_DRIVE)
88 return;
89
90 debug(D_TRAPS3, "init_path(%d, %s, %s)\n", drive, base, dir);
91
92 d = &paths[drive];
93
94 if (d->path)
95 free(d->path);
96
97 if ((d->path = ustrdup(base)) == NULL)
98 fatal("strdup in init_path for %c:%s: %s", drntol(drive), base,
99 strerror(errno));
100
101 if (d->maxlen < 2) {
102 d->maxlen = 128;
103 if ((d->cwd = (u_char *)malloc(d->maxlen)) == NULL)
104 fatal("malloc in init_path for %c:%s: %s", drntol(drive), base,
105 strerror(errno));
106 }
107
108 d->cwd[0] = '\\';
109 d->cwd[1] = 0;
110 d->len = 1;
111 if (dir) {
112 if (ustrncmp(base, dir, ustrlen(base)) == 0)
113 dir += ustrlen(base);
114 while (*dir == '/')
115 ++dir;
116
117 while (*dir) {
118 u_char dosname[15];
119 u_char realname[256];
fc6d0222 120 u_char *r = realname;
984263bc
MD
121
122 while ((*r = *dir) && *dir++ != '/') {
123 ++r;
124 }
125 *r = 0;
126 while (*dir == '/')
127 ++dir;
128
129 dosname[0] = drntol(drive);
130 dosname[1] = ':';
131 real_to_dos(realname, &dosname[2]);
132
133 if (dos_setcwd(dosname)) {
134 fprintf(stderr, "Failed to CD to directory %s in %s\n",
135 dosname, d->cwd);
136 }
137 }
138 }
139}
140
141/*
142 * Mark this drive as read only
143 */
144void
145dos_makereadonly(int drive)
146{
147
148 if (drive < 0 || drive >= MAX_DRIVE)
149 return;
150 paths[drive].read_only = 1;
151}
152
153/*
154 * Return read-only status of drive
155 */
156int
157dos_readonly(int drive)
158{
159
160 if (drive < 0 || drive >= MAX_DRIVE)
161 return (0);
162 debug(D_REDIR, "dos_readonly(%d) -> %d\n", drive, paths[drive].read_only);
163 return (paths[drive].read_only);
164}
165
166/*
167 * Return DOS's idea of the CWD for drive
168 * Return 0 if the drive specified is not mapped (or bad)
169 */
170u_char *
171dos_getcwd(int drive)
172{
173
174 if (drive < 0 || drive >= MAX_DRIVE)
175 return (0);
176 debug(D_REDIR, "dos_getcwd(%d) -> %s\n", drive, paths[drive].cwd);
177 return (paths[drive].cwd);
178}
179
180/*
181 * Return DOS's idea of the CWD for drive
182 * Return 0 if the drive specified is not mapped (or bad)
183 */
184u_char *
185dos_getpath(int drive)
186{
187
188 if (drive < 0 || drive >= MAX_DRIVE)
189 return (0);
190 debug(D_REDIR, "dos_getpath(%d) -> %s\n", drive, paths[drive].path);
191 return (paths[drive].path);
192}
193
194/*
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
198 */
199int
200dos_makepath(u_char *where, u_char *newpath)
201{
202 int drive;
203 u_char **dirs;
204 u_char *np;
205 Path_t *d;
206 u_char tmppath[1024];
207 u_char *snewpath = newpath;
208
209 if (where[0] != '\0' && where[1] == ':') {
210 drive = drlton(*where);
211 *newpath++ = *where++;
212 *newpath++ = *where++;
213 } else {
214 drive = diskdrive;
215 *newpath++ = drntol(diskdrive);
216 *newpath++ = ':';
217 }
218
219 if (drive < 0 || drive >= MAX_DRIVE) {
220 debug(D_REDIR,"drive %c invalid\n", drntol(drive));
221 return (DISK_DRIVE_INVALID);
222 }
223
224 d = &paths[drive];
225 if (d->cwd == NULL) {
226 debug(D_REDIR,"no cwd for drive %c\n",drntol(drive));
227 return (DISK_DRIVE_INVALID);
228 }
229
230 debug(D_REDIR, "dos_makepath(%d, %s)\n", drive, where);
231
232 np = newpath;
233 if (*where != '\\' && *where != '/') {
234 ustrncpy(tmppath, d->cwd, 1024);
235 if (d->cwd[1])
236 ustrncat(tmppath, "/", 1024 - ustrlen(tmppath));
237 ustrncat(tmppath, where, 1024 - ustrlen(tmppath));
238 } else {
239 ustrncpy(tmppath, where, 1024 - ustrlen(tmppath));
240 }
241
242 dirs = get_entries(tmppath);
243 if (dirs == NULL)
244 return (PATH_NOT_FOUND);
245
246 np = newpath;
247 while (*dirs) {
248 u_char *dir = *dirs++;
249 if (*dir == '/' || *dir == '\\') {
250 np = newpath + 1;
251 newpath[0] = '\\';
252 } else if (dir[0] == '.' && dir[1] == 0) {
253 ;
254 } else if (dir[0] == '.' && dir[1] == '.' && dir[2] == '\0') {
255 while (np[-1] != '/' && np[-1] != '\\')
256 --np;
257 if (np - 1 > newpath)
258 --np;
259 } else {
260 if (np[-1] != '\\')
261 *np++ = '\\';
262 while ((*np = *dir++) && np - snewpath < 1023)
263 ++np;
264 }
265 }
266 *np = 0;
267
268 return (0);
269}
270
271/*
272 * Set DOS's idea of the CWD for drive to be where.
273 * Returns DOS errno on failuer.
274 */
275int
276dos_setcwd(u_char *where)
277{
278 u_char new_path[1024];
279 u_char real_path[1024];
280 int drive;
281 struct stat sb;
282 Path_t *d;
283 int error;
284
285 debug(D_REDIR, "dos_setcwd(%s)\n", where);
286
287 error = dos_makepath(where, new_path);
288 if (error)
289 return (error);
290
291 error = dos_to_real_path(new_path, real_path, &drive);
292 if (error)
293 return (error);
294
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);
299
300 d = &paths[drive];
301 d->len = ustrlen(new_path + 2);
302
303 if (d->len + 1 > d->maxlen) {
304 free(d->cwd);
305 d->maxlen = d->len + 1 + 32;
306 d->cwd = (u_char *)malloc(d->maxlen);
307 if (d->cwd == NULL)
308 fatal("malloc in dos_setcwd for %c:%s: %s", drntol(drive),
309 new_path, strerror(errno));
310 }
311 ustrncpy(d->cwd, new_path + 2, d->maxlen - d->len);
312 return (0);
313}
314
315/*
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.
319 */
320int
321dos_to_real_path(u_char *dos_path, u_char *real_path, int *drivep)
322{
323 Path_t *d;
324 u_char new_path[1024];
325 u_char *rp;
326 u_char **dirs;
327 u_char *dir;
328 int drive;
329
330 debug(D_REDIR, "dos_to_real_path(%s)\n", dos_path);
331
332 if (dos_path[0] != '\0' && dos_path[1] == ':') {
333 drive = drlton(*dos_path);
334 dos_path++;
335 dos_path++;
336 } else {
337 drive = diskdrive;
338 }
339
340 d = &paths[drive];
341 if (d->cwd == NULL)
342 return (DISK_DRIVE_INVALID);
343
344 ustrcpy(real_path, d->path);
345
346 rp = real_path;
347 while (*rp)
348 ++rp;
349
350 ustrncpy(new_path, dos_path, 1024 - ustrlen(new_path));
351
352 dirs = get_entries(new_path);
353 if (dirs == NULL)
354 return (PATH_NOT_FOUND);
355
356 /*
357 * Skip the leading /
358 * There are no . or .. entries to worry about either
359 */
360
678e8cc6 361 while ((dir = *++dirs) != NULL) {
984263bc
MD
362 *rp++ = '/';
363 dos_to_real(dir, rp);
364 while (*rp)
365 ++rp;
366 }
367
368 *drivep = drive;
369 return (0);
370}
371
372/*
373 * Provide a few istype() style functions.
374 * isvalid: True if the character is a valid DOS filename character
375 * isdot: True if '.'
376 * isslash: True if '/' or '\'
377 *
378 * 0 - invalid
379 * 1 - okay
380 * 2 - *
381 * 3 - dot
382 * 4 - slash
383 * 5 - colon
384 * 6 - ?
385 * 7 - lowercase
386 */
387u_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,
404};
405
406static inline int
407isvalid(unsigned c)
408{
409 return (cattr[c & 0xff] == 1);
410}
411
412static inline int
413isdot(unsigned c)
414{
415 return (cattr[c & 0xff] == 3);
416}
417
418static inline int
419isslash(unsigned c)
420{
421 return (cattr[c & 0xff] == 4);
422}
423
424/*
425 * Given a real component, compute the DOS component.
426 */
427void
428real_to_dos(u_char *real, u_char *dos)
429{
430 Name_t *n;
431 Name_t *nn;
432 u_char *p;
433 u_char nm[9], ex[4];
434 int ncnt, ecnt;
435 int echar = '0';
436 int nchar = '0';
437
438 if (real[0] == '.' && (real[1] == '\0'
439 || (real[1] == '.' && real[2] == '\0'))) {
440 sprintf((char *)dos, "%.8s", real);
441 return;
442 }
443
444 n = names;
445 while (n) {
446 if (ustrcmp(real, n->real) == 0) {
447 if (n->ext[0])
448 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
449 else
450 sprintf((char *)dos, "%.8s", n->name);
451 return;
452 }
453 n = n->next;
454 }
455
456 p = real;
457 ncnt = ecnt = 0;
458 while (isvalid(*p) && ncnt < 8) {
459 nm[ncnt] = *p;
460 ++ncnt;
461 ++p;
462 }
463 if (isdot(*p)) {
464 ++p;
465 while (isvalid(*p) && ecnt < 3) {
466 ex[ecnt] = *p;
467 ++ecnt;
468 ++p;
469 }
470 }
471 nm[ncnt] = '\0';
472 ex[ecnt] = '\0';
473
474 if (!*p && ncnt <= 8 && ecnt <= 3) {
475 n = names;
476 while (n) {
477 if (ustrncmp(n->name, nm, 8) == 0 && ustrncmp(n->ext, ex, 3) == 0) {
478 break;
479 }
480 n = n->next;
481 }
678e8cc6 482 if (n == NULL) {
984263bc
MD
483 ustrcpy(dos, real);
484 return;
485 }
486 }
487
488 n = (Name_t *)malloc(sizeof(Name_t));
489
490 if (!n)
491 fatal("malloc in real_to_dos: %s\n", strerror(errno));
492
493 n->real = ustrdup(real);
494
495 if (!n->real)
496 fatal("strdup in real_to_dos: %s\n", strerror(errno));
497
498 p = real;
499 ncnt = ecnt = 0;
500 while (*p && ncnt < 8) {
501 if (isvalid(*p))
502 n->name[ncnt] = *p;
503 else if (islower(*p))
504 n->name[ncnt] = toupper(*p);
505 else if (isdot(*p))
506 break;
507 else
508 n->name[ncnt] = (*p |= 0x80);
509 ++ncnt;
510 ++p;
511 }
512 if (isdot(*p)) {
513 ++p;
514 while (*p && ecnt < 3) {
515 if (isvalid(*p))
516 n->ext[ecnt] = *p;
517 else if (islower(*p))
518 n->ext[ecnt] = toupper(*p);
519#if 0
520 else if (isdot(*p))
521 ERROR
522#endif
523 else
524 n->ext[ecnt] = (*p |= 0x80);
525 ++ecnt;
526 ++p;
527 }
528 }
529 n->name[ncnt] = '\0';
530 n->ext[ecnt] = '\0';
531
532 for (;;) {
533 nn = names;
534 while (nn) {
535 if (ustrncmp(n->name, nn->name, 8) == 0 &&
536 ustrncmp(n->ext, nn->ext, 3) == 0) {
537 break;
538 }
539 nn = nn->next;
540 }
541 if (!nn)
542 break;
543 /*
544 * Dang, this name was already in the cache.
545 * Let's munge it a little and try again.
546 */
547 if (ecnt < 3) {
548 n->ext[ecnt] = echar;
549 if (echar == '9') {
550 echar = 'A';
551 } else if (echar == 'Z') {
552 ++ecnt;
553 echar = '0';
554 } else {
555 ++echar;
556 }
557 } else if (ncnt < 8) {
558 n->name[ncnt] = nchar;
559 if (nchar == '9') {
560 nchar = 'A';
561 } else if (nchar == 'Z') {
562 ++ncnt;
563 nchar = '0';
564 } else {
565 ++nchar;
566 }
567 } else if (n->ext[2] < 'Z')
568 n->ext[2]++;
569 else if (n->ext[1] < 'Z')
570 n->ext[1]++;
571 else if (n->ext[0] < 'Z')
572 n->ext[0]++;
573 else if (n->name[7] < 'Z')
574 n->name[7]++;
575 else if (n->name[6] < 'Z')
576 n->name[6]++;
577 else if (n->name[5] < 'Z')
578 n->name[5]++;
579 else if (n->name[4] < 'Z')
580 n->name[4]++;
581 else if (n->name[3] < 'Z')
582 n->name[3]++;
583 else if (n->name[2] < 'Z')
584 n->name[2]++;
585 else if (n->name[1] < 'Z')
586 n->name[1]++;
587 else if (n->name[0] < 'Z')
588 n->name[0]++;
589 else
590 break;
591 }
592
593 if (n->ext[0])
594 sprintf((char *)dos, "%.8s.%.3s", n->name, n->ext);
595 else
596 sprintf((char *)dos, "%.8s", n->name);
597 n->next = names;
598 names = n;
599}
600
601
602/*
603 * Given a DOS component, compute the REAL component.
604 */
605void
606dos_to_real(u_char *dos, u_char *real)
607{
608 int ncnt = 0;
609 int ecnt = 0;
610 u_char name[8];
611 u_char ext[3];
612 Name_t *n = names;
613
614 while (ncnt < 8 && (isvalid(*dos) || islower(*dos))) {
615 name[ncnt++] = islower(*dos) ? toupper(*dos) : *dos;
616 ++dos;
617 }
618 if (ncnt < 8)
619 name[ncnt] = 0;
620
621 if (isdot(*dos)) {
622 while (ecnt < 3 && (isvalid(*++dos) || islower(*dos))) {
623 ext[ecnt++] = islower(*dos) ? toupper(*dos) : *dos;
624 }
625 }
626 if (ecnt < 3)
627 ext[ecnt] = 0;
628
629 while (n) {
630 if (!ustrncmp(name, n->name, 8) && !ustrncmp(ext, n->ext, 3)) {
631 ustrcpy(real, n->real);
632 return;
633 }
634 n = n->next;
635 }
636
637 if (ext[0])
638 sprintf((char *)real, "%-.8s.%-.3s", name, ext);
639 else
640 sprintf((char *)real, "%-.8s", name);
641
642 while (*real) {
643 if (isupper(*real))
644 *real = tolower(*real);
645 ++real;
646 }
647}
648
649/*
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.
657 */
658u_char **
659get_entries(u_char *path)
660{
661 static u_char *entries[128]; /* Maximum depth... */
662 static u_char mypath[1024];
663 u_char **e = entries;
664 u_char *p = mypath;
665
666 ustrncpy(mypath+1, path, 1022);
667 p = mypath+1;
668 mypath[1023] = 0;
669 if (path[0] == '/' || path[0] == '\\') {
670 mypath[0] = path[0];
671 *e++ = mypath;
672 *p++ = 0;
673 }
674 while (*p && e < entries + 127) {
675 while (*p && (*p == '/' || *p == '\\')) {
676 ++p;
677 }
678
679 if (!*p)
680 break;
681 *e++ = p;
682 while (*p && (*p != '/' && *p != '\\')) {
683 if (islower(*p))
684 *p = tolower(*p);
685 ++p;
686 }
687 /*
688 * skip over the '/' or '\'
689 */
690 if (*p)
691 *p++ = 0;
692 }
678e8cc6 693 *e = NULL;
984263bc
MD
694 return (entries);
695}
696
697/*
698 * Return file system statistics for drive.
699 * Return the DOS errno on failure.
700 */
701int
702get_space(int drive, fsstat_t *fs)
703{
704 Path_t *d;
705 struct statfs *buf;
706 int nfs;
707 int i;
678e8cc6 708 struct statfs *me = NULL;
984263bc
MD
709
710 if (drive < 0 || drive >= MAX_DRIVE)
711 return (DISK_DRIVE_INVALID);
712
713 d = &paths[drive];
714
715 if (!d->path)
716 return (DISK_DRIVE_INVALID);
717
718 nfs = getfsstat(0, 0, MNT_WAIT);
719
720 buf = (struct statfs *)malloc(sizeof(struct statfs) * nfs);
721 if (buf == NULL) {
722 perror("get_space");
723 return (DISK_DRIVE_INVALID);
724 }
725 nfs = getfsstat(buf, sizeof(struct statfs) * nfs, MNT_WAIT);
726
727 for (i = 0; i < nfs; ++i) {
728 if (strncmp(buf[i].f_mntonname, (char *)d->path, strlen(buf[i].f_mntonname)))
729 continue;
730 if (me && strlen(me->f_mntonname) > strlen(buf[i].f_mntonname))
731 continue;
732 me = buf + i;
733 }
734 if (!me) {
735 free(buf);
736 return (3);
737 }
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;
744 }
745 fs->avail_clusters = me->f_bavail / fs->sectors_cluster;
746 free(buf);
747 return (0);
748}
749
750#if 0
751DIR *dp = 0;
752u_char searchdir[1024];
753u_char *searchend;
754#endif
755
756/*
757 * Convert a dos filename into normal form (8.3 format, space padded)
758 */
759static void
760to_dos_fcb(u_char *p, u_char *expr)
761{
762 int i;
763
764 if (expr[0] == '.') {
765 p[0] = '.';
766 if (expr[1] == '\0') {
767 for (i = 1; i < 11; i++)
768 p[i] = ' ';
769 return;
770 }
771 if (expr[1] == '.') {
772 p[1] = '.';
773 if (expr[2] == '\0') {
774 for (i = 2; i < 11; i++)
775 p[i] = ' ';
776 return;
777 }
778 }
779 }
780
781 for (i = 8; i > 0; i--) {
782 switch (*expr) {
783 case '\0':
784 case '.':
785 for (; i > 0; i--)
786 *p++ = ' ';
787 break;
788 case '*':
789 for (; i > 0; i--)
790 *p++ = '?';
791 break;
792 default:
793 if (islower(*expr)) {
794 *p++ = toupper(*expr++);
795 break;
796 }
797 case '?':
798 *p++ = *expr++;
799 break;
800 }
801 }
802
803 while (*expr != '\0' && *expr != '.')
804 ++expr;
805 if (*expr)
806 ++expr;
807
808 for (i = 3; i > 0; i--) {
809 switch (*expr) {
810 case '\0':
811 case '.':
812 for (; i > 0; i--)
813 *p++ = ' ';
814 break;
815 case '*':
816 for (; i > 0; i--)
817 *p++ = '?';
818 break;
819 default:
820 if (islower(*expr)) {
821 *p++ = toupper(*expr++);
822 break;
823 }
824 case '?':
825 *p++ = *expr++;
826 break;
827 }
828 }
829}
830
831/*
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.
837*/
838static search_t dir_search;
839
840/*
841 * Find the first file on drive which matches the path with the given
842 * attributes attr.
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.
846 */
847int
848find_first(u_char *path, int attr, dosdir_t *dir, find_block_t *dta)
849{
850 u_char new_path[1024], real_path[1024];
851 u_char *expr, *slash;
852 int drive;
853 int error;
854 search_t *search = &dir_search;
855
856 debug(D_REDIR, "find_first(%s, %x, %x)\n", path, attr, (int)dta);
857
858 error = dos_makepath(path, new_path);
859 if (error)
860 return (error);
861
862 expr = new_path;
678e8cc6 863 slash = NULL;
984263bc
MD
864 while (*expr != '\0') {
865 if (*expr == '\\' || *expr == '/')
866 slash = expr;
867 expr++;
868 }
869 *slash++ = '\0';
870
871 error = dos_to_real_path(new_path, real_path, &drive);
872 if (error)
873 return (error);
874
875 if (attr == VOLUME_LABEL) /* never find a volume label */
876 return (NO_MORE_FILES);
877
878 if (search->dp) /* stale search? */
879 closedir(search->dp);
880
881 search->dp = opendir(real_path);
882 if (search->dp == NULL)
883 return (PATH_NOT_FOUND);
884
885 ustrncpy(search->searchdir, real_path, 1024 - ustrlen(real_path));
886 search->searchend = search->searchdir;
887 while (*search->searchend)
888 ++search->searchend;
889 *search->searchend++ = '/';
890
891 search->dp->dd_fd = squirrel_fd(search->dp->dd_fd);
892
893 dta->drive = drive | 0x80;
894 to_dos_fcb(dta->pattern, slash);
895 dta->flag = attr;
896
897 return (find_next(dir, dta));
898}
899
900/*
901 * Continue on where find_first left off.
902 * The results will be placed in dir.
903 * DTA state area is ignored.
904 */
905int
906find_next(dosdir_t *dir, find_block_t *dta)
907{
908 search_t *search = &dir_search;
909 struct dirent *d;
910 struct stat sb;
911 u_char name[16];
912
913 if (!search->dp)
914 return (NO_MORE_FILES);
915
916#if 0
917 debug(D_REDIR, "find_next()\n");
918#endif
919
678e8cc6 920 while ((d = readdir(search->dp)) != NULL) {
984263bc
MD
921 real_to_dos((u_char *)d->d_name, name);
922 to_dos_fcb(dir->name, name);
923#if 0
924printf("find_next: |%-11.11s| |%-11.11s| |%s| |%s|\n", dta->pattern, dir->name, d->d_name, name);
925#endif
926 if (dos_match(dta->pattern, dir->name) == 0)
927 continue;
928
929 ustrcpy(search->searchend, (u_char *)d->d_name);
930 if (ustat(search->searchdir, &sb) < 0)
931 continue;
932#if 0
933printf("find_next: %x\n", sb.st_mode);
934#endif
935 if (S_ISDIR(sb.st_mode)) {
936 if (!(dta->flag & DIRECTORY)) {
937 continue;
938 }
939 }
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);
943 dir->start = 1;
944 dir->size = sb.st_size;
945#if 0
946printf("find_next: found %s\n",name);
947#endif
948 return (0);
949 }
950 closedir(search->dp);
951 search->dp = NULL;
952 return (NO_MORE_FILES);
953}
954
955/*
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.
961 *
962 * XXX This appears to be severely busted! (no * handling - normal?)
963 */
964int
965dos_match(u_char *pattern, u_char *string)
966{
967 int i;
968
969 /*
970 * Check the base part first
971 */
972 for (i = 11; i > 0; i--) {
973 if (*pattern != '?' && *string != *pattern)
974 return (0);
975 pattern++, string++;
976 }
977 return (1);
978}