blob: b1d468596bdbb2a5b50e972b8d52b1cf2bab6b43 [file] [log] [blame]
Alistair Delvabeaee832021-02-24 11:27:23 -08001/* Copyright 1986-1992 Emmet P. Gray.
2 * Copyright 1996-2002,2004,2007-2009 Alain Knaff.
3 * This file is part of mtools.
4 *
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.
9 *
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.
14 *
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/>.
17 *
18 * mdir.c:
19 * Display an MSDOS directory
20 */
21
22#include "sysincludes.h"
23#include "msdos.h"
24#include "vfat.h"
25#include "mtools.h"
26#include "file.h"
27#include "mainloop.h"
28#include "fs.h"
29#include "codepage.h"
30#include "file_name.h"
31
32#ifdef TEST_SIZE
33#include "fsP.h"
34#endif
35
36static int recursive;
37static int wide;
38static int all;
39static int concise;
40static int fast=0;
41#if 0
42static int testmode = 0;
43#endif
44static const char *dirPath;
45static char *dynDirPath;
46static char currentDrive;
47static Stream_t *currentDir;
48
49static int filesInDir; /* files in current dir */
50static int filesOnDrive; /* files on drive */
Yi Kong39bbd962022-01-09 19:41:38 +080051
Alistair Delvabeaee832021-02-24 11:27:23 -080052static int dirsOnDrive; /* number of listed directories on this drive */
53
54static int debug = 0; /* debug mode */
55
Yi Kong39bbd962022-01-09 19:41:38 +080056static mt_off_t bytesInDir;
57static mt_off_t bytesOnDrive;
58static Stream_t *RootDir;
Alistair Delvabeaee832021-02-24 11:27:23 -080059
60
61static char mdir_shortname[4*12+1];
62static char mdir_longname[4*MAX_VNAMELEN+1];
63
64
65/*
66 * Print an MSDOS directory date stamp.
67 */
68static __inline__ void print_date(struct directory *dir)
69{
70 char year[5];
71 char day[3];
72 char month[3];
73 const char *p;
74
75 sprintf(year, "%04d", DOS_YEAR(dir));
76 sprintf(day, "%02d", DOS_DAY(dir));
77 sprintf(month, "%02d", DOS_MONTH(dir));
78
79 for(p=mtools_date_string; *p; p++) {
80 if(!strncasecmp(p, "yyyy", 4)) {
81 printf("%04d", DOS_YEAR(dir));
82 p+= 3;
83 continue;
84 } else if(!strncasecmp(p, "yy", 2)) {
85 printf("%02d", DOS_YEAR(dir) % 100);
86 p++;
87 continue;
88 } else if(!strncasecmp(p, "dd", 2)) {
89 printf("%02d", DOS_DAY(dir));
90 p++;
91 continue;
92 } else if(!strncasecmp(p, "mm", 2)) {
93 printf("%02d", DOS_MONTH(dir));
94 p++;
95 continue;
96 }
97 putchar(*p);
98 }
99}
100
101/*
102 * Print an MSDOS directory time stamp.
103 */
104static __inline__ void print_time(struct directory *dir)
105{
106 char am_pm;
107 int hour = DOS_HOUR(dir);
Yi Kong39bbd962022-01-09 19:41:38 +0800108
Alistair Delvabeaee832021-02-24 11:27:23 -0800109 if(!mtools_twenty_four_hour_clock) {
110 am_pm = (hour >= 12) ? 'p' : 'a';
111 if (hour > 12)
112 hour = hour - 12;
113 if (hour == 0)
114 hour = 12;
115 } else
116 am_pm = ' ';
117
118 printf("%2d:%02d%c", hour, DOS_MINUTE(dir), am_pm);
119}
120
121/*
122 * Return a number in dotted notation
123 */
Yi Kong39bbd962022-01-09 19:41:38 +0800124static const char *dotted_num(mt_off_t num, size_t width, char **buf)
Alistair Delvabeaee832021-02-24 11:27:23 -0800125{
126 size_t len;
127 register char *srcp, *dstp;
Yi Kong39bbd962022-01-09 19:41:38 +0800128 size_t size;
Alistair Delvabeaee832021-02-24 11:27:23 -0800129
130 unsigned long numlo;
131 unsigned long numhi;
132
133 size = width + width;
134 *buf = malloc(size+1);
135
136 if (*buf == NULL)
137 return "";
Yi Kong39bbd962022-01-09 19:41:38 +0800138
Alistair Delvabeaee832021-02-24 11:27:23 -0800139 /* Create the number in maximum width; make sure that the string
140 * length is not exceeded (in %6ld, the result can be longer than 6!)
141 */
142
143 numlo = num % 1000000000;
Yi Kong39bbd962022-01-09 19:41:38 +0800144 numhi = (unsigned long) (num / 1000000000);
Alistair Delvabeaee832021-02-24 11:27:23 -0800145
146 if(numhi && size > 9) {
Yi Kong39bbd962022-01-09 19:41:38 +0800147 sprintf(*buf, "%.*lu%09lu", (int)(size-9), numhi, numlo);
Alistair Delvabeaee832021-02-24 11:27:23 -0800148 } else {
Yi Kong39bbd962022-01-09 19:41:38 +0800149 sprintf(*buf, "%.*lu", (int) size, numlo);
Alistair Delvabeaee832021-02-24 11:27:23 -0800150 }
151
152 for (srcp=*buf; srcp[1] != '\0'; ++srcp)
153 if (srcp[0] == '0')
154 srcp[0] = ' ';
155 else
156 break;
Yi Kong39bbd962022-01-09 19:41:38 +0800157
Alistair Delvabeaee832021-02-24 11:27:23 -0800158 len = strlen(*buf);
159 srcp = (*buf)+len;
160 dstp = (*buf)+len+1;
161
162 for ( ; dstp >= (*buf)+4 && isdigit (srcp[-1]); ) {
163 srcp -= 3; /* from here we copy three digits */
164 dstp -= 4; /* that's where we put these 3 digits */
165 }
166
167 /* now finally copy the 3-byte blocks to their new place */
168 while (dstp < (*buf) + len) {
169 dstp[0] = srcp[0];
170 dstp[1] = srcp[1];
171 dstp[2] = srcp[2];
172 if (dstp + 3 < (*buf) + len)
173 /* use spaces instead of dots: they please both
174 * Americans and Europeans */
Yi Kong39bbd962022-01-09 19:41:38 +0800175 dstp[3] = ' ';
Alistair Delvabeaee832021-02-24 11:27:23 -0800176 srcp += 3;
177 dstp += 4;
178 }
179
180 return (*buf) + len-width;
181}
182
183static __inline__ int print_volume_label(Stream_t *Dir, char drive)
184{
185 Stream_t *Stream = GetFs(Dir);
186 direntry_t entry;
Yi Kong39bbd962022-01-09 19:41:38 +0800187 DeclareThis(Fs_t);
Alistair Delvabeaee832021-02-24 11:27:23 -0800188 char shortname[13];
189 char longname[VBUFSIZE];
190 int r;
191
192 RootDir = OpenRoot(Stream);
193 if(concise)
194 return 0;
Yi Kong39bbd962022-01-09 19:41:38 +0800195
Alistair Delvabeaee832021-02-24 11:27:23 -0800196 /* find the volume label */
197
198 initializeDirentry(&entry, RootDir);
199 if((r=vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY,
200 shortname, sizeof(shortname),
201 longname, sizeof(longname))) ) {
202 if (r == -2) {
203 /* I/O Error */
204 return -1;
205 }
206 printf(" Volume in drive %c has no label", drive);
207 } else if (*longname)
208 printf(" Volume in drive %c is %s (abbr=%s)",
209 drive, longname, shortname);
210 else
211 printf(" Volume in drive %c is %s",
212 drive, shortname);
Yi Kong39bbd962022-01-09 19:41:38 +0800213 if(getSerialized(This)) {
214 unsigned long serial_number = getSerialNumber(This);
Alistair Delvabeaee832021-02-24 11:27:23 -0800215 printf("\n Volume Serial Number is %04lX-%04lX",
Yi Kong39bbd962022-01-09 19:41:38 +0800216 (serial_number >> 16) & 0xffff,
217 serial_number & 0xffff);
218 }
Alistair Delvabeaee832021-02-24 11:27:23 -0800219 return 0;
220}
221
222
Yi Kong39bbd962022-01-09 19:41:38 +0800223static void printSummary(int files, mt_off_t bytes)
Alistair Delvabeaee832021-02-24 11:27:23 -0800224{
225 if(!filesInDir)
226 printf("No files\n");
Yi Kong39bbd962022-01-09 19:41:38 +0800227 else {
Alistair Delvabeaee832021-02-24 11:27:23 -0800228 char *s1 = NULL;
229 printf(" %3d file", files);
230 if(files == 1)
231 putchar(' ');
232 else
233 putchar('s');
234 printf(" %s bytes\n",
235 dotted_num(bytes, 13, &s1));
236 if(s1)
237 free(s1);
238 }
239}
240
241static void leaveDirectory(int haveError);
242
243static void leaveDrive(int haveError)
244{
245 if(!currentDrive)
246 return;
247 leaveDirectory(haveError);
248 if(!concise && !haveError) {
249
250 if(dirsOnDrive > 1) {
251 printf("\nTotal files listed:\n");
252 printSummary(filesOnDrive, bytesOnDrive);
253 }
254 if(RootDir && !fast) {
255 char *s1 = NULL;
256 mt_off_t bytes = getfree(RootDir);
257 if(bytes == -1) {
258 fprintf(stderr, "Fat error\n");
259 goto exit_1;
260 }
261 printf(" %s bytes free\n\n",
262 dotted_num(bytes,17, &s1));
263#ifdef TEST_SIZE
264 ((Fs_t*)GetFs(RootDir))->freeSpace = 0;
265 bytes = getfree(RootDir);
266 printf(" %s bytes free\n\n",
267 dotted_num(bytes,17, &s1));
268#endif
269 if(s1)
270 free(s1);
271 }
272 }
273 exit_1:
274 FREE(&RootDir);
275 currentDrive = '\0';
276}
277
278
279static int enterDrive(Stream_t *Dir, char drive)
280{
281 int r;
282 if(currentDrive == drive)
283 return 0; /* still the same */
Yi Kong39bbd962022-01-09 19:41:38 +0800284
Alistair Delvabeaee832021-02-24 11:27:23 -0800285 leaveDrive(0);
286 currentDrive = drive;
Yi Kong39bbd962022-01-09 19:41:38 +0800287
Alistair Delvabeaee832021-02-24 11:27:23 -0800288 r = print_volume_label(Dir, drive);
289 if (r)
290 return r;
291
292
293 bytesOnDrive = 0;
294 filesOnDrive = 0;
295 dirsOnDrive = 0;
296 return 0;
297}
298
299static const char *emptyString="<out-of-memory>";
300
301static void leaveDirectory(int haveError)
302{
303 if(!currentDir)
304 return;
305
306 if (!haveError) {
307 if(dirPath && dirPath != emptyString)
308 free(dynDirPath);
309 if(wide)
310 putchar('\n');
Yi Kong39bbd962022-01-09 19:41:38 +0800311
Alistair Delvabeaee832021-02-24 11:27:23 -0800312 if(!concise)
313 printSummary(filesInDir, bytesInDir);
314 }
315 FREE(&currentDir);
316}
317
318static int enterDirectory(Stream_t *Dir)
319{
320 int r;
321 char drive;
322 if(currentDir == Dir)
323 return 0; /* still the same directory */
324
325 leaveDirectory(0);
326
327 drive = getDrive(Dir);
328 r=enterDrive(Dir, drive);
329 if(r)
330 return r;
331 currentDir = COPY(Dir);
332
333 dynDirPath = getPwd(getDirentry(Dir));
334 if(!dynDirPath)
335 dirPath=emptyString;
336 else {
337 if(!dynDirPath[3] && concise)
338 dynDirPath[2]='\0';
339 dirPath=dynDirPath;
340 }
341
342 /* print directory title */
343 if(!concise)
344 printf("\nDirectory for %s\n", dirPath);
345
346 if(!wide && !concise)
347 printf("\n");
348
349 dirsOnDrive++;
350 bytesInDir = 0;
351 filesInDir = 0;
352 return 0;
353}
354
355static int list_file(direntry_t *entry, MainParam_t *mp UNUSEDP)
356{
357 unsigned long size;
358 int i;
359 int Case;
360 int r;
361
362 wchar_t ext[4];
363 wchar_t name[9];
364 doscp_t *cp;
365
366 if(!all && (entry->dir.attr & 0x6))
367 return 0;
368
369 if(concise && isSpecialW(entry->name))
370 return 0;
371
372 r=enterDirectory(entry->Dir);
373 if (r)
374 return ERROR_ONE;
375 if (wide) {
376 if(filesInDir % 5)
Yi Kong39bbd962022-01-09 19:41:38 +0800377 putchar(' ');
Alistair Delvabeaee832021-02-24 11:27:23 -0800378 else
379 putchar('\n');
380 }
Yi Kong39bbd962022-01-09 19:41:38 +0800381
Alistair Delvabeaee832021-02-24 11:27:23 -0800382 if(IS_DIR(entry)){
383 size = 0;
384 } else
385 size = FILE_SIZE(&entry->dir);
Yi Kong39bbd962022-01-09 19:41:38 +0800386
Alistair Delvabeaee832021-02-24 11:27:23 -0800387 Case = entry->dir.Case;
Yi Kong39bbd962022-01-09 19:41:38 +0800388 if(!(Case & (BASECASE | EXTCASE)) &&
Alistair Delvabeaee832021-02-24 11:27:23 -0800389 mtools_ignore_short_case)
390 Case |= BASECASE | EXTCASE;
Yi Kong39bbd962022-01-09 19:41:38 +0800391
Alistair Delvabeaee832021-02-24 11:27:23 -0800392 cp = GET_DOSCONVERT(entry->Dir);
393 dos_to_wchar(cp, entry->dir.ext, ext, 3);
394 if(Case & EXTCASE){
395 for(i=0; i<3;i++)
396 ext[i] = ch_towlower(ext[i]);
397 }
398 ext[3] = '\0';
399 if (entry->dir.name[0] == '\x05') {
400 dos_to_wchar(cp, "\xE5", name, 1);
401 dos_to_wchar(cp, entry->dir.name+1, name+1, 7);
402 } else {
403 dos_to_wchar(cp, entry->dir.name, name, 8);
404 }
405 if(Case & BASECASE){
406 for(i=0; i<8;i++)
407 name[i] = ch_towlower(name[i]);
408 }
409 name[8]='\0';
410 if(wide){
411 if(IS_DIR(entry))
412 printf("[%s]%*s", mdir_shortname,
413 (int) (15 - 2 - strlen(mdir_shortname)), "");
414 else
415 printf("%-15s", mdir_shortname);
Yi Kong39bbd962022-01-09 19:41:38 +0800416 } else if(!concise) {
Alistair Delvabeaee832021-02-24 11:27:23 -0800417 char tmpBasename[4*8+1];
418 char tmpExt[4*3+1];
419 WCHAR_TO_NATIVE(name,tmpBasename,8);
420 WCHAR_TO_NATIVE(ext,tmpExt,3);
421
Yi Kong39bbd962022-01-09 19:41:38 +0800422 if (name[0] == ' ')
Alistair Delvabeaee832021-02-24 11:27:23 -0800423 printf(" ");
424 else if(mtools_dotted_dir)
425 printf("%-12s ", mdir_shortname);
426 else
427 printf("%s %s ", tmpBasename, tmpExt);
428 /* is a subdirectory */
429 if(IS_DIR(entry))
430 printf("<DIR> ");
431 else
432 printf(" %8ld", (long) size);
433 printf(" ");
434 print_date(&entry->dir);
435 printf(" ");
436 print_time(&entry->dir);
437
438 if(debug)
439 printf(" %s %d ", tmpBasename, START(&entry->dir));
Yi Kong39bbd962022-01-09 19:41:38 +0800440
Alistair Delvabeaee832021-02-24 11:27:23 -0800441 if(*mdir_longname)
442 printf(" %s", mdir_longname);
443 printf("\n");
444 } else {
445 char tmp[4*MAX_VNAMELEN+1];
446 wchar_to_native(entry->name,tmp,
447 MAX_VNAMELEN, sizeof(tmp));
448
449 printf("%s/%s", dirPath, tmp);
450 if(IS_DIR(entry))
451 putchar('/');
452 putchar('\n');
453 }
454
455 filesOnDrive++;
456 filesInDir++;
457
Yi Kong39bbd962022-01-09 19:41:38 +0800458 bytesOnDrive += size;
459 bytesInDir += size;
Alistair Delvabeaee832021-02-24 11:27:23 -0800460 return GOT_ONE;
461}
462
463static int list_non_recurs_directory(direntry_t *entry, MainParam_t *mp)
464{
465 int r;
466 /* list top-level directory
467 * If this was matched by wildcard in the basename, list it as
468 * file, otherwise, list it as directory */
469 if (mp->basenameHasWildcard) {
470 /* wildcard, list it as file */
471 return list_file(entry, mp);
472 } else {
473 /* no wildcard, list it as directory */
474 MainParam_t subMp;
475
476 r=enterDirectory(mp->File);
477 if(r)
478 return ERROR_ONE;
479
480 subMp = *mp;
481 subMp.dirCallback = subMp.callback;
482 return mp->loop(mp->File, &subMp, "*") | GOT_ONE;
483 }
484}
485
486
487static int list_recurs_directory(direntry_t *entry UNUSEDP,
488 MainParam_t *mp UNUSEDP)
489{
490 MainParam_t subMp;
491 int ret;
492
493 /* first list the files */
494 subMp = *mp;
495 subMp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN;
496 subMp.dirCallback = list_file;
497 subMp.callback = list_file;
498
499 ret = mp->loop(mp->File, &subMp, "*");
500
501 /* then list subdirectories */
502 subMp = *mp;
503 subMp.lookupflags = ACCEPT_DIR | NO_DOTS | NO_MSG | DO_OPEN;
Yi Kong39bbd962022-01-09 19:41:38 +0800504 return ret | mp->loop(mp->File, &subMp, "*") | GOT_ONE;
Alistair Delvabeaee832021-02-24 11:27:23 -0800505}
506
507#if 0
508static int test_directory(direntry_t *entry, MainParam_t *mp)
509{
510 Stream_t *File=mp->File;
511 Stream_t *Target;
512 char errmsg[80];
513
514 if ((Target = SimpleFileOpen(0, 0, "-",
515 O_WRONLY,
516 errmsg, 0, 0, 0))) {
517 copyfile(File, Target);
518 FREE(&Target);
519 }
520 return GOT_ONE;
521}
522#endif
523
524static void usage(int ret) NORETURN;
525static void usage(int ret)
526{
527 fprintf(stderr, "Mtools version %s, dated %s\n",
528 mversion, mdate);
529 fprintf(stderr, "Usage: %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosdirectory\n",
530 progname);
531 fprintf(stderr,
532 " %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosfile [msdosfiles...]\n",
533 progname);
534 exit(ret);
535}
536
537void mdir(int argc, char **argv, int type UNUSEDP) NORETURN;
538void mdir(int argc, char **argv, int type UNUSEDP)
539{
540 int ret;
541 MainParam_t mp;
542 int c;
543 const char *fakedArgv[] = { "." };
544
545 concise = 0;
546 recursive = 0;
547 wide = all = 0;
548 /* first argument */
549 if(helpFlag(argc, argv))
550 usage(0);
551 while ((c = getopt(argc, argv, "i:waXbfds/h")) != EOF) {
552 switch(c) {
553 case 'i':
554 set_cmd_line_image(optarg);
555 break;
556 case 'w':
557 wide = 1;
558 break;
559 case 'a':
560 all = 1;
561 break;
562 case 'b':
563 case 'X':
564 concise = 1;
565 /*recursive = 1;*/
566 break;
567 case 's':
568 case '/':
569 recursive = 1;
570 break;
571 case 'f':
572 fast = 1;
573 break;
574 case 'd':
575 debug = 1;
576 break;
577#if 0
578 case 't': /* test mode */
579 testmode = 1;
580 break;
581#endif
582 case 'h':
583 usage(0);
584 default:
585 usage(1);
586 }
587 }
588
589 /* fake an argument */
590 if (optind == argc) {
591 argv = (char **)fakedArgv;
592 argc = 1;
593 optind = 0;
594 }
595
596 init_mp(&mp);
597 currentDrive = '\0';
598 currentDir = 0;
599 RootDir = 0;
600 dirPath = 0;
601#if 0
602 if (testmode) {
603 mp.lookupflags = ACCEPT_DIR | NO_DOTS;
604 mp.dirCallback = test_directory;
Yi Kong39bbd962022-01-09 19:41:38 +0800605 } else
Alistair Delvabeaee832021-02-24 11:27:23 -0800606#endif
Yi Kong39bbd962022-01-09 19:41:38 +0800607 if(recursive) {
608 mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN | DO_OPEN_DIRS | NO_DOTS;
Alistair Delvabeaee832021-02-24 11:27:23 -0800609 mp.dirCallback = list_recurs_directory;
Yi Kong39bbd962022-01-09 19:41:38 +0800610 mp.callback = list_file;
Alistair Delvabeaee832021-02-24 11:27:23 -0800611 } else {
612 mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN | DO_OPEN_DIRS;
613 mp.dirCallback = list_non_recurs_directory;
614 mp.callback = list_file;
615 }
616 mp.longname.data = mdir_longname;
617 mp.longname.len = sizeof(mdir_longname);
618 mp.shortname.data = mdir_shortname;
619 mp.shortname.len = sizeof(mdir_shortname);
620 ret=main_loop(&mp, argv + optind, argc - optind);
621 leaveDirectory(ret);
622 leaveDrive(ret);
623 exit(ret);
624}