/* Copyright 1986-1992 Emmet P. Gray. * Copyright 1996-2002,2004,2007-2009 Alain Knaff. * This file is part of mtools. * * Mtools is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Mtools is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Mtools. If not, see . * * mdir.c: * Display an MSDOS directory */ #include "sysincludes.h" #include "msdos.h" #include "vfat.h" #include "mtools.h" #include "file.h" #include "mainloop.h" #include "fs.h" #include "codepage.h" #include "file_name.h" #ifdef TEST_SIZE #include "fsP.h" #endif static int recursive; static int wide; static int all; static int concise; static int fast=0; #if 0 static int testmode = 0; #endif static const char *dirPath; static char *dynDirPath; static char currentDrive; static Stream_t *currentDir; static int filesInDir; /* files in current dir */ static int filesOnDrive; /* files on drive */ static int dirsOnDrive; /* number of listed directories on this drive */ static int debug = 0; /* debug mode */ static mt_size_t bytesInDir; static mt_size_t bytesOnDrive; static Stream_t *RootDir; static char global_shortname[13]; static char global_longname[VBUFSIZE]; /* * Print an MSDOS directory date stamp. */ static __inline__ void print_date(struct directory *dir) { char year[5]; char day[3]; char month[3]; const char *p; sprintf(year, "%04d", DOS_YEAR(dir)); sprintf(day, "%02d", DOS_DAY(dir)); sprintf(month, "%02d", DOS_MONTH(dir)); for(p=mtools_date_string; *p; p++) { if(!strncasecmp(p, "yyyy", 4)) { printf("%04d", DOS_YEAR(dir)); p+= 3; continue; } else if(!strncasecmp(p, "yy", 2)) { printf("%02d", DOS_YEAR(dir) % 100); p++; continue; } else if(!strncasecmp(p, "dd", 2)) { printf("%02d", DOS_DAY(dir)); p++; continue; } else if(!strncasecmp(p, "mm", 2)) { printf("%02d", DOS_MONTH(dir)); p++; continue; } putchar(*p); } } /* * Print an MSDOS directory time stamp. */ static __inline__ void print_time(struct directory *dir) { char am_pm; int hour = DOS_HOUR(dir); if(!mtools_twenty_four_hour_clock) { am_pm = (hour >= 12) ? 'p' : 'a'; if (hour > 12) hour = hour - 12; if (hour == 0) hour = 12; } else am_pm = ' '; printf("%2d:%02d%c", hour, DOS_MINUTE(dir), am_pm); } /* * Return a number in dotted notation */ static const char *dotted_num(mt_size_t num, int width, char **buf) { int len; register char *srcp, *dstp; int size; unsigned long numlo; unsigned long numhi; if (num < 0) { /* warn about negative numbers here. They should not occur */ fprintf(stderr, "Invalid negative number\n"); } size = width + width; *buf = malloc(size+1); if (*buf == NULL) return ""; /* Create the number in maximum width; make sure that the string * length is not exceeded (in %6ld, the result can be longer than 6!) */ numlo = num % 1000000000; numhi = num / 1000000000; if(numhi && size > 9) { sprintf(*buf, "%.*lu%09lu", size-9, numhi, numlo); } else { sprintf(*buf, "%.*lu", size, numlo); } for (srcp=*buf; srcp[1] != '\0'; ++srcp) if (srcp[0] == '0') srcp[0] = ' '; else break; len = strlen(*buf); srcp = (*buf)+len; dstp = (*buf)+len+1; for ( ; dstp >= (*buf)+4 && isdigit (srcp[-1]); ) { srcp -= 3; /* from here we copy three digits */ dstp -= 4; /* that's where we put these 3 digits */ } /* now finally copy the 3-byte blocks to their new place */ while (dstp < (*buf) + len) { dstp[0] = srcp[0]; dstp[1] = srcp[1]; dstp[2] = srcp[2]; if (dstp + 3 < (*buf) + len) /* use spaces instead of dots: they please both * Americans and Europeans */ dstp[3] = ' '; srcp += 3; dstp += 4; } return (*buf) + len-width; } static __inline__ int print_volume_label(Stream_t *Dir, char drive) { Stream_t *Stream = GetFs(Dir); direntry_t entry; DeclareThis(FsPublic_t); char shortname[13]; char longname[VBUFSIZE]; int r; RootDir = OpenRoot(Stream); if(concise) return 0; /* find the volume label */ initializeDirentry(&entry, RootDir); if((r=vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY, shortname, longname)) ) { if (r == -2) { /* I/O Error */ return -1; } printf(" Volume in drive %c has no label", drive); } else if (*longname) printf(" Volume in drive %c is %s (abbr=%s)", drive, longname, shortname); else printf(" Volume in drive %c is %s", drive, shortname); if(This->serialized) printf("\n Volume Serial Number is %04lX-%04lX", (This->serial_number >> 16) & 0xffff, This->serial_number & 0xffff); return 0; } static void printSummary(int files, mt_size_t bytes) { if(!filesInDir) printf("No files\n"); else { char *s1 = NULL; printf(" %3d file", files); if(files == 1) putchar(' '); else putchar('s'); printf(" %s bytes\n", dotted_num(bytes, 13, &s1)); if(s1) free(s1); } } static void leaveDirectory(int haveError); static void leaveDrive(int haveError) { if(!currentDrive) return; leaveDirectory(haveError); if(!concise && !haveError) { if(dirsOnDrive > 1) { printf("\nTotal files listed:\n"); printSummary(filesOnDrive, bytesOnDrive); } if(RootDir && !fast) { char *s1 = NULL; mt_off_t bytes = getfree(RootDir); if(bytes == -1) { fprintf(stderr, "Fat error\n"); goto exit_1; } printf(" %s bytes free\n\n", dotted_num(bytes,17, &s1)); #ifdef TEST_SIZE ((Fs_t*)GetFs(RootDir))->freeSpace = 0; bytes = getfree(RootDir); printf(" %s bytes free\n\n", dotted_num(bytes,17, &s1)); #endif if(s1) free(s1); } } exit_1: FREE(&RootDir); currentDrive = '\0'; } static int enterDrive(Stream_t *Dir, char drive) { int r; if(currentDrive == drive) return 0; /* still the same */ leaveDrive(0); currentDrive = drive; r = print_volume_label(Dir, drive); if (r) return r; bytesOnDrive = 0; filesOnDrive = 0; dirsOnDrive = 0; return 0; } static const char *emptyString=""; static void leaveDirectory(int haveError) { if(!currentDir) return; if (!haveError) { if(dirPath && dirPath != emptyString) free(dynDirPath); if(wide) putchar('\n'); if(!concise) printSummary(filesInDir, bytesInDir); } FREE(¤tDir); } static int enterDirectory(Stream_t *Dir) { int r; char drive; if(currentDir == Dir) return 0; /* still the same directory */ leaveDirectory(0); drive = getDrive(Dir); r=enterDrive(Dir, drive); if(r) return r; currentDir = COPY(Dir); dynDirPath = getPwd(getDirentry(Dir)); if(!dynDirPath) dirPath=emptyString; else { if(!dynDirPath[3] && concise) dynDirPath[2]='\0'; dirPath=dynDirPath; } /* print directory title */ if(!concise) printf("\nDirectory for %s\n", dirPath); if(!wide && !concise) printf("\n"); dirsOnDrive++; bytesInDir = 0; filesInDir = 0; return 0; } static int list_file(direntry_t *entry, MainParam_t *mp UNUSEDP) { unsigned long size; int i; int Case; int r; wchar_t ext[4]; wchar_t name[9]; doscp_t *cp; if(!all && (entry->dir.attr & 0x6)) return 0; if(concise && isSpecialW(entry->name)) return 0; r=enterDirectory(entry->Dir); if (r) return ERROR_ONE; if (wide) { if(filesInDir % 5) putchar(' '); else putchar('\n'); } if(IS_DIR(entry)){ size = 0; } else size = FILE_SIZE(&entry->dir); Case = entry->dir.Case; if(!(Case & (BASECASE | EXTCASE)) && mtools_ignore_short_case) Case |= BASECASE | EXTCASE; cp = GET_DOSCONVERT(entry->Dir); dos_to_wchar(cp, entry->dir.ext, ext, 3); if(Case & EXTCASE){ for(i=0; i<3;i++) ext[i] = towlower(ext[i]); } ext[3] = '\0'; dos_to_wchar(cp, entry->dir.name, name, 8); if(Case & BASECASE){ for(i=0; i<8;i++) name[i] = towlower(name[i]); } name[8]='\0'; if(wide){ if(IS_DIR(entry)) printf("[%s]%*s", global_shortname, (int) (15 - 2 - strlen(global_shortname)), ""); else printf("%-15s", global_shortname); } else if(!concise) { char tmpBasename[4*8+1]; char tmpExt[4*8+1]; wchar_to_native(name,tmpBasename,8); wchar_to_native(ext,tmpExt,3); if (name[0] == ' ') printf(" "); else if(mtools_dotted_dir) printf("%-12s ", global_shortname); else printf("%s %s ", tmpBasename, tmpExt); /* is a subdirectory */ if(IS_DIR(entry)) printf(" "); else printf(" %8ld", (long) size); printf(" "); print_date(&entry->dir); printf(" "); print_time(&entry->dir); if(debug) printf(" %s %d ", tmpBasename, START(&entry->dir)); if(*global_longname) printf(" %s", global_longname); printf("\n"); } else { char tmp[4*MAX_VNAMELEN+1]; wchar_to_native(entry->name,tmp,MAX_VNAMELEN); printf("%s/%s", dirPath, tmp); if(IS_DIR(entry)) putchar('/'); putchar('\n'); } filesOnDrive++; filesInDir++; bytesOnDrive += (mt_size_t) size; bytesInDir += (mt_size_t) size; return GOT_ONE; } static int list_non_recurs_directory(direntry_t *entry, MainParam_t *mp) { int r; /* list top-level directory * If this was matched by wildcard in the basename, list it as * file, otherwise, list it as directory */ if (mp->basenameHasWildcard) { /* wildcard, list it as file */ return list_file(entry, mp); } else { /* no wildcard, list it as directory */ MainParam_t subMp; r=enterDirectory(mp->File); if(r) return ERROR_ONE; subMp = *mp; subMp.dirCallback = subMp.callback; return mp->loop(mp->File, &subMp, "*") | GOT_ONE; } } static int list_recurs_directory(direntry_t *entry UNUSEDP, MainParam_t *mp UNUSEDP) { MainParam_t subMp; int ret; /* first list the files */ subMp = *mp; subMp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN; subMp.dirCallback = list_file; subMp.callback = list_file; ret = mp->loop(mp->File, &subMp, "*"); /* then list subdirectories */ subMp = *mp; subMp.lookupflags = ACCEPT_DIR | NO_DOTS | NO_MSG | DO_OPEN; return ret | mp->loop(mp->File, &subMp, "*"); } #if 0 static int test_directory(direntry_t *entry, MainParam_t *mp) { Stream_t *File=mp->File; Stream_t *Target; char errmsg[80]; if ((Target = SimpleFileOpen(0, 0, "-", O_WRONLY, errmsg, 0, 0, 0))) { copyfile(File, Target); FREE(&Target); } return GOT_ONE; } #endif static void usage(int ret) NORETURN; static void usage(int ret) { fprintf(stderr, "Mtools version %s, dated %s\n", mversion, mdate); fprintf(stderr, "Usage: %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosdirectory\n", progname); fprintf(stderr, " %s: [-V] [-w] [-a] [-b] [-s] [-f] msdosfile [msdosfiles...]\n", progname); exit(ret); } void mdir(int argc, char **argv, int type UNUSEDP) { int ret; MainParam_t mp; int c; const char *fakedArgv[] = { "." }; concise = 0; recursive = 0; wide = all = 0; /* first argument */ if(helpFlag(argc, argv)) usage(0); while ((c = getopt(argc, argv, "i:waXbfds/h")) != EOF) { switch(c) { case 'i': set_cmd_line_image(optarg); break; case 'w': wide = 1; break; case 'a': all = 1; break; case 'b': case 'X': concise = 1; /*recursive = 1;*/ break; case 's': case '/': recursive = 1; break; case 'f': fast = 1; break; case 'd': debug = 1; break; #if 0 case 't': /* test mode */ testmode = 1; break; #endif case 'h': usage(0); default: usage(1); } } /* fake an argument */ if (optind == argc) { argv = (char **)fakedArgv; argc = 1; optind = 0; } init_mp(&mp); currentDrive = '\0'; currentDir = 0; RootDir = 0; dirPath = 0; #if 0 if (testmode) { mp.lookupflags = ACCEPT_DIR | NO_DOTS; mp.dirCallback = test_directory; } else #endif if(recursive) { mp.lookupflags = ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS; mp.dirCallback = list_recurs_directory; } else { mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN | DO_OPEN_DIRS; mp.dirCallback = list_non_recurs_directory; mp.callback = list_file; } mp.longname = global_longname; mp.shortname = global_shortname; ret=main_loop(&mp, argv + optind, argc - optind); leaveDirectory(ret); leaveDrive(ret); exit(ret); }