• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&currentDir);
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