Commit Graph

55 Commits

Author SHA1 Message Date
Sebastian Spaeth
6f9b171ffd Don't pass a list to ui.copyingmessage()
We only copy to a single folder anyway, so clean up the code to only
pass in a single folder.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-08-17 20:41:32 +02:00
Sebastian Spaeth
634b6cd49e Don't make Blinkenlight statuschar configurable
Allowing to specify the char to use in the BLinkenlights is a bit over
the top and bloats our default offlineimap.conf. The dot is just fine,
so let us settle for it and cut the example config file by an unneeded
section.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-08-15 12:10:20 +02:00
Sebastian Spaeth
51629b8ffe Update example in code documentation of ui.error()
sys.exc_traceback is long deprecated and is seems removed in python2.7,
so document the legitimate use of sys.exc_info()[2] instead.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-08-14 17:18:51 +02:00
Sebastian Spaeth
f937a86571 Implement ui.error() and output them at end of offlinimap sync
Output all raised Exceptions error strings to the error log. If we are
in debug mode, we also output the traceback of the exception.

Save all exceptions that occur during the run time to a Queue and output
them all again when the offlineimap sync is finished.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-08-11 19:14:28 +02:00
Sebastian Spaeth
8e2589982c Don't use CStringIO to format a traceback
The traceback module has format_exc() for this purpose so let's use that.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-06-30 21:28:06 +02:00
Nicolas Sebrecht
d5493fe894 threadutil: explicitly import get_ident from thread
The python module thread is the low-level module we should avoid to use in favor
of threading. We still need it to support old python because Thread.ident
doesn't exist before python 2.6:

	http://docs.python.org/library/threading.html#threading.Thread.ident

Make it clear we should avoid it.

Reviewed-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-12 18:28:49 +02:00
Nicolas Sebrecht
8a34edc8ca cleanup import satements
- conform to PEP8
- explicitly define symbols instead of 'import *'
- remove unused import

Reviewed-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-12 18:26:29 +02:00
Sebastian Spaeth
89619838b0 Remove weird SigListener class
The SigListener class was used to queue folders that we need to sync and
to receive "resync" and "abort" signals. It was undocumented and weird
and we had to pass "siglisteners" through the whole program.

Simply do away with it, and make 2 functions in the Account() class:
set_abort_event and get_abort_event which can be used to set and check
for such signals. This way we do not need to pass siglisteners all over
the place. Tested Blinkenlights and TTYUI uis to make sure that SIGUSR1
and SIGUSR2 actually still work.

Document those signals in MANUAL.rst. They were completly undocumented.

This simplifies the code and interdependencies by passing less stuff
around. Removes an undocumented and weirdly named class.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-08 14:25:16 +02:00
Sebastian Spaeth
bd3766eca5 Make getnicename work again for classes that derive from object()
Python's new style classes derive from object and str(class().__class__)
will return a slightly different format. class().__class.__name__ will
still work for both old and new style classes, so use that instead.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-07 13:29:11 +02:00
Nicolas Sebrecht
c6259fbb86 Merge branch 'master' into next
Conflicts:
	Changelog.draft.rst
