lingo 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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