I cannot see anything at all that could be causing this bug :-/
Are you sure that the /usr is coloured with filled_fs_colour,
not with readonly_fs_colour? (or the colours are not identical... 
could you send me your pydfrc as well?) If so, could you please run
the attached pydf-debug and send me the output?

Thanks,

-- 
 -----------------------------------------------------------
| Radovan GarabĂ­k http://kassiopeia.juls.savba.sk/~garabik/ |
| __..--^^^--..__    garabik @ kassiopeia.juls.savba.sk     |
 -----------------------------------------------------------
Antivirus alert: file .signature infected by signature virus.
Hi! I'm a signature virus! Copy me into your signature file to help me spread!
#! /usr/bin/python

import sys, os, string, types, commands, struct
from optparse import OptionParser

from math import log

from statvfs import *

if not 'lexists' in dir(os.path):
    # for python < 2.4
    # will not give the same result for broken symbolic links, but who cares...
    os.path.lexists = os.path.exists

str_ljust = string.ljust
str_rjust = string.rjust
str_center = string.center


# again an ugly hack for python < 2.4
try:
    str_ljust('dummy', 1, '.')
except TypeError:
    str_ljust  = lambda x, y, z: string.ljust  (x, y).replace(' ', z)
    str_rjust  = lambda x, y, z: string.rjust  (x, y).replace(' ', z)
    str_center = lambda x, y, z: string.center (x, y).replace(' ', z)

class Bar:
    def __init__(self, percentage=0, width=2, header=False):
        self.percentage = percentage
        self.width = width
        self.header = header
        
    def __len__(self):
        return self.width

    def __str__(self):
        return format(self, 'l')
    
    def format(self, pos):
        if self.header:
            return ' '*self.width
        size = int(round(self.percentage*(self.width-2)))
        return '['+manglestring(size*barchar, self.width-2, pos, 
bar_fillchar)+']'


def get_terminal_width_termios():
    try:
        import fcntl, termios
    except ImportError:
        return None
    s = struct.pack("HHHH", 0, 0, 0, 0)
    try:
        lines, cols, xpixels, ypixels = \
            struct.unpack(
                "HHHH", 
                fcntl.ioctl(sys.stdout.fileno(),
                termios.TIOCGWINSZ, s)
                )
    except (IOError, AttributeError):
        return None
    return cols
    

def get_terminal_width_resize():
    c = commands.getoutput('resize').split('\n')
    c = [x for x in c if x.startswith('COLUMNS=')]
    if c:
        c = c[0]
        dummy, c = c.split('=', 1)
        if c[-1]==';':
            c = c[:-1]
    if c:
        return int(c)
    else:
        return None

def get_terminal_width_dumb():
    return 80

def get_terminal_width():
    handlers = [get_terminal_width_termios, get_terminal_width_resize, 
get_terminal_width_dumb]
    for handler in handlers:
        width = handler()
        if width is not None:
            return width
    
    return 80 # fallback, should not happen


def find_mountpoint(path):
    if not os.path.lexists(path):
        print >> sys.stderr, 'pydf: %s: No such file or directory' % repr(path)
        return None
    while not os.path.ismount(path):
        path = os.path.dirname(path)
    return path



#some default definitions
colours = {    
            'none'       :    "",
            'default'    :    "\033[0m",
            'bold'       :    "\033[1m",
            'underline'  :    "\033[4m",
            'blink'      :    "\033[5m",
            'reverse'    :    "\033[7m",
            'concealed'  :    "\033[8m",

            'black'      :    "\033[30m", 
            'red'        :    "\033[31m",
            'green'      :    "\033[32m",
            'yellow'     :    "\033[33m",
            'blue'       :    "\033[34m",
            'magenta'    :    "\033[35m",
            'cyan'       :    "\033[36m",
            'white'      :    "\033[37m",

            'on_black'   :    "\033[40m", 
            'on_red'     :    "\033[41m",
            'on_green'   :    "\033[42m",
            'on_yellow'  :    "\033[43m",
            'on_blue'    :    "\033[44m",
            'on_magenta' :    "\033[45m",
            'on_cyan'    :    "\033[46m",
            'on_white'   :    "\033[47m",

            'beep'       :    "\007"
            }


normal_colour = 'default'
header_colour = 'yellow'
local_fs_colour = 'default'
remote_fs_colour = 'green'
special_fs_colour = 'blue'
readonly_fs_colour = 'cyan'
filled_fs_colour = 'red'
full_fs_colour = 'on_red'

sizeformat = "-h"
column_separator = ' '
column_separator_colour = 'none'
row_separator = ''

