1 /* Copyright 1986-1992 Emmet P. Gray.
2 * Copyright 1996-2002,2004,2007-2009 Alain Knaff.
3 * This file is part of mtools.
5 * Mtools is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * Mtools is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
19 * Display an MSDOS directory
22 #include "sysincludes.h"
30 #include "file_name.h"
42 static int testmode = 0;
44 static const char *dirPath;
45 static char *dynDirPath;
46 static char currentDrive;
47 static Stream_t *currentDir;
49 static int filesInDir; /* files in current dir */
50 static int filesOnDrive; /* files on drive */
52 static int dirsOnDrive; /* number of listed directories on this drive */
54 static int debug = 0; /* debug mode */
56 static mt_size_t bytesInDir;
57 static mt_size_t bytesOnDrive;
58 static Stream_t *RootDir;
61 static char global_shortname[13];
62 static char global_longname[VBUFSIZE];
66 * Print an MSDOS directory date stamp.
68 static __inline__ void print_date(struct directory *dir)
75 sprintf(year, "%04d", DOS_YEAR(dir));
76 sprintf(day, "%02d", DOS_DAY(dir));
77 sprintf(month, "%02d", DOS_MONTH(dir));
79 for(p=mtools_date_string; *p; p++) {
80 if(!strncasecmp(p, "yyyy", 4)) {
81 printf("%04d", DOS_YEAR(dir));
84 } else if(!strncasecmp(p, "yy", 2)) {
85 printf("%02d", DOS_YEAR(dir) % 100);
88 } else if(!strncasecmp(p, "dd", 2)) {
89 printf("%02d", DOS_DAY(dir));
92 } else if(!strncasecmp(p, "mm", 2)) {
93 printf("%02d", DOS_MONTH(dir));
102 * Print an MSDOS directory time stamp.
104 static __inline__ void print_time(struct directory *dir)
107 int hour = DOS_HOUR(dir);
109 if(!mtools_twenty_four_hour_clock) {
110 am_pm = (hour >= 12) ? 'p' : 'a';
118 printf("%2d:%02d%c", hour, DOS_MINUTE(dir), am_pm);
122 * Return a number in dotted notation
124 static const char *dotted_num(mt_size_t num, int width, char **buf)
127 register char *srcp, *dstp;
134 /* warn about negative numbers here. They should not occur */
135 fprintf(stderr, "Invalid negative number\n");
138 size = width + width;
139 *buf = malloc(size+1);
144 /* Create the number in maximum width; make sure that the string
145 * length is not exceeded (in %6ld, the result can be longer than 6!)
148 numlo = num % 1000000000;
149 numhi = num / 1000000000;
151 if(numhi && size > 9) {
152 sprintf(*buf, "%.*lu%09lu", size-9, numhi, numlo);
154 sprintf(*buf, "%.*lu", size, numlo);
157 for (srcp=*buf; srcp[1] != '\0'; ++srcp)
167 for ( ; dstp >= (*buf)+4 && isdigit (srcp[-1]); ) {
168 srcp -= 3; /* from here we copy three digits */
169 dstp -= 4; /* that's where we put these 3 digits */
172 /* now finally copy the 3-byte blocks to their new place */
173 while (dstp < (*buf) + len) {
177 if (dstp + 3 < (*buf) + len)
178 /* use spaces instead of dots: they please both
179 * Americans and Europeans */
185 return (*buf) + len-width;
188 static __inline__ int print_volume_label(Stream_t *Dir, char drive)
190 Stream_t *Stream = GetFs(Dir);
192 DeclareThis(FsPublic_t);
194 char longname[VBUFSIZE];
197 RootDir = OpenRoot(Stream);
201 /* find the volume label */
203 initializeDirentry(&entry, RootDir);
204 if((r=vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY,
205 shortname, longname)) ) {
210 printf(" Volume in drive %c has no label", drive);
211 } else if (*longname)
212 printf(" Volume in drive %c is %s (abbr=%s)",
213 drive, longname, shortname);
215 printf(" Volume in drive %c is %s",
218 printf("\n Volume Serial Number is %04lX-%04lX",
219 (This->serial_number >> 16) & 0xffff,
220 This->serial_number & 0xffff);
225 static void printSummary(int files, mt_size_t bytes)
228 printf("No files\n");
231 printf(" %3d file", files);
236 printf(" %s bytes\n",
237 dotted_num(bytes, 13, &s1));
243 static void leaveDirectory(int haveError);
245 static void leaveDrive(int haveError)
249 leaveDirectory(haveError);
250 if(!concise && !haveError) {
252 if(dirsOnDrive > 1) {
253 printf("\nTotal files listed:\n");
254 printSummary(filesOnDrive, bytesOnDrive);
256 if(RootDir && !fast) {
258 mt_off_t bytes = getfree(RootDir);
260 fprintf(stderr, "Fat error\n");
263 printf(" %s bytes free\n\n",
264 dotted_num(bytes,17, &s1));
266 ((Fs_t*)GetFs(RootDir))->freeSpace = 0;
267 bytes = getfree(RootDir);
268 printf(" %s bytes free\n\n",
269 dotted_num(bytes,17, &s1));
281 static int enterDrive(Stream_t *Dir, char drive)
284 if(currentDrive == drive)
285 return 0; /* still the same */
288 currentDrive = drive;
290 r = print_volume_label(Dir, drive);
301 static const char *emptyString="<out-of-memory>";
303 static void leaveDirectory(int haveError)
309 if(dirPath && dirPath != emptyString)
315 printSummary(filesInDir, bytesInDir);
320 static int enterDirectory(Stream_t *Dir)
324 if(currentDir == Dir)
325 return 0; /* still the same directory */
329 drive = getDrive(Dir);
330 r=enterDrive(Dir, drive);
333 currentDir = COPY(Dir);
335 dynDirPath = getPwd(getDirentry(Dir));
339 if(!dynDirPath[3] && concise)
344 /* print directory title */
346 printf("\nDirectory for %s\n", dirPath);
348 if(!wide && !concise)
357 static int list_file(direntry_t *entry, MainParam_t *mp)
368 if(!all && (entry->dir.attr & 0x6))
371 if(concise && isSpecialW(entry->name))
374 r=enterDirectory(entry->Dir);
387 size = FILE_SIZE(&entry->dir);
389 Case = entry->dir.Case;
390 if(!(Case & (BASECASE | EXTCASE)) &&
391 mtools_ignore_short_case)
392 Case |= BASECASE | EXTCASE;
394 cp = GET_DOSCONVERT(entry->Dir);
395 dos_to_wchar(cp, entry->dir.ext, ext, 3);
398 ext[i] = towlower(ext[i]);
401 dos_to_wchar(cp, entry->dir.name, name, 8);
404 name[i] = towlower(name[i]);
409 printf("[%s]%*s", global_shortname,
410 (int) (15 - 2 - strlen(global_shortname)), "");
412 printf("%-15s", global_shortname);
413 } else if(!concise) {
414 char tmpBasename[4*8+1];
416 wchar_to_native(name,tmpBasename,8);
417 wchar_to_native(ext,tmpExt,3);
421 else if(mtools_dotted_dir)
422 printf("%s", global_shortname);
424 printf("%s %s ", tmpBasename, tmpExt);
425 /* is a subdirectory */
429 printf(" %8ld", (long) size);
431 print_date(&entry->dir);
433 print_time(&entry->dir);
436 printf(" %s %d ", tmpBasename, START(&entry->dir));
439 printf(" %s", global_longname);
442 char tmp[4*MAX_VNAMELEN+1];
443 wchar_to_native(entry->name,tmp,MAX_VNAMELEN);
445 printf("%s/%s", dirPath, tmp);
454 bytesOnDrive += (mt_size_t) size;
455 bytesInDir += (mt_size_t) size;
459 static int list_non_recurs_directory(direntry_t *entry, MainParam_t *mp)
462 /* list top-level directory
463 * If this was matched by wildcard in the basename, list it as
464 * file, otherwise, list it as directory */
465 if (mp->basenameHasWildcard) {
466 /* wildcard, list it as file */
467 return list_file(entry, mp);
469 /* no wildcard, list it as directory */
472 r=enterDirectory(mp->File);
477 subMp.dirCallback = subMp.callback;
478 return mp->loop(mp->File, &subMp, "*") | GOT_ONE;
483 static int list_recurs_directory(direntry_t *entry, MainParam_t *mp)
488 /* first list the files */
490 subMp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN;
491 subMp.dirCallback = list_file;
492 subMp.callback = list_file;
494 ret = mp->loop(mp->File, &subMp, "*");
496 /* then list subdirectories */
498 subMp.lookupflags = ACCEPT_DIR | NO_DOTS | NO_MSG | DO_OPEN;
499 return ret | mp->loop(mp->File, &subMp, "*");
503 static int test_directory(direntry_t *entry, MainParam_t *mp)
505 Stream_t *File=mp->File;
509 if ((Target = SimpleFileOpen(0, 0, "-",
512 copyfile(File, Target);
519 static void usage(int ret) NORETURN;
520 static void usage(int ret)
522 fprintf(stderr, "Mtools version %s, dated %s\n",
524 fprintf(stderr, "Usage: %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosdirectory\n",
527 " %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosfile [msdosfiles...]\n",
533 void mdir(int argc, char **argv, int type)
539 const char *fakedArgv[] = { "." };
545 if(helpFlag(argc, argv))
547 while ((c = getopt(argc, argv, "i:waXbfds/h")) != EOF) {
550 set_cmd_line_image(optarg, 0);
574 case 't': /* test mode */
585 /* fake an argument */
587 if (optind == argc) {
588 argv = (char **)fakedArgv;
600 mp.lookupflags = ACCEPT_DIR | NO_DOTS;
601 mp.dirCallback = test_directory;
605 mp.lookupflags = ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS;
606 mp.dirCallback = list_recurs_directory;
608 mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN | DO_OPEN_DIRS;
609 mp.dirCallback = list_non_recurs_directory;
610 mp.callback = list_file;
612 mp.longname = global_longname;
613 mp.shortname = global_shortname;
614 ret=main_loop(&mp, argv + optind, argc - optind);