macaw 0.0.1 → 0.0.40

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -16
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +27 -0
  5. data/LICENSE.txt +1 -1
  6. data/README.md +82 -0
  7. data/Rakefile +132 -0
  8. data/bin/macaw +308 -0
  9. data/lib/macaw.rb +2 -3
  10. data/lib/macaw/i18n/de.yml +171 -0
  11. data/lib/macaw/i18n/en.yml +157 -0
  12. data/lib/macaw/i18n/es.yml +159 -0
  13. data/lib/macaw/i18n/fr.yml +160 -0
  14. data/lib/macaw/i18n/it.yml +157 -0
  15. data/lib/macaw/i18n/pt-BR.yml +154 -0
  16. data/lib/macaw/i18n/pt.yml +155 -0
  17. data/lib/macaw/i18n/ru.yml +159 -0
  18. data/lib/macaw/i18n/tr.yml +148 -0
  19. data/lib/macaw/rules/animate.rb +26 -0
  20. data/lib/macaw/rules/biber.rb +8 -0
  21. data/lib/macaw/rules/bibtex.rb +8 -0
  22. data/lib/macaw/rules/clean.rb +10 -0
  23. data/lib/macaw/rules/dvipdfm.rb +9 -0
  24. data/lib/macaw/rules/dvipdfmx.rb +9 -0
  25. data/lib/macaw/rules/dvips.rb +9 -0
  26. data/lib/macaw/rules/dvipsps2pdf.rb +10 -0
  27. data/lib/macaw/rules/frontespizio.rb +14 -0
  28. data/lib/macaw/rules/indent.rb +36 -0
  29. data/lib/macaw/rules/latex.rb +17 -0
  30. data/lib/macaw/rules/lmkclean.rb +13 -0
  31. data/lib/macaw/rules/lualatex.rb +17 -0
  32. data/lib/macaw/rules/lualatexmk.rb +17 -0
  33. data/lib/macaw/rules/luatex.rb +17 -0
  34. data/lib/macaw/rules/make.rb +9 -0
  35. data/lib/macaw/rules/makeglossaries.rb +9 -0
  36. data/lib/macaw/rules/makeindex.rb +16 -0
  37. data/lib/macaw/rules/nomencl.rb +17 -0
  38. data/lib/macaw/rules/pdflatex.rb +17 -0
  39. data/lib/macaw/rules/pdflatexmk.rb +17 -0
  40. data/lib/macaw/rules/pdftex.rb +16 -0
  41. data/lib/macaw/rules/ps2pdf.rb +16 -0
  42. data/lib/macaw/rules/sketch.rb +14 -0
  43. data/lib/macaw/rules/songidx.rb +8 -0
  44. data/lib/macaw/rules/sumatrapdf.rb +10 -0
  45. data/lib/macaw/rules/tex.rb +9 -0
  46. data/lib/macaw/rules/texcount.rb +9 -0
  47. data/lib/macaw/rules/xdvipdfmx.rb +9 -0
  48. data/lib/macaw/rules/xelatex.rb +16 -0
  49. data/lib/macaw/rules/xelatexmk.rb +14 -0
  50. data/lib/macaw/rules/xetex.rb +15 -0
  51. data/lib/macaw/version.rb +2 -2
  52. data/macaw.gemspec +24 -17
  53. data/test/test_macaw.rb +8 -0
  54. metadata +133 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a12149d550e79e2ac52b9804cda48e101604944
4
- data.tar.gz: 3a8085b6f2d2cc3727c8ea9a2eb46a4f49c728d2
3
+ metadata.gz: 06dfef52346c5bafb0f0fc2ee6ffa2f95298cd1a
4
+ data.tar.gz: 1f11bf5f437fb1c5665385cad0d91541613716e9
5
5
  SHA512:
6
- metadata.gz: a9330cdb1257c56cb3ecaae185bcf3280d6bc1a2ac58b626a291304dd74d623611a87582d29d4708b1947f69cca74675d9e532c0093d3b5f0be5c18ae5cf8b18
7
- data.tar.gz: 43a5e30ef71af18d0094e692d3d739f1c9c7721e4705c617dddaf17fc41d71eea736e7555aac99f182f4fd8229cdd87e0d3a07dc765ac9f4e27cde167b263adc
6
+ metadata.gz: 6861e834380c57a5a354178c533ebfe9cdc084d765a5d063f5ac73c0c7e82c5476aae0e85f0788f1165f015ad72cb7c0d12527e0053ef3355762680d96a7a0a0
7
+ data.tar.gz: dd05f1a8f17e67eb2f2506010e5fce59688597fc63683c2d5d2589c8e21ab8c1d070f450fe1b04aa3178724b26bb1226cca6193c11ee76704730d6a9a3f19618
data/.gitignore CHANGED
@@ -1,17 +1,3 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
1
  pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
