Welcome, guest | Sign In | My Account | Store | Cart
#!/usr/bin/python

import os
import os.path
import sys
import md5
from stat import *
from optparse import OptionParser

class Stats:
       
def __init__(self):
               
self.filenb = 0
               
self.dirnb = 0
               
self.othernb = 0
               
self.unstatablenb = 0

def scan_tree(lst, maxlen, dirname, dirpath, prefix, nxt_prefix, options, stats):
       
"""params:
         lst: I/O list of (tree_ascii_art_repr_line, path_if_regular_file_else_None)
          where both are strings and the second one can also be None
         maxlen: integer that contains the rightmost column number of ascii repr of
          the tree known by the caller
         dirname: name of the directory from which a tree repr is wanted
         dirpath: path to the directory from which a tree repr is wanted
         prefix: string to prepend to the dirname to form the first line of the ascii
          repr of the subtree
         nxt_prefix: string to prepend to every lines of the repr of the subtree but
          the first one (which uses prefix)
         options: options as extracted by the optparse module from cmd line options
         stats: Stats instance
        returns a new value for maxlen
        """

       
try:
                dir_content
= os.listdir(dirpath)
                dir_content
.sort()
       
except OSError:
                dir_content
= None
        ascii_art_tree_repr
= prefix + dirname
        maxlen
= max(maxlen, len(ascii_art_tree_repr))
       
if dir_content is None:
                lst
.append((ascii_art_tree_repr + ' [error reading dir]', None))
               
return maxlen
       
if not options.all:
                dir_content
= [child for child in dir_content if child[0] != '.']
        lst
.append((ascii_art_tree_repr, None))
        sub_prefix    
= nxt_prefix + '|-- '
        sub_nxt_prefix
= nxt_prefix + '|   '
       
for num, child in enumerate(dir_content):
               
if num == len(dir_content) - 1:
                        sub_prefix    
= nxt_prefix + '`-- '
                        sub_nxt_prefix
= nxt_prefix + '    '
                joined_path
= os.path.join(dirpath, child)
               
try:
                        lmode
= os.lstat(joined_path)[ST_MODE]
               
except:
                        lmode
= None
                ascii_art_tree_repr
= sub_prefix + child
                maxlen
= max(maxlen, len(ascii_art_tree_repr))
               
if lmode is None:
                        stats
.unstatablenb += 1
                        lst
.append((ascii_art_tree_repr + ' [error stating child]', None))
               
elif S_ISREG(lmode):
                        stats
.filenb += 1
                        lst
.append((ascii_art_tree_repr, joined_path))
               
elif S_ISDIR(lmode):
                        stats
.dirnb += 1
                        maxlen
= scan_tree(lst, maxlen, child, joined_path, sub_prefix, sub_nxt_prefix, options, stats)
               
elif S_ISLNK(lmode):
                        stats
.filenb += 1
                       
try:
                                lst
.append((ascii_art_tree_repr + ' -> ' + os.readlink(joined_path), None))
                       
except OSError:
                                lst
.append((ascii_art_tree_repr + ' [cannot read symlink]', None))
               
elif S_ISCHR(lmode):
                        stats
.othernb += 1
                        lst
.append((ascii_art_tree_repr + ' [char device]', None))
               
elif S_ISBLK(lmode):
                        stats
.othernb += 1
                        lst
.append((ascii_art_tree_repr + ' [block device]', None))
               
elif S_ISFIFO(lmode):
                        stats
.othernb += 1
                        lst
.append((ascii_art_tree_repr + ' [fifo]', None))
               
elif S_ISSOCK(lmode):
                        stats
.othernb += 1
                        lst
.append((ascii_art_tree_repr + ' [socket]', None))
               
else:
                        stats
.othernb += 1
                        lst
.append((ascii_art_tree_repr + ' [unknown]', None))
       
return maxlen          

def md5_from_path(path):
       
"""Returns an hex repr of the md5sum of the file content path points to.
        On IOError returns '<unable to read file>'.
        """

       
try:
                f
= open(path)
                m
= md5.new()
               
while True:
                        b
= f.read(262144)
                       
if not b:
                               
break
                        m
.update(b)
                f
.close()
               
return m.hexdigest()
       
except IOError:
               
return '<unable to read file>'

def main():
        parser
= OptionParser(usage="usage: %prog [options] [dir1 [dir2 [...]]]")
        parser
.add_option("-a", "--all", action='store_true', dest='all', default=False, help="All files are listed.")
        options
, roots = parser.parse_args()
        stats
= Stats()
       
if not roots:
                roots
= ['.']
       
for root in roots:
                lst
= []
                maxlen
= scan_tree(lst, 0, root, root, "", "", options, stats)
               
for line, path in lst:
                       
if path is not None:
                                m
= md5_from_path(path)
                               
print line + ' ' * (maxlen+1-len(line)) + m
                       
else:
                               
print line
       
print
       
print ', '.join((
               
('%d directory', '%d directories')[stats.dirnb > 1] % stats.dirnb,
               
('%d file', '%d files')[stats.filenb > 1] % stats.filenb,
               
('%d other', '%d others')[stats.othernb > 1] % stats.othernb,
               
('%d unstatable', '%d unstatables')[stats.unstatablenb > 1] % stats.unstatablenb))

if __name__ == "__main__":
        main
()

History