root/livinglogic.python.xist/src/ll/scripts/uls.py @ 4422:fe09ca906d4e

Revision 4422:fe09ca906d4e, 10.6 KB (checked in by Walter Doerwald <walter@…>, 8 years ago)

Bump copyright year. Change encoding of remaining files to UTF-8. Remove trailing whitespace.

RevLine 
[3736]1#!/usr/local/bin/python
2# -*- coding: utf-8 -*-
3
4
[4422]5## Copyright 2009-2011 by LivingLogic AG, Bayreuth/Germany.
6## Copyright 2009-2011 by Walter Dörwald
[3736]7##
8## All Rights Reserved
9##
10## See ll/__init__.py for the license
11
12
[4400]13"""
[4421]14``uls`` is a script that lists the content of directories. It is an URL-enabled
15version of the ``ls`` system command. Via :mod:`ll.url` and :mod:`ll.orasql`
16``uls`` supports ``ssh`` and ``oracle`` URLs too.
[4400]17
18
19Options
20-------
21
22``uls`` supports the following options:
23
24    ``urls``
25        Zero or more URLs. If no URL is given the current directory is listed.
26
27    ``-c``, ``--color`` : ``yes``, ``no`` or ``auto``
28        Should the ouput be colored. If ``auto`` is specified (the default) then
29        the output is colored if stdout is a terminal.
30
31    ``-1``, ``--one`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
32        Force output to be one URL per line. The default is to output URLs in
[4405]33        multiple columns (as many as fit on the screen).
[4400]34
35    ``-l``, ``--long`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
36        Ouput in long format: One URL per line containing the following information:
37        file mode, owner name, group name, number of bytes in the file,
[4405]38        number of links, URL.
[4400]39
40    ``-s``, ``--human-readable-sizes`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
[4405]41        Output the file size in human readable form (e.g. ``42M`` for 42 megabytes).
[4400]42
43    ``-r``, ``--recursive`` : ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
[4421]44        List directories recursively.
[4400]45
46    ``-w``, ``--spacing`` : integer
[4408]47        The number of spaces (or padding characters) between columns (only
[4421]48        relevant for multicolumn ouput, i.e. when neither ``--long`` nor
49        ``--one`` is specified).
[4400]50
[4407]51    ``-P``, ``--padding`` : characters
52        The characters using for padding output in multicolumn or long format.
[4404]53
[4407]54    ``-S``, ``--separator`` : characters
[4405]55        The characters used for separating columns in long format.
[4404]56
[4400]57    ``-i``, ``--include`` : regular expression
[4421]58        Only URLs matching this regular expression will be output.
[4400]59
60    ``-e``, ``--expression`` : regular expression
[4421]61        URLs matching this regular expression will be not be output.
[4402]62
63    ``-a``, ``--all`` :  ``false``, ``no``, ``0``, ``true``, ``yes`` or ``1``
[4413]64        Output dot files (i.e. files and directories whose name starts with a
65        ``.``). Not that the content of directories whose name start with a dot
66        will still be listed.
[4410]67
68
69Examples
70--------
71
72List the current directory::
73
74    $ uls
75    CREDITS.rst   installer.bmp   NEWS.rst           scripts/    test/
76    demos/        Makefile        OLDMIGRATION.rst   setup.cfg
77    docs/         MANIFEST.in     OLDNEWS.rst        setup.py
78    INSTALL.rst   MIGRATION.rst   README.rst         src/
79
80List the current directory in long format with human readable file sizes::
81
82    $ uls -s -l
83    rw-r--r--  walter    staff      1114    1  2008-01-06 22:27:15  CREDITS.rst
84    rwxr-xr-x  walter    staff       170    5  2007-12-03 23:35:33  demos/
85    rwxr-xr-x  walter    staff       340   10  2010-12-08 16:48:53  docs/
86    rw-r--r--  walter    staff        2K    1  2010-12-08 16:48:53  INSTALL.rst
87    rw-r--r--  walter    staff       35K    1  2007-12-03 23:35:33  installer.bmp
88    rw-r--r--  walter    staff      1763    1  2011-01-21 17:22:32  Makefile
89    rw-r--r--  walter    staff       346    1  2011-02-25 11:13:18  MANIFEST.in
90    rw-r--r--  walter    staff       34K    1  2011-03-04 13:48:35  MIGRATION.rst
91    rw-r--r--  walter    staff      107K    1  2011-03-04 18:18:42  NEWS.rst
92    rw-r--r--  walter    staff        8K    1  2010-12-08 16:48:53  OLDMIGRATION.rst
93    rw-r--r--  walter    staff       75K    1  2010-12-08 16:48:53  OLDNEWS.rst
94    rw-r--r--  walter    staff        3K    1  2010-12-08 16:48:53  README.rst
95    rwxr-xr-x  walter    staff       578   17  2010-12-08 16:48:53  scripts/
96    rw-r--r--  walter    staff        39    1  2010-12-08 16:48:53  setup.cfg
97    rw-r--r--  walter    staff        7K    1  2011-03-03 13:33:21  setup.py
98    rwxr-xr-x  walter    staff       136    4  2007-12-04 01:43:13  src/
99    rwxr-xr-x  walter    staff        2K   68  2011-03-03 13:27:46  test/
100
101Recursively list a remote directory::
102
103    uls ssh://user@www.example.org/~/dir/ -r
104    ...
105
106Recursively list the schema objects in an Oracle database::
107
108    uls oracle://user:pwd@oracle.example.org/ -r
[4412]109    ...
[4400]110"""
111
112
[4383]113import sys, re, argparse, contextlib, datetime, pwd, grp, stat, curses
[3736]114
[4283]115from ll import misc, url
[3736]116
117try:
118    import astyle
119except ImportError:
120    from ll import astyle
121
[3745]122try:
123    from ll import orasql # Activate oracle URLs
124except ImportError:
125    pass
126
127
[4400]128__docformat__ = "reStructuredText"
129
130
[3748]131style_file = astyle.Style.fromstr("white:black")
132style_dir = astyle.Style.fromstr("yellow:black")
[3736]133style_pad = astyle.Style.fromstr("black:black:bold")
[3749]134style_sizeunit = astyle.Style.fromstr("cyan:black")
[3736]135
[3745]136
[4421]137def encodedstring(s):
138    return s.decode(sys.stdin.encoding)
139
140
[3736]141def main(args=None):
142    uids = {}
143    gids = {}
144    modedata = (
145        (stat.S_IRUSR, "-r"),
146        (stat.S_IWUSR, "-w"),
147        (stat.S_IXUSR, "-x"),
148        (stat.S_IRGRP, "-r"),
149        (stat.S_IWGRP, "-w"),
150        (stat.S_IXGRP, "-x"),
151        (stat.S_IROTH, "-r"),
152        (stat.S_IWOTH, "-w"),
153        (stat.S_IXOTH, "-x"),
154    )
155    curses.setupterm()
156    width = curses.tigetnum('cols')
[4383]157
[4404]158    def rpad(s, l):
159        meas = str(s)
160        if not isinstance(s, (basestring, astyle.Text)):
161            s = str(s)
162        if len(meas) < l:
[4407]163            size = l-len(meas)
164            psize = len(args.padding)
165            repeats = (size+psize-1)//psize
166            padding = (args.padding*repeats)[-size:]
167            return astyle.style_default(s, style_pad(padding))
[4404]168        return s
169
170    def lpad(s, l):
171        meas = str(s)
172        if not isinstance(s, (basestring, astyle.Text)):
173            s = str(s)
174        if len(meas) < l:
[4407]175            size = l-len(meas)
176            psize = len(args.padding)
177            repeats = (size+psize-1)//psize
178            padding = (args.padding*repeats)[:size]
179            return astyle.style_default(style_pad(padding), s)
[4404]180        return s
181
[4403]182    def match(url):
183        strurl = str(url)
[4383]184        if args.include is not None and args.include.search(strurl) is None:
185            return False
186        if args.exclude is not None and args.exclude.search(strurl) is not None:
187            return False
[4409]188        if not args.all:
189            if url.file:
190                name = url.file
191            elif len(url.path) >=2:
192                name = url.path[-2]
193            else:
194                name = ""
195            if name.startswith("."):
196                return False
[4383]197        return True
198
[4400]199    def findcolcount(urls):
200        def width4cols(numcols):
201            cols = [0]*numcols
202            rows = (len(urls)+numcols-1)//numcols
203            for (i, (u, su)) in enumerate(urls):
204                cols[i//rows] = max(cols[i//rows], len(su))
205            return (sum(cols) + (numcols-1)*args.spacing, rows, cols)
206
207        numcols = len(urls)
208        if numcols:
209            while True:
210                (s, rows, cols) = width4cols(numcols)
211                if s <= width or numcols == 1:
212                    return (rows, cols)
213                numcols -= 1
214        else:
215            return (0, 0)
216
217    def printone(url):
218        if args.long:
[4407]219            sep = style_pad(args.separator)
[3736]220            stat = url.stat()
[4183]221            owner = url.owner()
222            group = url.group()
[3736]223            mtime = datetime.datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
224            mode = "".join([text[bool(stat.st_mode&bit)] for (bit, text) in modedata])
[3741]225            size = stat.st_size
[4400]226            if args.human:
[3741]227                s = "BKMGTP"
228                for c in s:
229                    if size < 2048:
230                        if c == "B":
231                            size = str(int(size))
232                        else:
233                            size = astyle.style_default(str(int(size)), style_sizeunit(c))
234                        break
235                    size /= 1024.
[4400]236            stdout.write(mode, sep, rpad(owner, 8), sep, rpad(group, 8), sep, lpad(size, 5 if args.human else 12), sep, lpad(stat.st_nlink, 3), sep, mtime, sep)
[3736]237        if url.isdir():
[3748]238            stdout.writeln(style_dir(str(url)))
[3736]239        else:
[3748]240            stdout.writeln(style_file(str(url)))
[3736]241
[4400]242    def printblock(url, urls):
[3747]243        if url is not None:
[3748]244            stdout.writeln(style_dir(str(url)), ":")
[4400]245        (rows, cols) = findcolcount(urls)
[3743]246        for i in xrange(rows):
247            for (j, w) in enumerate(cols):
248                index = i+j*rows
249                try:
[3744]250                    (u, su) = urls[index]
[3743]251                except IndexError:
252                    pass
253                else:
[3744]254                    if u.isdir():
[3748]255                        su = style_dir(su)
[3743]256                    else:
[3748]257                        su = style_file(su)
[3743]258                    if index + rows < len(urls):
[4400]259                        su = rpad(su, w+args.spacing)
[3744]260                    stdout.write(su)
[3743]261            stdout.writeln()
262
[4400]263    def printall(base, url):
[3736]264        if url.isdir():
[3893]265            if url.path.segments[-1]:
266                url.path.segments.append("")
[4400]267            if not args.long and not args.one:
268                if args.recursive:
[4403]269                    urls = [(url/child, str(child)) for child in url.files() if match(url/child)]
[3746]270                    if urls:
[4400]271                        printblock(url, urls)
[3746]272                    for child in url.dirs():
[4400]273                        printall(base, url/child)
[3746]274                else:
[4403]275                    urls = [(url/child, str(child)) for child in url.listdir() if match(url/child)]
[4400]276                    printblock(None, urls)
[3743]277            else:
[3736]278                for child in url.listdir():
[4361]279                    child = url/child
[4403]280                    if match(child):
[4400]281                        if not args.recursive or child.isdir(): # For files the print call is done by the recursive call to ``printall``
282                            printone(child)
283                    if args.recursive:
284                        printall(base, child)
[3736]285        else:
[4403]286            if match(url):
[4400]287                printone(url)
[3736]288
[4113]289    p = argparse.ArgumentParser(description="List the content of one or more URLs")
[4400]290    p.add_argument("urls", metavar="url", help="URLs to be listed (default: current dir)", nargs="*", default=[url.Dir("./", scheme=None)], type=url.URL)
[4283]291    p.add_argument("-c", "--color", dest="color", help="Color output (default: %(default)s)", default="auto", choices=("yes", "no", "auto"))
292    p.add_argument("-1", "--one", dest="one", help="One entry per line? (default: %(default)s)", action=misc.FlagAction, default=False)
293    p.add_argument("-l", "--long", dest="long", help="Long format? (default: %(default)s)", action=misc.FlagAction, default=False)
294    p.add_argument("-s", "--human-readable-sizes", dest="human", help="Human readable file sizes? (default: %(default)s)", action=misc.FlagAction, default=False)
295    p.add_argument("-r", "--recursive", dest="recursive", help="Recursive listing? (default: %(default)s)", action=misc.FlagAction, default=False)
[4408]296    p.add_argument("-w", "--spacing", dest="spacing", metavar="INTEGER", help="Space between columns (default: %(default)s)", type=int, default=3)
[4421]297    p.add_argument("-P", "--padding", dest="padding", metavar="CHARS", help="Characters used for column padding (default: %(default)s)", default=u" ", type=encodedstring)
298    p.add_argument("-S", "--separator", dest="separator", metavar="CHARS", help="Characters used for separating columns in long format (default: %(default)s)", default=u"  ", type=encodedstring)
[4383]299    p.add_argument("-i", "--include", dest="include", metavar="PATTERN", help="Include only URLs matching PATTERN (default: %(default)s)", type=re.compile)
300    p.add_argument("-e", "--exclude", dest="exclude", metavar="PATTERN", help="Exclude URLs matching PATTERN (default: %(default)s)", type=re.compile)
[4402]301    p.add_argument("-a", "--all", dest="all", help="Include dot files? (default: %(default)s)", action=misc.FlagAction, default=False)
[4042]302
[4113]303    args = p.parse_args(args)
[3736]304
[4113]305    if args.color == "yes":
[3736]306        color = True
[4113]307    elif args.color == "no":
[3736]308        color = False
309    else:
310        color = None
311    stdout = astyle.Stream(sys.stdout, color)
312    stderr = astyle.Stream(sys.stderr, color)
313
314    with url.Context():
[4113]315        for u in args.urls:
[4400]316            printall(u, u)
[3736]317
318
319if __name__ == "__main__":
320    sys.exit(main())
Note: See TracBrowser for help on using the browser.