2
+ test.*
3
+ arara
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
+ #ruby=2.1
2
+ #ruby-gemset=macaw
3
+
1
4
  source 'https://rubygems.org'
2
5
 
6
+ # Specify your gem's dependencies in macaw.gemspec
3
7
  gemspec
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ macaw (0.0.40)
5
+ i18n
6
+ json_pure
7
+ os
8
+ require_all
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ i18n (0.6.9)
14
+ java_properties (0.0.4)
15
+ json_pure (1.8.1)
16
+ os (0.9.6)
17
+ rake (10.3.2)
18
+ require_all (1.3.2)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ bundler (~> 1.3)
25
+ java_properties
26
+ macaw!
27
+ rake
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Martin Jagusch
1
+ Copyright (c) 2013 friflaj
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1 +1,83 @@
1
1
  # Macaw
2
+
3
+ Macaw is a Ruby Arara, a Ruby port of http://cereda.github.io/arara/. This is in many aspects very rough around the
4
+ edges at this moment as it was written in a weekend while I was busy doing other things. Ruby does rock.
5
+
6
+ ## Installation
7
+
8
+ To install, make sure you have Ruby 1.9 or later installed, and then (on a command line, sorry) type
9
+
10
+ gem install macaw
11
+
12
+ To update Macaw, you type
13
+
14
+ gem update macaw
15
+ gem cleanup macaw
16
+
17
+ ## Usage
18
+
19
+ In day-to-day use it will (should) behave exactly like Arara. It accepts the same command line parameters and reads the
20
+ same *standard* rules that are bundled with Arara; if you use only those, there's no need to change anything in your
21
+ LaTeX documents. Instead of
22
+
23
+ arara
24
+
25
+ you call
26
+
27
+ macaw
28
+
29
+ (or macaw.bat if you want to run it from a Windows IDE)
30
+
31
+ ## Custom rules
32
+
33
+ Macaw doesn't use yaml for its rules; rules are Ruby scripts. Macaw will read your araraconfig.yaml to find the paths to
34
+ your custom rules; in that location, you can drop files that end in '.rb', which are structured like this:
35
+
36
+ class Macaw # this line is mandatory
37
+ # define your macaw rule here. The name after 'def' is the name of your rule,
38
+ # the options between parenthesis are the parameters your rule will accept.
39
+ # Optional parameters are marked using '=nil'.
40
+ #
41
+ # For those of you who know ruby, this looks familiar but behaves oddly;
42
+ # using anything else than 'nil' will likewise make the parameter optional,
43
+ # but you will *always* be passed 'nil' if the Arara rule in the tex file
44
+ # did not pass the parameters).
45
+ def zoterobib(collection, format=nil, port=nil, exportCharset=nil, exportNotes=nil, useJournalAbbreviation=nil)
46
+ # ||= assigns a value if it wasn't set; set your default values here
47
+ format ||= 'biblatex'
48
+ port ||= 23119
49
+
50
+ # this adds the optional parameters to the url
51
+ params = []
52
+ params << "&exportCharset=#{exportCharset}" if exportCharset
53
+ params << "&exportNotes=#{exportNotes}" if exportNotes
54
+ params << "&useJournalAbbreviation=#{useJournalAbbreviation}" if useJournalAbbreviation
55
+
56
+ # the ~ is a Macaw feature -- it will escape the path of the string after it to be
57
+ # safe to be passed on the command line. It will do the right thing depending on
58
+ # the platform it's being run on.
59
+ bib = ~"#{@base}.bib"
60
+ url = ~"http://localhost:#{port}/better-bibtex/collection?#{collection}.#{format}#{params}"
61
+
62
+ # Macaw.system executes the command, captures output, does logging, etc
63
+ Macaw.system "curl --connect-timeout 5 -z #{bib} -o #{bib} #{url}"
64
+ end
65
+ end
66
+
67
+ You are not required to use Macaw.system BTW; Arara is mainly focused on running shell programs, but since Macaw rules
68
+ are ruby scripts, they can do some things natively:
69
+
70
+ # Clean rule for arara
71
+ # author: Paulo Cereda
72
+ # requires arara 3.0+
73
+ class Macaw
74
+ def clean(files)
75
+ files.reject{|f| f.downcase == @file.downcase}.each{|f|
76
+ Macaw.log "removing #{f}"
77
+ File.unlink(f)
78
+ }
79
+ end
80
+ end
81
+
82
+ If you don't use Macaw.system, you can use *Macaw.log* to pass output to the logging system (which will be subject to
83
+ choices the user makes on the command line, or *Macaw.error* to show a fatal error message and halt the build.
data/Rakefile CHANGED
@@ -1 +1,133 @@
1
1
  require 'bundler/gem_tasks'
