Newer
Older
tdir / tdir
  1. #!/usr/bin/env python
  2. """
  3. tdir - Display Formatted Directory Listings
  4. Copyright (c) 2001-2018 TundraWare Inc., All Rights Reserved.
  5. """
  6.  
  7. # python Library Imports
  8.  
  9. import getopt
  10. import os
  11. import sys
  12.  
  13. # Version info
  14.  
  15. VERSION = "$Id: tdir,v 1.73 2018/06/17 00:00:00 tundra Exp $"
  16.  
  17.  
  18. # Supporting Functions
  19.  
  20. # Found at: http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python
  21.  
  22. def GetTerminalSize():
  23.  
  24. env = os.environ
  25. def ioctl_GWINSZ(fd):
  26.  
  27. try:
  28. import fcntl, termios, struct, os
  29. cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
  30.  
  31. except:
  32. return
  33.  
  34. return cr
  35.  
  36. cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
  37. if not cr:
  38.  
  39. try:
  40. fd = os.open(os.ctermid(), os.O_RDONLY)
  41. cr = ioctl_GWINSZ(fd)
  42. os.close(fd)
  43.  
  44. except:
  45. pass
  46.  
  47. if not cr:
  48. cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
  49.  
  50. ### Use get(key[, default]) instead of a try/catch
  51. #try:
  52. # cr = (env['LINES'], env['COLUMNS'])
  53. #except:
  54. # cr = (25, 80)
  55.  
  56. return int(cr[1]), int(cr[0])
  57.  
  58. # End of 'GetTerminalSize()'
  59.  
  60.  
  61. # Output formatting constants.
  62.  
  63. OWIDTH = 80 # Default output width fixed for non-*NIX systems
  64.  
  65. if os.name == 'posix': # On *NIX get the current window parameter instead
  66. OWIDTH = GetTerminalSize()[0] - 1
  67.  
  68. COLWIDTH = 19 # Width of each column
  69. TWIDTH = COLWIDTH - 1 # Text width
  70. MAXCOL, INDENT = divmod(OWIDTH, COLWIDTH) # No of output cols & indent
  71. PAD = " " # Padding character
  72. SEP = "." # Filename "extension" separator
  73. TRUNC = "^" # Character indicating name truncation
  74. DOTFILE = '.' # Dotfiles start with this
  75.  
  76. # Defaults
  77.  
  78. RECURSE = False # No recursion
  79. SHOWDIR = True # Show directories in listing
  80. SHOWDOT = True # Show dotfiles in listing
  81. SHOWFILE = True # Show files in listing
  82. SORTBYEXT = True # Sort file names by extension
  83.  
  84.  
  85. def OrderByExtension(list):
  86. ExtList = {}
  87. ExtList[""] = [] # List of files with no extension
  88.  
  89. for x in list:
  90. y = x.rfind(SEP)
  91. if not y: # Handle special case of 'dot file' type files
  92. ext = ""
  93. name = x
  94. elif y != -1: # File has extension
  95. ext = x[y:]
  96. name = x[:y]
  97. if ext not in ExtList:
  98. ExtList[ext]= []
  99. else: # File has no extension
  100. ext = ""
  101. name = x
  102. ExtList[ext].append(name)
  103.  
  104. return ExtList
  105.  
  106.  
  107. def OutputColumns(list, ISDIRLIST):
  108. col = 0
  109. if ISDIRLIST: # If listing directory, printed length
  110. BIAS = 2 # is longer by BIAS chars than actual name
  111. else:
  112. BIAS = 0
  113. for x in list:
  114. if col == MAXCOL:
  115. sys.stdout.write("\n")
  116. col = 0
  117. if len(x) > TWIDTH - BIAS:
  118. x = x[:TWIDTH - BIAS - 1 ] + TRUNC
  119. if ISDIRLIST:
  120. x = "[" + x + "]"
  121. if col == 0:
  122. sys.stdout.write(INDENT * PAD)
  123. sys.stdout.write(x + (COLWIDTH - len(x)) * PAD)
  124. col += 1
  125. sys.stdout.write("\n\n")
  126.  
  127.  
  128. def DisplayOutput(dir, DirList, FileList):
  129. if (dir[-1] != "/") and (dir[-1] != "\\"):
  130. DIREND = "/\n"
  131. else:
  132. DIREND = "\n"
  133. sys.stdout.write(dir.replace("\\", "/") + DIREND)
  134.  
  135. if SHOWDIR or SHOWFILE:
  136. sys.stdout.write("\n")
  137.  
  138. if len(DirList) > 0:
  139. DirList.sort()
  140. OutputColumns(DirList, True)
  141.  
  142. if len(FileList) > 0:
  143. if SORTBYEXT:
  144. ExtList = OrderByExtension(FileList)
  145. Ext = list(ExtList.keys())
  146. Ext.sort()
  147. for x in Ext:
  148. FileList = ExtList[x]
  149. if len(FileList) > 0:
  150. FileList.sort()
  151. sys.stdout.write("(" + x + ")\n")
  152. OutputColumns(FileList, False)
  153.  
  154. else:
  155. FileList.sort()
  156. OutputColumns(FileList, False)
  157.  
  158.  
  159.  
  160. def BuildFileList (args, dir, files):
  161. DirList = []
  162. FileList = []
  163. for x in files:
  164. df = x.startswith(DOTFILE) # Track whether name is a 'dotfile/dir'
  165. if (dir[-1] == '/') or (dir[-1] == '\\'): # This if/else sequence necessary because
  166. dirstring = dir + x # 'tdir /' did not properly report directories
  167. else: # on WinDoze32 systems which appears to
  168. dirstring = dir + "/" + x # handle '//' or '\/' in files names very well.
  169. if os.path.isdir(dirstring):
  170. if SHOWDIR and (not df or SHOWDOT):
  171. DirList.append(x)
  172. elif SHOWFILE and (not df or SHOWDOT):
  173. FileList.append(x)
  174. DisplayOutput(dir, DirList, FileList)
  175.  
  176.  
  177. def Usage():
  178. UsageInfo = (
  179. ("tdir " + VERSION.split()[2] +
  180. " - Copyright (c) 2001-2018 TundraWare Inc., All Rights Reserved. \n", ""),
  181. (" usage: tdir [-DRdefhtv] [-c #] [-s c] [-w #] [dir...] where,\n\n", ""),
  182. ("-D", "Do not display dot files\n"),
  183. ("-R", "Recurse down each named directory tree\n"),
  184. ("-c #", "Column width\n"),
  185. ("-d", "Do not display directories in output\n"),
  186. ("-e", "Do not sort files by extension\n"),
  187. ("-f", "Do not display files in output\n"),
  188. ("-h", "Display this help information\n"),
  189. ("-s c", "Separator character\n"),
  190. ("-t", "Display only the directory tree - same as -Rdf\n"),
  191. ("-v", "Display tdir version information\n"),
  192. ("-w #", "Width of output\n"),
  193. ("dir...", "List of directories to display. Defaults to ./\n")
  194. )
  195.  
  196. for x, y in UsageInfo:
  197. if len(x) < 10: # Only indent for the actual argument info
  198. sys.stdout.write(10 * PAD)
  199. sys.stdout.write(x)
  200. sys.stdout.write((8 - len(x)) * PAD)
  201. sys.stdout.write(y)
  202.  
  203. # Program entry and command line processing
  204.  
  205. try:
  206. opts, args = getopt.getopt(sys.argv[1:], '-DRc:edfhs:tvw:')
  207. except getopt.GetoptError:
  208. Usage()
  209. sys.exit(2)
  210.  
  211. for opt, val in opts:
  212. if opt == "-D":
  213. SHOWDOT = False
  214. if opt == "-R":
  215. RECURSE = True
  216. if opt == "-c":
  217. COLWIDTH = int(val)
  218. if opt == "-d":
  219. SHOWDIR = False
  220. if opt == "-e":
  221. SORTBYEXT = False
  222. if opt == "-f":
  223. SHOWFILE = False
  224. if opt == "-h":
  225. Usage()
  226. sys.exit(0)
  227. if opt == "-s":
  228. SEP = val[0]
  229. if opt == "-t":
  230. RECURSE = True
  231. SHOWDIR = False
  232. SHOWFILE = False
  233. if opt == "-v":
  234. sys.stdout.write(VERSION + "\n")
  235. sys.exit(0)
  236. if opt == "-w":
  237. OWIDTH = int(val)
  238.  
  239. if OWIDTH < COLWIDTH:
  240. sys.stdout.write("Huh? Column width exceeds output width!\n")
  241. sys.exit(2)
  242.  
  243. TWIDTH = COLWIDTH - 1 # Text width
  244. MAXCOL, INDENT = divmod(OWIDTH, COLWIDTH) # No of output cols & indent
  245.  
  246. if len(args) == 0: # Default to local directory if none given
  247. args = ["./"]
  248.  
  249. for root in args:
  250. if not os.path.isdir(root):
  251. sys.stdout.write(root + " is not a directory!\n")
  252. sys.exit(2)
  253. if RECURSE:
  254. for root, dir, files in os.walk(root):
  255. if root[-1] != os.sep:
  256. root += os.sep
  257. print(root)
  258. else:
  259. BuildFileList(None, root, os.listdir(root))