|
| Charles Curley - Software Engineer, Writer | << | < | > | >> + Larger Font | - Smaller Font |
Charles Curley |
A find utility for DOS
To find files on MS-DOS® use fn.c. It finds all file names on the drive, including hidden and system. The user may pipe the output for further processing. The executable is available for download.
This is a sample program in C. It was originally written for Mark Williams® C on the Atari ST®, and later converted for Microsoft® C 7.0 running on MS-DOS®. Since then, I have adapted it for Visual C++ 4.0. The present version is compiled on VC++ 5.0, and may compile on older versions.
It is not heavily commented, except for the user information at the top. It should be written clearly enough that a programmer reasonably familiar with MS-DOS or operating systems in general can figure it out.
This code is released to the public domain. Wear it in good health.
You may also download a pre-compiled version. This runs on Windows 95, 98, NT 4 and 5 (later known as Windows 2000). This version also prints out the full NT attributes, such as encryption and compression, and the Windows 2000 attributes, like sparse files.
See the change notes in the header file for further tweaks & additions.
If this won't quite do what you want, look at the Cygwin tools.
/* fn.c Finds all file names on the system. Output can be piped for further processing. E.g: fn *.wp | egrep "filename" */ static char *TimeStamp = "Time-stamp: <98/10/29 06:50:15 a-ccurle>"; /* options: 1) no arguments: prints out contents of current directory and all directories below it. 2) one argument: argument is taken as a file spec, and only files meeting that file spec in current and lower directories are printed out. 3) one argument, a dash question mark ("-?"). The help message is printed out. 4) one argument, which is take as a path, in which case all files in the given path and subdirectories are printed out. 5) two arguments, a path followed by a file spec, separated by at least one space. The path specifies the starting directory. All files in the given path and lower directories which meet the file spec are printed out. This is consistent with the Unix find facility, if not the Mess-DOS dir /s facility, which did not exist when I started this. Switches: -? The help message is printed out. No further processing is done. -a Print known file attributes. -d Print out the last access time & date. The time and date are printed out as follows: yyyy:mm:dd:hh:mm:ss 1996:09:22:12:25:02 This is intended to make sorting by date much easier, e.g.: fn -d *.c | sort -l Print out length of file. -q print out directories only -g print times in GMT (UTC) rather than local. -r _prevents_ recursion into lower directories. This is the opposite of Unix usage, but the default is to recurse, where typically Unix usage the default is to not recurse. -n; causes the file name to be surrounded with quotes. This may be useful for file names that have spaces in them. Anything not a valid switch will be ignored. History: Originally from Mark Williams C manual entry on Fgetdta. Begun 22 7 90 crc modified 11 8 90 crc. mess-dos version begun 2 10 90, last modified 12 28 90 crc 8 8 92 now will show up hidden & system files\dirs crc Began adding switches from Atari ST version. 4 feb 94 crc Converted for Visual C++ 4.0 and added sort-able date printout. We now print out the time last modified, as VC++/W95/Wnt store unix data: time of creation, last modified, and last accessed. 22 9 96 crc 1998 05 4 c^2: Added the -q switch, which lets the user print out directories only. 1998 05 4 c^2: Fixed a bug in filtering directories. In order to avoid printing out "." and "..", the code used to check for the first character being equal to '.'. This is OK on Mess-DOS's 8.3 naming scheme, but doesn't work on W95/NT3.51 and up. For example, it misses the emacs configuration file ".emacs". I corrected the test to look for ".." and ".". 1998 10 08 c^2: Hmmm, you also have to detect directory names that start with . or .. so you can descend into them, so I changed the test for valid directories to exclude only "." and "..". The program will now descend into, e.g., .foo to find .foo/bar. Also added a newline at the end of the total files string so it works better with BASH. 1998 10 19 C^2: updated from the crt functions (_findfirst, etc,) to the win32 functions (FindFirstFile). This means I can also show more attribute bits, so I extended the attribute showing code to 16 bits of attributes. So far. Added the -g switch to allow GMT time/date. No longer do we translate to lower case, as case is significant when accessing a VFAT volume from Linux. 1998 10 28 15:03:28: fixed a small bug: we were not printing out the r (read-only) bit in the attributes subroutine. Oops. Inlined all the functions (compiler's option) except find(). It might make things marginally faster & doesn't appear to take up any space. Added the -r (do _not_ recurse) switch. Turned over the minor version number to 3.1. 1998 10 29 06:27:10: added the -n switch. */ #define _WIN32_WINNT 0x0400 #include <windows.h> char *generic = "*"; BOOL quote = 0; // Shall we print file name w/ quotes? BOOL recurse = 1; // shall we recurse? Default is yes. BOOL atts = 0; // print out attributes BOOL td = 0; // print out time & date of last write to file BOOL ln = 0; // print out file lengths BOOL directories = 0; // if 0, print out file names. BOOL gmt; // print times in GMT, not local unsigned _int64 sigma; /* accumulator for lengths */ /* Print out the time and date of last modification to the file. On Windows NT, use GMT if the gmt flag is set. The time and date are printed so as to be easily sorted, e.g: fn -d *.foo | sort */ void __inline date(FileTimeLastMod) FILETIME FileTimeLastMod; { BOOL result; SYSTEMTIME SysTime; FILETIME LocalTime; result = FileTimeToSystemTime (&FileTimeLastMod, &SysTime); if(!gmt) { result = FileTimeToLocalFileTime(&FileTimeLastMod, &LocalTime); result = FileTimeToSystemTime (&LocalTime, &SysTime); } printf ("%04d:%02d:%02d:%02d:%02d:%02d ", SysTime.wYear, SysTime.wMonth, SysTime.wDay, SysTime.wHour, SysTime.wMinute, SysTime.wSecond); } /* print out attributes as given. */ void __inline attributes(attr) register DWORD attr; { static char *attrs = "?ecoCrstndaDvshr"; register int i; register int mask = 0x8000; for( i=0 ; i<16 ; i++) { putchar( attr & mask ? attrs[i]:'-'); mask >>= 1; } // printf(" "); putchar (' '); } /* do help printout */ void __inline dohelp(char * argv) { printf("%s, a public domain file search utility by C. Curley.\n", argv); printf(" Version 3.1, %s\n", TimeStamp); printf("usage: %s [-switch] [path] [file spec]\n", argv); printf(" find a file in a given path and its subdirectories.\n"); printf(" defaults: current directory, *\n"); printf("Switches: -?, help. -a, attributes. -d, local time/date. -r, no recursion\n"); printf(" -g: GMT time/date. -l, length of file. -q, directories only.\n"); printf(" -n: enclose file name in quotes.\n"); } /* concatenate path suffix to path prefix */ __inline char * dircat(pfx, sfx) register char *pfx, *sfx; { extern char *strcat(); register char *p; register int nb, npfx; nb = (npfx = strlen(pfx)) + 1 + strlen(sfx) + 1; if(( p = malloc(nb)) == (char *)0) exit(1); strcpy(p, pfx); if(npfx != 0 && pfx[npfx-1] != '\\') strcat(p, "\\"); return strcat(p, sfx); } /* search the directory specified by dirname */ void find(name, spec) char *name; char *spec; { register char *globname, *newname; WIN32_FIND_DATA FileData; HANDLE hFile; /* printf("In Find()\n"); */ if (recurse) { globname = dircat(name, generic); if((hFile = FindFirstFile(globname, &FileData)) != INVALID_HANDLE_VALUE) { do { if (!(!strcmp (FileData.cFileName, ".") || !strcmp (FileData.cFileName, ".."))) { if(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { newname = dircat(name, FileData.cFileName); /* printf("Found Directory: %s\n", newname); */ find(newname, spec); free(newname); } } } while (FindNextFile(hFile, &FileData) != 0); } free(globname); FindClose( hFile ); } globname = dircat(name, spec); if( (hFile = FindFirstFile(globname, &FileData)) != INVALID_HANDLE_VALUE) { do { if(!(!strcmp (FileData.cFileName, ".") || !strcmp (FileData.cFileName, ".."))) { newname = dircat(name, FileData.cFileName); // if we are doing files or this is a dir if(!directories || FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if(atts) attributes(FileData.dwFileAttributes); if(td) date(FileData.ftLastWriteTime); if(ln) { /* Thanks to the Win32 API's kludgy approach to handling 64 bit values, we have to reconstruct the file length from two 32 bit values. Yucch. */ _int64 fl = (_int64) FileData.nFileSizeLow + ((_int64) FileData.nFileSizeHigh << 8*sizeof (DWORD)); printf("%12I64d ", fl ); sigma += fl; } if (quote) { printf ("\"%s\"\n", newname); } else { printf ("%s\n", (newname)); } } free(newname); } } while (FindNextFile(hFile, &FileData) != 0); } free(globname); FindClose( hFile ); } main(argc, argv) int argc; char *argv[]; { char *spec, *path; int sw = 0; /* switch present? */ int oops = 0; /* error in user entry? */ /* check for switches & process them. */ if( argc > 1 && (argv[1][0] == '-' || argv[1][0] == '/')) { if( strpbrk( argv[1], "?" )) { dohelp(argv[0]); exit(0); } sw++; if( strpbrk( argv[1], "Aa")) atts = 1; if( strpbrk( argv[1], "Dd")) td = 1; if( strpbrk( argv[1], "Gg")) { gmt= 1; td = 1; } if( strpbrk( argv[1], "Ll")) { ln = 1; sigma = 0; } if( strpbrk( argv[1], "Qq")) directories = 1; if( strpbrk( argv[1], "Rr")) recurse = 0; if( strpbrk( argv[1], "Nn")) quote = 1; } if(argc > 2+sw) { path = argv[1+sw]; spec = argv[2+sw]; } else { if(argc > 1+sw) { if((int)memchr(&argv[1+sw][0], '\\', strlen(argv[1+sw]))) { spec = generic; path = argv[1+sw]; } else { path = ""; spec = argv[1+sw]; } } else { path = ""; spec = generic; } } find(path, spec); if( ln ) { printf( "total length is %I64u.\n", sigma ); } return(0); }