1 /* 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
36 static int recursive;
37 static int wide;
38 static int all;
39 static int concise;
40 static int fast=0;
41 #if 0
42 static int testmode = 0;
43 #endif
44 static const char *dirPath;
45 static char *dynDirPath;
46 static char currentDrive;
47 static Stream_t *currentDir;
48
49 static int filesInDir; /* files in current dir */
50 static int filesOnDrive; /* files on drive */
51
52 static int dirsOnDrive; /* number of listed directories on this drive */
53
54 static int debug = 0; /* debug mode */
55
56 static mt_size_t bytesInDir;
57 static mt_size_t bytesOnDrive;
58 static Stream_t *RootDir;
59
60
61 static char mdir_shortname[4*12+1];
62 static char mdir_longname[4*MAX_VNAMELEN+1];
63
64
65 /*
66 * Print an MSDOS directory date stamp.
67 */
print_date(struct directory * dir)68 static __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 */
print_time(struct directory * dir)104 static __inline__ void print_time(struct directory *dir)
105 {
106 char am_pm;
107 int hour = DOS_HOUR(dir);
108
109 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 */
dotted_num(mt_size_t num,int width,char ** buf)124 static const char *dotted_num(mt_size_t num, int width, char **buf)
125 {
126 size_t len;
127 register char *srcp, *dstp;
128 int size;
129
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 "";
138
139 /* 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;
144 numhi = num / 1000000000;
145
146 if(numhi && size > 9) {
147 sprintf(*buf, "%.*lu%09lu", size-9, numhi, numlo);
148 } else {
149 sprintf(*buf, "%.*lu", size, numlo);
150 }
151
152 for (srcp=*buf; srcp[1] != '\0'; ++srcp)
153 if (srcp[0] == '0')
154 srcp[0] = ' ';
155 else
156 break;
157
158 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 */
175 dstp[3] = ' ';
176 srcp += 3;
177 dstp += 4;
178 }
179
180 return (*buf) + len-width;
181 }
182
print_volume_label(Stream_t * Dir,char drive)183 static __inline__ int print_volume_label(Stream_t *Dir, char drive)
184 {
185 Stream_t *Stream = GetFs(Dir);
186 direntry_t entry;
187 DeclareThis(FsPublic_t);
188 char shortname[13];
189 char longname[VBUFSIZE];
190 int r;
191
192 RootDir = OpenRoot(Stream);
193 if(concise)
194 return 0;
195
196 /* 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);
213 if(This->serialized)
214 printf("\n Volume Serial Number is %04lX-%04lX",
215 (This->serial_number >> 16) & 0xffff,
216 This->serial_number & 0xffff);
217 return 0;
218 }
219
220
printSummary(int files,mt_size_t bytes)221 static void printSummary(int files, mt_size_t bytes)
222 {
223 if(!filesInDir)
224 printf("No files\n");
225 else {
226 char *s1 = NULL;
227 printf(" %3d file", files);
228 if(files == 1)
229 putchar(' ');
230 else
231 putchar('s');
232 printf(" %s bytes\n",
233 dotted_num(bytes, 13, &s1));
234 if(s1)
235 free(s1);
236 }
237 }
238
239 static void leaveDirectory(int haveError);
240
leaveDrive(int haveError)241 static void leaveDrive(int haveError)
242 {
243 if(!currentDrive)
244 return;
245 leaveDirectory(haveError);
246 if(!concise && !haveError) {
247
248 if(dirsOnDrive > 1) {
249 printf("\nTotal files listed:\n");
250 printSummary(filesOnDrive, bytesOnDrive);
251 }
252 if(RootDir && !fast) {
253 char *s1 = NULL;
254 mt_off_t bytes = getfree(RootDir);
255 if(bytes == -1) {
256 fprintf(stderr, "Fat error\n");
257 goto exit_1;
258 }
259 printf(" %s bytes free\n\n",
260 dotted_num(bytes,17, &s1));
261 #ifdef TEST_SIZE
262 ((Fs_t*)GetFs(RootDir))->freeSpace = 0;
263 bytes = getfree(RootDir);
264 printf(" %s bytes free\n\n",
265 dotted_num(bytes,17, &s1));
266 #endif
267 if(s1)
268 free(s1);
269 }
270 }
271 exit_1:
272 FREE(&RootDir);
273 currentDrive = '\0';
274 }
275
276
enterDrive(Stream_t * Dir,char drive)277 static int enterDrive(Stream_t *Dir, char drive)
278 {
279 int r;
280 if(currentDrive == drive)
281 return 0; /* still the same */
282
283 leaveDrive(0);
284 currentDrive = drive;
285
286 r = print_volume_label(Dir, drive);
287 if (r)
288 return r;
289
290
291 bytesOnDrive = 0;
292 filesOnDrive = 0;
293 dirsOnDrive = 0;
294 return 0;
295 }
296
297 static const char *emptyString="<out-of-memory>";
298
leaveDirectory(int haveError)299 static void leaveDirectory(int haveError)
300 {
301 if(!currentDir)
302 return;
303
304 if (!haveError) {
305 if(dirPath && dirPath != emptyString)
306 free(dynDirPath);
307 if(wide)
308 putchar('\n');
309
310 if(!concise)
311 printSummary(filesInDir, bytesInDir);
312 }
313 FREE(¤tDir);
314 }
315
enterDirectory(Stream_t * Dir)316 static int enterDirectory(Stream_t *Dir)
317 {
318 int r;
319 char drive;
320 if(currentDir == Dir)
321 return 0; /* still the same directory */
322
323 leaveDirectory(0);
324
325 drive = getDrive(Dir);
326 r=enterDrive(Dir, drive);
327 if(r)
328 return r;
329 currentDir = COPY(Dir);
330
331 dynDirPath = getPwd(getDirentry(Dir));
332 if(!dynDirPath)
333 dirPath=emptyString;
334 else {
335 if(!dynDirPath[3] && concise)
336 dynDirPath[2]='\0';
337 dirPath=dynDirPath;
338 }
339
340 /* print directory title */
341 if(!concise)
342 printf("\nDirectory for %s\n", dirPath);
343
344 if(!wide && !concise)
345 printf("\n");
346
347 dirsOnDrive++;
348 bytesInDir = 0;
349 filesInDir = 0;
350 return 0;
351 }
352
list_file(direntry_t * entry,MainParam_t * mp UNUSEDP)353 static int list_file(direntry_t *entry, MainParam_t *mp UNUSEDP)
354 {
355 unsigned long size;
356 int i;
357 int Case;
358 int r;
359
360 wchar_t ext[4];
361 wchar_t name[9];
362 doscp_t *cp;
363
364 if(!all && (entry->dir.attr & 0x6))
365 return 0;
366
367 if(concise && isSpecialW(entry->name))
368 return 0;
369
370 r=enterDirectory(entry->Dir);
371 if (r)
372 return ERROR_ONE;
373 if (wide) {
374 if(filesInDir % 5)
375 putchar(' ');
376 else
377 putchar('\n');
378 }
379
380 if(IS_DIR(entry)){
381 size = 0;
382 } else
383 size = FILE_SIZE(&entry->dir);
384
385 Case = entry->dir.Case;
386 if(!(Case & (BASECASE | EXTCASE)) &&
387 mtools_ignore_short_case)
388 Case |= BASECASE | EXTCASE;
389
390 cp = GET_DOSCONVERT(entry->Dir);
391 dos_to_wchar(cp, entry->dir.ext, ext, 3);
392 if(Case & EXTCASE){
393 for(i=0; i<3;i++)
394 ext[i] = ch_towlower(ext[i]);
395 }
396 ext[3] = '\0';
397 if (entry->dir.name[0] == '\x05') {
398 dos_to_wchar(cp, "\xE5", name, 1);
399 dos_to_wchar(cp, entry->dir.name+1, name+1, 7);
400 } else {
401 dos_to_wchar(cp, entry->dir.name, name, 8);
402 }
403 if(Case & BASECASE){
404 for(i=0; i<8;i++)
405 name[i] = ch_towlower(name[i]);
406 }
407 name[8]='\0';
408 if(wide){
409 if(IS_DIR(entry))
410 printf("[%s]%*s", mdir_shortname,
411 (int) (15 - 2 - strlen(mdir_shortname)), "");
412 else
413 printf("%-15s", mdir_shortname);
414 } else if(!concise) {
415 char tmpBasename[4*8+1];
416 char tmpExt[4*3+1];
417 WCHAR_TO_NATIVE(name,tmpBasename,8);
418 WCHAR_TO_NATIVE(ext,tmpExt,3);
419
420 if (name[0] == ' ')
421 printf(" ");
422 else if(mtools_dotted_dir)
423 printf("%-12s ", mdir_shortname);
424 else
425 printf("%s %s ", tmpBasename, tmpExt);
426 /* is a subdirectory */
427 if(IS_DIR(entry))
428 printf("<DIR> ");
429 else
430 printf(" %8ld", (long) size);
431 printf(" ");
432 print_date(&entry->dir);
433 printf(" ");
434 print_time(&entry->dir);
435
436 if(debug)
437 printf(" %s %d ", tmpBasename, START(&entry->dir));
438
439 if(*mdir_longname)
440 printf(" %s", mdir_longname);
441 printf("\n");
442 } else {
443 char tmp[4*MAX_VNAMELEN+1];
444 wchar_to_native(entry->name,tmp,
445 MAX_VNAMELEN, sizeof(tmp));
446
447 printf("%s/%s", dirPath, tmp);
448 if(IS_DIR(entry))
449 putchar('/');
450 putchar('\n');
451 }
452
453 filesOnDrive++;
454 filesInDir++;
455
456 bytesOnDrive += (mt_size_t) size;
457 bytesInDir += (mt_size_t) size;
458 return GOT_ONE;
459 }
460
list_non_recurs_directory(direntry_t * entry,MainParam_t * mp)461 static int list_non_recurs_directory(direntry_t *entry, MainParam_t *mp)
462 {
463 int r;
464 /* list top-level directory
465 * If this was matched by wildcard in the basename, list it as
466 * file, otherwise, list it as directory */
467 if (mp->basenameHasWildcard) {
468 /* wildcard, list it as file */
469 return list_file(entry, mp);
470 } else {
471 /* no wildcard, list it as directory */
472 MainParam_t subMp;
473
474 r=enterDirectory(mp->File);
475 if(r)
476 return ERROR_ONE;
477
478 subMp = *mp;
479 subMp.dirCallback = subMp.callback;
480 return mp->loop(mp->File, &subMp, "*") | GOT_ONE;
481 }
482 }
483
484
list_recurs_directory(direntry_t * entry UNUSEDP,MainParam_t * mp UNUSEDP)485 static int list_recurs_directory(direntry_t *entry UNUSEDP,
486 MainParam_t *mp UNUSEDP)
487 {
488 MainParam_t subMp;
489 int ret;
490
491 /* first list the files */
492 subMp = *mp;
493 subMp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN;
494 subMp.dirCallback = list_file;
495 subMp.callback = list_file;
496
497 ret = mp->loop(mp->File, &subMp, "*");
498
499 /* then list subdirectories */
500 subMp = *mp;
501 subMp.lookupflags = ACCEPT_DIR | NO_DOTS | NO_MSG | DO_OPEN;
502 return ret | mp->loop(mp->File, &subMp, "*");
503 }
504
505 #if 0
506 static int test_directory(direntry_t *entry, MainParam_t *mp)
507 {
508 Stream_t *File=mp->File;
509 Stream_t *Target;
510 char errmsg[80];
511
512 if ((Target = SimpleFileOpen(0, 0, "-",
513 O_WRONLY,
514 errmsg, 0, 0, 0))) {
515 copyfile(File, Target);
516 FREE(&Target);
517 }
518 return GOT_ONE;
519 }
520 #endif
521
522 static void usage(int ret) NORETURN;
usage(int ret)523 static void usage(int ret)
524 {
525 fprintf(stderr, "Mtools version %s, dated %s\n",
526 mversion, mdate);
527 fprintf(stderr, "Usage: %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosdirectory\n",
528 progname);
529 fprintf(stderr,
530 " %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosfile [msdosfiles...]\n",
531 progname);
532 exit(ret);
533 }
534
535 void mdir(int argc, char **argv, int type UNUSEDP) NORETURN;
mdir(int argc,char ** argv,int type UNUSEDP)536 void mdir(int argc, char **argv, int type UNUSEDP)
537 {
538 int ret;
539 MainParam_t mp;
540 int c;
541 const char *fakedArgv[] = { "." };
542
543 concise = 0;
544 recursive = 0;
545 wide = all = 0;
546 /* first argument */
547 if(helpFlag(argc, argv))
548 usage(0);
549 while ((c = getopt(argc, argv, "i:waXbfds/h")) != EOF) {
550 switch(c) {
551 case 'i':
552 set_cmd_line_image(optarg);
553 break;
554 case 'w':
555 wide = 1;
556 break;
557 case 'a':
558 all = 1;
559 break;
560 case 'b':
561 case 'X':
562 concise = 1;
563 /*recursive = 1;*/
564 break;
565 case 's':
566 case '/':
567 recursive = 1;
568 break;
569 case 'f':
570 fast = 1;
571 break;
572 case 'd':
573 debug = 1;
574 break;
575 #if 0
576 case 't': /* test mode */
577 testmode = 1;
578 break;
579 #endif
580 case 'h':
581 usage(0);
582 default:
583 usage(1);
584 }
585 }
586
587 /* fake an argument */
588 if (optind == argc) {
589 argv = (char **)fakedArgv;
590 argc = 1;
591 optind = 0;
592 }
593
594 init_mp(&mp);
595 currentDrive = '\0';
596 currentDir = 0;
597 RootDir = 0;
598 dirPath = 0;
599 #if 0
600 if (testmode) {
601 mp.lookupflags = ACCEPT_DIR | NO_DOTS;
602 mp.dirCallback = test_directory;
603 } else
604 #endif
605 if(recursive) {
606 mp.lookupflags = ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS;
607 mp.dirCallback = list_recurs_directory;
608 } else {
609 mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN | DO_OPEN_DIRS;
610 mp.dirCallback = list_non_recurs_directory;
611 mp.callback = list_file;
612 }
613 mp.longname.data = mdir_longname;
614 mp.longname.len = sizeof(mdir_longname);
615 mp.shortname.data = mdir_shortname;
616 mp.shortname.len = sizeof(mdir_shortname);
617 ret=main_loop(&mp, argv + optind, argc - optind);
618 leaveDirectory(ret);
619 leaveDrive(ret);
620 exit(ret);
621 }
622