go2-1.20121210/ 0000755 0001750 0001750 00000000000 12061444105 011643 5 ustar piotr piotr go2-1.20121210/Changelog 0000644 0001750 0001750 00000003222 12061444105 013454 0 ustar piotr piotr -*- coding: utf-8 -*-
2008-08-30:
* Bug fix: directory names containing spaces in --cd mode.
2008-08-18:
* Feature: --cd option to emulate 'cd' but storing directory
changes.
* go.sh: alias for 'cd'
2008-05-08:
* Now, the current directory is always the first entry
2008-02-21:
* Bug fix: go2 finish when "pending" work left
* Feature: New file with mandatory "include"s
2008-01-09:
* Bug fix: path highlight case-insensitive
2008-01-07:
* Feature: Search in children of visited directories (HistoryWorker)
2007-12-14:
* Feature: FindWorker added
* Feature: New "list-only" mode
2007-12-09
* Feature: If current folder match, it appears at end
* Feature: Follows links in not-root searching
2007-03-21
* Feature: New option --no-locale
* Deleted: opt.force_search
* Added: opt.parent_mode
* Feature: The locateWorker starts 5 secs after beginning
* Deleted: --setup
2007-03-XX
* Feature: New DiskWorkers are created for directories that
match first item in composite patterns
2006-08-05
* BugFix: Pattern matching with current directory fixed. 'opt' has now
the user original pattern and a 're_pattern' for regular expressions.
2006-07-29
* BugFix: Actually stops when pathList is FULL
* Feature: Option -i to 'case insensitive' and highlight the matching portion
* Feature: Option -r to search (also) from root directory
* Feature: Option -H to enable "history ONLY search"
* BugFix: Fixed pathList management in cacheWorker and historyWorker, they modified
the iterated list losing items.
2006-07-26
* Feature: If pattern match a existing subdirectory, it appear in first position
* Feature: Setup mode: go2 --setup
go2-1.20121210/osfs.py 0000755 0001750 0001750 00000011261 12061444105 013173 0 ustar piotr piotr #!/usr/bin/python
# -*- coding:utf-8; tab-width:4; mode:python -*-
# It contains code chunks from python-filesystem (fs.osfs module)
import sys
import os
import errno
from functools import wraps
ERR_PATH_EXISTS_AS_FILE = \
"Cannot create directory, there's already a file of that name."
ERR_DIR_ALREADY_EXISTS = \
"Can not create a directory that already exists."
class FSError(Exception):
pass
class ResourceNotFoundError(FSError):
pass
class ResourceInvalidError(FSError):
pass
class DestinationExistsError(FSError):
pass
class ParentDirectoryMissingError(FSError):
pass
def convert_os_errors(func):
opname = func.__name__
@wraps(func)
def wrapper(self, *args, **kwds):
try:
return func(self, *args, **kwds)
except (OSError, IOError), e:
(exc_type, exc_inst, tb) = sys.exc_info()
path = getattr(e, "filename", None)
# if path and path[0] == "/" and hasattr(self, "root_path"):
# path = normpath(path)
# if isprefix(self.root_path,path):
# path = path[len(self.root_path):]
# if not hasattr(e,"errno") or not e.errno:
# raise OperationFailedError(opname,details=e),None,tb
if e.errno == errno.ENOENT:
raise ResourceNotFoundError(path, opname=opname, details=e), None, tb
if e.errno == errno.ESRCH:
raise ResourceNotFoundError(path, opname=opname, details=e), None, tb
# if e.errno == errno.ENOTEMPTY:
# raise DirectoryNotEmptyError(path, opname=opname, details=e), None, tb
if e.errno == errno.EEXIST:
raise DestinationExistsError(path, opname=opname, details=e), None, tb
if e.errno == 183: # some sort of win32 equivalent to EEXIST
raise DestinationExistsError(path, opname=opname, details=e), None, tb
if e.errno == errno.ENOTDIR:
raise ResourceInvalidError(path, opname=opname, details=e), None, tb
if e.errno == errno.EISDIR:
raise ResourceInvalidError(path, opname=opname, details=e), None, tb
if e.errno == errno.EINVAL:
raise ResourceInvalidError(path, opname=opname, details=e), None, tb
# if e.errno == errno.EOPNOTSUPP:
# raise UnsupportedError(opname, details=e), None, tb
# if e.errno == errno.ENOSPC:
# raise StorageSpaceError(opname,details=e),None,tb
# if e.errno == errno.EPERM:
# raise PermissionDeniedError(opname,details=e),None,tb
# if e.errno == errno.EACCES:
# if sys.platform == "win32":
# if e.args[0] and e.args[0] == 32:
# raise ResourceLockedError(path,opname=opname,details=e),None,tb
# raise PermissionDeniedError(opname,details=e),None,tb
# # Sometimes windows gives some random errors...
# if sys.platform == "win32":
# if e.errno in (13,):
# raise ResourceInvalidError(path,opname=opname,details=e),None,tb
# raise OperationFailedError(opname,details=e),None,tb
return wrapper
class OSFS(object):
def __init__(self, root):
self.root = root
def makedir(self, path, recursive=False, allow_recreate=False):
try:
if recursive:
os.makedirs(path)
else:
os.mkdir(path)
except OSError, e:
if e.errno in [errno.EEXIST, 183]:
if self.isfile(path):
raise ResourceInvalidError(
path, msg=ERR_PATH_EXISTS_AS_FILE)
if not allow_recreate:
raise DestinationExistsError(
path, msg=ERR_PATH_EXISTS_AS_FILE)
return
if e.errno == errno.ENOENT:
raise ParentDirectoryMissingError(path)
raise
def getcontents(self, fpath):
try:
with file(fpath) as fd:
return fd.read()
except IOError:
raise ResourceNotFoundError(fpath)
def setcontents(self, fpath, contents):
try:
with file(fpath, 'w') as fd:
fd.write(contents)
except IOError:
raise ResourceNotFoundError(fpath)
def open(self, fpath, mode='r'):
try:
return open(fpath, mode)
except IOError:
raise ResourceNotFoundError
def exists(self, fpath):
return os.path.exists(fpath)
def isfile(self, fpath):
return os.path.isfile(fpath)
def isdir(self, path):
return os.path.isdir(path)
go2-1.20121210/TODO 0000644 0001750 0001750 00000002172 12061444105 012335 0 ustar piotr piotr
- en las rutas relativas al home del usuario actual cambiar $(HOME) por ~ [OK]
- Guardar en un fichero las rutas a ignorar $HOME/.go/ignore [OK]
- No seguir los enlaces simbólicos
- poder indicar como argumento un directorio para que lo guarde en la
cache, debe servir también . [OK]
- una opción para hacerlo case-sensitive o no [OK, con -i]
- eliminar '/' al final del patrón [OK]
- Problemas al buscar algo como "algo/" [OK]
- si solo se encuentra una, elegirla automáticamente [OK]
- Una opción (-f) para forzar la búsqueda aunque haya un subdirectorio que
coincide exactamente con el patrón [OK, lo hace siempre, no hay -f]
- Priorizar las rutas en las que hay matches [OK, con nuevos workers]
- Una opción (-h-H) para historial de búsquedas [OK]
- tamaño de caché y de historia configurable en el fichero
- en go.sh, cuando no hay argumento, la función invoca "go.py ''". Hacer
que no ponga nada si no hay argumento. [OK]
- Nuevas opciones:
--no-locate
- Opciones para el .go/config
--log-level
- GtkUI
- doble-click elige una entrada del treeview
- el botón "Search" no está activo si no hay patrón
go2-1.20121210/go2.glade2 0000644 0001750 0001750 00000047770 12061444105 013431 0 ustar piotr piotr
True
:: go2 ::
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_MOUSE
False
450
300
True
False
gtk-find
True
False
False
GDK_WINDOW_TYPE_HINT_NORMAL
GDK_GRAVITY_NORTH_WEST
True
False
4
True
False
0
True
False
0
True
True
True
True
True
0
True
*
True
0
True
True
True
True
True
True
gtk-find
True
GTK_RELIEF_NORMAL
True
0
False
False
0
False
True
True
False
0
4
True
0
0.5
GTK_SHADOW_ETCHED_IN
True
True
False
0
True
0.5
0.5
1
1
0
0
12
0
True
False
0
True
False
0
True
True
whole file system
True
GTK_RELIEF_NORMAL
True
False
False
True
0
False
False
True
True
ignore case
True
GTK_RELIEF_NORMAL
True
False
False
True
0
False
False
True
True
exclude hidden
True
GTK_RELIEF_NORMAL
False
True
False
True
0
False
False
0
False
True
True
6
False
False
True
False
0
True
0.5
0.5
1
1
0
0
8
4
True
False
0
True
False
0
True
Command
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
False
False
120
True
False
True
True
True
True
0
True
â—
False
0
True
True
0
False
False
True
0
True
True
0
True
True
True
0
True
True
0
True
True
True
Options
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
label_item
0
False
False
True
0
True
True
0
False
True
True
True
GTK_POLICY_AUTOMATIC
GTK_POLICY_ALWAYS
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
True
True
True
True
True
False
False
False
0
True
True
True
GTK_BUTTONBOX_END
4
4
False
True
True
False
0
True
GTK_PROGRESS_LEFT_TO_RIGHT
0
0.10000000149
PANGO_ELLIPSIZE_NONE
2
True
True
True
False
True
gtk-cancel
True
GTK_RELIEF_NORMAL
True
0
False
False
True
False
True
gtk-clear
True
GTK_RELIEF_NORMAL
True
2
False
False
True
False
True
GTK_RELIEF_NORMAL
True
True
False
0
True
gtk-jump-to
4
0.5
0.5
0
0
0
True
True
True
Go to
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
True
True
2
False
True
0
False
False
go2-1.20121210/go2.py 0000755 0001750 0001750 00000070513 12061444105 012715 0 ustar piotr piotr #!/usr/bin/python
# -*- coding:utf-8; tab-width:4; mode:python -*-
# go2.py
#
# Copyright © 2004-2011 David Villa Alises
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
VERSION = '1.20121210'
DESCRIPTION = '''\
go2 is a fast directory finder.
This is version {0}, Copyright (C) 2004-2012 David Villa Alises.
go2 comes with ABSOLUTELY NO WARRANTY; This is free software, and you
are welcome to redistribute it under certain conditions; See COPYING
for details.'''
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
try:
sys.path.remove(os.getcwd())
except (OSError, ValueError):
pass
#os.environ['LANG'] = 'C'
import pwd
import tty
import termios
import time
import signal
import re
import logging
import locale
import atexit
from threading import Thread
import multiprocessing as mp
import multiprocessing.queues as mp_queues
import subprocess as subp
import shlex
from traceback import print_exc
from itertools import cycle
from fnmatch import fnmatch
import gobject
import argparse
from osfs import OSFS, ResourceNotFoundError
ESC = 27
CTRL_C = 3
ENTER = 13
HIGH = chr(27) + '[1m'
LOW = chr(27) + '[m'
USERDIR = os.environ['HOME']
try:
CWD = os.getcwd()
except OSError:
CWD = USERDIR
GO2DIR = os.path.join(USERDIR, '.go2')
GO2IGNORE = os.path.join(GO2DIR, 'ignore')
GO2HISTORY = os.path.join(GO2DIR, 'history')
GO2CACHE = os.path.join(GO2DIR, 'cache')
#GO2CONFIG = os.path.join(GO2DIR, 'config')
GO2TMP = os.path.join(GO2DIR, 'tmp')
GO2LOG = os.path.join(GO2DIR, 'log')
ACTIVATION_CMD = "[ -e /usr/lib/go2/go2.sh ] && source /usr/lib/go2/go2.sh"
CD_MODE_CMD = "alias cd='go2 --cd'"
STOP = 'STOP'
logging.basicConfig(
filename=os.path.join('/tmp/go2-%s' % pwd.getpwuid(os.getuid())[0]),
filemode='w',
format='%(asctime)-15s %(message)s',
level=logging.DEBUG)
def to_unicode(text, encoding=None):
encoding = encoding or config.encoding
if isinstance(text, unicode):
return text
return unicode(text, encoding=encoding, errors='replace')
class Go2Exception(Exception):
def __str__(self):
return repr(self)
class PathItem:
def __init__(self, path, level=0):
self.path = to_unicode(path)
self.level = level
def __eq__(self, other):
return self.path == other.path and self.level == other.level
def go2setup():
bashrc = os.path.join(USERDIR, '.bashrc')
open_flags = 'a'
try:
if ACTIVATION_CMD in config.fs.getcontents(bashrc):
print('go2 already configured, skipping.')
return 1
except ResourceNotFoundError:
open_flags = 'w'
with config.fs.open(os.path.join(USERDIR, '.bashrc'), open_flags) as fd:
fd.write("{0}\n{1}\n".format(ACTIVATION_CMD, CD_MODE_CMD))
print('Setting up go2. It will be ready in next shell session.')
def rprint(text=''):
sys.stdout.write("\r{0}".format(text.encode(config.encoding)))
# sys.stdout.flush()
def message(text):
rprint("({0})\r\n".format(text))
def high(text):
return config.before + text + config.after
def abbreviate_home_prefix(path):
return re.sub(u'^{0}'.format(USERDIR), '~', path)
def high_pattern_formatter(index, path, suffix):
path = config.matcher.high_pattern(path)
path = abbreviate_home_prefix(path)
return u'\r{0:c}: {1}{2}\r\n'.format(index, path, suffix)
class ListUniq(list):
def append(self, item):
if item in self:
return
list.append(self, item)
def extend(self, other):
for item in other:
self.append(item)
class MatchLevel:
NO_MATCH = -1
START = 0
START_IGNORECASE = 1
CONTAIN = 2
CONTAIN_IGNORECASE = 3
class PathMatcher(object):
def __init__(self, pattern):
self.pre_compute(pattern)
def pre_compute(self, pattern):
self.re_start = self.pattern2re_start(pattern)
self.re_contain = self.pattern2re_contain(pattern)
self.test_pattern(pattern)
def test_pattern(self, pattern):
try:
re.match(self.re_start, "")
except re.error:
self.pre_compute([re.escape(x) for x in pattern])
def match(self, path):
if re.match(self.re_start, path, re.UNICODE):
return MatchLevel.START
if re.match(self.re_start, path, re.UNICODE | re.IGNORECASE):
return MatchLevel.START_IGNORECASE
if re.match(self.re_contain, path, re.UNICODE):
return MatchLevel.CONTAIN
if re.match(self.re_contain, path, re.UNICODE | re.IGNORECASE):
return MatchLevel.CONTAIN_IGNORECASE
return MatchLevel.NO_MATCH
@staticmethod
def pattern2re_start(pattern):
return unicode.join(u'', [u'.*/({0})[^/]*'.format(x) for x in pattern]) + u'$'
@staticmethod
def pattern2re_contain(pattern):
return unicode.join(u'', [u'.*/.*({0})[^/]*'.format(x) for x in pattern]) + u'$'
def high_pattern(self, path):
retval = ''
begin = 0
mo = re.match(self.re_contain, path, re.IGNORECASE)
for i in range(1, mo.lastindex + 1):
retval += mo.string[begin:mo.start(i)]
retval += config.before + mo.group(i) + config.after
begin = mo.end(i)
retval += mo.string[begin:]
return retval
def save_target(path):
config.fs.setcontents(GO2TMP, path.encode(config.encoding))
class PathFileStore:
"Manage a file holding a path set"
def __init__(self, path, size=1000):
self.path = path
self.size = size
self.data = {}
self.load()
# self.log = logging.getLogger('PathFileStore [%s]' % os.path.split(path)[-1])
def load(self):
try:
with config.fs.open(self.path) as fd:
self._load_file_lines(fd)
except ResourceNotFoundError:
pass
def _load_file_lines(self, fd):
def decode(path):
return path.strip().decode('utf8')
for line in fd:
line = line.strip()
if not line:
continue
try:
visits, path = line.split(':', 1)
except ValueError:
visits, path = 1, line
try:
self.data[decode(path)] = int(visits)
except UnicodeDecodeError:
continue
def __iter__(self):
def _cmp(p1, p2):
retval = -cmp(p1[1], p2[1]) # visits
if retval == 0:
return cmp(p1[0], p2[0]) # names
return retval
for path, visits in sorted(self.data.items(), cmp=_cmp):
yield path
def add_visit_seq(self, seq):
assert isinstance(seq, list)
for path in seq:
self.add_visit(path)
return self
def add_visit(self, path):
assert isinstance(path, unicode), path
visits = self.data.get(path, 0)
self.data[path] = visits + 1
return self
def save(self):
with config.fs.open(self.path, 'w') as fd:
for path, visits in self.data.items()[:self.size]:
line = u"{0}:{1}\n".format(visits, path)
fd.write(line.encode('utf8'))
def __unicode__(self):
return "".format(self.path)
def __repr__(self):
return unicode(self).encode(config.encoding)
def save_in_history(path):
PathFileStore(GO2HISTORY).add_visit(path).save()
def from_file_provider(path_store):
for path in path_store:
level = config.matcher.match(path)
if level == MatchLevel.NO_MATCH:
continue
if not config.fs.exists(path):
del path_store.data[path]
continue
yield PathItem(path, level)
class CommandProvider:
def __init__(self, command):
self.command = command
self.ps = subp.Popen(
shlex.split(command),
bufsize = 0,
shell = False,
close_fds = True,
stdout = subp.PIPE,
stderr = subp.PIPE,
preexec_fn = os.setsid)
logging.info('%s: starts', self)
self.abort = False
signal.signal(signal.SIGTERM, self.terminate)
def is_alive(self):
value = os.waitpid(self.ps.pid, os.WNOHANG)
logging.debug('%s: waitpid %s ', self, value)
return value == (0, 0)
def terminate(self, *args):
if self.abort:
return
self.abort = True
logging.info('%s: terminate', self)
try:
self.ps.send_signal(signal.SIGTERM)
# os.killpg(self.ps.pid, signal.SIGTERM)
time.sleep(0.15)
while self.is_alive():
logging.info('%s: SIGKILLed', self)
self.ps.send_signal(signal.SIGKILL)
# os.killpg(self.ps.pid, signal.SIGKILL)
time.sleep(0.1)
except OSError, e:
logging.error('%s: %s', self, e)
# WARN: This kills the worker running this provider
sys.exit()
def __iter__(self):
try:
for path in self.ps.stdout:
if self.abort:
return
path = path.strip()
level = config.matcher.match(path)
if level == MatchLevel.NO_MATCH:
continue
if not config.fs.exists(path):
continue
yield PathItem(path, level)
except IOError:
self.terminate()
def __str__(self):
return "CommandProvider pid:{0} cmd:'{1}'".format(self.ps.pid, self.command)
def tree_provider(path):
assert os.path.isabs(path)
assert config.fs.isdir(path)
return CommandProvider('tree -dfin --noreport %s' % path)
def walk_provider(path):
print 'walk_provider'
assert os.path.isabs(path)
assert config.fs.isdir(path)
for root, dirnames, filenames in os.walk(path):
paths = [os.path.join(root, x) for x in dirnames
if not x.startswith('.')]
for path in paths:
#FIXME: refactor this, is repeated in all providers
level = config.matcher.match(path)
if level == MatchLevel.NO_MATCH:
continue
if not config.fs.exists(path):
continue
yield PathItem(path, level)
class CancelException(Go2Exception):
pass
class NotMatchException(Go2Exception):
pass
class NotExistsException(Go2Exception):
pass
class NothingToDoException(Go2Exception):
pass
class TTY:
def __init__(self):
self.old_settings = tty.tcgetattr(sys.stdin)
def set_raw(self):
tty.setraw(sys.stdin)
def restore(self):
tty.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings)
class ThreadStarted(Thread):
def __init__(self, *args, **kargs):
Thread.__init__(self, *args, **kargs)
self.start()
class Worker(mp.Process):
def __init__(self, tasks, output):
self.tasks = tasks
self.output = output
mp.Process.__init__(self)
self.start()
def run(self):
for func, args in iter(self.tasks.get, STOP):
self.push(func(*args))
self.tasks.task_done()
self.tasks.task_done()
def push(self, source):
try:
for x in source:
self.output.put(x)
except TypeError:
self.output.put(source)
class TaskQueue(mp_queues.JoinableQueue):
def unfinished_tasks_count(self):
return self._unfinished_tasks._semlock._get_value()
class ProcessPool(object):
def __init__(self, num_workers=None):
self.task_queue = TaskQueue()
self.output_queue = mp.Queue()
num_workers = num_workers or mp.cpu_count()
self.workers = [Worker(self.task_queue, self.output_queue)
for x in range(num_workers)]
def add_task(self, func, *args):
logging.info('new task for: %s%s', func.__name__, args)
self.task_queue.put((func, args))
def sleep_free_workers(self, seconds):
for i in range(len(self.workers) - self.task_queue.unfinished_tasks_count()):
self.add_task(time.sleep, seconds)
def has_tasks(self):
return self.task_queue.unfinished_tasks_count() != 0
def terminate(self):
if not any(w.is_alive() for w in self.workers):
return
logging.debug('pool: terminating')
for w in self.workers:
self.task_queue.put(STOP)
time.sleep(0.1)
for w in self.workers:
if w.is_alive():
w.terminate()
logging.debug('pool: terminated')
def join(self):
while self.has_tasks():
time.sleep(0.1)
self.terminate()
class QueueExtractor(object):
def __init__(self, callback):
self.callback = callback
def __call__(self, fd, condition, queue, *args):
event = queue.get()
self.callback(event)
return True
class QueueReactor(gobject.MainLoop):
class Callback(object):
def __init__(self, reactor, func):
self.reactor = reactor
self.func = func
def __call__(self, *args):
try:
return self.func(*args)
except Exception, e:
if not isinstance(e, Go2Exception):
print_exc()
logging.warning('{0!r}: {1}'.format(self.reactor, e))
self.reactor.exception = e
self.reactor.quit()
return False
def __init__(self):
gobject.MainLoop.__init__(self)
self.context = self.get_context()
self.at_quit_func = lambda: None
self.exception = None
def queue_add_watch(self, queue, func, *args):
gobject.io_add_watch(
queue._reader, gobject.IO_IN,
self.Callback(self, func), queue, *args,
priority=gobject.PRIORITY_HIGH)
def io_add_watch(self, fd, func, *args):
gobject.io_add_watch(
fd, gobject.IO_IN,
self.Callback(self, func), *args)
def timeout_add(self, t, func, *args):
gobject.timeout_add(t, self.Callback(self, func), *args)
def at_quit(self, func):
self.at_quit_func = func
def process_pending(self):
time.sleep(0.1)
while self.context.pending():
self.context.iteration()
def iteration(self):
time.sleep(0.1)
self.context.iteration()
def run(self):
time.sleep(0.1)
gobject.MainLoop.run(self)
self.at_quit_func()
if self.exception is not None:
raise self.exception
def quit(self):
logging.debug('reactor quit')
gobject.MainLoop.quit(self)
def __repr__(self):
return self.__class__.__name__
class PathBuffer(object):
def __init__(self, sink=None):
self.sink = sink
self.groups = [list() for x in range(4)]
self.filters = []
def set_sink(self, sink):
self.sink = sink
def add(self, path_item):
if path_item is None:
return True
try:
self._tryTo_add(path_item)
except Sink.FullSinkException:
return False
return True
def _tryTo_add(self, item):
# print '\r', repr(item.path)
# print '\r', self.groups[item.level]
if item.path in self.groups[item.level] or \
any(f(item.path) for f in self.filters):
return
self.groups[item.level].append(item.path)
if item.level == MatchLevel.START:
self.sink.add_entry(item.path)
def add_filter(self, f):
self.filters.append(f)
def flush_alternate(self):
def add_paths_of_group(group):
for path in group:
self.sink.add_entry(path)
try:
for level in range(1, 4):
if not self.groups[level]:
continue
if level != 3:
self.sink.next_group()
add_paths_of_group(self.groups[level])
except Sink.FullSinkException:
return
class Sink(object):
class FullSinkException(Go2Exception):
pass
class InvalidChoiceException(Go2Exception):
pass
def add_entry(self, path):
raise NotImplementedError
def next_group(self):
pass
class PrintSink(Sink):
def add_entry(self, path):
rprint(path + '\n\r')
class Menu(Sink):
def __init__(self, reactor, out, max_size=26):
self.reactor = reactor
self.fd = out
self.max_size = max_size
self.entries = []
self.is_full = False
self.target = None
self.formatter = self.default_formatter
def perform_choice(self, choice):
if not self.is_valid_choice(choice):
raise self.InvalidChoiceException()
logging.debug("choice: {0}, entries: {1}".format(
choice, self.entries))
self.target = self.entries[choice]
self.reactor.quit()
def is_valid_choice(self, choice):
return choice in range(0, len(self.entries))
def next_group(self):
self.out_write('\r --- \n')
def add_entry(self, entry):
if len(self.entries) >= self.max_size:
self.is_full = True
raise self.FullSinkException
self.entries.append(to_unicode(entry))
self.write_last()
def write_last(self):
suffix = ''
if len(self.entries) == 1:
suffix += ' [ENTER]'
line = self.formatter(
len(self.entries) + ord('a') - 1,
self.entries[-1],
suffix)
self.out_write(line)
def default_formatter(self, index, path, suffix):
return '{0:c}: {1}'.format(index, path)
def out_write(self, text):
self.fd.write(text)
self.fd.flush()
def user_break_checker(key):
if key in [ESC, CTRL_C]:
raise CancelException
class UserInputHandler(object):
def __call__(self, fd, condition=None):
key = ord(fd.read(1))
user_break_checker(key)
return True
class UserChoiceHandler(object):
def __init__(self, sink):
self.sink = sink
def __call__(self, fd, condition=None):
key = ord(fd.read(1))
# key = os.read(fd, 1)
user_break_checker(key)
try:
choice = self.key2choice(key)
self.sink.perform_choice(choice)
except Sink.InvalidChoiceException:
return True
return False
def key2choice(self, key):
if key == ENTER:
return 0
return key - ord('a')
class IgnoreManager(object):
def __init__(self, content):
self.patterns = []
for line in content.split():
line = line.strip()
if not os.sep in line:
line = '*/{0}/*'.format(line)
self.patterns.append(line)
@classmethod
def from_file(cls, fname):
return IgnoreManager(config.fs.getcontents(fname))
def is_ignored(self, path):
assert path.startswith(os.sep), "It must be an absolute path"
path = os.path.normpath(path) + os.sep
retval = any(fnmatch(path, x) for x in self.patterns)
if retval:
logging.debug("Ignored: %s", path)
return retval
class Go2Base(object):
def __init__(self):
self.pool = ProcessPool()
self.reactor = QueueReactor()
self.path_buffer = PathBuffer()
self.setup_stdin()
self.setup_ignore()
self.reactor.queue_add_watch(self.pool.output_queue,
QueueExtractor(self.path_buffer.add))
self.reactor.timeout_add(250, self.end_checker)
def setup_stdin(self):
stdin_tty = TTY()
stdin_tty.set_raw()
atexit.register(stdin_tty.restore)
def setup_ignore(self):
try:
ignore_manager = IgnoreManager.from_file(GO2IGNORE)
self.path_buffer.add_filter(ignore_manager.is_ignored)
except ResourceNotFoundError:
pass
def stop(self, *args):
self.pool.terminate()
def run(self):
retval = 1
self.history = PathFileStore(GO2HISTORY)
self.cache = PathFileStore(GO2CACHE)
self.create_tasks()
try:
self.reactor.run()
self.on_success()
retval = 0
except CancelException:
message(u"canceled by user")
except NotMatchException:
message(u"pattern not found")
self.pool.terminate()
return retval
def create_tasks(self):
self.create_file_tasks()
self.create_command_tasks()
def create_file_tasks(self):
self.pool.add_task(from_file_provider, self.history)
self.pool.sleep_free_workers(seconds=0.5)
self.pool.add_task(from_file_provider, self.cache)
def not_overlapped_history(self):
history_paths = []
for p in sorted(self.history.data.keys(), key=len):
if any(p.startswith(x) for x in history_paths):
continue
history_paths.append(p)
# print str.join('\r\n', history_paths)
return history_paths
def create_command_tasks(self):
paths = ListUniq(config.search_path.split(':'))
# paths.extend(self.not_overlapped_history())
for path in set(paths):
path = os.path.abspath(path)
if not config.fs.isdir(path):
message("'{0}' does not exist".format(path))
self.pool.add_task(tree_provider, path)
# self.pool.add_task(walk_provider, path)
def end_checker(self):
return False
def on_success(self):
pass
class Go2Interactive(Go2Base):
def __init__(self):
Go2Base.__init__(self)
self.setup_stdout()
self.menu = Menu(self.reactor, sys.stdout)
self.menu.formatter = high_pattern_formatter
self.path_buffer.set_sink(self.menu)
self.reactor.io_add_watch(sys.stdin, UserChoiceHandler(self.menu))
self.progress = cycle(range(4))
def setup_stdout(self):
def stdout_restore():
sys.stdout = sys.__stdout__
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
atexit.register(stdout_restore)
# fl = fcntl.fcntl(fd.fileno(), fcntl.F_GETFL)
# fl |= os.O_SYNC # or os.O_DSYNC (if you don't care the file timestamp updates)
# fcntl.fcntl(fd.fileno(), fcntl.F_SETFL, fl)
def end_checker(self):
if self.menu.is_full:
message(u"warning: too many matches!")
rprint('Select path: ')
self.pool.terminate()
return False
if self.pool.has_tasks():
rprint(u"\rSearching{0:3} ".format('.' * self.progress.next()))
return True
self.path_buffer.flush_alternate()
if len(self.menu.entries) == 0:
raise NotMatchException
if len(self.menu.entries) == 1:
message(u"single match")
self.menu.perform_choice(0)
return False
rprint(u"Select path: ")
return False
def on_success(self):
rprint(u"Changing to: {0}\r\n".format(
high(abbreviate_home_prefix(self.menu.target))))
save_target(self.menu.target)
self.history.add_visit(self.menu.target).save()
logging.debug("Saved history %s", len(self.menu.entries))
self.cache.add_visit_seq(self.menu.entries).save()
class Go2ListOnly(Go2Base):
def __init__(self):
Go2Base.__init__(self)
self.path_buffer.set_sink(PrintSink())
self.reactor.io_add_watch(sys.stdin, UserInputHandler())
def end_checker(self):
if not self.pool.has_tasks():
self.reactor.quit()
return True
def create_directory_wizzard(path):
def show_make_and_change(path):
path = os.path.abspath(path)
path = config.matcher.high_pattern(path)
path = abbreviate_home_prefix(path)
print("go2: Making and changing to directory: %s" % path)
if path.startswith(CWD):
path = os.path.abspath(path)[len(CWD):].strip('/')
print "go2: '%s' does not exist.\n(c)ancel, (s)earch or (m)ake? (C/s/m):" % path,
try:
answer = raw_input().lower()
except KeyboardInterrupt:
answer = ''
print
if len(answer) != 1 or answer not in 'sm':
message(u'canceled')
return 1
if answer == 'm':
try:
config.fs.makedir(path, recursive=True)
except OSError, e:
print e
sys.exit(1)
show_make_and_change(path)
save_in_history(path)
save_target(path)
return 0
# search
Go2Interactive().run()
def chdir_decorator(wizzard=False):
if not config.pattern:
save_target(USERDIR)
return 0
params = str.join(' ', config.pattern)
try:
target = os.path.abspath(params)
except IOError, e:
logging.error(e)
save_target(target)
return 0
except OSError, e:
logging.error(e)
save_target(USERDIR)
return 0
if params == '-':
save_target('-')
return 0
if config.fs.exists(target):
save_in_history(target)
save_target(target)
return 0
raise NotExistsException(target)
def get_config(args=None):
args = args or []
encoding = locale.getdefaultlocale()[1]
parser = argparse.ArgumentParser(
prog = 'go2',
description = DESCRIPTION.format(VERSION),
epilog = '.',
formatter_class = argparse.RawDescriptionHelpFormatter)
parser.add_argument('pattern', nargs='*', help="pattern to find")
parser.add_argument('--cd', dest='chdir', action="store_true",
help="'cd' alias with caching")
parser.add_argument('-i', '--ignore-case', dest='ignorecase',
action='store_true', default=False,
help="ignore case search")
parser.add_argument('-l', '--list-only', dest='listonly', action='store_true',
help="list matches and exits")
parser.add_argument('-p', '--path', metavar='PATH', dest='search_path',
default="{0}:{1}".format(CWD, USERDIR),
help='set search path (default: "./:{0}")'.format(USERDIR))
parser.add_argument('-r', '--from-root', dest='from_root',
action='store_true',
help="add / to the search path")
parser.add_argument('-v', '--version', action='version',
version='%(prog)s ' + VERSION)
parser.add_argument('--setup',
action='store_true',
help="install go2 in your .bashrc")
retval = parser.parse_args(args)
retval.parser = parser
retval.engine = Go2ListOnly if retval.listonly else Go2Interactive
retval.encoding = encoding
retval.pattern = [to_unicode(x, encoding) for x in retval.pattern]
# process args
if retval.ignorecase:
retval.pattern = [x.lower() for x in retval.pattern]
# retval.apply_case = str.lower if retval.ignorecase else str
# print retval.pattern
if retval.from_root:
retval.search_path += ':/'
# application globals
retval.matcher = PathMatcher(retval.pattern)
retval.fs = OSFS('/')
retval.fs.makedir(GO2DIR, allow_recreate=True)
retval.before = HIGH
retval.after = LOW
return retval
if __name__ == '__main__':
config = get_config(sys.argv[1:])
if config.setup:
sys.exit(go2setup())
if config.chdir:
try:
sys.exit(chdir_decorator())
except NotExistsException, target:
sys.exit(create_directory_wizzard(target.args[0]))
if not config.pattern:
print("go2.py: error: too few arguments\n")
config.parser.print_help()
sys.exit(1)
signal.signal(signal.SIGINT, config.engine.stop)
sys.exit(config.engine().run())
go2-1.20121210/code.py 0000755 0001750 0001750 00000001455 12061444105 013137 0 ustar piotr piotr #!/usr/bin/python
# -*- coding:utf-8; tab-width:4; mode:python -*-
class QueueReactor(object):
class QueueExtractor(object):
def __init__(self, queue, callback, *args):
self.queue = queue
self.callback = callback
self.args = args
def __call__(self):
self.callback(self.queue.get(), *self.args)
def add_queue_watch(self, queue, callback, *args):
gobject.io_add_watch(queue._reader.fileno(),
gobject.IO_IN,
QueueExtractor(queue, callback, *args))
def run(self):
gobject.MainLoop().run()
ui = UI()
path_list = PathList(ui.add_entry)
queue = mp.Queue()
r = QueueReactor()
r.add_queue_watch(queue, ob.notify)
r.add_io_watch(stdin, ui.on_key_pressed)
r.run()
go2-1.20121210/debian-vcs.mk 0000755 0001750 0001750 00000000764 12061444105 014221 0 ustar piotr piotr #!/usr/bin/make -f
# -*- mode:makefile -*-
URL_AUTH=svn+ssh://${ALIOTH_USER}@svn.debian.org/svn/python-apps/packages/go2/trunk
URL_ANON=svn://svn.debian.org/svn/python-apps/packages/go2/trunk
debian:
if [ ! -z "$${ALIOTH_USER}" ]; then \
svn co ${URL_AUTH} -N; \
else \
svn co ${URL_ANON} -N; \
fi
mv trunk/.svn .
rmdir trunk
svn up debian
clean:
find . -name *.pyc -delete
find . -name *.pyo -delete
find . -name *~ -delete
$(RM) -r dist build
$(RM) -r .svn debian MANIFEST
go2-1.20121210/CHANGES.rst 0000644 0001750 0001750 00000002707 12061444105 013453 0 ustar piotr piotr Release 0.20121210
------------------
* Bug fixed when print new created directories (--cd mode)
Release 1.20121112
------------------
* Trivial missing directory in --cd mode fixed
Release 1.20120203
------------------
* auto-escape pattern when it is not a valid re.
Release 1.20120111
------------------
* refactoring: new classes UserInputHandler and UserChoiceHandler
* highlight and abbreviate path in the chdir-wizzard
Release 1.20111223
------------------
* removed --cwd
* new --path command line argument
Release 1.20111223
------------------
* new --list-only command line argument
* new --cwd command line argument
Release 1.20111219
------------------
* OSFS fake to reduce initialization time
Release 1.20111216
------------------
* "ENTER without matches bug" fixed
Release 1.20111212
------------------
* Added support for .go2/ignore file
Release 1.20111028
------------------
* Refactoring: QueueReactor, PathBuffer, Menu
Release 1.20111011
------------------
* single match fixed.
Release 1.20111009
------------------
* Subprocess termination fixed.
* Automatic election for single match.
Release 1.20110916
------------------
* New multiprocess version (without GUI)
* Log to /tmp/go2-$USER
Release 0.20110716
------------------
* /usr/bin/go2 assures correct setup.
* Bug with non-existent directories FIXED.
.. Local Variables:
.. coding: utf-8
.. mode: rst
.. mode: flyspell
.. ispell-local-dictionary: "american"
.. End:
go2-1.20121210/go2.rst 0000644 0001750 0001750 00000003671 12061444105 013073 0 ustar piotr piotr ===
go2
===
----------------
directory finder
----------------
:Author: David.Villa@uclm.es
:date: 2011-08-05
:Manual section: 1
SYNOPSIS
========
``go2`` [-h] [--cd] [-i] [-r] [--setup] [--version] [pattern [pattern ...]]
DESCRIPTION
===========
This manual page documents briefly the ``go2`` command.
This manual page was written for the Debian(TM) distribution because
the original program does not have a manual page.
``go2`` is a program that finds (and changes to) directories.
IMPORTANT
=========
``go2`` requires be loaded from the shell. To do this include the next
sentence in your $HOME/.bashrc file:
|
| [ -e /usr/lib/go2/go2.sh ] && source /usr/lib/go2/go2.sh
If your wish improve directory caching, you may include also the next
sentence:
|
| alias cd='go2 --cd'
Both are made by the setup process the first time you invoke ``go2``.
OPTIONS
=======
This program follows the usual GNU command line syntax, with long
options starting with two dashes ('-'). A summary of options is
included below. For a complete description, see the Info files.
--cd Just change working directory
-i Case insensitive.
-r Search from root directory.
-d Search in hidden directories.
-l List only, print matches and exists.
--setup Install go2 in your .bashrc.
SEE ALSO
========
| This program is fully documented in
| http://arco.esi.uclm.es/~david.villa/go2.html
COPYRIGHT
=========
Copyright © 2011 David Villa
This manual page was written for the Debian system (and may be used by
others).
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU General Public License, Version 2 or (at
your option) any later version published by the Free Software
Foundation.
On Debian systems, the complete text of the GNU General Public License
can be found in /usr/share/common-licenses/GPL.
.. Local Variables:
.. coding: utf-8
.. mode: flyspell
.. ispell-local-dictionary: "american"
.. End:
go2-1.20121210/MANUAL 0000644 0001750 0001750 00000001342 12061444105 012543 0 ustar piotr piotr
Description: http://arco.inf-cr.uclm.es/~david.villa/go2.html
1. USAGE
go2 name searchs "name*" in current dir and home dir
go2 -i name searchs "name*" case insensitive
go2 -r name searchs "name*" in root dir, current dir and home dir
go2 -H searchs only in the history list
go2 pat1 pat2 searchs directories that match with /.../pat1*/.../pat2*
2. KEY BINDINDS
Any key can be pressed in any moment.
ESC cancel the current search
ENTER select the first entry
[a-z] select an entry, if exists
3. FILES
~/.go2/config command line optiones always applied
~/.go2/include directories always processed (full paths, one per line)
~/.go2/exclude directories never processed (full paths, one per line)
go2-1.20121210/setup.py 0000755 0001750 0001750 00000001033 12061444105 013355 0 ustar piotr piotr #!/usr/bin/python
from distutils.core import setup
setup(name = 'go2',
version = '1.20121210',
description = 'go2 directory finder',
author = 'David Villa Alises',
author_email = 'David.Villa@uclm.es>',
url = 'http://savannah.nongnu.org/projects/go2/',
license = 'GPL v2 or later',
data_files = [('/usr/lib/go2', ['go2.sh', 'go2.py', 'osfs.py']),
('/usr/share/man/man1', ['go2.1']),
('/usr/bin', ['go2'])],
)
go2-1.20121210/test/ 0000755 0001750 0001750 00000000000 12061444105 012622 5 ustar piotr piotr go2-1.20121210/test/system/ 0000755 0001750 0001750 00000000000 12061444105 014146 5 ustar piotr piotr go2-1.20121210/test/system/tree_killed.test 0000755 0001750 0001750 00000000346 12061444105 017340 0 ustar piotr piotr #!/usr/bin/atheist
# -*- mode:python; coding:utf-8 -*-
import signal
go2 = TestBG('./go2.py -r sha',
signal=signal.SIGTERM, expected=-15,
post=Not(ProcessRunning('tree')))
TaskTerminator(go2, delay=8)
go2-1.20121210/test/system/without-arguments.test 0000755 0001750 0001750 00000000324 12061444105 020557 0 ustar piotr piotr #!/usr/bin/atheist
# -*- mode:python; coding:utf-8 -*-
Test('$basedir/go2.py',
expected=1,
post=StdOutContains(
['error: too few arguments',
'go2 is a fast directory finder.'])
)
go2-1.20121210/test/unit_tests.py 0000644 0001750 0001750 00000026602 12061444105 015403 0 ustar piotr piotr #!/usr/bin/atheist
# -*- mode:python; coding:utf-8 -*-
import sys
import os
import time
import multiprocessing as mp
from unittest import TestCase
import fs
from fs.memoryfs import MemoryFS
from fs import ResourceNotFoundError
from pyDoubles.framework import *
import gobject
import go2
HOME = '/home/user'
def hello_task():
return ["hi"]
def foo_filter(value):
return value == 'foo'
class StubFS(object):
def setUp(self):
self.fs = MemoryFS()
self.mkdir('lab')
config = go2.get_config([])
config.fs = self.fs
go2.config = config
def mkdir(self, relpath):
self.fs.makedir(fs.join(HOME, relpath), recursive=True)
class TestPool(TestCase):
def setUp(self):
self.pool = go2.ProcessPool()
def test_create(self):
self.pool.terminate()
# Esta prueba no funciona con atheist
def test_add_task_hello(self):
self.pool.add_task(hello_task)
self.assertEquals(self.pool.output_queue.get(timeout=1), "hi")
self.pool.join()
class Test_QueueReactor(TestCase):
def setUp(self):
self.sut = go2.QueueReactor()
def test_queue_add_watch(self):
# given:
queue = mp.Queue()
queue.put('foo')
handler = empty_mock()
expect_call(handler.handle).with_args(ANY_ARG, gobject.IO_IN, queue).then_return(False)
self.sut.queue_add_watch(queue, handler.handle)
# when
self.sut.process_pending()
# then
handler.assert_that_is_satisfied()
def test_quit(self):
# given
gobject.timeout_add(1, self.sut.quit)
# when
self.sut.run()
# then
# reach this
def test_quit_on_conditiion(self):
def quit(reactor):
reactor.quit()
# given
gobject.timeout_add(1, quit, self.sut)
# when
self.sut.run()
# then
# reach this
def test_exception_break_reactor_and_re_raises_exception(self):
def raise_exception():
raise go2.CancelException()
# given
self.sut.timeout_add(1, raise_exception)
# when/then
with self.assertRaises(go2.CancelException):
self.sut.run()
class Test_PathBuffer(TestCase):
def setUp(self):
self.path = '/usr/share'
def test_add_level0(self):
# given
sut = go2.PathBuffer(empty_stub())
# when
sut.add(go2.PathItem(self.path, 0))
# then
self.assertIn(self.path, sut.groups[0])
def test_add_level0_ignore_repeat(self):
# given
sut = go2.PathBuffer(empty_stub())
# when
sut.add(go2.PathItem(self.path, 0))
sut.add(go2.PathItem(self.path, 0))
# then
self.assertEquals(len(sut.groups[0]), 1)
def test_add_level0_goes_to_menu(self):
# given
menu = empty_mock()
expect_call(menu.add_entry)
sut = go2.PathBuffer(menu)
# when
sut.add(go2.PathItem(self.path, 0))
# then
menu.assert_that_is_satisfied()
def test_add_level_not0_does_not_go_to_menu(self):
# given
menu = empty_mock()
sut = go2.PathBuffer(menu)
# when
sut.add(go2.PathItem(self.path, 1))
# then
menu.assert_that_is_satisfied()
def test_add_alternate(self):
# given
menu = empty_mock()
expect_call(menu.next_group)
expect_call(menu.add_entry).with_args(self.path)
sut = go2.PathBuffer(menu)
# when
sut.add(go2.PathItem(self.path, 1))
sut.flush_alternate()
# then
menu.assert_that_is_satisfied()
def test_add_filtered(self):
# given
menu = empty_mock()
sut = go2.PathBuffer(menu)
sut.add_filter(foo_filter)
# when
sut.add(go2.PathItem('foo', 0))
# then
menu.assert_that_is_satisfied()
class PathFileHelper(StubFS):
def PathFileStore_with_content(self, store, more=''):
self.fs.createfile(store, u'''\
1: /home/user/lab
2: /home/user/lab/media
''' + more)
return go2.PathFileStore(store)
class Test_PathFileStore(TestCase, PathFileHelper):
def setUp(self):
StubFS.setUp(self)
self.store_fpath = '/paths'
def assert_visits(self, store_path, path, visits):
self.assertIn("{0}:{1}\n".format(visits, path),
self.fs.getcontents(self.store_fpath))
def store_create_and_insert(self, store_path, path, size=None, visits=None):
sut = self.PathFileStore_with_content(store_path)
sut.add_visit(path).save()
if size:
self.assertEquals(len(sut.data), size)
if visits:
self.assert_visits(store_path, path, visits)
return sut
def test_load_ok(self):
sut = self.PathFileStore_with_content(store=self.store_fpath)
self.assertItemsEqual(
sut.data.keys(),
[u'/home/user/lab', u'/home/user/lab/media'])
def test_load_missing(self):
with self.assertRaises(ResourceNotFoundError):
go2.PathFileStore('missing')
def test_insert_new(self):
self.store_create_and_insert(self.store_fpath, u'/XXX', 3, 1)
def test_insert_old_most_visited(self):
path = u'/home/user/lab/media'
sut = self.store_create_and_insert(
self.store_fpath, path, size=2, visits=3)
self.assertEquals(list(sut)[0], path)
def test_insert_old_less_visited(self):
path = u'/home/user/lab'
sut = self.store_create_and_insert(
self.store_fpath, path, size=2, visits=2)
sut.add_visit(path).save()
self.assert_visits(self.store_fpath, path, 3)
self.assertEquals(list(sut)[0], path)
def test_deprecated_file_format(self):
# given
self.fs.createfile(self.store_fpath, u'''\
/home/user/lab
/home/user/lab/media
''')
# when
go2.PathFileStore(self.store_fpath).save()
# then
self.assertItemsEqual(
self.fs.getcontents(self.store_fpath).split(),
u'''\
1:/home/user/lab
1:/home/user/lab/media
'''.split())
def test_wrong_file_encoding_lines_are_pruned(self):
# given
go2.config.encoding = 'utf-8'
self.fs.createfile(self.store_fpath, u'''\
1:/foo
1:/home/ñandú
'''.encode('latin1'))
# when
go2.PathFileStore(self.store_fpath).save()
# then
self.assertEquals(
self.fs.getcontents(self.store_fpath).strip(),
"1:/foo")
class Test_from_file_provider(TestCase, PathFileHelper):
def setUp(self):
StubFS.setUp(self)
def test_search_existing(self):
# given
go2.config.matcher = go2.PathMatcher(['la'])
store = self.PathFileStore_with_content('paths')
# whem
paths = list(go2.from_file_provider(store))
# then
self.assertIn(go2.PathItem(u'/home/user/lab'), paths)
def test_search_missing(self):
"from_file_provider remove checked missing paths"
sut = self.PathFileStore_with_content('paths')
go2.config.pattern = ['med']
paths = list(go2.from_file_provider(sut))
self.assertFalse(paths)
class Test_Menu(TestCase):
def setUp(self):
self.reactor_spy = empty_spy()
self.fd_stub = empty_stub()
def new_menu(self, **kargs):
params = dict(reactor=self.reactor_spy, out=self.fd_stub)
params.update(kargs)
return go2.Menu(**params)
def test_add_entry(self):
sut = self.new_menu()
sut.add_entry('foo')
self.assertIn('foo', sut.entries)
def test_FullSinkException(self):
# given
sut = self.new_menu(max_size=1)
# when/then
sut.add_entry('foo')
with self.assertRaises(go2.Menu.FullSinkException):
sut.add_entry('bar')
self.assert_(sut.is_full)
def test_print_ENTER_for_first(self):
# given
fd = empty_spy()
sut = self.new_menu(out=fd)
# when
sut.add_entry('foo')
# then
assert_that_was_called(fd.write).with_args('a: foo')
def test_print_second(self):
# given
fd = empty_spy()
sut = self.new_menu(out=fd)
# when
sut.add_entry('foo')
sut.add_entry('bar')
# then
assert_that_was_called(fd.write).with_args(ANY_ARG)
assert_that_was_called(fd.write).with_args('b: bar')
def test_select_first_with_empty_menu(self):
# given
sut = self.new_menu()
# when/then
with self.assertRaises(go2.Sink.InvalidChoiceException):
sut.perform_choice(0)
def test_valid_key(self):
# given
sut = self.new_menu()
sut.add_entry('foo')
# when
sut.perform_choice(0)
# then
self.assertEquals(sut.target, 'foo')
assert_that_was_called(self.reactor_spy.quit)
def test_wrong_key_raises_invalid(self):
# given
sut = self.new_menu()
sut.add_entry('foo')
# when/then
with self.assertRaises(go2.Sink.InvalidChoiceException):
sut.perform_choice(1)
def FIXME_test_groups(self):
pass
class Test_UserChoiceHanlder(TestCase):
def input_stub(self, key):
retval = empty_stub()
when(retval.read).then_return(key)
return retval
def test_ESC_quit_reactor(self):
# given
sut = go2.UserInputHandler()
fd = self.input_stub(chr(go2.ESC))
# when/then
with self.assertRaises(go2.CancelException):
sut(fd)
# then
# assert_that_was_called(self.reactor_spy.quit)
class TestIgnore(TestCase):
def create_manager(self, content):
return go2.IgnoreManager(content)
def test_ignored_path_with_1_pattern(self):
sut = self.create_manager("foo")
self.assert_(
sut.is_ignored("/home/user/repo/foo"))
def test_ignored_path_with_2_patterns(self):
# given
sut = self.create_manager('''
foo
bar''')
self.assert_(sut.is_ignored("/home/foo"))
self.assert_(sut.is_ignored("/home/bar"))
def test_not_matching_path(self):
sut = self.create_manager("foo")
self.assertFalse(
sut.is_ignored('/usr/share'))
def test_path_match_in_any_place(self):
sut = self.create_manager("foo")
self.assert_(
sut.is_ignored('/usr/foo/share'))
def test_path_not_match_if_not_full_name(self):
sut = self.create_manager("foo")
self.assertFalse(
sut.is_ignored('/usr/xxfoo'))
self.assertFalse(
sut.is_ignored('/usr/fooxx'))
def test_absolute_dir(self):
sut = self.create_manager('/usr/local/*')
self.assert_(sut.is_ignored('/usr/local/foo'))
self.assert_(sut.is_ignored('/usr/local'))
def test_from_file__missing_file(self):
# given
fs = MemoryFS()
config = go2.get_config()
config.fs = fs
go2.config = config
# when
with self.assertRaises(ResourceNotFoundError):
go2.IgnoreManager.from_file('ignore')
def test_from_file(self):
# given
fs = MemoryFS()
fs.createfile('ignore', 'foo')
config = go2.get_config()
config.fs = fs
go2.config = config
# when
sut = go2.IgnoreManager.from_file('ignore')
# then
self.assert_(sut.is_ignored('/home/foo'))
go2-1.20121210/test/integration_tests.py 0000644 0001750 0001750 00000007011 12061444105 016740 0 ustar piotr piotr # -*- mode:python; coding:utf-8 -*-
import os
import time
import multiprocessing as mp
import socket
from unittest import TestCase
from pyDoubles.framework import empty_spy, empty_stub, \
when, assert_that_was_called
import go2
def foo_filter(value):
return value == 'foo'
# providers -> queue.put -> QueueReactor -> PathStorage.add -> menu.add_entry
# PathStorage.flush -> menu.add_entry
# key -> Reactor -> -> menu.input_handler
def pipe():
a, b = socket.socketpair()
read_end = a.makefile()
write_end = b.makefile('w', 0)
return read_end, write_end
class Test_Search(TestCase):
def setUp(self):
go2.config = go2.get_config()
def create_scenario(self, menu_size=26):
self.queue = mp.Queue()
self.read_end, self.write_end = pipe()
self.out = empty_spy()
self.reactor = go2.QueueReactor()
self.menu = go2.Menu(self.reactor, self.out, max_size=menu_size)
self.input_handler = go2.UserChoiceHandler(self.menu)
self.path_buffer = go2.PathBuffer(self.menu)
self.reactor.queue_add_watch(
self.queue,
go2.QueueExtractor(self.path_buffer.add))
def register_input(self):
self.reactor.io_add_watch(self.read_end, self.input_handler)
def put(self, path):
self.queue.put(go2.PathItem(path))
def test_choice_first_entry_with_previous_ignored(self):
# given
self.create_scenario()
self.register_input()
self.path_buffer.add_filter(foo_filter)
# when
self.put('foo')
self.put('bar')
self.write_end.write('a')
self.reactor.run()
# then
self.assertEquals(self.path_buffer.groups[0], ['bar'])
self.assertEquals(self.menu.entries, ['bar'])
assert_that_was_called(self.out.write).with_args('a: bar')
self.assertEquals(self.menu.target, 'bar')
def test_full_menu(self):
# given
self.create_scenario(menu_size=1)
self.register_input()
# when
self.put('foo')
self.put('bar')
self.reactor.process_pending()
# then
assert_that_was_called(self.out.write).with_args('a: foo')
def atest_list_mode(self):
def raw_formatter(index, path, suffix):
return path + '\n'
paths = ['foo', 'bar']
# given
pool = empty_stub()
when(pool.has_tasks).then_return(False)
self.create_scenario()
self.reactor.timeout_add(250, EndChecker(
pool, self.path_buffer, self.menu))
self.menu.formatter = raw_formatter
# when
for i in paths:
self.put(i)
self.reactor.run()
# then
for i in paths:
assert_that_was_called(self.out.write).with_args(i + '\n')
def test_unicode(self):
os.path.exists('ñandáéÃóú')
# given: un pool con tareas
# when: el pool queda ocioso
# then: el menu espera una elección
# given: menu con una sola entrada
# when: todas las tareas han terminado
# then: elegir la única entrada sin preguntar
# given: un reactor
# when: el reactor acaba
# then: enviar las rutas alternativas al menú
# given: un reactor y un menú
# when: el menú se llena
# then: el menu espera una elección
# condiciones de fin
# - ESC o CTRL-C - CancelException
# - Ninguna coincidencia
# - Solo una coincidencia
# - Elegir entre varias coincidencias
# - Menu lleno, elegir
go2-1.20121210/test/unit/ 0000755 0001750 0001750 00000000000 12061444105 013601 5 ustar piotr piotr go2-1.20121210/test/unit/go2setup.test 0000755 0001750 0001750 00000002206 12061444105 016255 0 ustar piotr piotr #!/usr/bin/atheist
# -*- mode:python; coding:utf-8 -*-
import sys
import os
import unittest2 as unittest
import fs
from fs.memoryfs import MemoryFS
from pyDoubles.framework import *
sys.path.append('$basedir')
import go2
def create_basic_dirs(fs):
home = go2.USERDIR
fs.makedir(home, recursive=True)
BASHRC = os.path.join(go2.USERDIR, '.bashrc')
class Test_setup(unittest.TestCase):
def setUp(self):
self.fs = MemoryFS()
create_basic_dirs(self.fs)
config = go2.get_config([])
config.fs = self.fs
go2.config = config
go2.ResourceNotFoundError = fs.ResourceNotFoundError
os.chdir(go2.USERDIR)
def test_bashrc_missing(self):
go2.go2setup()
self.assert_(go2.ACTIVATION_CMD in self.fs.getcontents(BASHRC))
def test_bashrc_exists(self):
self.fs.setcontents(BASHRC, "something")
self.assertEquals(go2.go2setup(), None)
self.assert_(go2.ACTIVATION_CMD in self.fs.getcontents(BASHRC))
def test_already_setup(self):
self.fs.setcontents(BASHRC, go2.ACTIVATION_CMD)
self.assertEquals(go2.go2setup(), 1)
UnitTestCase()
go2-1.20121210/test/unit/chdir-decorator.test 0000755 0001750 0001750 00000003607 12061444105 017564 0 ustar piotr piotr #!/usr/bin/atheist
# -*- mode:python; coding:utf-8 -*-
import sys
import os
import unittest2 as unittest
from fs.memoryfs import MemoryFS
from pyDoubles.framework import *
sys.path.append('$basedir')
import go2
def create_basic_dirs(fs):
home = go2.USERDIR
fs.makedir(home, recursive=True)
class Test_chdir_decorator(unittest.TestCase):
def setUp(self):
self.fs = MemoryFS()
create_basic_dirs(self.fs)
config = go2.get_config([])
config.fs = self.fs
go2.config = config
os.chdir(go2.USERDIR)
def tearDown(self):
pass
def mkdir(self, name):
path = os.path.join(go2.USERDIR, name)
self.fs.makedir(path, name)
return path
def test_no_pattern(self):
spy = empty_spy()
go2.save_target = spy.save_target
go2.chdir_decorator()
assert_that_was_called(spy.save_target).with_args(go2.USERDIR)
def test_no_hyphen(self):
go2.config.pattern = ['-']
spy = empty_spy()
go2.save_target = spy.save_target
go2.chdir_decorator()
assert_that_was_called(spy.save_target).with_args('-')
def mock_dir_history_save(self, relpath):
path = self.mkdir(relpath)
mock = empty_mock()
go2.save_in_history = mock.save_in_history
go2.save_target = mock.save_target
expect_call(mock.save_in_history).with_args(path)
expect_call(mock.save_target).with_args(path)
return mock
def test_existing_dir_1_word(self):
go2.config.pattern = ['foo']
mock = self.mock_dir_history_save('foo')
go2.chdir_decorator()
mock.assert_that_is_satisfied()
def test_existing_dir_2_words(self):
go2.config.pattern = ['foo bar']
mock = self.mock_dir_history_save('foo bar')
go2.chdir_decorator()
mock.assert_that_is_satisfied()
UnitTestCase()
go2-1.20121210/test/unit/matchers.test 0000755 0001750 0001750 00000010341 12061444105 016312 0 ustar piotr piotr #!/usr/bin/atheist
# -*- mode:python; coding:utf-8 -*-
import sys
import os
import unittest2 as unittest
from fs.memoryfs import MemoryFS
from pyDoubles.framework import *
sys.path.append('$basedir')
import go2
from go2 import MatchLevel as Level
class Test_match(unittest.TestCase):
# def setUp(self):
# self.config = go2.get_config([])
# go2.config = self.config
def assert_match(self, pattern, result, paths):
matcher = go2.PathMatcher(pattern)
for p in paths:
self.assertEquals(matcher.match(p), result)
def test_single(self):
self.assert_match(
['foo'], Level.START,
['/foo',
'/buzz/foobar',
'/foo/bar/foofoo',
])
def test_single_i18n(self):
self.assert_match(
[u'ñandú'], Level.START,
[u'/ñandú',
u'/buzz/ñandúmar',
])
def test_single_negative(self):
self.assert_match(
['foo'], Level.NO_MATCH,
['/foa',
'/buzz/foo/bar',
'/foo/bar/but',
])
def test_single_ignorecase(self):
self.assert_match(
['foo'], Level.START_IGNORECASE,
['/Foo',
'/buzz/fOo',
'/foo/bar/FOO',
])
def disable_test_single_ignorecase_i18n(self):
self.assert_match(
['ñandú'], Level.START_IGNORECASE,
['/Ñandúfoo',
'/ñandÚbar',
'/buzz/ñandúmar',
])
def test_single_contain(self):
self.assert_match(
['foo'], Level.CONTAIN,
['/bafoo',
'/buzz/tafoobar',
'/foo/bar/fofoofoOO',
])
def test_single_contain_insensitve(self):
self.assert_match(
['FOO'], Level.CONTAIN_IGNORECASE,
['/bafoo',
'/buzz/tafOobar',
'/foo/bar/fofoofOO',
])
def test_multi(self):
self.assert_match(
['usr', 'loc'], Level.START,
['/usr/share/local',
'/mnt/usr-doc/lib/loc',
])
def test_multi_negative(self):
self.assert_match(
['usr', 'loc'], Level.NO_MATCH,
['/usr/share/aloal',
'/bin/local',
'/usr/share/doc',
])
def test_multi_ignorecase(self):
self.assert_match(
['usr', 'loc'], Level.START_IGNORECASE,
['/usr/share/LOCAL',
# '/mnt/Usr-doc/lib/loc',
])
def test_multi_contain(self):
self.assert_match(
['sr', 'oca'], Level.CONTAIN,
['/usr/share/local',
'/mnt/usr-doc/lib/local',
])
class Test_high_pattern(unittest.TestCase):
def setUp(self):
self.config = go2.get_config([])
self.config.before = '['
self.config.after = ']'
go2.config = self.config
def test_single(self):
matcher = go2.PathMatcher(['sto'])
self.assertEquals(matcher.high_pattern('/usr/local/stow'),
'/usr/local/[sto]w')
self.assertEquals(matcher.high_pattern('/usr/local/stow-1/stow-2'),
'/usr/local/stow-1/[sto]w-2')
def test_single_ignorecase(self):
matcher = go2.PathMatcher(['STO'])
self.assertEquals(matcher.high_pattern('/usr/mostow/local/stow'),
'/usr/mostow/local/[sto]w')
matcher = go2.PathMatcher(['sto'])
self.assertEquals(matcher.high_pattern('/usr/local/STOW'),
'/usr/local/[STO]W')
def test_single_contains(self):
matcher = go2.PathMatcher(['tow'])
self.assertEquals(matcher.high_pattern('/usr/mostow/local/stow'),
'/usr/mostow/local/s[tow]')
def test_multi(self):
matcher = go2.PathMatcher(['shar', 'sto'])
self.assertEquals(matcher.high_pattern('/usr/share/local/stow'),
'/usr/[shar]e/local/[sto]w')
def test_multi_contains(self):
matcher = go2.PathMatcher(['hare', 'tow'])
self.assertEquals(matcher.high_pattern('/usr/shares/local/stows'),
'/usr/s[hare]s/local/s[tow]s')
UnitTestCase()
go2-1.20121210/AUTHORS 0000644 0001750 0001750 00000000051 12061444105 012707 0 ustar piotr piotr David Villa Alises
go2-1.20121210/BUGS 0000644 0001750 0001750 00000010607 12061444105 012332 0 ustar piotr piotr
Desde un directorio que ya no existe:
sh: 0: getcwd() failed: No such file or directory
sh: 0: getcwd() failed: No such file or directory
lsh: 0: getcwd() failed: No such file or directory
sTraceback (most recent call last):
File "/usr/lib/go2/go2.py", line 756, in
sys.exit(chdir_decorator())
File "/usr/lib/go2/go2.py", line 684, in chdir_decorator
target = os.path.abspath(params)
File "/usr/lib/python2.7/posixpath.py", line 347, in abspath
cwd = os.getcwd()
OSError: [Errno 2] No such file or directory
At press ENTER without matches:
Searching. Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 1090, in
sys.exit(Go2().main())
File "/usr/lib/go2/go2.py", line 908, in main
self.reactor.run()
File "/usr/lib/go2/go2.py", line 742, in run
raise self.exception
IndexError: list index out of range
-------------------------------------------------------
$ cd repos # repos existe
Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 942, in
sys.exit(chdir_decorator())
File "/usr/lib/go2/go2.py", line 872, in chdir_decorator
save_in_history(target)
File "/usr/lib/go2/go2.py", line 284, in save_in_history
PathFileStore(GO2HISTORY).insert_and_save(path)
File "/usr/lib/go2/go2.py", line 217, in init_
self.load()
File "/usr/lib/go2/go2.py", line 223, in load
self._load_file_lines(fd)
File "/usr/lib/go2/go2.py", line 242, in _load_file_lines
self.data[decode(path)] = int(visits)
File "/usr/lib/go2/go2.py", line 230, in decode
return path.strip().decode('utf8')
File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xf1 in position 64:
-------------------------------------------------------
$ cd not/existing/path
-------------------------------------------------------
$ go2 c++
-------------------------------------------------------
not/existing/path$ cd ..
Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 1075, in
sys.exit(chdir_decorator())
File "/usr/lib/go2/go2.py", line 988, in chdir_decorator
target = os.path.abspath(params)
File "/usr/lib/python2.7/posixpath.py", line 345, in abspath
cwd = os.getcwdu()
OSError: [Errno 2] No such file or directory
-------------------------------------------------------
$ cd /usr/share/texmf-texlive/tex/latex/arco/arco-listings.sty
Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 1058, in
sys.exit(create_directory_wizzard(target.args[0]))
File "/usr/lib/go2/go2.py", line 934, in
create_directory_wizzard
print "go2: '%s' does not exist.\n(c)ancel, (s)earch or (m)ake?
(C/s/m):" % target,
UnboundLocalError: local variable 'target' referenced before assignment
$ cd ../...
Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 1058, in
sys.exit(create_directory_wizzard(target.args[0]))
File "/usr/lib/go2/go2.py", line 934, in create_directory_wizzard
print "go2: '%s' does not exist.\n(c)ancel, (s)earch or (m)ake? (C/s/m):" % target,
UnboundLocalError: local variable 'target' referenced before assignment
$ cd /usr/share/texmf-texlive/tex/latex/arco/arco-listings.sty
Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 1058, in
sys.exit(create_directory_wizzard(target.args[0]))
File "/usr/lib/go2/go2.py", line 934, in
create_directory_wizzard
print "go2: '%s' does not exist.\n(c)ancel, (s)earch or (m)ake?
(C/s/m):" % target,
UnboundLocalError: local variable 'target' referenced before assignment
$ cd ../...
Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 1058, in
sys.exit(create_directory_wizzard(target.args[0]))
File "/usr/lib/go2/go2.py", line 934, in create_directory_wizzard
print "go2: '%s' does not exist.\n(c)ancel, (s)earch or (m)ake? (C/s/m):" % target,
UnboundLocalError: local variable 'target' referenced before assignment
--
$ cd ../some
Traceback (most recent call last):
File "/usr/lib/go2/go2.py", line 1100, in
sys.exit(create_directory_wizzard(target.args[0]))
File "/usr/lib/go2/go2.py", line 972, in create_directory_wizzard
print "go2: '%s' does not exist.\n(c)ancel, (s)earch or (m)ake? (C/s/m):" % target,
UnboundLocalError: local variable 'target' referenced before assignment
go2-1.20121210/go2.sh 0000755 0001750 0001750 00000000226 12061444105 012671 0 ustar piotr piotr #!/bin/sh --
GO2TMP=~/.go2/tmp
go2 () {
/usr/lib/go2/go2.py $*;
if [ -e $GO2TMP ]; then
cd "$(cat $GO2TMP)"
rm $GO2TMP > /dev/null
fi
}
go2-1.20121210/COPYING 0000644 0001750 0001750 00000043104 12061444105 012700 0 ustar piotr piotr GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
go2-1.20121210/go2 0000755 0001750 0001750 00000000052 12061444105 012255 0 ustar piotr piotr #!/bin/sh --
/usr/lib/go2/go2.py --setup
go2-1.20121210/INSTALL 0000644 0001750 0001750 00000000176 12061444105 012700 0 ustar piotr piotr
To use go2 you must execute:
go2 --setup
or include next line into your .bashrc file as well:
source /usr/lib/go2/go2.sh