diff options
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | .gitmodules | 4 | ||||
-rw-r--r-- | .htaccess | 6 | ||||
-rw-r--r-- | GNUmakefile | 14 | ||||
-rw-r--r-- | INSTALL | 117 | ||||
-rw-r--r-- | README | 63 | ||||
-rw-r--r-- | __init__.py | 0 | ||||
m--------- | dico | 0 | ||||
-rw-r--r-- | dicoclient/__init__.py | 19 | ||||
-rw-r--r-- | dicoclient/dicoclient.py | 416 | ||||
-rw-r--r-- | dicoclient/dicoshell.py | 332 | ||||
-rw-r--r-- | dicoclient/setup.py | 39 | ||||
-rw-r--r-- | dicoweb.wsgi | 27 | ||||
-rw-r--r-- | docker-compose.override.yml | 24 | ||||
-rw-r--r-- | docker/Dockerfile | 9 | ||||
-rw-r--r-- | docker/gcide.css (renamed from static/gcide.css) | 0 | ||||
-rw-r--r-- | docker/settings_docker.py | 255 | ||||
-rw-r--r-- | docker/templates/404.html (renamed from templates/404.html) | 0 | ||||
-rw-r--r-- | docker/templates/500.html (renamed from templates/500.html) | 0 | ||||
-rw-r--r-- | docker/templates/about.html (renamed from templates/about.html) | 0 | ||||
-rw-r--r-- | docker/templates/base.html (renamed from templates/base.html) | 29 | ||||
-rw-r--r-- | docker/templates/download.html (renamed from templates/download.html) | 0 | ||||
-rw-r--r-- | docker/templates/index.html (renamed from templates/index.html) | 28 | ||||
-rw-r--r-- | docker/templates/info.html | 38 | ||||
-rw-r--r-- | docker/templates/license.html (renamed from templates/license.html) | 0 | ||||
-rw-r--r-- | docker/templates/opensearch.xml (renamed from templates/opensearch.xml) | 0 | ||||
-rw-r--r-- | docker/urls.py (renamed from urls.py) | 22 | ||||
-rw-r--r-- | docker/views.py (renamed from views.py) | 127 | ||||
-rw-r--r-- | docker/withsetting.py (renamed from templatetags/withsetting.py) | 2 | ||||
-rw-r--r-- | dummy_translations.py | 40 | ||||
-rw-r--r-- | gcide.wsgi | 25 | ||||
-rw-r--r-- | index.html | 15 | ||||
-rw-r--r-- | manage.py | 30 | ||||
-rw-r--r-- | settings-sample.py | 171 | ||||
-rw-r--r-- | static/18px-Bulbgraph.png | bin | 1210 -> 0 bytes | |||
-rw-r--r-- | static/dicoweb.css | 156 | ||||
-rw-r--r-- | static/dicoweb.js | 98 | ||||
-rw-r--r-- | static/gnu-head-sm.jpg | bin | 5286 -> 0 bytes | |||
-rw-r--r-- | templatetags/__init__.py | 0 | ||||
-rw-r--r-- | templatetags/dictlookup.py | 28 | ||||
-rw-r--r-- | templatetags/macros.py | 160 |
41 files changed, 513 insertions, 1787 deletions
@@ -1,4 +1,4 @@ -.emacs.desktop -settings.py +.emacs* +.env *~ -tmp/ +\#* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9529e2c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "dico"] + path = dico + url = git://git.gnu.org.ua/dico.git + branch = docker diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 2df5844..0000000 --- a/.htaccess +++ /dev/null @@ -1,6 +0,0 @@ -Options All -Indexes -<Files ~ "^\.emacs"> - Order allow,deny - Deny from all - Satisfy All -</Files> diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..5726476 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,14 @@ +command=docker compose -p gcideweb -f dico/docker/docker-compose.yml -f docker-compose.override.yml --env-file .env --profile=lighttpd +define goal = +$(1): + $$(command) $(1)$(if $(filter-out up, $(1)),, -d) +endef + +$(foreach verb,build config up down ps, $(eval $(call goal, $(verb)))) + +bootstrap: + git submodule init + git submodule update + cd dico && ./bootstrap --force --skip-po + + diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 3a59d38..0000000 --- a/INSTALL +++ /dev/null @@ -1,117 +0,0 @@ -GNU Dico - Dicoweb INSTALL -See end of file for copying conditions. - -* Dicoweb requirements -====================== - -- Django -- a Python Web framework (http://www.djangoproject.com/) -- wikitrans -- a wiki translator (https://pypi.org/project/wikitrans/) -- python-memcached -- Pure python memcached client - (https://pypi.org/project/python-memcached) - -* Installation instructions -=========================== - -The instructions below assume dicoweb sources are located in -/var/www/dicoweb. - -1. Create the virtual environment directory in /var/www/dicoweb. -Assuming the directory will be named 'venv': - - $ cd /var/www/dicoweb - $ virtualenv venv - -2. Activate the environment: - - $ . venv/bin/activate - -3. Install the prerequisites - - $ pip install -r requirements.txt - -4. Deactivate the environment - - $ deactivate - -5. Rename 'settings-sample.py' to 'settings.py' and edit your -local Dicoweb site configuration. - -6. Enable the following Apache modules: mod_alias, mod_env, mod_wsgi. - -The exact instructions depend on your Apache configuration layout. In -general, they boil down to adding the following three statements to -the Apache configuration: - - LoadModule alias_module modules/mod_alias.so - LoadModule env_module modules/mod_env.so - LoadModule wsgi_module modules/mod_wsgi.so - -7. Configure Apache virtual host. The minimal configuration is as -follows: - - <VirtualHost *:80> - ServerName dict.example.org - DocumentRoot /var/www/dicoweb - - # Ensure proper encoding (for Python 3) - SetEnv LC_ALL en_US.utf8 - - # Configure virtual environment. - # (If not using virtual environment, omit the WSGIDaemonProcess and - # WSGIProcessGroup statements). - - # Make sure to edit the statement below to match to the actual - # path to the site-packages directory. - WSGIDaemonProcess dicoweb python-path=/var/www/dicoweb/venv/lib/python3.5/site-packages - WSGIProcessGroup dicoweb - - # Start up the module. - WSGIScriptAlias / /var/www/dicoweb/wsgi.py - # Provide access to the static files. - Alias /static /var/www/dicoweb/static - <Directory "/var/www/dicoweb/"> - AllowOverride All - Options None - Require all granted - </Directory> - </VirtualHost> - -For a general discussion of deployment of the Django applications, -please see https://docs.djangoproject.com/en/dev/howto/deployment. - -** The development/test server ------------------------------- - -To start the stand-alone development server, change to /var/www/dicoweb -and run the command `python manage.py runserver'. You will see -the following output: - - Validating models... - 0 errors found. - - Django version 1.11.5, using settings 'dicoweb.settings' - Development server is running at http://127.0.0.1:8000/ - Quit the server with CONTROL-C. - -Use your web browser to query the displayed URL. - -* Copyright information: - -Copyright (C) 2008-2018 Wojciech Polak - - Permission is granted to anyone to make or distribute verbatim copies - of this document as received, in any medium, provided that the - copyright notice and this permission notice are preserved, - thus giving the recipient permission to redistribute in turn. - - Permission is granted to distribute modified versions - of this document, or of portions of it, - under the above conditions, provided also that they - carry prominent notices stating who last changed them. - - -Local Variables: -mode: outline -paragraph-separate: "[ ]*$" -version-control: never -End: @@ -0,0 +1,63 @@ +* Overview + +This file describes docker-based setup for the gcide web site. + +* Installation + +1. make bootstrap + +This will prepare GNU dico sources for building. + +2. make build + +This will build the docker images. + +3. Edit the .env file. It should contain the following variables: + + DICOWEB_NAME + Domain name of the server. + DICOWEB_PORT + Port the dicoweb container is listening on. + DICOWEB_ADMIN + Email address of the administrator. + DICT_SERVER + IP address or hostname of the dict server to use. The server must + offer the gcide database. + PIES_SYSLOG_SERVER + IP address and port number of the syslog server. + +4. Configure your syslog server to receive and route log messages from + gcideweb. + +5. docker compose up -d + +* Maintenance + +Most commands are performed by invoking `make' with the corresponding +command verb: + +** make config + + Inspect the resulting docker-compose.yml. + +** make build + + Build all images. + +** make up + + Start up the services. + +** make down + + Pull the services down. + +** make ps + + List running services. + +Local Variables: +mode: outline +paragraph-separate: "[ ]*$" +version-control: never +End: diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/__init__.py +++ /dev/null diff --git a/dico b/dico new file mode 160000 +Subproject b6bce074e9bc85002b55d98397d4d3a6a7b540c diff --git a/dicoclient/__init__.py b/dicoclient/__init__.py deleted file mode 100644 index 9fcbe2b..0000000 --- a/dicoclient/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file is part of GNU Dico. -# Copyright (C) 2008-2009, 2012, 2013 Wojciech Polak -# -# GNU Dico 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 3, or (at your option) -# any later version. -# -# GNU Dico 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 GNU Dico. If not, see <http://www.gnu.org/licenses/>. - -__all__ = ["dicoclient"] - -from .dicoclient import * diff --git a/dicoclient/dicoclient.py b/dicoclient/dicoclient.py deleted file mode 100644 index f49c582..0000000 --- a/dicoclient/dicoclient.py +++ /dev/null @@ -1,416 +0,0 @@ -# -*- coding: utf-8 -*- -# This file is part of GNU Dico. -# Copyright (C) 2008-2010, 2012, 2013, 2015 Wojciech Polak -# -# GNU Dico 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 3, or (at your option) -# any later version. -# -# GNU Dico 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 GNU Dico. If not, see <http://www.gnu.org/licenses/>. - -import re -import socket -import base64 -import quopri - -try: - from django.utils.six.moves import range, reduce -except ImportError: - from six.moves import range, reduce - -__version__ = '1.1' - - -class DicoClient: - - """GNU Dico client module written in Python - (a part of GNU Dico software)""" - - host = None - levenshtein_distance = 0 - mime = False - - verbose = 0 - timeout = 10 - transcript = False - __connected = False - - def __init__(self, host=None): - if host != None: - self.host = host - - def __del__(self): - if self.__connected: - self.socket.close() - - def open(self, host=None, port=2628): - """Open the connection to the DICT server.""" - if host != None: - self.host = host - if self.verbose: - self.__debug('Connecting to %s:%d' % (self.host, port)) - socket.setdefaulttimeout(int(self.timeout)) - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.connect((self.host, port)) - self.__connected = True - self.fd = self.socket.makefile() - - self.server_banner = self.__read()[0] - capas, msgid = re.search('<(.*)> (<.*>)$', - self.server_banner).groups() - self.server_capas = capas.split('.') - self.server_msgid = msgid - - self.__send_client() - self.__read() - - def close(self): - """Close the connection.""" - if self.__connected: - self.__send_quit() - self.__read() - self.socket.close() - self.__connected = False - - def option(self, name, *args): - """Send the OPTION command.""" - if self.__connected: - self.__send('OPTION %s%s' % - (name, reduce(lambda x, y: str(x) + ' ' + str(y), - args, ''))) - res = self.__read() - code, msg = res[0].split(' ', 1) - if int(code) == 250: - if name.lower() == 'mime': - self.mime = True - return True - return False - - def __get_mime(self, lines): - cnt = 0 - mimeinfo = {} - firstline = lines[0].lower() - if firstline.find ('content-type:') != -1 or \ - firstline.find('content-transfer-encoding:') != -1: - cnt += 1 - for line in lines: - if line == '': - break - t = line.split(':', 1) - mimeinfo[t[0].lower()] = t[1].strip() - cnt += 1 - for i in range(0, cnt): - lines.pop(0) - else: - lines.pop(0) - if 'content-transfer-encoding' in mimeinfo: - if mimeinfo['content-transfer-encoding'].lower() == 'base64': - buf = base64.decodestring('\n'.join(lines)) - lines[:] = (buf.split('\r\n')) - if lines[-1] == '': - del lines[-1] - del mimeinfo['content-transfer-encoding'] - elif mimeinfo['content-transfer-encoding'].lower() == 'quoted-printable': - buf = quopri.decodestring('\n'.join(lines)) - try: - lines[:] = buf.split('\r\n') - except TypeError: - lines[:] = buf.decode('utf-8').split('\r\n') - if lines[-1] == '': - del lines[-1] - del mimeinfo['content-transfer-encoding'] - return mimeinfo - - def __get_rs(self, line): - code, text = line.split(' ', 1) - code = int(code) - return code, text - - def __read(self): - if not self.__connected: - raise DicoNotConnectedError('Not connected') - buf = [] - line = self.__readline() - if len(line) == 0: - raise DicoNotConnectedError('Not connected') - buf.append(line) - code, text = self.__get_rs(line) - - if code >= 100 and code < 200: - if code == 150: - while True: - rs = self.__readline() - code, text = self.__get_rs(rs) - if code != 151: - buf.append(rs) - break - buf.append([rs, self.__readblock()]) - else: - buf.append(self.__readblock()) - buf.append(self.__readline()) - return buf - - def __readline(self): - line = self.fd.readline().rstrip() - if self.transcript: - self.__debug('S:%s' % line) - return line - - def __readblock(self): - buf = [] - while True: - line = self.__readline() - if line == '.': - break - buf.append(line) - return buf - - def __send(self, command): - if not self.__connected: - raise DicoNotConnectedError('Not connected') - cmd = command + "\r\n" - try: - self.socket.send(cmd) - except (UnicodeEncodeError, TypeError): - self.socket.send(cmd.encode('utf-8')) - if self.transcript: - self.__debug('C:%s' % command) - - def __send_client(self): - if self.verbose: - self.__debug('Sending client information') - self.__send('CLIENT "%s %s"' % ("GNU Dico (Python Edition)", - __version__)) - - def __send_quit(self): - if self.verbose: - self.__debug('Quitting') - self.__send('QUIT') - - def __send_show(self, what, arg=None): - if arg != None: - self.__send('SHOW %s "%s"' % (what, arg)) - else: - self.__send('SHOW %s' % what) - return self.__read() - - def __send_define(self, database, word): - if self.verbose: - self.__debug('Sending query for word "%s" in database "%s"' % - (word, database)) - self.__send('DEFINE "%s" "%s"' % (database, word)) - return self.__read() - - def __send_match(self, database, strategy, word): - if self.verbose: - self.__debug('Sending query to match word "%s" in database "%s", using "%s"' - % (word, database, strategy)) - self.__send('MATCH "%s" "%s" "%s"' % (database, strategy, word)) - return self.__read() - - def __send_xlev(self, distance): - self.__send('XLEV %u' % distance) - return self.__read() - - def show_databases(self): - """List all accessible databases.""" - if self.verbose: - self.__debug('Getting list of databases') - res = self.__send_show('DATABASES') - if self.mime: - mimeinfo = self.__get_mime(res[1]) - dbs_res = res[1:-1][0] - dbs = [] - for d in dbs_res: - short_name, full_name = d.split(' ', 1) - dbs.append([short_name, self.__unquote(full_name)]) - dct = { - 'count': len(dbs), - 'databases': dbs, - } - return dct - - def show_strategies(self): - """List available matching strategies.""" - if self.verbose: - self.__debug('Getting list of strategies') - res = self.__send_show('STRATEGIES') - if self.mime: - mimeinfo = self.__get_mime(res[1]) - sts_res = res[1:-1][0] - sts = [] - for s in sts_res: - short_name, full_name = s.split(' ', 1) - sts.append([short_name, self.__unquote(full_name)]) - dct = { - 'count': len(sts), - 'strategies': sts, - } - return dct - - def show_info(self, database): - """Provide information about the database.""" - res = self.__send_show("INFO", database) - code, msg = res[0].split(' ', 1) - if int(code) < 500: - if self.mime: - mimeinfo = self.__get_mime(res[1]) - dsc = res[1] - return {'desc': '\n'.join(dsc)} - else: - return {'error': code, 'msg': msg} - - def show_lang_db(self): - """Show databases with their language preferences.""" - res = self.__send_show('LANG DB') - code, msg = res[0].split(' ', 1) - if int(code) < 500: - if self.mime: - mimeinfo = self.__get_mime(res[1]) - dsc = res[1] - lang_src = {} - lang_dst = {} - for i in dsc: - pair = i.split(' ', 1)[1] - src, dst = pair.split(':', 1) - for j in src: - lang_src[src.strip()] = True - for j in dst: - lang_dst[dst.strip()] = True - return { - 'desc': '\n'.join(dsc), - 'lang_src': list(lang_src.keys()), - 'lang_dst': list(lang_dst.keys()), - } - else: - return {'error': code, 'msg': msg} - - def show_lang_pref(self): - """Show server language preferences.""" - res = self.__send_show('LANG PREF') - code, msg = res[0].split(' ', 1) - if int(code) < 500: - return {'msg': msg} - else: - return {'error': code, 'msg': msg} - - def show_server(self): - """Provide site-specific information.""" - res = self.__send_show('SERVER') - code, msg = res[0].split(' ', 1) - if int(code) < 500: - dsc = res[1] - return {'desc': '\n'.join(dsc)} - else: - return {'error': code, 'msg': msg} - - def define(self, database, word): - """Look up word in database.""" - database = database.replace('"', "\\\"") - word = word.replace('"', "\\\"") - res = self.__send_define(database, word) - code, msg = res[-1].split(' ', 1) - if int(code) < 500: - defs_res = res[1:-1] - defs = [] - rx = re.compile( - '^\d+ ("[^"]+"|\w+) ([a-zA-Z0-9_\-]+) ("[^"]*"|\w+)') - for i in defs_res: - term, db, db_fullname = rx.search(i[0]).groups() - df = { - 'term': self.__unquote(term), - 'db': db, - 'db_fullname': self.__unquote(db_fullname), - } - if self.mime: - mimeinfo = self.__get_mime(i[1]) - df.update(mimeinfo) - df['desc'] = '\n'.join(i[1]) - defs.append(df) - dct = { - 'count': len(defs), - 'definitions': defs, - } - return dct - else: - return {'error': code, 'msg': msg} - - def match(self, database, strategy, word): - """Match word in database using strategy.""" - if not self.__connected: - raise DicoNotConnectedError('Not connected') - - if self.levenshtein_distance and 'xlev' in self.server_capas: - res = self.__send_xlev(self.levenshtein_distance) - code, msg = res[-1].split(' ', 1) - if int(code) != 250 and self.verbose: - self.__debug('Server rejected XLEV command') - self.__debug('Server reply: %s' % msg) - - database = database.replace('"', "\\\"") - strategy = strategy.replace('"', "\\\"") - word = word.replace('"', "\\\"") - - res = self.__send_match(database, strategy, word) - code, msg = res[-1].split(' ', 1) - if int(code) < 500: - if self.mime: - mimeinfo = self.__get_mime(res[1]) - mts_refs = res[1:-1][0] - mts = {} - for i in mts_refs: - db, term = i.split(' ', 1) - if db in mts: - mts[db].append(self.__unquote(term)) - else: - mts[db] = [self.__unquote(term)] - dct = { - 'matches': mts, - } - return dct - else: - return {'error': code, 'msg': msg} - - def xlev(self, distance): - """Set Levenshtein distance.""" - self.levenshtein_distance = distance - res = self.__send_xlev(distance) - code, msg = res[0].split(' ', 1) - if int(code) == 250: - return True - return False - - def __unquote(self, s): - s = s.replace("\\\\'", "'") - if s[0] == '"' and s[-1] == '"': - s = s[1:-1] - try: - s = self.__decode(s) - except UnicodeEncodeError: - pass - return s - - def __decode(self, encoded): - for octc in (c for c in re.findall(r'\\(\d{3})', encoded)): - encoded = encoded.replace(r'\%s' % octc, chr(int(octc, 8))) - return encoded - - def __debug(self, msg): - print('dico: Debug: %s' % msg) - - -class DicoNotConnectedError (Exception): - - def __init__(self, value): - self.parameter = value - - def __str__(self): - return repr(self.parameter) diff --git a/dicoclient/dicoshell.py b/dicoclient/dicoshell.py deleted file mode 100644 index 8cbac3d..0000000 --- a/dicoclient/dicoshell.py +++ /dev/null @@ -1,332 +0,0 @@ -# This file is part of GNU Dico. -# Copyright (C) 2008-2010, 2012, 2013, 2015 Wojciech Polak -# -# GNU Dico 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 3, or (at your option) -# any later version. -# -# GNU Dico 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 GNU Dico. If not, see <http://www.gnu.org/licenses/>. - -import re -import os -import sys -import atexit -import getopt -import readline -import curses.ascii -import socket -import dicoclient - -try: - from django.utils.six.moves import range, input -except ImportError: - from six.moves import range, input - - -class Shell: - """Simple GNU Dico-Python Shell.""" - - prompt = 'dico> ' - prefix = '.' - - default_host = 'gnu.org.ua' - database = '!' - strategy = '.' - last_matches = [] - last_databases = [] - last_strategies = [] - transcript = False - - def __init__(self, opts, args): - for o, a in opts: - if o in ('-h', '--host'): - self.default_host = a - - self.dc = dicoclient.DicoClient(self.default_host) - - def run(self): - histfile = os.path.expanduser('~/.dico_history') - try: - readline.read_history_file(histfile) - except IOError: - pass - atexit.register(readline.write_history_file, histfile) - - print('\nType ? for help summary\n') - while True: - try: - inputcmd = input(self.prompt).strip() - except (EOFError, KeyboardInterrupt): - print() - sys.exit() - - try: - self.parse(inputcmd) - except socket.timeout: - self.__error('socket timed out') - except dicoclient.DicoNotConnectedError: - try: - self.dc.open() - dict = self.dc.show_databases() - self.last_databases = dict['databases'] - dict = self.dc.show_strategies() - self.last_strategies = dict['strategies'] - self.parse(inputcmd) - except socket.error as serror: - (errno, strerror) = serror.args - self.__error(strerror) - - def parse(self, inputcmd): - if len(inputcmd) < 1: - return - - if inputcmd[0] == self.prefix: - self.parse_command(inputcmd[1:]) - - elif inputcmd == '?': - self.print_help() - - elif re.match(r'^[0-9]+$', inputcmd): - try: - match = self.last_matches[int(inputcmd)] - dict = self.dc.define(match[0], match[1]) - if 'count' in dict: - for d in dict['definitions']: - print('From %s, %s:' % (d['db'], d['db_fullname'])) - print(d['desc']) - elif 'error' in dict: - print(dict['msg']) - except IndexError: - self.__error('No previous match') - - elif inputcmd[0] == '/': - if len(inputcmd) > 1: - dict = self.dc.match(self.database, self.strategy, inputcmd[1:]) - if 'matches' in dict: - self.last_matches = [] - lmi = 0 - for db in dict['matches']: - print('From %s, %s:' % (db, self.__lookup_db(db))) - for term in dict['matches'][db]: - print('%4d) "%s"' % (lmi, term)) - self.last_matches.append([db, term]) - lmi = lmi + 1 - elif 'error' in dict: - print(dict['msg']) - else: - if len(self.last_matches) > 0: - m = {} - lmi = 0 - for i, db in enumerate(self.last_matches): - if not db[0] in m: - m[db[0]] = [] - m[db[0]].append(self.last_matches[i][1]) - for db in m: - print('From %s, %s:' % (db, self.__lookup_db(db))) - for term in m[db]: - print('%4d) "%s"' % (lmi, term)) - lmi = lmi + 1 - else: - self.__error('No previous match') - - elif inputcmd[0] == '!': - if re.match(r'^![0-9]+$', inputcmd): - number = int(inputcmd[1:]) - readline.insert_text(readline.get_history_item(number)) - readline.redisplay() - - else: - dict = self.dc.define(self.database, inputcmd) - if 'count' in dict: - for d in dict['definitions']: - print('From %s, %s:' % (d['db'], d['db_fullname'])) - print(d['desc']) - elif 'error' in dict: - print(dict['msg']) - - def parse_command(sel |