2
+ require 'fileutils'
3
+ require 'yaml'
4
+ require 'pp'
5
+ require 'java_properties'
6
+
7
+ task :default => :build
8
+
9
+ task :bump do
10
+ version = Macaw::VERSION.split('.').collect{|v| Integer(v)}
11
+ version[2] += 1
12
+ version = version.collect{|v| v.to_s}.join('.')
13
+ File.open('lib/macaw/version.rb', 'w'){|f| f.write("class Macaw\n VERSION = #{version.inspect}\nend\n") }
14
+ puts `git add lib/macaw/version.rb Gemfile.lock`
15
+ end
16
+
17
+ task :publish do
18
+ sh "git tag #{Macaw::VERSION}"
19
+ Dir['pkg/*.gem'].each{|f| File.unlink(f)}
20
+ Rake::Task["build"].invoke
21
+ sh "gem push #{Dir['pkg/*.gem'].join(' ')}"
22
+ sh "git push"
23
+ end
24
+
25
+ task :test do
26
+ sh "cp test/test.tex ."
27
+ sh "./bin/macaw -p -l"
28
+ sh "rm test.*"
29
+ end
30
+
31
+
32
+ class LanguageManager
33
+ class NoTranslation < StandardError; end
34
+
35
+ def initialize
36
+ @translations = {}
37
+ Dir['lib/macaw/i18n/*.yml'].each{|yml|
38
+ @translations[File.basename(yml, File.extname(yml))] = YAML.load_file(yml)
39
+ }
40
+ @en = @translations.delete('en')
41
+ @keys = keys(@en[@en.keys[0]])
42
+ end
43
+
44
+ def keys(h, prefix=[])
45
+ c = []
46
+ h.each_pair{|k, v|
47
+ if v.is_a?(Hash)
48
+ c += keys(v, prefix + [k])
49
+ else
50
+ c << prefix + [k]
51
+ end
52
+ }
53
+ c
54
+ end
55
+
56
+ def resolve(h, k)
57
+ raise NoTranslation if h.nil?
58
+ h = h[k.shift]
59
+ return h if k.empty?
60
+ return resolve(h, k)
61
+ end
62
+
63
+ def vars(s)
64
+ v = []
65
+ s.gsub(/%\{([^}]+)\}/){|k| v << k}
66
+ return v.sort
67
+ end
68
+
69
+ def port
70
+ Dir['arara/translations/*.input'].each{|prop|
71
+ lang = File.basename(prop, File.extname(prop)).gsub('_', '-').gsub(/^Messages-/, '')
72
+ lang = 'en' if lang == 'Messages'
73
+ next if lang == 'Messages.sample'
74
+ strings = JavaProperties::Properties.new(prop)
75
+
76
+ tr = {lang => {}}
77
+ strings.each{|key, value|
78
+ section, localkey = *key.to_s.split('_', 2)
79
+ section.downcase!
80
+
81
+ case key
82
+ when :Log_ProcessingFile, :Msg_NoDirectivesFound, :Error_FileDoesNotExist
83
+ value.gsub!('{0}', '%{file}')
84
+ when :Msg_SpecialThanks
85
+ value.gsub!(/Alan.*Kottwitz/i, '%{contributors}')
86
+ when :Error_InvalidLanguageConfigurationFile
87
+ value.gsub!('{0}', '%{languages}')
88
+ end
89
+
90
+ value.gsub!(/(\{[0-9]+\})/){"%#{$1}"}
91
+ value.gsub!('arara', 'macaw')
92
+ value.gsub!('Arara', 'Macaw')
93
+ value.gsub!("''", '"')
94
+
95
+ tr[lang][section] ||= {}
96
+ tr[lang][section][localkey] = value
97
+ }
98
+ tr[lang]['help']['Usage'] ||= 'Usage'
99
+ tr[lang]['help']['Progress'] ||= 'Print dots to mark progress'
100
+ File.open("lib/macaw/i18n/#{lang}.yml", 'w'){|f| f.write(tr.to_yaml)}
101
+ }
102
+ end
103
+
104
+
105
+ def test
106
+ @translations.values.each{|l|
107
+ @keys.each{|k|
108
+ ens = resolve(@en[@en.keys[0]], k.dup)
109
+ ls = nil
110
+ begin
111
+ ls = resolve(l[l.keys[0]], k.dup)
112
+ rescue NoTranslation
113
+ ls = nil
114
+ end
115
+
116
+ if ls.to_s == ''
117
+ puts "No translation: #{l.keys[0]}: #{k.join('.')}"
118
+ next
119
+ end
120
+
121
+ puts "Var mismatch: #{l.keys[0]}: #{k.join('.')}\n #{vars(ens).inspect}\n #{vars(ls).inspect}" if vars(ens) != vars(ls)
122
+ }
123
+ }
124
+ end
125
+ end
126
+
127
+ task :gettranslations do
128
+ LanguageManager.new.port
129
+ end
130
+
131
+ task :checktranslations do
132
+ LanguageManager.new.test
133
+ end
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'yaml'
5
+ require 'pp'
6
+ require_relative '../lib/macaw'
7
+ require_relative '../lib/macaw/version'
8
+ require 'json/pure'
9
+ require 'os'
10
+ require 'ostruct'
11
+ require 'shellwords'
12
+ require 'optparse'
13
+ require 'timeout'
14
+ require 'i18n'
15
+ require 'open3'
16
+
17
+ puts [
18
+ ' __ __ ',
19
+ '| \/ | __ _ ___ __ ___ __',
20
+ '| |\/| |/ _` |/ __/ _` \ \ /\ / /',
21
+ '| | | | (_| | (_| (_| |\ V V / ',
22
+ '|_| |_|\__,_|\___\__,_| \_/\_/ ',
23
+ ' ',
24
+ ].join("\n")
25
+
26
+ CONTRIBUTORS = [
27
+ 'Alan Munn',
28
+ 'Andrew Stacey',
29
+ 'Brent Longborough',
30
+ 'Clemens Niederberger',
31
+ 'David Carlisle',
32
+ 'Enrico Gregorio',
33
+ 'Francesco Endrici',
34
+ 'Gonzalo Medina',
35
+ 'Harish Kumar',
36
+ '?lhan Polat',
37
+ 'Joseph Wright',
38
+ 'Marco Daniel',
39
+ 'Mikaδl Maunier',
40
+ 'Patrick Gundlach',
41
+ 'Rasmus Roulund',
42
+ 'Sergey Ulyanov',
43
+ 'Stefan Kottwitz'
44
+ ].join(', ')
45
+
46
+ I18n.enforce_available_locales = true
47
+ I18n.load_path = Dir[File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'macaw', 'i18n', '*.yml'))]
48
+ I18n.default_locale = :en
49
+
50
+ def options!
51
+ options = {}
52
+ [true, false].collect{|dryrun|
53
+ opts = OptionParser.new{|parser|
54
+ parser.banner = "#{I18n.t('help.Usage')}: macaw [options] FILE"
55
+ parser.separator ""
56
+
57
+ options[:language] = I18n.default_locale
58
+ parser.on('-L', '--language LANG', I18n.t('help.Language')){|lang|
59
+ begin
60
+ I18n.locale = lang.intern
61
+ options[:language] = lang.intern
62
+ rescue I18n::InvalidLocale
63
+ if !dryrun
64
+ puts I81n.t('error.InvalidLanguageConfigurationFile', languages: I18n.available_locales.collect{|l| l.to_s}.join(', '))
65
+ exit
66
+ end
67
+ end
68
+ }
69
+
70
+ parser.on('-p', '--progress', I18n.t('help.Progress')) {|v|
71
+ options[:progress] = v
72
+ }
73
+ parser.on('-l', '--log', I18n.t('help.Log')) {|v|
74
+ options[:log] = v
75
+ }
76
+
77
+ parser.on('-t', '--timeout MILLISECONDS', I18n.t('help.Timeout')){|timeout|
78
+ options[:timeout] = Float(timeout) / 1000
79
+ }
80
+
81
+ parser.on('-r', '--rules', 'Available rules') {|v|
82
+ options[:rules] = v
83
+ }
84
+
85
+ parser.on('-v', '--verbose', I18n.t('help.Verbose')) {|v|
86
+ options[:verbose] = v
87
+ }
88
+
89
+ parser.on_tail('-h', '--help', '--usage', I18n.t('help.Help')){
90
+ if !dryrun
91
+ puts parser.help
92
+ exit
93
+ end
94
+ }
95
+
96
+ parser.on_tail('-V', '--version', I18n.t('help.Version')){
97
+ if !dryrun
98
+ puts "macaw #{Macaw::VERSION} - #{I18n.t('header.Slogan')}\nCopyright (c) 2012, Paulo Roberto Massa Cereda\n"
99
+ puts I18n.t('header.AllRightsReserved')
100
+ puts "\n"
101
+ puts I18n.t('msg.SpecialThanks', :contributors => CONTRIBUTORS).gsub(/(.{1,#{60}})(?: +|$)\n?|(.{#{60}})/, "\\1\\2\n")
102
+ exit
103
+ end
104
+ }
105
+ }
106
+ if dryrun
107
+ opts.parse!(ARGV.dup)
108
+ else
109
+ opts.parse!
110
+ end
111
+ }
112
+ OpenStruct.new(options)
113
+ end
114
+
115
+ CONFIGFILE = [File.join(Dir.home, 'araraconfig.yaml'), File.join(Dir.home, '.araraconfig.yaml')].detect{|config| File.file?(config) }
116
+ CONFIG = CONFIGFILE ? YAML.load_file(CONFIGFILE) : {}
117
+
118
+ class String
119
+ def ~
120
+ if OS.windows?
121
+ return '"' + self.gsub('"', '""') + '"'
122
+ else
123
+ return self.shellescape
124
+ end
125
+ end
126
+ end
127
+
128
+ class JSON::Pure::Parser
129
+ IDENTIFIER = /[^:\s\\",{}\[\]]+/i
130
+ alias :parse_quoted_string :parse_string
131
+
132
+ def parse_string
133
+ if match?(IDENTIFIER)
134
+ str = scan(IDENTIFIER)
135
+ return true if str == 'yes'
136
+ return false if str == 'no'
137
+ return str
138
+ end
139
+
140
+ return parse_quoted_string
141
+ end
142
+ end
143
+
144
+ class Macaw
145
+ class CommandlineError < StandardError; end
146
+
147
+ @@log = nil
148
+
149
+ def initialize(tex)
150
+ @file = tex
151
+ @base = File.join(File.dirname(@file), File.basename(@file, File.extname(@file)))
152
+ end
153
+ attr_reader :file, :base
154
+
155
+ def halt
156
+ error 'Halted on user request'
157
+ end
158
+
159
+ ERRORMODES=[:exit, :throw, :ignore]
160
+ def self.error(msg, onfail=:exit)
161
+ throw "errormode can only be #{ERRORMODES.inspect}" unless ERRORMODES.include?(onfail)
162
+ case onfail
163
+ when :exit
164
+ @@log.close if @@log
165
+ puts "\n\n#{msg}"
166
+ exit 1
167
+ when :throw
168
+ raise CommandlineError, msg
169
+ else
170
+ puts msg
171
+ end
172
+ end
173
+
174
+ def self.options
175
+ @@options ||= options!
176
+ end
177
+
178
+ def self.run
179
+ @@options ||= options!
180
+
181
+ if @@options.rules
182
+ vars = Macaw.new('').instance_variables.collect{|v| v.to_s.sub(/^@/, '').intern}
183
+ w = 0
184
+ rules = {}
185
+ self.instance_methods(false).sort{|a, b| a.to_s <=> b.to_s }.each{|method|
186
+ next if vars.include?(method)
187
+ w = [w, method.to_s.length].max
188
+ rules[method.to_s] = self.instance_method(method).parameters.sort{|a, b|
189
+ a = a.collect{|v| v.to_s}
190
+ b = b.collect{|v| v.to_s}
191
+ if a[0] != b[0]
192
+ a[0] <=> b[0]
193
+ else
194
+ b[1] <=> a[1]
195
+ end
196
+ }.collect{|p| "#{p[1]}:#{p[0]}"}.join(', ')
197
+ }
198
+ rules.each_pair{|method, params|
199
+ if params.length == 0
200
+ puts method
201
+ else
202
+ puts "#{method.ljust(w, ' ')}: #{params}"
203
+ end
204
+ }
205
+ exit
206
+ end
207
+
208
+
209
+ if ARGV.size == 0 && Dir['*.tex'].size == 1
210
+ puts "No TeX file offered, defaulting to #{Dir['*.tex'][0]}"
211
+ ARGV.push(Dir['*.tex'][0])
212
+ end
213
+ error("No filename to process") if ARGV.size == 0
214
+ error("Expected exactly one filename to process #{ARGV.inspect}") if ARGV.size != 1
215
+ tex = File.file?(ARGV[0]) ? ARGV[0] : ARGV[0] + '.tex'
216
+ error(I18n.t('error.FileDoesNotExist', file: tex)) if !File.exists?(tex)
217
+
218
+ Macaw.load_rules
219
+
220
+ puts I18n.t('log.ProcessingFile', file: tex)
221
+
222
+ macaw = Macaw.new(tex)
223
+ @@log = File.open(macaw.base + '.log', 'w') if @@options.log
224
+
225
+ executed = 0
226
+ IO.readlines(tex).each_with_index{|line, lineno|
227
+ @@lineno = lineno + 1
228
+
229
+ next unless line =~ /^% arara: /
230
+ executed += 1
231
+ line.strip!
232
+ line.sub!(/^%\s+arara\s*:\s*/, '')
233
+ data = line.split(':', 2).collect{|v| v.strip}
234
+
235
+ cmd = data[0]
236
+ next if cmd == ''
237
+
238
+ params = {}
239
+ begin
240
+ params = JSON.parse(data[1]) if data[1]
241
+ rescue => e
242
+ error("cannot parse command #{line.inspect}: #{e}")
243
+ end
244
+
245
+ cmd = cmd.intern
246
+
247
+ error "no such rule #{cmd}" unless macaw.respond_to?(cmd)
248
+
249
+ accept = Macaw.instance_method(cmd).parameters
250
+ required = accept.collect{|k, v| k == :req ? v : nil}.compact
251
+ optional = accept.collect{|k, v| k != :req ? v : nil}.compact
252
+
253
+ missing = required - params.keys.collect{|k| k.intern}
254
+ error "#{line}: missing required parameter#{missing.size > 1 ? 's' : ''}: #{missing.inspect}" if missing.size > 0
255
+
256
+ unexpected = params.keys.collect{|k| k.intern} - (required + optional)
257
+ error "#{line}: unexpected parameter#{unexpected.size > 1 ? 's' : ''}: #{unexpected.inspect}" if unexpected.size > 0
258
+
259
+ puts "\n** #{line} **\n"
260
+ macaw.send(cmd, *accept.collect{|k, v| params[v.to_s]})
261
+ }
262
+
263
+ if executed == 0
264
+ puts I18n.t('log.NoDirectivesFound', file: tex)
265
+ else
266
+ puts I18n.t('log.Done')
267
+ end
268
+ end
269
+
270
+ def self.log(line, force=false)
271
+ if @@options.verbose || @@options.progress || force
272
+ print (options.progress && !force ? '.' : line)
273
+ STDOUT.flush
274
+ end
275
+ @@log.write(line) if @@log
276
+ end
277
+
278
+ def self.system(cmd, onfail=:exit)
279
+ throw "errormode can only be #{ERRORMODES.inspect}" unless ERRORMODES.include?(onfail)
280
+
281
+ cmd = cmd.compact.join(' ') if cmd.is_a?(Array)
282
+ log(cmd)
283
+
284
+ output = ''
285
+ begin
286
+ Timeout::timeout(@@options.timeout || 0) {
287
+ Open3.popen2e(cmd) do |stdin, stdout_err, wait_thr|
288
+ while line = stdout_err.gets
289
+ self.log(line)
290
+ output += line + "\n"
291
+ end
292
+
293
+ exit_status = wait_thr.value
294
+ error "cmd failed: #{cmd}", onfail unless exit_status.success?
295
+ end
296
+ }
297
+ rescue Timeout::Error
298
+ error "#{cmd}: timed out after #{@@options.timeout} seconds" if failonerror
299
+ end
300
+ return output
301
+ end
302
+
303
+ def self.load_rules
304
+ CONFIG['paths'].each{|path| require_all path} if CONFIG['paths']
305
+ end
306
+ end
307
+
308
+ Macaw.run