stretch_screen = 0.3

FILL_THRESH = 95.0
FULL_THRESH = 99.0


format = [    
            ('fs', 10, "l"), ('size', 5, "r"), 
            ('used', 5, "r"), ('avail', 5, "r"), ('perc', 4, "r"),
            ('bar', 0.1, "l"), ('on', 11, "l")
         ]


barchar = '#'
bar_fillchar = '.'

mountfile = ['/etc/mtab', '/etc/mnttab', '/proc/mounts']


#end of default definitions

# read configuration file
for i in ["/etc/pydfrc", os.environ['HOME']+"/.pydfrc"]:
    if os.path.isfile(i):
        execfile(i)


header =    {        
            'fs'     :    "Filesystem",
            'size'   :    "Size",
            'used'   :    "Used",
            'avail'  :    "Avail",
            'on'     :    "Mounted on",
            'fstype' :    "Type",
            'perc'   :    "Use%",
            'bar'    :    Bar(header=True),
            }

def out(s):
    sys.stdout.write(s)

def hfnum(size, base):
    "human readable number"
    if size == 0:
        return "0"
    if size < 0:
        return "?"
    units = ["B", "k", "M", "G", "T", "P", "Z", "Y"]
    power = int(log(size)/log(base))
    if power<0:
        power = 0
    if power>=len(units):
        power = len(units)-1
    nsize = int(round(1.*size/(base**power)))
    if nsize<10 and power>=1:
        power -=1
        nsize = int(round(1.*size/(base**power)))
    r = str(nsize)+units[power]
    return r


def myformat(number, sizeformat, fs_blocksize):
    "format number as file size. fs_blocksize here is a filesysem blocksize"
    size = long(number)*fs_blocksize
    if blocksize: # that is, blocksize was explicitly set up
       sn = round(1.*size/blocksize)
       sn = int(sn)
       return str(sn)

    if sizeformat == "-k":
        sn = round(size/1024.)
        sn = int(sn)
        return str(sn)
    elif sizeformat == "-m":
        sn = round(size/(1024.*1024))
        sn = int(sn)
        return str(sn)
    elif sizeformat == "-g":
        sn = round(size/(1024.*1024*1024))
        sn = int(sn)
        return str(sn)
    elif sizeformat == "-h":
        return hfnum(size, 1024)
    elif sizeformat == "-H":
        return hfnum(size, 1000)
    elif sizeformat == "--blocks":
        return str(number)
    else: # this should not happen
        raise ValueError, "Impossible error, contact the author, 
sizeformat="+`sizeformat`

def myatof(s):
    "like float(), but be friendly to non-numerical values"
    try:
        return float(s)
    except ValueError:
        return 0


def manglestring(s, l, pos, fillchar=' '):
    "cut string to fit exactly into l chars"
    if pos == "r":
        ns = str_rjust(s, l, fillchar)
    elif pos == "l":
        ns = str_ljust(s, l, fillchar)
    elif pos == "c":
        ns = str_center(s, l, fillchar)
    else:
        raise ValueError, 'Error in manglestring'
    if len(ns) > l:
        ns = ns[:l/2] + "~" + ns[-l/2+1:]
    return ns

def makecolour(clist):
    "take list (or tuple or just one name) of colour names and return string of 
ANSI definitions"
    s = ""
    if type(clist) == types.StringType:
        lclist = [clist]
    else:
        lclist = clist
    for i in lclist:
        s = s + colours[i]
    return s

def version():
    return '5'

def get_all_mountpoints():
    "return all mountpoints in fs"

    # fallback when nothing else works
    dummy_result = {'/': ('/', '')}
    
    if isinstance(mountfile, str):
        f = open(mountfile,"r")
    else:
        for i in mountfile:
            if os.path.exists(i):
                f = open(i,"r")
                break
        else:
            # fallback, first try to parse mount output
            status, mout = commands.getstatusoutput('mount')
            if status !=0:
                return dummy_result
            mlines = mout.split('\n')
            r = {}
            for line in mlines:
                if not ' on ' in line:
                    continue
                device, on = line.split(' on ', 1)
                device = device.split()[0]
                onparts = on.split()
                on = onparts[0]
                # option format: (a,b,..)
                opts = onparts[-1][1:-1].split(",")
                r[on] = (device, '', opts)                
                
            if r:
                return r
            else:
                return dummy_result
                    
            
    mountlines = f.readlines()
    r = {}
    for l in mountlines:
        spl = l.split()
        if len(spl)<4:
            print "Error in", mountfile
            print `l`
            continue
        device, mp, typ, opts = spl[0:4]
        opts = opts.split(',')
        r[mp] = (device, typ, opts)
    return r
    