2011-05-05 21:16:02 +02:00
Sebastian Spaeth
deab62fbd8 Fix the broken thread debugging
Using threading._VERBOSE=1 is broken since python 2.6 till at least
python 3.2, (http://bugs.python.org/issue4188) so we can't use it for
our thread debugging.

Remove the usage of threading._VERBOSE, and implement a "light thread
debug log" that for now outputs information when a new thread is being
registered and when it is being unregistered. I am sure we will be able
to add more thread debugging information over the time.

Besides '-d thread' this will re-enable the usage of -d 'all' for the
most verbose debugging of all categories.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-05 21:07:24 +02:00
Sebastian Spaeth
3d4dc11a8b Introduce an *empty* debug type
This debug type will always be enabled whenever any debugging is enables
and it outputs debug messages that cannot be categorized among any of
imap, maildir (e.g. things that concern the sync logic).

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-02 20:41:36 +02:00
Sebastian Spaeth
6add201436 Improve the developer API documentation
Improve the code documentation (still much more to do) and also add some
more meat to the structure of the developer documentation.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-05-02 20:16:45 +02:00
Nicolas Sebrecht
44eefae043 cleanup import statements and conform to PEP-8
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-03-14 21:35:33 +01:00
Nicolas Sebrecht
90949a4bfc UIBase: fix regression while deletingflags
In commit 7a2a0225 [Don't pass list to ui.adding/deletingflags] we changed the
list logic for a per folder logic but forgot to remove one instance of
"destlist" which isn't valid anymore.

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-03-11 20:20:12 +01:00
Nicolas Sebrecht
5048d16913 Merge branch 'ss/declutter-tty-output' into next
Conflicts:
	Changelog.draft.rst
2011-03-10 19:26:26 +01:00
Sebastian Spaeth
efcce01d64 Declutter TTY output
Previously we would output:

Folder sync sspaeth.de[INBOX.INBOX201004]:
 Syncing INBOX.INBOX201004: IMAP -> Maildir
Folder sync sspaeth.de[INBOX.INBOX201006]:
 Syncing INBOX.INBOX201006: IMAP -> Maildir
Folder sync sspaeth.de[INBOX.INBOX201009]:
 Syncing INBOX.INBOX201009: IMAP -> Maildir

which is very repetitive and cluttered. By naming the folder sync
threads just according to the account and not the folder, the output
looks much nicer:

Folder sync [sspaeth.de]:
 Syncing INBOX.INBOX201004: IMAP -> Maildir
 Syncing INBOX.INBOX201006: IMAP -> Maildir
 Syncing INBOX.INBOX201009: IMAP -> Maildir

If syncing multiple accounts in parallel, we will still get headers
indicating the account:

Folder sync [sspaeth.de]:
 Syncing INBOX: IMAP -> Maildir
 Syncing INBOX.INBOX201006: IMAP -> Maildir
Folder sync [gmail]:
 Syncing INBOX: IMAP -> Maildir

This is a small fix that makes the output much nicer in my opinion.

Also don't output the thread name if we are in the MainThread, e.g. when
we output the initial offlineimap banner.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-03-10 19:04:57 +01:00
Sebastian Spaeth
7a2a02254e Don't pass list to ui.adding/deletingflags
We only have one "dstfolder" at a time when deleting/adding flags, so no
need to pass in a list of those to the ui functions that output the log
info.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-03-07 21:11:10 +01:00
Sebastian Spaeth
387fbf3aaa ui: clean up importment statements
They were not PEP-8 formatted, and some imports were simply
unnecessary. Removed those.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-03-07 21:11:10 +01:00
Sebastian Spaeth
4e28c7c93f Allow to use nicer UI names
The previous ui names were pretty unwieldy. Is it TTYUI.TTY or
TTY.TTYUI? Do I have to use capitals and where?

Simplify the names by making them case insensitive and by dropping
everything before the dot.

So "Curses.Blinkenlights" can now be invoked as "blinkenlights" or
"BLINKENLIGHTS". The old names will still work just fine so the
transition should be smooth. We issue a warning that the long names are
deprecated.

Document in offlineimap.conf that we don't accept lists of fallback UIs,
but only one UI option (this was already the case before this commit but
still wrongly documented).

The list of accepted ui names is:
  ttyui (default), basic, quiet, machineui, blinkenlights

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-03-06 15:33:29 +01:00
Nicolas Sebrecht
72d05bac09 restore compatibilty with python 2.5 for ui TTY
threading.currentThread() used an accessor to get its name.

Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-02-18 20:00:19 +01:00
Sebastian Spaeth
83a85bb3fb Remove MultiLock implementation
Currently the Curses code is broken. Importing offlineimap.ui.Curses
will not succeed due to cyclic imports (threadutils imports ui, but ui
wants threadutils.MultiLock). So Curses cannot be chosen.

Incidentally, the only part in the code that uses "MultiLock" is the
Curses UI, to prevent concurrent access from several threads to the
ui-internal thread list and to IO resources such as the
screen. Fortunately for these purposes we don't need a MultiLock, so we
can do away with that implementation completely. A simple RLock that
allows us to have a thread "own" a lock and makes other threads wanting
access to the resource wait until the owning thread is finished.

The MultiLock implementation looked a bit weird, so simplifying code
here is a good thing, it might well be that we fix some "hangs" that
have been reported (and that would only ever occur with the Curses UI).

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-01-25 19:19:14 +01:00
Thomas Schwinge
fa60f3f9b7 offlineimap.version is no more.
This fixes some leftover of 0b5b38d298.

Signed-off-by: Thomas Schwinge <thomas@schwinge.name>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-01-25 18:36:18 +01:00
Nicolas Sebrecht
43096ad378 Merge branch 'ss/ui-remove-detector' into next 2011-01-07 19:38:12 +01:00
Sebastian Spaeth
c3540de763 Remove ui.detector class
The ui.detector class was not really needed and leads to the illusion
that we provide GUI plugins. For the sake of code maintainability we
don't :-).

Rather than having GUI names equivalent to the classes they are in
(which leads to weird names like TTY.TTYUI), this patch allows to give
each GUI an arbitrary string name. GUI names remain still unchanged in
this patch, the default UI when none was configured is TTY.TTYUI.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2011-01-07 19:37:48 +01:00
Sebastian Spaeth
0b5b38d298 Define version constants etc in __init__.py
Move central constant definitions into __init__.py.  This does away
with version.py which contained nothing else and __init__.py is where
things like __VERSION__ are usually defined.

This commit also changes code to use offlineimap.__version__ rather
than offlineimap.version.__version__ as was before. Cleaned up some
duplicate or unneeded imports while touching those, formatting import
statements per PEP8 (one import per row).

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2010-12-22 19:45:01 +01:00
Sebastian Spaeth
35dd236155 Improve TTY ui to not always prepend 'sync account foo'
This is very excessive and a bit annoying. Output that information
only if the next line concerns a different account/thread than the
previous one. This quiets down the UI quite a bit without losing
information.

While modifying this line, use the newer Thread.name and not the as
per python doc's old syntax getName()

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2010-12-09 19:52:48 +01:00
Sebastian Spaeth
f68b626cb4 Don't display sleeping every second on the screen
Only every 10 seconds. Also fix up the documentation of that function
while at it. The Curses ui actually implements user abort it
seems. Not sure if we could do the same in the UIBase, but that is for
another time.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2010-12-09 19:52:48 +01:00
Sebastian Spaeth
6e62da435b Fix Machine ui to not error out on ui.warn()
All other uis (especially BaseUI) define as warn(self, msg, minor = 0)
just MachineUI required minor without a default. This leads the
Machine UI to error out with an exception if we pass it
ui.warn("string") which is the common thing in our code base. This
patch is therefore small but critical in fixing this UI.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
2010-12-09 19:52:48 +01:00
buergi
9239a2d326 Bugfix patch for offlineimap
Hello John,

i fixed some tiny bugs in offlineimap, mainly just for myself. They are
more dirty fixes than real bugfixes since I'm missing the deeper insight
into the code.
Especially the first one for Curses.py is very dirty and breaks the
scaling of the interface when the terminal size changes, but at least
the terminal is in proper state after exiting offlineimap.

In the order of appearance in the patchfile:
1. 'fixes' terminal breakage on quit of curses interface in python 2.6
to 2.6.5 (fixed since 2.6.6 http://bugs.python.org/issue7567)
2. fixes netrc password authentication
3. fixes user name querying from netrc

The patch is made for git revision 6b1cb5e036

Thanks a lot for the great application!

Best regards,
buergi
2010-08-20 08:29:47 -05:00
John Goerzen
e1fb9492f8 Patch for signal handling to start a sync by Jim Pryor
Here's the way I'd like to use offlineimap on my laptop:
    1. Have a regular cron job running infrequently. The cron job
    checks to see
if I'm online, plugged in, and that no other copy of offlineimap is
running. If
all of these conditions are satisfied, it runs offlineimap just once:
"offlineimap -o -u Noninteractive.Quiet"

    2. When I start up mutt, I do it by calling a wrapper script that
    delays
until cron-started copies of offlineimap have finished, then starts
    offlineimap
on its regular, stay-alive and keep checking schedule. When I quit
    mutt, the
wrapper script tells offlineimap to stop.

This way I get frequent regular checks while I have mutt running, but
I don't
waste my battery/cpu checking frequently for mail when I'm not
interested in
it.

To make this work, though, it'd be nicer if it were easier to tell
offlineimap,
from the outside, things like "terminate cleanly now" and "when you've
finished
synching, then terminate instead of sleeping and synching again."

OK, to put my money where my mouth is, I attach two patches against
offlineimap
6.0.3.

The first, "cleanup.patch", cleans up a few spots that tend to throw
exceptions
for me as offlineimap is exiting from a KeyboardInterrupt.

The second adds signaling capabilities to offlineimap.

* sending a SIGTERM tells offlineimap to terminate immediately but
  cleanly,
  just as if "q" had been pressed in the GUI interface

* sending a SIGUSR1 tells every account to do a full sync asap: if
  it's
  sleeping, then wake up and do the sync now. If it's mid-sync, then
  re-synch
  any folders whose syncing has already been started or completed, and
  continue
  to synch the other, queued but not-yet-synched folders.

* sending a SIGHUP tells every account to die as soon as it can (but
  not
  immediately: only after finishing any synch it's now engaged in)

* sending a SIGUSR2 tells every account to do a full sync asap (as
  with
  SIGUSR1), then die

It's tricky to mix signals with threads, but I think I've done this
correctly.
I've been using it now for a few weeks without any obvious
problems. But I'm passing it
on so that others can review the code and test it out on their
systems. I developed the
patch when I was running Python 2.5.2, but to my knowledge I don't use
any Python 2.5-specific
code. Now I'm using the patch with Python 2.6.

Although I said "without any obvious problems," let me confess that
I'm
seeing offlineimap regularly choke when I do things like this: start
up
my offlineimap-wrapped copy of mutt, wait a while, put the machine to
sleep (not sure if offlineimap is active in the background or idling),
move to a different spot, wake the machine up again and it acquires a
new network, sometimes a wired network instead of wifi. Offlineimap
doesn't like that so much. I don't yet have any reason to think the
problems here come from my patches. But I'm just acknowledging them,
so
that if others are able to use offlineimap without any difficulty in
situations like I described, then maybe the fault is with my patches.
2008-12-01 16:13:16 -06:00
John Goerzen
ab43d74549 Applied pre/post sync hooks
Patch from Sylvain FORET in refs #71
2008-10-01 00:03:04 -05:00
John Goerzen
1a15b6964f Fix for keys with -1
fixes #83
2008-07-16 14:12:38 -05:00
Vincent Beffara
f549baa074 UNDO: Synchronize newly created folders both ways
This involves several changes at different places:

- syncfoldersto() takes statusfolder as an argument, and returns the
  list of new folders and the list of folders that should be ingnored,
  typically those that were deleted. Warns the user about folders that
  are present only on one side and are not synced.

- syncfoldersto() is called both ways, and on folder creation
  forgetfolders() is used to rebuild the list and take the new creation
  into account. Probably not the most efficient, since it involves
  talking to the IMAP server again, but it will rarely be used anyway.

- Locally created folders are treated separately in the synchronization,
  namely the local messages are uploaded and then the normal sync still
  occurs. If the same folder is created on both sides and contains
  messages on both sides, a two-way sync occurs.
2007-09-02 01:43:15 +01:00
Vincent Beffara
b925fd1296 Synchronize newly created folders both ways
This involves several changes at different places:

- syncfoldersto() takes statusfolder as an argument, and returns the
  list of new folders and the list of folders that should be ingnored,
  typically those that were deleted. Warns the user about folders that
  are present only on one side and are not synced.

- syncfoldersto() is called both ways, and on folder creation
  forgetfolders() is used to rebuild the list and take the new creation
  into account. Probably not the most efficient, since it involves
  talking to the IMAP server again, but it will rarely be used anyway.

- Locally created folders are treated separately in the synchronization,
  namely the local messages are uploaded and then the normal sync still
  occurs. If the same folder is created on both sides and contains
  messages on both sides, a two-way sync occurs.
2007-09-02 01:43:15 +01:00
John Goerzen
cdccfd83b1 Fixed locked() for noninteractive UIs
From: "Mark A. Hershberger"

https://bugs.launchpad.net/ubuntu/+source/offlineimap/+bug/96710

the locked() method isn't implemented for non-interactive UIs, so
exceptions are thrown on cron jobs.  Ubuntu's new apport catches these
and ? well, you get the idea.

patch provided.
2007-10-10 00:12:22 +01:00
John Goerzen
3305d8cd4d Daniel Jacobowitz patches
fixes deb#433732

Date: Sun, 30 Sep 2007 13:54:56 -0400
From: Daniel Jacobowitz <drow@false.org>
To: offlineimap@complete.org
Subject: Assorted patches

Here's the result of a lazy Sunday hacking on offlineimap.  Sorry for
not breaking this into multiple patches.  They're mostly logically
independent so just ask if that would make a difference.
First, a new -q (quick) option.  The quick option means to only update
folders that seem to have had significant changes.  For Maildir, any
change to any message UID or flags is significant, because checking
the flags doesn't add a significant cost.  For IMAP, only a change to
the total number of messages or a change in the UID of the most recent
message is significant.  This should catch everything except for
flags changes.

The difference in bandwidth is astonishing: a quick sync takes 80K
instead of 5.3MB, and 28 seconds instead of 90.

There's a configuration variable that lets you say every tenth sync
should update flags, but let all the intervening ones be lighter.


Second, a fix to the UID validity problems many people have been
reporting with Courier.  As discussed in Debian bug #433732, I changed
the UID validity check to use SELECT unless the server complains that
the folder is read-only.  This avoids the Courier bug (see the Debian
log for more details).  This won't fix existing validity errors, you
need to remove the local status and validity files by hand and resync.


Third, some speedups in Maildir checking.  It's still pretty slow
due to a combination of poor performance in os.listdir (never reads
more than 4K of directory entries at a time) and some semaphore that
leads to lots of futex wake operations, but at least this saves
20% or so of the CPU time running offlineimap on a single folder:

Time with quick refresh and md5 in loop: 4.75s user 0.46s system 12%
cpu 41.751 total
Time with quick refresh and md5 out of loop: 4.38s user 0.50s system
14% cpu 34.799 total
Time using string compare to check folder: 4.11s user 0.47s system 13%
cpu 34.788 total


And fourth, some display fixes for Curses.Blinkenlights.  I made
warnings more visible, made the new quick sync message cyan, and
made all not explicitly colored messages grey.  That last one was
really bugging me.  Any time OfflineIMAP printed a warning in
this UI, it had even odds of coming out black on black!


Anyway, I hope these are useful.  I'm happy to revise them if you see
a problem.

-- 
Daniel Jacobowitz
CodeSourcery
2007-10-01 22:20:37 +01:00
John Goerzen
6949a31164 Resolve conflict 2007-10-13 07:07:30 +01:00
John Goerzen
84d1662482 Update changelog 2007-07-11 02:50:30 +01:00
Florian Friesdorf
ed005cbbdc UIBase config variable fix
Code is probably reached for the first time, thanks to the correct readonly
handling patch
2007-07-07 02:30:42 +01:00
John Goerzen
4370da1dcf UNDO: Checkpointing 2007-07-05 14:49:54 +01:00
John Goerzen
7a287cef57 Checkpointing 2007-07-05 14:49:54 +01:00
John Goerzen
1af12e99a1 Tweaks to machine 2007-07-05 14:22:57 +01:00
John Goerzen
c6f01fb3c8 Machine now runs 2007-07-05 14:21:33 +01:00
John Goerzen
8da3012857 Added Machine.py 2007-07-05 12:05:06 +01:00
John Goerzen
1e90e0fd78 Remove the Tk interfaces
These were a constant source of trouble.  Tkinter likely has multiple
memory leaks that OfflineIMAP was tickling.  I never used these, so poof,
goodbye.
2007-07-04 17:57:09 +01:00
David Favro
65a6b8aa81 Removed copyright notice; assigning copyright to John Goerzen 2007-03-16 03:45:28 +01:00
David Favro
b06845fc70 UID validity diagnostics improvement
* Reduced the number of parameters passed to ui.validityproblem() because they were all just method-calls to the folder object, which is already passed as the first parameter (reduction of unnecessary complexity).

* Improved the diagnostic message for an 'UID validity problem' by including the name of the repository in which the folder resides; previously it was not possible to determine from the diagnostic alone on which side the problem was.
2007-03-15 05:41:43 +01:00
John Goerzen
ca3a306ecc --help shows available UIs now
Thanks to Daniel Rall for the patch.
fixes #5
2007-01-11 10:15:06 +01:00
aaron
71c8b2e7c4 Configurable thread status character for ui.Curses.Blinkenlights 2006-12-11 06:12:00 +01:00