This program implements
a small subset
of the MS-Windows Find Filesdialog or the Unix
find command. However, it has much of the
structure needed to build a more complete version of either of these.
It uses a custom filename filter controlled by the
-n
command-line option, which is parsed using my
GetOpt
(see Section 2.8).
import com.darwinsys.util.*; import java.io.*; import java.io.*; /** * Find - find files by name, size, or other criteria. Non-GUI version. */ public class Find { /** Main program */ public static void main(String[] args) { Find finder = new Find( ); GetOpt argHandler = new GetOpt("n:s:"); int c; while ((c = argHandler.getopt(args)) != GetOpt.DONE) { switch(c) { case 'n': finder.filter.setNameFilter(argHandler.optarg( )); break; case 's': finder.filter.setSizeFilter(argHandler.optarg( )); break; default: System.out.println("Got: " + c); usage( ); } } if (args.length == 0 || argHandler.getOptInd( )-1 == args.length) { finder.doName("."); } else { for (int i = argHandler.getOptInd( )-1; i<args.length; i++) finder.doName(args[i]); } } protected FindFilter filter = new FindFilter( ); public static void usage( ) { System.err.println( "Usage: Find [-n namefilter][-s sizefilter][dir...]"); System.exit(1); } /** doName - handle one filesystem object by name */ private void doName(String s) { Debug.println("flow", "doName(" + s + ")"); File f = new File(s); if (!f.exists( )) { System.out.println(s + " does not exist"); return; } if (f.isFile( )) doFile(f); else if (f.isDirectory( )) { // System.out.println("d " + f.getPath( )); String objects[] = f.list(filter); for (int i=0; i<objects.length; i++) doName(s + f.separator + objects[i]); } else System.err.println("Unknown type: " + s); } /** doFile - process one regular file. */ private static void doFile(File f) { System.out.println("f " + f.getPath( )); } }
The program uses a class called
FindFilter
to implement matching:
import java.io.*; import org.apache.regexp.*; import com.darwinsys.util.Debug; /** Class to encapsulate the filtration for Find. * For now just setTTTFilter( ) methods. Really needs to be a real * data structure to allow complex things like * -n "*.html" -a ( -size < 0 -o mtime < 5 ). */ public class FindFilter implements FilenameFilter { boolean sizeSet; int size; String name; RE nameRE; public FindFilter( ) { } void setSizeFilter(String sizeFilter) { size = Integer.parseInt(sizeFilter); sizeSet = true; } /** Convert the given shell wildcard pattern into internal form (an RE) */ void setNameFilter(String nameFilter) { name = nameFilter; StringBuffer sb = new StringBuffer('^'), for (int i = 0; i < nameFilter.length( ); i++) { char c = nameFilter.charAt(i); switch(c) { case '.': sb.append("\."); break; case '*': sb.append(".*"); break; case '?': sb.append('.'), break; default: sb.append(c); break; } } sb.append('$'), Debug.println("name", "RE="" + sb + ""."); try { nameRE = new RE(sb.toString( )); } catch (RESyntaxException ex) { System.err.println("For shame! " + ex); } } /** Do the filtering. For now, only filter on name */ public boolean accept(File dir, String fileName) { File f = new File(dir, fileName); if (f.isDirectory( )) { return true; // allow recursion } if (name != null) { return nameRE.match(fileName); } // TODO size handling. // Catchall return false; } }
Exercise for the reader: in the source directory, you’ll find a
class called FindNumFilter
, which is meant to
(someday) allow relational comparison of sizes, modification times,
and the like, as most find
services already offer.
Make this work from the command line, and write
a GUI front-end to this program.
3.21.12.140