xref: /aosp_15_r20/external/mtools/mdir.c (revision d5c9a868b113e0ec0db2f27bc2ce8a253e77c4b0)
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_off_t bytesInDir;
57 static mt_off_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_off_t num,size_t width,char ** buf)124 static const char *dotted_num(mt_off_t num, size_t width, char **buf)
125 {
126 	size_t len;
127 	register char *srcp, *dstp;
128 	size_t 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 = (unsigned long) (num / 1000000000);
145 
146 	if(numhi && size > 9) {
147 		sprintf(*buf, "%.*lu%09lu", (int)(size-9), numhi, numlo);
148 	} else {
149 		sprintf(*buf, "%.*lu", (int) 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(Fs_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(getSerialized(This)) {
214 		unsigned long serial_number = getSerialNumber(This);
215 		printf("\n Volume Serial Number is %04lX-%04lX",
216 		       (serial_number >> 16) & 0xffff,
217 		       serial_number & 0xffff);
218 	}
219 	return 0;
220 }
221 
222 
printSummary(int files,mt_off_t bytes)223 static void printSummary(int files, mt_off_t bytes)
224 {
225 	if(!filesInDir)
226 		printf("No files\n");
227 	else {
228 		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 
241 static void leaveDirectory(int haveError);
242 
leaveDrive(int haveError)243 static 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 
enterDrive(Stream_t * Dir,char drive)279 static int enterDrive(Stream_t *Dir, char drive)
280 {
281 	int r;
282 	if(currentDrive == drive)
283 		return 0; /* still the same */
284 
285 	leaveDrive(0);
286 	currentDrive = drive;
287 
288 	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 
299 static const char *emptyString="<out-of-memory>";
300 
leaveDirectory(int haveError)301 static 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');
311 
312 		if(!concise)
313 			printSummary(filesInDir, bytesInDir);
314 	}
315 	FREE(&currentDir);
316 }
317 
enterDirectory(Stream_t * Dir)318 static 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 
list_file(direntry_t * entry,MainParam_t * mp UNUSEDP)355 static 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)
377 			putchar(' ');
378 		else
379 			putchar('\n');
380 	}
381 
382 	if(IS_DIR(entry)){
383 		size = 0;
384 	} else
385 		size = FILE_SIZE(&entry->dir);
386 
387 	Case = entry->dir.Case;
388 	if(!(Case & (BASECASE | EXTCASE)) &&
389 	   mtools_ignore_short_case)
390 		Case |= BASECASE | EXTCASE;
391 
392 	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);
416 	} else if(!concise) {
417 		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 
422 		if (name[0] == ' ')
423 			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));
440 
441 		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 
458 	bytesOnDrive += size;
459 	bytesInDir += size;
460 	return GOT_ONE;
461 }
462 
list_non_recurs_directory(direntry_t * entry,MainParam_t * mp)463 static 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 
list_recurs_directory(direntry_t * entry UNUSEDP,MainParam_t * mp UNUSEDP)487 static 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;
504 	return ret | mp->loop(mp->File, &subMp, "*") | GOT_ONE;
505 }
506 
507 #if 0
508 static 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 
524 static void usage(int ret) NORETURN;
usage(int ret)525 static 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 
537 void mdir(int argc, char **argv, int type UNUSEDP) NORETURN;
mdir(int argc,char ** argv,int type UNUSEDP)538 void 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;
605 	} else
606 #endif
607 	if(recursive) {
608 		mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN | DO_OPEN_DIRS | NO_DOTS;
609 		mp.dirCallback = list_recurs_directory;
610 		mp.callback = list_file;
611 	} 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 }
625