| Charles Curley - Software Engineer, Writer
| << | < | > | >>
+ Larger Font | - Smaller Font
Charles Curley

Valid XHTML 1.0! Valid CSS!


Fn

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);
}

Copyright © 1996 through 2008 by Charles Curley
Last Modified: 24 Feb, 2008
100% Microsoft-free web site.