lingo 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/ChangeLog +13 -0
  2. data/README +49 -29
  3. data/Rakefile +28 -4
  4. data/TODO +2 -9
  5. data/bin/lingo +24 -0
  6. data/bin/lingoctl +24 -0
  7. data/de/lingo-dic.txt +559 -74
  8. data/info/gpl-hdr.txt +21 -24
  9. data/lib/lingo.rb +83 -112
  10. data/lib/lingo/agenda_item.rb +53 -0
  11. data/lib/lingo/attendee.rb +261 -0
  12. data/lib/lingo/attendee/abbreviator.rb +95 -97
  13. data/lib/lingo/attendee/debugger.rb +94 -93
  14. data/lib/lingo/attendee/decomposer.rb +76 -83
  15. data/lib/lingo/attendee/dehyphenizer.rb +141 -144
  16. data/lib/lingo/attendee/formatter.rb +65 -0
  17. data/lib/lingo/attendee/multi_worder.rb +302 -0
  18. data/lib/lingo/attendee/noneword_filter.rb +89 -84
  19. data/lib/lingo/attendee/object_filter.rb +91 -0
  20. data/lib/lingo/attendee/sequencer.rb +159 -158
  21. data/lib/lingo/attendee/synonymer.rb +81 -84
  22. data/lib/lingo/attendee/text_reader.rb +242 -0
  23. data/lib/lingo/attendee/text_writer.rb +169 -0
  24. data/lib/lingo/attendee/tokenizer.rb +192 -191
  25. data/lib/lingo/attendee/variator.rb +152 -156
  26. data/lib/lingo/attendee/vector_filter.rb +140 -135
  27. data/lib/lingo/attendee/word_searcher.rb +98 -0
  28. data/lib/lingo/buffered_attendee.rb +69 -0
  29. data/lib/lingo/cachable.rb +58 -0
  30. data/lib/lingo/call.rb +72 -0
  31. data/lib/lingo/cli.rb +26 -0
  32. data/lib/lingo/config.rb +23 -26
  33. data/lib/lingo/core_ext.rb +42 -0
  34. data/lib/lingo/ctl.rb +239 -173
  35. data/lib/lingo/database.rb +148 -496
  36. data/lib/lingo/database/crypter.rb +85 -0
  37. data/lib/lingo/database/gdbm_store.rb +49 -0
  38. data/lib/lingo/database/hash_store.rb +67 -0
  39. data/lib/lingo/database/libcdb_store.rb +58 -0
  40. data/lib/lingo/database/sdbm_store.rb +64 -0
  41. data/lib/lingo/database/show_progress.rb +81 -0
  42. data/lib/lingo/database/source.rb +134 -0
  43. data/lib/lingo/database/source/key_value.rb +62 -0
  44. data/lib/lingo/database/source/multi_key.rb +65 -0
  45. data/lib/lingo/database/source/multi_value.rb +65 -0
  46. data/lib/lingo/database/source/single_word.rb +60 -0
  47. data/lib/lingo/database/source/word_class.rb +64 -0
  48. data/lib/lingo/error.rb +122 -0
  49. data/lib/lingo/language.rb +78 -518
  50. data/lib/lingo/language/dictionary.rb +173 -0
  51. data/lib/lingo/language/grammar.rb +211 -0
  52. data/lib/lingo/language/lexical.rb +66 -0
  53. data/lib/lingo/language/lexical_hash.rb +88 -0
  54. data/lib/lingo/language/token.rb +48 -0
  55. data/lib/lingo/language/word.rb +130 -0
  56. data/lib/lingo/language/word_form.rb +83 -0
  57. data/lib/lingo/reportable.rb +59 -0
  58. data/lib/lingo/version.rb +1 -1
  59. data/lingo-all.cfg +14 -10
  60. data/lingo-call.cfg +5 -5
  61. data/lingo.cfg +14 -12
  62. data/lingo.rb +26 -0
  63. data/lir.cfg +13 -9
  64. data/spec/spec_helper.rb +1 -0
  65. data/test.cfg +11 -11
  66. data/test/attendee/ts_abbreviator.rb +0 -6
  67. data/test/attendee/ts_decomposer.rb +0 -6
  68. data/test/attendee/{ts_multiworder.rb → ts_multi_worder.rb} +1 -7
  69. data/test/attendee/ts_noneword_filter.rb +1 -7
  70. data/test/attendee/{ts_objectfilter.rb → ts_object_filter.rb} +1 -7
  71. data/test/attendee/ts_sequencer.rb +0 -6
  72. data/test/attendee/ts_synonymer.rb +0 -6
  73. data/test/attendee/{ts_textreader.rb → ts_text_reader.rb} +1 -7
  74. data/test/attendee/{ts_textwriter.rb → ts_text_writer.rb} +1 -7
  75. data/test/attendee/ts_tokenizer.rb +0 -6
  76. data/test/attendee/ts_variator.rb +0 -6
  77. data/test/attendee/ts_vector_filter.rb +1 -7
  78. data/test/attendee/{ts_wordsearcher.rb → ts_word_searcher.rb} +1 -7
  79. data/test/ref/artikel.non +2 -29
  80. data/test/ref/artikel.seq +13 -8
  81. data/test/ref/artikel.vec +30 -15
  82. data/test/ref/artikel.ven +29 -14
  83. data/test/ref/artikel.ver +58 -43
  84. data/test/ref/lir.csv +146 -145
  85. data/test/ref/lir.non +186 -210
  86. data/test/ref/lir.seq +54 -50
  87. data/test/test_helper.rb +41 -36
  88. data/test/ts_database.rb +12 -11
  89. data/test/ts_language.rb +118 -68
  90. metadata +67 -29
  91. data/lib/lingo/attendee/multiworder.rb +0 -301
  92. data/lib/lingo/attendee/objectfilter.rb +0 -86
  93. data/lib/lingo/attendee/textreader.rb +0 -237
  94. data/lib/lingo/attendee/textwriter.rb +0 -196
  95. data/lib/lingo/attendee/wordsearcher.rb +0 -96
  96. data/lib/lingo/attendees.rb +0 -289
  97. data/lib/lingo/const.rb +0 -131
  98. data/lib/lingo/modules.rb +0 -98
  99. data/lib/lingo/types.rb +0 -285
  100. data/lib/lingo/utilities.rb +0 -40
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
25
+ #++
26
+
27
+ class Lingo
28
+
29
+ # Provides a simple caching mechanism.
30
+
31
+ module Cachable
32
+
33
+ def init_cachable
34
+ @cache = Hash.new(false)
35
+ end
36
+
37
+ def hit?(key)
38
+ @cache.has_key?(key)
39
+ end
40
+
41
+ def store(key, value)
42
+ @cache[key] = cache_value(value)
43
+ value
44
+ end
45
+
46
+ def retrieve(key)
47
+ cache_value(@cache[key])
48
+ end
49
+
50
+ private
51
+
52
+ def cache_value(value)
53
+ value.nil? ? nil : value.dup
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
25
+ #++
26
+
27
+ class Lingo
28
+
29
+ class Call < self
30
+
31
+ def initialize(args = [])
32
+ super(args, StringIO.new, StringIO.new, StringIO.new)
33
+ end
34
+
35
+ def call
36
+ invite
37
+
38
+ if block_given?
39
+ begin
40
+ yield self
41
+ ensure
42
+ reset
43
+ end
44
+ else
45
+ self
46
+ end
47
+ end
48
+
49
+ def talk(str)
50
+ config.stdin.reopen(str)
51
+
52
+ start
53
+
54
+ %w[stdout stderr].flat_map { |key|
55
+ io = config.send(key).tap(&:rewind)
56
+ io.readlines.each(&:chomp!).tap {
57
+ io.truncate(0)
58
+ io.rewind
59
+ }
60
+ }.tap { |res|
61
+ if block_given?
62
+ res.map!(&Proc.new)
63
+ else
64
+ res.sort!
65
+ res.uniq!
66
+ end
67
+ }
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -1,3 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
25
+ #++
26
+
1
27
  require 'nuggets/util/cli'
