diff options
Diffstat (limited to 'mdir.c')
-rw-r--r-- | mdir.c | 617 |
1 files changed, 617 insertions, 0 deletions
@@ -0,0 +1,617 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * 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="<out-of-memory>"; + +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("<DIR> "); + 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); +} |