go2-1.20121210/0000755000175000017500000000000012061444105011643 5ustar piotrpiotrgo2-1.20121210/Changelog0000644000175000017500000000322212061444105013454 0ustar piotrpiotr-*- 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.py0000755000175000017500000001126112061444105013173 0ustar piotrpiotr#!/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/TODO0000644000175000017500000000217212061444105012335 0ustar piotrpiotr - 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.glade20000644000175000017500000004777012061444105013431 0ustar piotrpiotr 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.py0000755000175000017500000007051312061444105012715 0ustar piotrpiotr#!/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.py0000755000175000017500000000145512061444105013137 0ustar piotrpiotr#!/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.mk0000755000175000017500000000076412061444105014221 0ustar piotrpiotr#!/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.rst0000644000175000017500000000270712061444105013453 0ustar piotrpiotrRelease 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.rst0000644000175000017500000000367112061444105013073 0ustar piotrpiotr=== 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/MANUAL0000644000175000017500000000134212061444105012543 0ustar piotrpiotr 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.py0000755000175000017500000000103312061444105013355 0ustar piotrpiotr#!/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/0000755000175000017500000000000012061444105012622 5ustar piotrpiotrgo2-1.20121210/test/system/0000755000175000017500000000000012061444105014146 5ustar piotrpiotrgo2-1.20121210/test/system/tree_killed.test0000755000175000017500000000034612061444105017340 0ustar piotrpiotr#!/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.test0000755000175000017500000000032412061444105020557 0ustar piotrpiotr#!/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.py0000644000175000017500000002660212061444105015403 0ustar piotrpiotr#!/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.py0000644000175000017500000000701112061444105016740 0ustar piotrpiotr# -*- 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/0000755000175000017500000000000012061444105013601 5ustar piotrpiotrgo2-1.20121210/test/unit/go2setup.test0000755000175000017500000000220612061444105016255 0ustar piotrpiotr#!/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.test0000755000175000017500000000360712061444105017564 0ustar piotrpiotr#!/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.test0000755000175000017500000001034112061444105016312 0ustar piotrpiotr#!/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/AUTHORS0000644000175000017500000000005112061444105012707 0ustar piotrpiotrDavid Villa Alises go2-1.20121210/BUGS0000644000175000017500000001060712061444105012332 0ustar piotrpiotr 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.sh0000755000175000017500000000022612061444105012671 0ustar piotrpiotr#!/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/COPYING0000644000175000017500000004310412061444105012700 0ustar piotrpiotr 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/go20000755000175000017500000000005212061444105012255 0ustar piotrpiotr#!/bin/sh -- /usr/lib/go2/go2.py --setup go2-1.20121210/INSTALL0000644000175000017500000000017612061444105012700 0ustar piotrpiotr To use go2 you must execute: go2 --setup or include next line into your .bashrc file as well: source /usr/lib/go2/go2.sh