def get_row_mp(mp):
    print 'DEBUG: get_row_mp', mp
    if mp:
        if mp in mountpoints:
            device, fstype, opts = mountpoints[mp]
        else:
            # oops, the mountpoint is not in /etc/mtab or equivalent
            # return dummy values
            device, fstype, opts = '-', '-', '-'
        rdonly = 'ro' in opts or fstype in  ("iso9660", "udf")

        try:
            status = os.statvfs(mp)
        except OSError, IOError:
            status = 10*[0]

        fs_blocksize = status[F_BSIZE]
        if fs_blocksize == 0:
            fs_blocksize = status[F_FRSIZE]
        free = status[F_BFREE]
        used = long(status[F_BLOCKS]-free)
        size = status[F_BLOCKS]
        if size==0 and not allfss:
            return
        avail = status[F_BAVAIL]
        f_flag = status[F_FLAG]
        size_f = myformat(size, sizeformat, fs_blocksize)
        used_f = myformat(used, sizeformat, fs_blocksize)
        avail_f = myformat(avail, sizeformat, fs_blocksize)

        try:
            perc = round(100.*used/size, 1)
        except ZeroDivisionError:
            perc = 0
        perc_f = str(perc)


        info = {
            'fs'     :    device,
            'size'   :    size_f,
            'used'   :    used_f,
            'avail'  :    avail_f,
            'on'     :    mp,
            'fstype' :    fstype,
            'perc'   :    perc_f,
            'bar'    :    None,
                }

        current_colour = local_fs_colour
        if is_remote_fs(fstype):
            current_colour = remote_fs_colour
        elif size == 0 or is_special_fs(fstype):
            current_colour = special_fs_colour
    else: # header
        current_colour = header_colour

    row = []

    for j in format:
        print 'DEBUG j', `j`

        if j[0]=='bar':
            width = j[1]
            if 0<width<1: # i.e. percentage
                width = int(width*terminal_width)-1

        if mp:
            if j[0] in ['perc', 'avail', 'bar']:
                if rdonly:
                    current_colour = readonly_fs_colour
                    print 'DEBUG: readonly', mp
                elif perc > FULL_THRESH:
                    current_colour = full_fs_colour
                    print 'DEBUG: perc > FULL_THRESH', mp, perc, FULL_THRESH
                elif perc > FILL_THRESH:
                    current_colour = filled_fs_colour
                    print 'DEBUG: perc > FILL_THRESH', mp, perc, FILL_THRESH
            if j[0]=='bar':
                info['bar'] = Bar(perc/100., width)
                
            text = info[j[0]]
            
        else:
            text = header[j[0]]
            if j[0]=='bar':
                text.width = width


        column = [current_colour, text]
        row.append(column)
        print 'DEBUG CC', `current_colour`

    return row


def is_remote_fs(fs):
    "test if fs (as type) is a remote one"
    fs = fs.lower()
    return fs in [ "nfs", "smbfs", "cifs", "ncpfs", "afs", "coda", "ftpfs", 
"mfs", "sshfs" ]

def is_special_fs(fs):
    "test if fs (as type) is a special one"
    "in addition, a filesystem is special if it has number of blocks equal to 0"
    fs = fs.lower()
    return fs in [ "tmpfs", "devpts", "proc", "sysfs", "usbfs" ]



def get_table(mps):
    "table is a list of rows"
    "row is a list of columns"
    "column is a list of [colour code, content]"
    "content is a string, unless it is a Bar() instance"
    rows = [get_row_mp(None)]
    for mp in mps:
        row = get_row_mp(mp)
        if row is not None:
            rows.append(row)    
    return rows
    

