"""Group of generators to recurse a file tree given the apex path. Copyright © 2022, 2021, by Phil D. Howard - all rights reserved Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The author may be contacted by decoding the number 11054987560151472272755686915985840251291393453694611309 (provu igi la numeron al duuma) """ from os import listdir,lstat,readlink,sep from stat import S_ISBLK,S_ISCHR,S_ISDIR,S_ISDOOR,S_ISFIFO from stat import S_ISLNK,S_ISPORT,S_ISREG,S_ISSOCK,S_ISWHT from stat import filemode from sys import maxsize,stdout def ftrgen1(apex, mindepth=0, maxdepth=maxsize, *, key=None, # provide alternate sort key reverse=False, # reverse order of directory list reads ascends=False, # yield directory when ascending ): """File tree recursion GENERATOR that yields all paths. in the same order as sorting the whole tree optionally sorted by key in key= applied to the path optionally sorted in reverse order with reverse=True optionally yielding directories at ascend with ascends=True to be yielded for each path: path """ for p,d in ftrgen2(apex, mindepth=mindepth, maxdepth=maxdepth, key=key, reverse=reverse, ascends=ascends ): yield p ftrgenp = ftrgen1 def ftrgen2(apex, mindepth=0, maxdepth=maxsize, *, key=None, # provide alternate sort key reverse=False, # reverse order of directory list reads ascends=False, # yield directory when ascending ): """File tree recursion GENERATOR that yields all paths. in the same order as sorting the whole tree optionally sorted by key in key= applied to the path optionally sorted in reverse order with reverse=True optionally yielding directories at ascend with ascends=True to be yielded for each path: path, depth """ global sep dotsep = '.'+sep if not isinstance(maxdepth,(int,float)): maxdepth = maxsize if not isinstance(mindepth,(int,float)): mindepth = 0 elif mindepth < 0: mindepth = 0 if isinstance(apex,bytes): sep = bytes(ord(x) for x in sep) dotsep = bytes(ord(x) for x in dotsep) depth = 0 # the apex is at this depth tree = [[apex]] # the top always has one name until all done while tree[0]: # loop while the top has a name if tree[-1]: # if the bottom is not empty path = sep.join([x[0] for x in tree]) if depth >= mindepth: if path[:2] == dotsep: path = path[2:] yield path,depth try: readlink(path) except OSError: try: # directory? new = sorted(listdir(path),key=key,reverse=reverse) # path is a directory if depth < maxdepth: # if allowed to go deeper, descend tree.append(new) # use the next list of names depth += 1 # calculate how much deeper continue except OSError: # not a directory pass # stay at this level tree[-1].pop(0) # select the next name else: # ascend depth -= 1 # less deep tree[-1:] = [] # shorten tree if ascends: # if ascends wanted path = sep.join([x[0] for x in tree]) # form path for ascend if path[:2] == dotsep: path = path[2:] if depth >= mindepth: yield path,depth tree[-1].pop(0) # select the next name (may become empty) ftrgenpd = ftrgen2 ftrgen = ftrgen2 def ftrgen3(apex, mindepth=0, maxdepth=maxsize, *, key=None, # provide alternate sort key reverse=False, # reverse order of directory list reads ascends=False, # yield directory when ascending ): """File tree recursion GENERATOR that yields all paths. in the same order as sorting the whole tree optionally sorted by key in key= applied to the path optionally sorted in reverse order with reverse=True optionally yielding directories at ascend with ascends=True to be yielded for each path: path, depth, stat """ for p,d in ftrgen2(apex, mindepth=mindepth, maxdepth=maxdepth, key=key, reverse=reverse, ascends=ascends ): try: yield p,d,lstat(p) except FileNotFoundError: continue ftrgenpds = ftrgen3 def ftrgen4(apex, mindepth=0, maxdepth=maxsize, types=None, *, key=None, # provide alternate sort key reverse=False, # reverse order of directory list reads ascends=False, # yield directory when ascending ): """File tree recursion GENERATOR that yields all paths. in the same order as sorting the whole tree optionally sorted by key in key= applied to the path optionally sorted in reverse order with reverse=True optionally yielding directories at ascend with ascends=True to be yielded for each path: path, depth, stat, type """ if types is True or not types: types = 'fdlbcpsPDW?' # default to all types types = frozenset(types) for p,d,s in ftrgen3(apex, mindepth=mindepth, maxdepth=maxdepth, key=key, reverse=reverse, ascends=ascends, ): t='?' if S_ISREG(s.st_mode):t='f' elif S_ISDIR(s.st_mode):t='d' elif S_ISLNK(s.st_mode):t='l' elif S_ISBLK(s.st_mode):t='b' elif S_ISCHR(s.st_mode):t='c' elif S_ISFIFO(s.st_mode):t='p' elif S_ISSOCK(s.st_mode):t='s' elif S_ISPORT(s.st_mode):t='P' elif S_ISDOOR(s.st_mode):t='D' elif S_ISWHT(s.st_mode):t='W' if t in types: yield p,d,s,t ftrgenpdst = ftrgen4 def ftrgen5(apex, mindepth=0, maxdepth=maxsize, types=None, *, key=None, # provide alternate sort key reverse=False, # reverse order of directory list reads ascends=False, # yield directory when ascending ): """File tree recursion GENERATOR that yields all paths. in the same order as sorting the whole tree optionally sorted by key in key= applied to the path optionally sorted in reverse order with reverse=True optionally yielding directories at ascend with ascends=True to be yielded for each path: path, depth, stat, type, mode """ for p,d,s,t in ftrgen4(apex, mindepth=mindepth, maxdepth=maxdepth, types=types, key=key, reverse=reverse, ascends=ascends, ): m=filemode(s.st_mode) if t!='f': m=t+m[1:] yield p,d,s,t,m ftrgenpdstm = ftrgen5 if __name__ == '__main__': from sys import argv argv.pop(0) reverse=False if argv and argv[0].lower() in ('-r','--reverse'): argv.pop(0) reverse=True try: # sort the arguments for args in sorted(argv,reverse=reverse) if argv else '.': for p,d in ftrgen2(args,reverse=reverse): if p[:2] == './': p = p[:2] print(' '*d+p) stdout.flush() except BrokenPipeError: exit(0)