#!/usr/bin/env python
"""
tdir - Display Formatted Directory Listings
Copyright (c) 2001-2018 TundraWare Inc., All Rights Reserved.
"""
# python Library Imports
import getopt
import os
import sys
# Version info
VERSION = "$Id: tdir,v 1.73 2018/06/17 00:00:00 tundra Exp $"
# Supporting Functions
# Found at: http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python
def GetTerminalSize():
env = os.environ
def ioctl_GWINSZ(fd):
try:
import fcntl, termios, struct, os
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
except:
return
return cr
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
if not cr:
try:
fd = os.open(os.ctermid(), os.O_RDONLY)
cr = ioctl_GWINSZ(fd)
os.close(fd)
except:
pass
if not cr:
cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
### Use get(key[, default]) instead of a try/catch
#try:
# cr = (env['LINES'], env['COLUMNS'])
#except:
# cr = (25, 80)
return int(cr[1]), int(cr[0])
# End of 'GetTerminalSize()'
# Output formatting constants.
OWIDTH = 80 # Default output width fixed for non-*NIX systems
if os.name == 'posix': # On *NIX get the current window parameter instead
OWIDTH = GetTerminalSize()[0] - 1
COLWIDTH = 19 # Width of each column
TWIDTH = COLWIDTH - 1 # Text width
MAXCOL, INDENT = divmod(OWIDTH, COLWIDTH) # No of output cols & indent
PAD = " " # Padding character
SEP = "." # Filename "extension" separator
TRUNC = "^" # Character indicating name truncation
DOTFILE = '.' # Dotfiles start with this
# Defaults
RECURSE = False # No recursion
SHOWDIR = True # Show directories in listing
SHOWDOT = True # Show dotfiles in listing
SHOWFILE = True # Show files in listing
SORTBYEXT = True # Sort file names by extension
def OrderByExtension(list):
ExtList = {}
ExtList[""] = [] # List of files with no extension
for x in list:
y = x.rfind(SEP)
if not y: # Handle special case of 'dot file' type files
ext = ""
name = x
elif y != -1: # File has extension
ext = x[y:]
name = x[:y]
if ext not in ExtList:
ExtList[ext]= []
else: # File has no extension
ext = ""
name = x
ExtList[ext].append(name)
return ExtList
def OutputColumns(list, ISDIRLIST):
col = 0
if ISDIRLIST: # If listing directory, printed length
BIAS = 2 # is longer by BIAS chars than actual name
else:
BIAS = 0
for x in list:
if col == MAXCOL:
sys.stdout.write("\n")
col = 0
if len(x) > TWIDTH - BIAS:
x = x[:TWIDTH - BIAS - 1 ] + TRUNC
if ISDIRLIST:
x = "[" + x + "]"
if col == 0:
sys.stdout.write(INDENT * PAD)
sys.stdout.write(x + (COLWIDTH - len(x)) * PAD)
col += 1
sys.stdout.write("\n\n")
def DisplayOutput(dir, DirList, FileList):
if (dir[-1] != "/") and (dir[-1] != "\\"):
DIREND = "/\n"
else:
DIREND = "\n"
sys.stdout.write(dir.replace("\\", "/") + DIREND)
if SHOWDIR or SHOWFILE:
sys.stdout.write("\n")
if len(DirList) > 0:
DirList.sort()
OutputColumns(DirList, True)
if len(FileList) > 0:
if SORTBYEXT:
ExtList = OrderByExtension(FileList)
Ext = list(ExtList.keys())
Ext.sort()
for x in Ext:
FileList = ExtList[x]
if len(FileList) > 0:
FileList.sort()
sys.stdout.write("(" + x + ")\n")
OutputColumns(FileList, False)
else:
FileList.sort()
OutputColumns(FileList, False)
def BuildFileList (args, dir, files):
DirList = []
FileList = []
for x in files:
df = x.startswith(DOTFILE) # Track whether name is a 'dotfile/dir'
if (dir[-1] == '/') or (dir[-1] == '\\'): # This if/else sequence necessary because
dirstring = dir + x # 'tdir /' did not properly report directories
else: # on WinDoze32 systems which appears to
dirstring = dir + "/" + x # handle '//' or '\/' in files names very well.
if os.path.isdir(dirstring):
if SHOWDIR and (not df or SHOWDOT):
DirList.append(x)
elif SHOWFILE and (not df or SHOWDOT):
FileList.append(x)
DisplayOutput(dir, DirList, FileList)
def Usage():
UsageInfo = (
("tdir " + VERSION.split()[2] +
" - Copyright (c) 2001-2018 TundraWare Inc., All Rights Reserved. \n", ""),
(" usage: tdir [-DRdefhtv] [-c #] [-s c] [-w #] [dir...] where,\n\n", ""),
("-D", "Do not display dot files\n"),
("-R", "Recurse down each named directory tree\n"),
("-c #", "Column width\n"),
("-d", "Do not display directories in output\n"),
("-e", "Do not sort files by extension\n"),
("-f", "Do not display files in output\n"),
("-h", "Display this help information\n"),
("-s c", "Separator character\n"),
("-t", "Display only the directory tree - same as -Rdf\n"),
("-v", "Display tdir version information\n"),
("-w #", "Width of output\n"),
("dir...", "List of directories to display. Defaults to ./\n")
)
for x, y in UsageInfo:
if len(x) < 10: # Only indent for the actual argument info
sys.stdout.write(10 * PAD)
sys.stdout.write(x)
sys.stdout.write((8 - len(x)) * PAD)
sys.stdout.write(y)
# Program entry and command line processing
try:
opts, args = getopt.getopt(sys.argv[1:], '-DRc:edfhs:tvw:')
except getopt.GetoptError:
Usage()
sys.exit(2)
for opt, val in opts:
if opt == "-D":
SHOWDOT = False
if opt == "-R":
RECURSE = True
if opt == "-c":
COLWIDTH = int(val)
if opt == "-d":
SHOWDIR = False
if opt == "-e":
SORTBYEXT = False
if opt == "-f":
SHOWFILE = False
if opt == "-h":
Usage()
sys.exit(0)
if opt == "-s":
SEP = val[0]
if opt == "-t":
RECURSE = True
SHOWDIR = False
SHOWFILE = False
if opt == "-v":
sys.stdout.write(VERSION + "\n")
sys.exit(0)
if opt == "-w":
OWIDTH = int(val)
if OWIDTH < COLWIDTH:
sys.stdout.write("Huh? Column width exceeds output width!\n")
sys.exit(2)
TWIDTH = COLWIDTH - 1 # Text width
MAXCOL, INDENT = divmod(OWIDTH, COLWIDTH) # No of output cols & indent
if len(args) == 0: # Default to local directory if none given
args = ["./"]
for root in args:
if not os.path.isdir(root):
sys.stdout.write(root + " is not a directory!\n")
sys.exit(2)
if RECURSE:
for root, dir, files in os.walk(root):
if root[-1] != os.sep:
root += os.sep
print(root)
else:
BuildFileList(None, root, os.listdir(root))