def squeeze_table(table, desired_width):
    "squeeze table to fit into width characters"

    cols = len(table[0])


    # build a row of minimal (possible, from format) cell sizes
    minrow = []
    for j in format:
        width = j[1]
        if 0<width<1: # i.e. percentage
            width = int(width*terminal_width)-1
        minrow.append(width)

    # row of maximal cell sizes 
    maxrow = [0]*cols
    
    for row in table:
        for col in range(cols):
            colsize = len(row[col][1])
            maxrow[col] = max(maxrow[col], colsize)

    # maximal differences between (real cell size - minimal possible cell size)
    deltarow = [maxrow[i]-minrow[i] for i in range(cols)]
    
    deltas = zip(deltarow, range(cols))
    deltas.sort()
    deltas.reverse()

    # how many characters we need to cut off from table width
    to_reduce = sum(maxrow) + (cols-1)*len(column_separator) - desired_width

    to_stretch = 0
    # if there is free space
    if to_reduce < 0 and stretch_screen:
        # -to_reduce is now number of spare characters
        to_stretch = int(-to_reduce * stretch_screen)
        
    new_maxrow = maxrow[:] # new sizes
    for delta, i in deltas:
        if to_reduce < 0:
            # we have finished
            break
        if delta >= to_reduce:
            new_maxrow[i] -= to_reduce
            # and we finished
            to_reduce = 0
            break
        else:
            new_maxrow[i] -= delta # now it contains the minimal possible width
            to_reduce -= delta

    if to_reduce>0:
        # we were not able to reduce the size enough
        # since it will wrap anywway, we might as well display 
        # complete long lines
        new_maxrow = maxrow
    for row in table:
        for col in range(cols):
            cell_content = row[col][1]
            if isinstance(cell_content, Bar):
                cell_content.width += to_stretch
                formatted_cell_content = cell_content.format(format[col][2])
            else:
                formatted_cell_content = manglestring(cell_content, 
new_maxrow[col], format[col][2])
            row[col][1] = formatted_cell_content
            
    

def display_table(table, terminal_width):
    "display our internal output table"

    squeeze_table(table, terminal_width-1)
    
    colsepcol = makecolour(column_separator_colour)
    for row in table:
        firstcol = True
        for colourcode, text in row:
            if firstcol:
                firstcol = False
            else:
                out(colsepcol)
                out(column_separator)
            out(makecolour(colourcode))
            out(text)
        out(row_separator)
        out( colours['default'] )
        out('\n')



# the fun begins here

parser = OptionParser(usage="usage: %prog [options] arg", add_help_option=False)

parser.version = '%prog version ' + version()

parser.add_option("", "--help", action="help", help="show this help message")
parser.add_option("-v", "--version", action="version", help="show version")
  
parser.add_option("-a", "--all",
      action="store_true", dest="show_all", default=False,
      help="include filesystems having 0 blocks")
parser.add_option("-h", "--human-readable",
      action="store_const", const='-h', dest="sizeformat",
      help="print sizes in human readable format (e.g., 1K 234M 2G)")
parser.add_option("-H", "--si",
      action="store_const", const='-H', dest="sizeformat",
      help="likewise, but use powers of 1000 not 1024")
parser.add_option("-b", "--blocks-size",
      action="store", dest="blocksize", default=0, type="int",
      help="use SIZE-byte blocks")
parser.add_option("-l", "--local",
      action="store_true", dest="local_only", default=False,
      help="limit listing to local filesystems")
parser.add_option("-k", "--kilobytes",
      action="store_const", const='-k', dest="sizeformat",
      help="like --block-size=1024")
parser.add_option("-m", "--megabytes",
      action="store_const", const='-m', dest="sizeformat",
      help="like --block-size=1048576")
parser.add_option("-g", "--gigabytes",
      action="store_const", const='-g', dest="sizeformat",
      help="like --block-size=1073741824")
parser.add_option("", "--blocks",
      action="store_const", const='--blocks', dest="sizeformat",
      help="use filesystem native block size")
parser.add_option("", "--bw",
      action="store_true", dest="b_w", default=False,
      help="do not use colours")
parser.add_option("", "--mounts",
      action="store", dest="mounts_file", type="string",
      help="""File to get mount information from.
On normal linux system, only /etc/mtab or proc/mounts make sense.
Some other unices use /etc/mnttab.
Use /proc/mounts when /etc/mtab is corrupted or inaccesable 
(the output looks a bit weird in this case).

""")

(options, args) = parser.parse_args()

blocksize = options.blocksize
allfss = options.show_all
localonly = options.local_only

if options.sizeformat:
    sizeformat = options.sizeformat

if options.b_w:
    normal_colour = header_colour = local_fs_colour = remote_fs_colour = 
special_fs_colour = filled_fs_colour = full_fs_colour = 'none'
if options.mounts_file:
    mountfile = options.mounts_file

terminal_width = get_terminal_width()

mountpoints = get_all_mountpoints()

if args:
    mp_to_display = [find_mountpoint(os.path.realpath(x)) for x in args]
    mp_to_display = [x for x in mp_to_display if x is not None]
else:
    mp_to_display = mountpoints.keys()
    if localonly:
        mp_to_display = [x for x in mp_to_display if not 
is_remote_fs(mountpoints[x][1])]
    mp_to_display.sort()

table = get_table(mp_to_display)
display_table(table, terminal_width)


Reply via email to