2
28
 
3
29
  class Lingo
@@ -1,30 +1,27 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  #--
4
- # LINGO ist ein Indexierungssystem mit Grundformreduktion, Kompositumzerlegung,
5
- # Mehrworterkennung und Relationierung.
6
- #
7
- # Copyright (C) 2005-2007 John Vorhauer
8
- # Copyright (C) 2007-2011 John Vorhauer, Jens Wille
9
- #
10
- # This program is free software; you can redistribute it and/or modify it under
11
- # the terms of the GNU Affero General Public License as published by the Free
12
- # Software Foundation; either version 3 of the License, or (at your option)
13
- # any later version.
14
- #
15
- # This program is distributed in the hope that it will be useful, but WITHOUT
16
- # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
- # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18
- # details.
19
- #
20
- # You should have received a copy of the GNU Affero General Public License along
21
- # with this program; if not, write to the Free Software Foundation, Inc.,
22
- # 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
23
- #
24
- # For more information visit http://www.lex-lingo.de or contact me at
25
- # welcomeATlex-lingoDOTde near 50°55'N+6°55'E.
26
- #
27
- # Lex Lingo rules from here on
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
28
25
  #++
29
26
 
30
27
  require 'yaml'
@@ -44,13 +41,13 @@ class Lingo
44
41
  load_config('config')
