0dd807c320ab

Start implementing some actual functionality.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sat, 22 Sep 2012 15:26:22 -0400
parents c2b6722131ff
children f393d2ad42d7
branches/tags (none)
files ffind

Changes

--- a/ffind	Sat Sep 22 14:29:47 2012 -0400
+++ b/ffind	Sat Sep 22 15:26:22 2012 -0400
@@ -8,21 +8,81 @@
 #
 #                         The friendlier file finder.
 
+import os
 import optparse
+import string
+import sys
 from optparse import OptionParser, OptionGroup
 
+
+# Constants -------------------------------------------------------------------
 CASE_SENSITIVE = 1
 CASE_INSENSITIVE = 2
 CASE_SMART = 3
 
+VCS_DIRS = ['.hg', '.git', '.svn']
+
+# Global Options --------------------------------------------------------------
+# (it's a prototype, shut up)
+options = None
+
+# Output ----------------------------------------------------------------------
+def out(s):
+    sys.stdout.write(s + '\n')
+
+def err(s):
+    sys.stderr.write(s + '\n')
+
+def die(s, exitcode=1):
+    err('error: ' + s)
+    sys.exit(exitcode)
+
+
+# Searching! ------------------------------------------------------------------
+def should_ignore(basename, path):
+    if basename in VCS_DIRS:
+        return True
+
+    return False
+
+def match(query, path):
+    def _match():
+        if options.case == CASE_INSENSITIVE:
+            return query.lower() in path.lower()
+        else:
+            return query in path
+
+    result = _match()
+    return not result if options.invert else result
+
+def search(query, dir='.', depth=0):
+    contents = os.listdir(dir)
+
+    next = []
+    for item in contents:
+        path = os.path.join(dir, item)
+        if not should_ignore(item, path):
+            if match(query, path):
+                print path
+
+            is_dir = os.path.isdir(path)
+            if is_dir:
+                next.append(path)
+
+    if depth < options.depth:
+        for d in next:
+            search(query, d, depth + 1)
+
+
+# Option Parsing and Main -----------------------------------------------------
 def build_option_parser():
     p = OptionParser("usage: %prog [options] PATTERN")
 
     # Main options
-    p.add_option('-d', '--dir',
+    p.add_option('-d', '--dir', default='.',
                  help='root the search in DIR (default .)',
                  metavar='DIR')
-    p.add_option('-D', '--depth',
+    p.add_option('-D', '--depth', default='25',
                  help='search at most N directories deep (default 25)',
                  metavar='N')
     p.add_option('-f', '--follow',
@@ -79,6 +139,9 @@
     g.add_option('-a', '--all',
                  action='store_true', default=False,
                  help="don't ignore anything (ALL files can match)")
+    g.add_option('-I', '--ignore', metavar='PATTERN',
+                 action='append',
+                 help="add a pattern to be ignored (can be given multiple times)")
     p.add_option_group(g)
 
     # Time filtering
@@ -142,7 +205,7 @@
                     "x (symlinked files), "
                     "y (symlinked dirs). "
                     "If multiple types are given they will be unioned together:  "
-                    "--type es would match real files and all symlinks.")
+                    "--type 'es' would match real files and all symlinks.")
     g.add_option('-t', '--type',
                  action='store', default=False, metavar='TYPE(S)',
                  help='match only specific types of things (files, dirs, non-symlinks, symlinks)')
@@ -150,9 +213,42 @@
 
     return p
 
+
 def main():
+    global options
+
     (options, args) = build_option_parser().parse_args()
-    print options, args
+
+    # PATTERN
+    if len(args) > 1:
+        die("only one search pattern can be given")
+        sys.exit(1)
+
+    query = args[0] if args else ''
+
+    # --dir
+    if options.dir:
+        try:
+            os.chdir(options.dir)
+        except OSError:
+            die('could not change to directory "%s"' % options.dir)
+
+    # --depth
+    try:
+        options.depth = int(options.depth)
+    except ValueError:
+        die('depth must be a non-negative integer (got "%s")' % options.depth)
+
+    # --case-*
+    if options.case == CASE_SMART:
+        if any(c in string.uppercase for c in query):
+            options.case = CASE_SENSITIVE
+        else:
+            options.case = CASE_INSENSITIVE
+
+    # Go!
+    search(query)
+
 
 if __name__ == '__main__':
     main()