macaw 0.0.1 → 0.0.40

Sign up to get free protection for your applications and to get access to all the features.
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