45
42
 
46
43
  Array(self['meeting/attendees']).each { |a|
47
- r = a['textreader'] or next
44
+ r = a['text_reader'] || a['textreader'] or next
48
45
 
49
46
  f = @cli.files
50
47
 
51
48
  if i = r['files']
52
49
  r['files'] = i.strip == '$(files)' ?
53
- f : i.split(STRING_SEPERATOR_PATTERN)
50
+ f : i.split(STRING_SEPARATOR_RE)
54
51
  elsif !f.empty?
55
52
  r['files'] = f
56
53
  end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
25
+ #++
26
+
27
+ require 'unicode'
28
+
29
+ class String
30
+
31
+ alias_method :_lingo_original_downcase, :downcase
32
+ alias_method :_lingo_original_downcase!, :downcase!
33
+
34
+ def downcase
35
+ Unicode.downcase(self)
36
+ end
37
+
38
+ def downcase!
39
+ replace(downcase)
40
+ end
41
+
42
+ end
@@ -1,173 +1,239 @@
1
- require 'optparse'
2
- require 'fileutils'
3
- require 'nuggets/enumerable/minmax'
4
-
5
- class Lingo
6
-
7
- module Ctl
8
-
9
- extend self
10
-
11
- PROG, VERSION = $0, '0.0.1'
12
- PROGNAME = File.basename(PROG)
13
-
14
- COMMANDS = {}
15
-
16
- { config: %w[configuration],
17
- lang: %w[language],
18
- dict: %w[dictionary dictionaries],
19
- store: %w[store] }.each { |what, (sing, plur)|
20
- COMMANDS["list#{what}"] = [
21
- "List available #{plur || "#{sing}s"}", 'Arguments: [name...]'
22
- ] if what != :store
23
- COMMANDS["find#{what}"] = [
24
- "Find #{sing} in Lingo search path", 'Arguments: name'
25
- ]
26
- COMMANDS["copy#{what}"] = [
27
- "Copy #{sing} to local Lingo directory", 'Arguments: name'
28
- ] if what != :store
29
-
30
- %w[list find copy].each { |method|
31
- class_eval %Q{def do_#{method}#{what}; #{method}(:#{what}); end}
32
- }
33
- }
34
-
35
- COMMANDS.update(
36
- 'path' => 'Print search path for dictionaries and configurations',
37
- 'help' => 'Print help for available commands',
38
- 'version' => 'Print Lingo version number'
39
- )
40
-
41
- USAGE = <<EOT
42
- Usage: #{PROG} <command> [arguments] [options]
43
- #{PROG} [-h|--help] [--version]
44
- EOT
45
-
46
- OPTIONS = {}
47
-
48
- def do
49
- parse_options
50
- send("do_#{COMMANDS.has_key?(command = ARGV.shift) ? command : 'usage'}")
51
- end
52
-
53
- private
54
-
55
- def list(what)
56
- names = Regexp.union(*ARGV.empty? ? '' : ARGV)
57
-
58
- Lingo.list(what, path: path_for_scope).each { |file|
59
- puts file if File.basename(file) =~ names
60
- }
61
- end
62
-
63
- def find(what, doit = true)
64
- name = ARGV.shift or do_usage('Required argument `name\' missing.')
65
- no_args
66
-
67
- file = Lingo.find(what, name, path: path_for_scope, &method(:do_usage))
68
- doit ? puts(file) : file
69
- end
70
-
71
- def copy(what)
72
- do_usage('Source and target are the same.') if OPTIONS[:scope] == :local
73
-
74
- source = find(what, false)
75
- target = File.join(path_for_scope(:local), Lingo.basepath(what, source))
76
-
77
- do_usage('Source and target are the same.') if source == target
78
-
79
- FileUtils.mkdir_p(File.dirname(target))
80
- FileUtils.cp(source, target, verbose: true)
81
- end
82
-
83
- def do_path
84
- no_args
85
- puts path_for_scope || PATH
86
- end
87
-
88
- def do_help(opts = nil)
89
- no_args
90
-
91
- msg = %w[Commands:]
92
- msg.unshift(opts) if opts
93
-
94
- max = COMMANDS.keys.max(:length)
95
-
96
- COMMANDS.each { |command, description|
97
- description = [*description]
98
- msg << " %-#{max}s - %s" % [command, description.shift]
99
-
100
- description.each { |extra|
101
- msg << " %#{max}s + %s" % [' ', extra]
102
- } unless opts
103
- }
104
-
105
- abort msg.join("\n")
106
- end
107
-
108
- def do_version(doit = true)
109
- no_args
110
-
111
- msg = "Lingo v#{Lingo::VERSION}"
112
- doit ? puts(msg) : msg
113
- end
114
-
115
- def do_usage(msg = nil)
116
- msg = msg ? "#{PROGNAME}: #{msg}\n\n" : ''
117
- abort msg << USAGE
118
- end
119
-
120
- def parse_options
121
- OptionParser.new(USAGE, 14) { |opts|
122
- opts.separator ''
123
- opts.separator 'Scope options:'
124
-
125
- opts.on('--system', 'Restrict command to the system-wide Lingo directory') {
126
- OPTIONS[:scope] = :system
127
- }
128
-
129
- opts.on('--global', 'Restrict command to the user\'s personal Lingo directory') {
130
- OPTIONS[:scope] = :global
131
- }
132
-
133
- opts.on('--local', 'Restrict command to the local Lingo directory') {
134
- OPTIONS[:scope] = :local
135
- }
136
-
137
- opts.separator ''
138
- opts.separator 'Generic options:'
139
-
140
- opts.on('-h', '--help', 'Print this help message and exit') {
141
- do_help(opts)
142
- }
143
-
144
- opts.on('--version', 'Print program version and exit') {
145
- abort "#{PROGNAME} v#{VERSION} (#{do_version(false)})"
146
- }
147
- }.parse!
148
- end
149
-
150
- def path_for_scope(scope = OPTIONS[:scope])
151
- case scope
152
- when :system then [BASE]
153
- when :global then [HOME]
154
- when :local then [CURR]
155
- when nil
156
- else do_usage("Invalid scope `#{scope.inspect}'.")
157
- end
158
- end
159
-
160
- def no_args
161
- do_usage('Too many arguments.') unless ARGV.empty?
162
- end
163
-
164
- end
165
-
166
- def self.ctl
167
- Ctl.do
168
- rescue => err
169
- raise if $VERBOSE
170
- abort "#{err.backtrace.first}: #{err} (#{err.class})"
171
- end
172
-
173
- end
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # Lingo -- A full-featured automatic indexing system #
7
+ # #
8
+ # Copyright (C) 2005-2007 John Vorhauer #
9
+ # Copyright (C) 2007-2012 John Vorhauer, Jens Wille #
10
+ # #
11
+ # Lingo is free software; you can redistribute it and/or modify it under the #
12
+ # terms of the GNU Affero General Public License as published by the Free #
13
+ # Software Foundation; either version 3 of the License, or (at your option) #
14
+ # any later version. #
15
+ # #
16
+ # Lingo is distributed in the hope that it will be useful, but WITHOUT ANY #
17
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
18
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
19
+ # more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with Lingo. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
25
+ #++
26
+
27
+ require 'optparse'
28
+ require 'fileutils'
29
+
30
+ class Lingo
31
+
32
+ module Ctl
33
+
34
+ extend self
35
+
36
+ PROG, VERSION, OPTWIDTH = $0, '0.0.2', 18
37
+ PROGNAME, OPTIONS = File.basename(PROG), {}
38
+
39
+ COMMANDS, ALIASES = {}, Hash.new { |h, k|
40
+ h[k] = COMMANDS.has_key?(k) ? k : 'usage'
41
+ }
42
+
43
+ ALIASES['ls'] # OCCUPY
44
+
45
+ { config: %w[configuration],
46
+ lang: %w[language],
47
+ dict: %w[dictionary dictionaries],
48
+ store: %w[store],
49
+ sample: %w[sample\ text\ file] }.each { |what, (sing, plur)|
50
+ COMMANDS["list#{what}"] = [
51
+ "List available #{plur || "#{sing}s"}", 'Arguments: [name...]'
52
+ ] if what != :store
53
+ COMMANDS["find#{what}"] = [
54
+ "Find #{sing} in Lingo search path", 'Arguments: name'
55
+ ]
56
+ COMMANDS["copy#{what}"] = [
57
+ "Copy #{sing} to local Lingo directory", 'Arguments: name'
58
+ ] if what != :store
59
+
60
+ %w[list find copy].each { |method|
61
+ next unless COMMANDS.has_key?(name = "#{method}#{what}")
62
+ class_eval %Q{def do_#{name}; #{method}(:#{what}); end}
63
+
64
+ [0, -1].repeated_permutation(2) { |i, j|
65
+ key = "#{method[i]}#{what[j]}"
66
+ break ALIASES[key] = name unless ALIASES.has_key?(key)
67
+ }
68
+ }
69
+
70
+ if what == :store
71
+ COMMANDS['clearstore'] = [
72
+ 'Remove store files to force rebuild', 'Arguments: name'
73
+ ]
74
+ ALIASES['cs'] = 'clearstore'
75
+ end
76
+ }
77
+
78
+ { demo: ['Initialize demo directory (Default: current directory)',
79
+ 'Arguments: [path]'],
80
+ path: 'Print search path for dictionaries and configurations',
81
+ help: 'Print help for available commands',
82
+ version: 'Print Lingo version number' }.each { |what, description|
83
+ COMMANDS[name = what.to_s] = description; ALIASES[name[0]] = name
84
+ }
85
+
86
+ USAGE = <<EOT
87
+ Usage: #{PROG} <command> [arguments] [options]
88
+ #{PROG} [-h|--help] [--version]
89
+ EOT
90
+
91
+ def do
92
+ parse_options
93
+ send("do_#{ALIASES[ARGV.shift]}")
94
+ end
95
+
96
+ private
97
+
98
+ def list(what, doit = true)
99
+ names = Regexp.union(*ARGV.empty? ? '' : ARGV)
100
+
101
+ Lingo.list(what, path: path_for_scope).select { |file|
102
+ File.basename(file) =~ names ? doit ? puts(file) : true : false
103
+ }
104
+ end
105
+
106
+ def find(what, doit = true)
107
+ name = ARGV.shift or do_usage('Required argument `name\' missing.')
108
+ no_args
109
+
110
+ file = Lingo.find(what, name, path: path_for_scope, &method(:do_usage))
111
+ doit ? puts(file) : file
112
+ end
113
+
114
+ def copy(what)
115
+ do_usage('Source and target are the same.') if OPTIONS[:scope] == :local
116
+
117
+ source = find(what, false)
118
+ target = File.join(path_for_scope(:local), Lingo.basepath(what, source))
119
+
120
+ do_usage('Source and target are the same.') if source == target
121
+
122
+ FileUtils.mkdir_p(File.dirname(target))
123
+ FileUtils.cp(source, target, verbose: true)
124
+ end
125
+
126
+ def do_clearstore
127
+ store = Dir["#{find(:store, false)}.*"]
128
+ FileUtils.rm(store, verbose: true) unless store.empty?
129
+ end
130
+
131
+ def do_demo
132
+ OPTIONS.update(path: ARGV.shift, scope: :system)
133
+ no_args
134
+
135
+ copy_list(:config) { |i| !File.basename(i).start_with?('test') }
136
+ copy_list(:lang)
137
+ copy_list(:dict) { |i| File.basename(i).start_with?('user') }
138
+ copy_list(:sample)
139
+ end
140
+
141
+ def do_path
142
+ no_args
143
+ puts path_for_scope || PATH
144
+ end
145
+
146
+ def do_help(opts = nil)
147
+ no_args
148
+
149
+ msg = opts ? [opts, 'Commands:'] : []
150
+
151
+ aliases = Hash.new { |h, k| h[k] = [] }
152
+ ALIASES.each { |k, v| aliases[v] << k }
153
+
154
+ COMMANDS.each { |c, (d, *e)|
155
+ a = aliases[c]
156
+ c = "#{c} (#{a.join(', ')})" unless a.empty?
157
+
158
+ if opts
159
+ msg << " %-#{OPTWIDTH}s %s" % [c, d]
160
+ else
161
+ msg << "#{c}" << " - #{d}"
162
+ e.each { |i| msg << " + #{i}" }
163
+ end
164
+ }
165
+
166
+ abort msg.join("\n")
167
+ end
168
+
169
+ def do_version(doit = true)
170
+ no_args
171
+
172
+ msg = "Lingo v#{Lingo::VERSION}"
173
+ doit ? puts(msg) : msg
174
+ end
175
+
176
+ def do_usage(msg = nil)
177
+ abort "#{"#{PROGNAME}: #{msg}\n\n" if msg}#{USAGE}"
178
+ end
179
+
180
+ def parse_options
181
+ OptionParser.new(USAGE, OPTWIDTH) { |opts|
182
+ opts.separator ''
183
+ opts.separator 'Scope options:'
184
+
185
+ opts.on('--system', 'Restrict command to the system-wide Lingo directory') {
186
+ OPTIONS[:scope] = :system
187
+ }
188
+
189
+ opts.on('--global', 'Restrict command to the user\'s personal Lingo directory') {
190
+ OPTIONS[:scope] = :global
191
+ }
192
+
193
+ opts.on('--local', 'Restrict command to the local Lingo directory') {
194
+ OPTIONS[:scope] = :local
195
+ }
196
+
197
+ opts.separator ''
198
+ opts.separator 'Generic options:'
199
+
200
+ opts.on('-h', '--help', 'Print this help message and exit') {
201
+ do_help(opts)
202
+ }
203
+
204
+ opts.on('--version', 'Print program version and exit') {
205
+ abort "#{PROGNAME} v#{VERSION} (#{do_version(false)})"
206
+ }
207
+ }.parse!
208
+ end
209
+
210
+ def path_for_scope(scope = OPTIONS[:scope])
211
+ case scope
212
+ when :system then [BASE]
213
+ when :global then [HOME]
214
+ when :local then [OPTIONS[:path] || CURR]
215
+ when nil
216
+ else do_usage("Invalid scope `#{scope.inspect}'.")
217
+ end
218
+ end
219
+
220
+ def no_args
221
+ do_usage('Too many arguments.') unless ARGV.empty?
222
+ end
223
+
224
+ def copy_list(what)
225
+ files = list(what, false)
226
+ files.select!(&Proc.new) if block_given?
227
+ files.each { |file| ARGV.replace([file]); copy(what) }
228
+ end
229
+
230
+ end
231
+
232
+ def self.ctl
233
+ Ctl.do
234
+ rescue => err
235
+ raise if $VERBOSE
236
+ abort "#{err.backtrace.first}: #{err} (#{err.class})"
237
+ end
238
+
239
+ end