deplate 0.7.3
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.
- data/AUTHORS.TXT +26 -0
- data/CHANGES.TXT +177 -0
- data/LICENSE.TXT +340 -0
- data/NEWS.TXT +29 -0
- data/README.TXT +86 -0
- data/TODO.TXT +202 -0
- data/VERSION.TXT +1 -0
- data/bin/deplate +3 -0
- data/bin/deplate.bat +2 -0
- data/etc/deplate.ini +361 -0
- data/lib/deplate.rb +31 -0
- data/lib/deplate/abstract-class.rb +30 -0
- data/lib/deplate/builtin.rb +11 -0
- data/lib/deplate/cache.rb +59 -0
- data/lib/deplate/commands.rb +693 -0
- data/lib/deplate/common.rb +335 -0
- data/lib/deplate/converter.rb +99 -0
- data/lib/deplate/core.rb +2705 -0
- data/lib/deplate/css/article.css +545 -0
- data/lib/deplate/css/deplate.css +699 -0
- data/lib/deplate/css/heading-navbar.css +29 -0
- data/lib/deplate/css/layout-deplate-print.css +540 -0
- data/lib/deplate/css/layout-deplate.css +764 -0
- data/lib/deplate/css/sans-serif.css +160 -0
- data/lib/deplate/css/serif-e.css +170 -0
- data/lib/deplate/css/serif-rel.css +121 -0
- data/lib/deplate/css/serif.css +190 -0
- data/lib/deplate/css/slides.css +11 -0
- data/lib/deplate/css/tabbar-left.css +91 -0
- data/lib/deplate/css/tabbar-right-ie.css +14 -0
- data/lib/deplate/css/tabbar-right.css +118 -0
- data/lib/deplate/css/tabbar-top.css +64 -0
- data/lib/deplate/css/tabbar.css +81 -0
- data/lib/deplate/css/text-sans-serif.css +154 -0
- data/lib/deplate/css/text-serif.css +175 -0
- data/lib/deplate/define.rb +439 -0
- data/lib/deplate/docbook.rb +738 -0
- data/lib/deplate/elements.rb +1355 -0
- data/lib/deplate/etc.rb +199 -0
- data/lib/deplate/external.rb +135 -0
- data/lib/deplate/fmt/dbk-article-4.1.2.rb +21 -0
- data/lib/deplate/fmt/dbk-article.rb +46 -0
- data/lib/deplate/fmt/dbk-book.rb +46 -0
- data/lib/deplate/fmt/dbk-ref.rb +105 -0
- data/lib/deplate/fmt/dbk-slides.rb +47 -0
- data/lib/deplate/fmt/dbk-snippet.rb +21 -0
- data/lib/deplate/fmt/html-snippet.rb +21 -0
- data/lib/deplate/fmt/html.rb +1696 -0
- data/lib/deplate/fmt/htmlsite.rb +419 -0
- data/lib/deplate/fmt/htmlslides.rb +21 -0
- data/lib/deplate/fmt/htmlwebsite.rb +70 -0
- data/lib/deplate/fmt/latex-snippet.rb +22 -0
- data/lib/deplate/fmt/latex.rb +1242 -0
- data/lib/deplate/fmt/php.rb +19 -0
- data/lib/deplate/fmt/phpsite.rb +19 -0
- data/lib/deplate/fmt/plain.rb +598 -0
- data/lib/deplate/fmt/template.rb +34 -0
- data/lib/deplate/fmt/xhtml10t.rb +41 -0
- data/lib/deplate/formatter-snippet.rb +17 -0
- data/lib/deplate/formatter.rb +1210 -0
- data/lib/deplate/input.rb +492 -0
- data/lib/deplate/input/deplate-headings.rb +48 -0
- data/lib/deplate/input/deplate-restricted.rb +70 -0
- data/lib/deplate/input/deplate.rb +28 -0
- data/lib/deplate/input/rdoc.rb +277 -0
- data/lib/deplate/input/template.rb +29 -0
- data/lib/deplate/lib/latex/highlight-extra.sty +15 -0
- data/lib/deplate/lib/latex/highlight-typical.sty +15 -0
- data/lib/deplate/lib/tabmenu.js +146 -0
- data/lib/deplate/locale/de.latin1 +708 -0
- data/lib/deplate/locale/ru.koi8-r +48 -0
- data/lib/deplate/locale/zh_cn.gb2312 +35 -0
- data/lib/deplate/macros.rb +639 -0
- data/lib/deplate/messages.rb +120 -0
- data/lib/deplate/metadata.rb +77 -0
- data/lib/deplate/metadata/marshal.rb +24 -0
- data/lib/deplate/metadata/xml.rb +42 -0
- data/lib/deplate/metadata/yaml.rb +26 -0
- data/lib/deplate/mod/anyword.rb +56 -0
- data/lib/deplate/mod/babelfish.rb +27 -0
- data/lib/deplate/mod/code-gvim.rb +52 -0
- data/lib/deplate/mod/code-highlight.rb +91 -0
- data/lib/deplate/mod/colored-log.rb +17 -0
- data/lib/deplate/mod/de.rb +19 -0
- data/lib/deplate/mod/en.rb +17 -0
- data/lib/deplate/mod/endnotes.rb +60 -0
- data/lib/deplate/mod/fr.rb +46 -0
- data/lib/deplate/mod/html-asciimath.rb +40 -0
- data/lib/deplate/mod/html-deplate-button.rb +15 -0
- data/lib/deplate/mod/html-headings-navbar.rb +39 -0
- data/lib/deplate/mod/html-obfuscate-email.rb +47 -0
- data/lib/deplate/mod/html-sidebar.rb +232 -0
- data/lib/deplate/mod/htmlslides-navbar-fh.rb +32 -0
- data/lib/deplate/mod/iconv.rb +35 -0
- data/lib/deplate/mod/imgurl.rb +30 -0
- data/lib/deplate/mod/inlatex-compound.rb +69 -0
- data/lib/deplate/mod/koma.rb +109 -0
- data/lib/deplate/mod/latex-emph-table-head.rb +38 -0
- data/lib/deplate/mod/latex-styles.rb +461 -0
- data/lib/deplate/mod/latex-verbatim-small.rb +29 -0
- data/lib/deplate/mod/makefile.rb +194 -0
- data/lib/deplate/mod/mark-external-urls.rb +38 -0
- data/lib/deplate/mod/markup-1-warn.rb +37 -0
- data/lib/deplate/mod/markup-1.rb +41 -0
- data/lib/deplate/mod/navbar-png.rb +33 -0
- data/lib/deplate/mod/noindent.rb +32 -0
- data/lib/deplate/mod/numpara.rb +40 -0
- data/lib/deplate/mod/particle-math.rb +34 -0
- data/lib/deplate/mod/php-extra.rb +44 -0
- data/lib/deplate/mod/pstoedit.rb +71 -0
- data/lib/deplate/mod/recode.rb +57 -0
- data/lib/deplate/mod/ru_koi8-r.rb +20 -0
- data/lib/deplate/mod/smiley.rb +50 -0
- data/lib/deplate/mod/soffice.rb +23 -0
- data/lib/deplate/mod/symbols-latin1.rb +58 -0
- data/lib/deplate/mod/symbols-od-utf-8.rb +16 -0
- data/lib/deplate/mod/symbols-plain.rb +58 -0
- data/lib/deplate/mod/symbols-sgml.rb +97 -0
- data/lib/deplate/mod/symbols-utf-8.rb +81 -0
- data/lib/deplate/mod/symbols-xml.rb +34 -0
- data/lib/deplate/mod/syntax-region-alt.rb +37 -0
- data/lib/deplate/mod/utf8.rb +49 -0
- data/lib/deplate/mod/validate-html.rb +35 -0
- data/lib/deplate/mod/xmlrpc.rb +233 -0
- data/lib/deplate/mod/zh-cn-autospace.rb +108 -0
- data/lib/deplate/mod/zh-cn.rb +59 -0
- data/lib/deplate/once-method.rb +44 -0
- data/lib/deplate/output.rb +249 -0
- data/lib/deplate/particles.rb +815 -0
- data/lib/deplate/regions.rb +1076 -0
- data/lib/deplate/structured.rb +763 -0
- data/lib/deplate/template.rb +430 -0
- data/lib/deplate/templates/html-doc.html +28 -0
- data/lib/deplate/templates/html-left-tabbar-js.html +37 -0
- data/lib/deplate/templates/html-left-tabbar.html +31 -0
- data/lib/deplate/templates/html-tabbar-right-table.html +43 -0
- data/lib/deplate/templates/html-tabbar-right.html +23 -0
- data/lib/deplate/templates/html-tabbar-top.html +43 -0
- data/lib/deplate/templates/html-tabbar.html +31 -0
- data/lib/deplate/wiki-markup.rb +117 -0
- data/lib/deplate/xml.rb +109 -0
- data/lib/deplate/zh-cn.rb +59 -0
- data/lib/ps2ppm.rb +239 -0
- data/man/man1/deplate.1 +692 -0
- metadata +210 -0
@@ -0,0 +1,335 @@
|
|
1
|
+
# common.rb -- The base class for deplate building blocks
|
2
|
+
# @Author: Thomas Link (samul AT web.de)
|
3
|
+
# @Website: http://deplate.sf.net/
|
4
|
+
# @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
|
5
|
+
# @Created: 02-Aug-2004.
|
6
|
+
# @Last Change: 01-Nov-2005.
|
7
|
+
# @Revision: 0.636
|
8
|
+
#
|
9
|
+
# = Description:
|
10
|
+
# Misc classes
|
11
|
+
|
12
|
+
# require 'forwardable'
|
13
|
+
|
14
|
+
module Deplate::CommonGround
|
15
|
+
#### pointer to the master deplater
|
16
|
+
attr_reader :deplate
|
17
|
+
attr_writer :container
|
18
|
+
attr_accessor :args
|
19
|
+
attr_accessor :styles
|
20
|
+
attr_writer :doc_type
|
21
|
+
attr_accessor :doc_slot
|
22
|
+
attr_reader :label_mode
|
23
|
+
attr_accessor :keep_whitespace
|
24
|
+
attr_accessor :match
|
25
|
+
attr_accessor :level_as_string
|
26
|
+
attr_accessor :level_as_list
|
27
|
+
attr_accessor :level
|
28
|
+
|
29
|
+
#### the element's parsed value
|
30
|
+
attr_accessor :elt
|
31
|
+
attr_accessor :text
|
32
|
+
attr_accessor :source
|
33
|
+
attr_accessor :prologue
|
34
|
+
attr_accessor :epilogue
|
35
|
+
# attr_accessor :expected
|
36
|
+
|
37
|
+
def initialize(deplate, args={})
|
38
|
+
@deplate = deplate
|
39
|
+
@level_as_string = args[:level_as_string] || deplate.get_current_heading
|
40
|
+
@level_as_list = args[:level_as_string] || deplate.current_heading.dup
|
41
|
+
@elt = args[:elt] || nil
|
42
|
+
@text = args[:text] || nil
|
43
|
+
@styles = args[:styles] || []
|
44
|
+
@source = args[:source] || nil
|
45
|
+
@expected = args[:expected] || nil
|
46
|
+
# @args = {:class => self.class, :self => self, :deplate => deplate}
|
47
|
+
@args = args[:args] || {:self => self, :deplate => deplate}
|
48
|
+
@container = args[:container] || nil
|
49
|
+
@prologue = args[:prologue] || []
|
50
|
+
@epilogue = args[:epilogue] || []
|
51
|
+
@can_be_labelled = args[:can_be_labelled] || true
|
52
|
+
@keep_whitespace = args[:keep_whitespace] || false
|
53
|
+
@doc_slot = args[:doc_slot] || nil
|
54
|
+
@doc_type = args[:doc_type] || nil
|
55
|
+
@label_mode = args[:label_mode] || nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def container
|
59
|
+
if @container
|
60
|
+
return @container.container || @container
|
61
|
+
else
|
62
|
+
return nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_args(opts={})
|
67
|
+
update_styles
|
68
|
+
update_id(opts)
|
69
|
+
end
|
70
|
+
|
71
|
+
def update_styles(styles=nil)
|
72
|
+
styles = @args["style"]
|
73
|
+
if styles
|
74
|
+
@styles += styles.split(/[ ,;]/)
|
75
|
+
@styles.uniq!
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_id(opts={})
|
80
|
+
my_id = @args['id']
|
81
|
+
if my_id != @args[:id]
|
82
|
+
aid = @args[:id]
|
83
|
+
@label << aid if aid
|
84
|
+
@args[:id] = my_id
|
85
|
+
if block_given?
|
86
|
+
yield
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_explicit_id
|
92
|
+
@args[:id] || @args['id']
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_id
|
96
|
+
# get_explicit_id || @label[0]
|
97
|
+
get_explicit_id
|
98
|
+
end
|
99
|
+
|
100
|
+
def styles_as_string(sep=" ")
|
101
|
+
if @styles.empty?
|
102
|
+
nil
|
103
|
+
else
|
104
|
+
@styles.join(sep)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def can_be_labelled
|
109
|
+
@can_be_labelled && doc_type == :body
|
110
|
+
end
|
111
|
+
|
112
|
+
def log(text, condition=nil)
|
113
|
+
@deplate.log(text, condition, @source)
|
114
|
+
end
|
115
|
+
|
116
|
+
def match_expected(expected=nil, invoker=self)
|
117
|
+
if kind_of?(Deplate::Regions::Inlatex)
|
118
|
+
return
|
119
|
+
elsif expected
|
120
|
+
if defined?(@prototype)
|
121
|
+
cc = element_or_particle(@prototype)
|
122
|
+
cl = @prototype.class
|
123
|
+
else
|
124
|
+
cc = expected
|
125
|
+
cl = self.class
|
126
|
+
end
|
127
|
+
unless kind_of?(expected) or expected != cc
|
128
|
+
invoker.log(["Expected something of a different class", expected, cl, invoker.class], :error)
|
129
|
+
end
|
130
|
+
# else
|
131
|
+
# invoker.log("Neither element nor particle", :anyway)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def element_or_particle(obj)
|
136
|
+
if obj.kind_of?(Deplate::Element::Clip)
|
137
|
+
obj = obj.elt.first
|
138
|
+
end
|
139
|
+
for c in [Deplate::Element, Deplate::Particle]
|
140
|
+
if obj.kind_of?(c)
|
141
|
+
return c
|
142
|
+
end
|
143
|
+
end
|
144
|
+
log(["Neither block element nor inline text particle", self.class], :error)
|
145
|
+
return nil
|
146
|
+
end
|
147
|
+
|
148
|
+
def doc_slot(default=:body, overwrite=false)
|
149
|
+
if (overwrite and default) or !@doc_slot
|
150
|
+
if @args["slot"]
|
151
|
+
@doc_slot = @args["slot"]
|
152
|
+
else
|
153
|
+
@doc_slot = default
|
154
|
+
end
|
155
|
+
end
|
156
|
+
return @doc_slot
|
157
|
+
end
|
158
|
+
|
159
|
+
def doc_type(default=:body, overwrite=false)
|
160
|
+
if (overwrite and default) or !@doc_type
|
161
|
+
@doc_type = @args["type"] || default
|
162
|
+
end
|
163
|
+
return @doc_type
|
164
|
+
end
|
165
|
+
|
166
|
+
def output(*body)
|
167
|
+
@deplate.formatter.output(self, *body)
|
168
|
+
end
|
169
|
+
|
170
|
+
def output_preferably_at(type, slot, *body)
|
171
|
+
@deplate.formatter.output_at(doc_type(type), doc_slot(slot), *body)
|
172
|
+
end
|
173
|
+
|
174
|
+
def output_at(type, slot, *body)
|
175
|
+
@deplate.formatter.output_at(type, slot, *body)
|
176
|
+
end
|
177
|
+
|
178
|
+
def warn_unpexpected(expected, got)
|
179
|
+
msg = "Expected %s but got %s" % [expected, got]
|
180
|
+
if @invoker
|
181
|
+
@invoker.log(msg, :error)
|
182
|
+
else
|
183
|
+
Deplate::Core.log(msg, :error, @source)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def format_element(agent, *args)
|
188
|
+
return @deplate.formatter.format_element(agent, *args)
|
189
|
+
end
|
190
|
+
|
191
|
+
def plain_caption?
|
192
|
+
@deplate.variables["headings"] == "plain" or @args["plain"]
|
193
|
+
end
|
194
|
+
|
195
|
+
def output_file_name(args={})
|
196
|
+
obj = args[:object] || self
|
197
|
+
label = args[:label]
|
198
|
+
relative = args[:relative]
|
199
|
+
basename = args[:basename]
|
200
|
+
level_as_string = args[:level_as_string]
|
201
|
+
if level_as_string
|
202
|
+
rv = @deplate.file_name_by_level(level_as_string)
|
203
|
+
elsif label
|
204
|
+
rv = @deplate.get_filename_for_label(self, label) || ""
|
205
|
+
else
|
206
|
+
if obj.kind_of?(Deplate::BaseParticle) or obj.kind_of?(Deplate::BaseParticle)
|
207
|
+
obj = obj.container
|
208
|
+
end
|
209
|
+
if obj.kind_of?(Deplate::BaseElement)
|
210
|
+
th = obj.top_heading
|
211
|
+
rv = th.destination
|
212
|
+
end
|
213
|
+
end
|
214
|
+
if rv
|
215
|
+
if basename
|
216
|
+
return File.basename(rv)
|
217
|
+
elsif relative == ""
|
218
|
+
return File.basename(rv)
|
219
|
+
elsif relative
|
220
|
+
dir = File.dirname(relative.output_file_name)
|
221
|
+
return @deplate.relative_path(rv, dir)
|
222
|
+
else
|
223
|
+
return rv
|
224
|
+
end
|
225
|
+
else
|
226
|
+
log(["Internal error in #output_file_name", obj.class.name], :error)
|
227
|
+
raise Exception
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def labels_sans_id
|
232
|
+
id = get_id
|
233
|
+
lbl = @label.dup
|
234
|
+
lbl.delete(id)
|
235
|
+
lbl
|
236
|
+
end
|
237
|
+
|
238
|
+
def output_location(args={})
|
239
|
+
location = [output_file_name(args)]
|
240
|
+
id = get_id
|
241
|
+
location << id if id
|
242
|
+
location.join("#")
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
module Deplate::CommonParticle
|
248
|
+
attr_accessor :current_heading
|
249
|
+
attr_reader :context
|
250
|
+
|
251
|
+
def top_heading
|
252
|
+
@container.top_heading
|
253
|
+
end
|
254
|
+
|
255
|
+
def destination
|
256
|
+
@container.destination
|
257
|
+
end
|
258
|
+
|
259
|
+
def plain_text(*args)
|
260
|
+
@deplate.formatter.plain_text(*args)
|
261
|
+
end
|
262
|
+
|
263
|
+
def format_particle(agent, *args)
|
264
|
+
return @deplate.formatter.format_particle(agent, *args)
|
265
|
+
end
|
266
|
+
|
267
|
+
def format_as_string(object)
|
268
|
+
# <+TBD+>: Doesn't work because the particles don't give info
|
269
|
+
# about their formatting method
|
270
|
+
# @deplate.formatter.format_particle_as_string(object)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
module Deplate::CommonElement
|
276
|
+
attr_accessor :top_heading
|
277
|
+
#### accumulated lines
|
278
|
+
attr_accessor :accum
|
279
|
+
|
280
|
+
def format_as_string(object)
|
281
|
+
klass = object.class
|
282
|
+
fm = klass.formatter
|
283
|
+
if fm
|
284
|
+
return @deplate.formatter.format_element_as_string(object, fm)
|
285
|
+
else
|
286
|
+
log(["Don't know how to format an object of this class", klass], :error)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def add_metadata(source, metadata)
|
291
|
+
if @deplate.options.metadata_model
|
292
|
+
@registered_metadata << @deplate.get_metadata(source, metadata)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def is_explicit?
|
297
|
+
false
|
298
|
+
end
|
299
|
+
|
300
|
+
def update_id(opts={})
|
301
|
+
super {@deplate.add_label(self, get_explicit_id, @level_as_string, :anyway => true)}
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
class Deplate::Base
|
307
|
+
include Deplate::CommonGround
|
308
|
+
|
309
|
+
class << self
|
310
|
+
attr_reader :formatter, :formatter2
|
311
|
+
|
312
|
+
def set_rx(rx)
|
313
|
+
@rx = rx
|
314
|
+
end
|
315
|
+
|
316
|
+
def set_formatter(formatter, alt=false)
|
317
|
+
if alt
|
318
|
+
@formatter2 = formatter
|
319
|
+
else
|
320
|
+
@formatter = formatter
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
class Deplate::BaseElement < Deplate::Base
|
328
|
+
include Deplate::CommonElement
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
class Deplate::BaseParticle < Deplate::Base
|
333
|
+
include Deplate::CommonParticle
|
334
|
+
end
|
335
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# converter.rb
|
3
|
+
# @Author: Thomas Link (samul AT web.de)
|
4
|
+
# @Website: http://deplate.sf.net/
|
5
|
+
# @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
|
6
|
+
# @Created: 14-Okt-2004.
|
7
|
+
# @Last Change: 23-Okt-2005.
|
8
|
+
# @Revision: 0.125
|
9
|
+
#
|
10
|
+
# = Description
|
11
|
+
# = Usage
|
12
|
+
#
|
13
|
+
# require "deplate/converter"
|
14
|
+
# t = <<EOF
|
15
|
+
# * Introduction
|
16
|
+
#
|
17
|
+
# ''deplate'' is a tool for converting wiki-like markup to latex, html, or
|
18
|
+
# "html-slides".
|
19
|
+
# EOF
|
20
|
+
# to_html = Deplate::Converter.new
|
21
|
+
# puts to_html.convert_string(t)
|
22
|
+
#
|
23
|
+
# = TODO
|
24
|
+
# = CHANGES
|
25
|
+
|
26
|
+
require "deplate"
|
27
|
+
|
28
|
+
class Deplate::Converter
|
29
|
+
attr_reader :options, :deplate
|
30
|
+
|
31
|
+
@setup_done = false
|
32
|
+
|
33
|
+
class << self
|
34
|
+
attr_reader :setup_done
|
35
|
+
|
36
|
+
def setup
|
37
|
+
unless @setup_done
|
38
|
+
Deplate::Core.collect_standard
|
39
|
+
@setup_done = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(formatter="html", args={})
|
45
|
+
Deplate::Converter.setup
|
46
|
+
@master = args[:master]
|
47
|
+
@options = args[:options]
|
48
|
+
@options ||= @master.options.dup if @master
|
49
|
+
@options ||= Deplate::Core.deplate_options
|
50
|
+
@options.fmt = formatter
|
51
|
+
@options.modules ||= args[:modules] || []
|
52
|
+
Deplate::Core.require_standard(@options)
|
53
|
+
if block_given?
|
54
|
+
yield(self)
|
55
|
+
end
|
56
|
+
@deplate = Deplate::Core.new(formatter, :options => @options)
|
57
|
+
vars = args[:variables]
|
58
|
+
vars ||= @master.variables.dup if @master
|
59
|
+
@deplate.instance_eval do
|
60
|
+
@variables = vars if vars
|
61
|
+
end
|
62
|
+
@formatter_method = "to_%s" % formatter.gsub(/[^a-zA-Z_]/, "_")
|
63
|
+
end
|
64
|
+
|
65
|
+
def convert_string(string)
|
66
|
+
@deplate.send(@formatter_method, string)
|
67
|
+
end
|
68
|
+
|
69
|
+
def convert_file(filename)
|
70
|
+
@deplate.send(@formatter_method, nil, filename)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# if __FILE__ == $0
|
75
|
+
# t = <<EOF
|
76
|
+
# * Introduction
|
77
|
+
#
|
78
|
+
# ''deplate'' is a tool for converting wiki-like markup to latex, html, or
|
79
|
+
# "html-slides".
|
80
|
+
# EOF
|
81
|
+
# to_html = Deplate::Converter.new
|
82
|
+
# to_latex = Deplate::Converter.new("latex")
|
83
|
+
# to_html_i = Deplate::Converter.new do |cvt|
|
84
|
+
# cvt.options.included = true
|
85
|
+
# end
|
86
|
+
# # to_dbk = Deplate::Converter.new("dbk-article")
|
87
|
+
#
|
88
|
+
# puts "----------------------------------------------------------------"
|
89
|
+
# puts to_html.convert_string(t)
|
90
|
+
# puts "----------------------------------------------------------------"
|
91
|
+
# puts to_html_i.convert_string(t)
|
92
|
+
# puts "----------------------------------------------------------------"
|
93
|
+
# puts to_latex.convert_string(t)
|
94
|
+
# puts "----------------------------------------------------------------"
|
95
|
+
# # puts to_dbk.convert_string(t)
|
96
|
+
# puts "----------------------------------------------------------------"
|
97
|
+
#
|
98
|
+
# end
|
99
|
+
#
|
data/lib/deplate/core.rb
ADDED
@@ -0,0 +1,2705 @@
|
|
1
|
+
# core.rb -- Convert wiki-like plain text pseudo markup to something else
|
2
|
+
# @Author: Thomas Link (samul AT web.de)
|
3
|
+
# @Website: http://deplate.sf.net/
|
4
|
+
# @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
|
5
|
+
# @Created: 24-Feb-2004.
|
6
|
+
# @Last Change: 02-Nov-2005.
|
7
|
+
|
8
|
+
require 'uri'
|
9
|
+
require 'optparse'
|
10
|
+
require 'ostruct'
|
11
|
+
require 'rbconfig'
|
12
|
+
require 'ftools'
|
13
|
+
require 'forwardable'
|
14
|
+
require 'pathname'
|
15
|
+
|
16
|
+
module Deplate; end
|
17
|
+
|
18
|
+
# Deplate::Core is responsible for managing the conversion process.
|
19
|
+
# Deplate::Core.deplate parses command line arguments, creates a
|
20
|
+
# preconfigured instance of Deplate::Core and initiates the conversion
|
21
|
+
# process.
|
22
|
+
#
|
23
|
+
# If you want to use deplate as a library, you should probably use the
|
24
|
+
# Deplate::Converter convenience class.
|
25
|
+
|
26
|
+
class Deplate::Core
|
27
|
+
extend Forwardable
|
28
|
+
|
29
|
+
Version = '0.7.3'
|
30
|
+
MicroRev = '2132'
|
31
|
+
if ENV['HOME']
|
32
|
+
CfgDir = File.join(ENV['HOME'].gsub(/\\/, '/'), '.deplate')
|
33
|
+
elsif ENV['USERPROFILE']
|
34
|
+
CfgDir = File.join(ENV['USERPROFILE'].gsub(/\\/, '/'), 'deplate.rc')
|
35
|
+
else
|
36
|
+
if ENV['WINDIR']
|
37
|
+
CfgDir = File.join(File.dirname(ENV['WINDIR'].gsub(/\\/, '/')) ,'deplate.rc')
|
38
|
+
else
|
39
|
+
CfgDir = '/etc/deplate.rc'
|
40
|
+
end
|
41
|
+
# CfgDir = File.join(Dir.pwd.gsub(/\\/, '/'), 'deplate.rc')
|
42
|
+
puts <<MESSAGE
|
43
|
+
Can't find your personal configuration directory. Neither HOME nor
|
44
|
+
USERPROFILE is set. I will look for configuration files in:
|
45
|
+
#{CfgDir}
|
46
|
+
MESSAGE
|
47
|
+
end
|
48
|
+
LibDir = File.dirname(__FILE__)
|
49
|
+
DataDir = File.join(Config::CONFIG['datadir'], 'deplate')
|
50
|
+
# FileCache = File.join(CfgDir, 'file_list.dat')
|
51
|
+
FileCache = nil
|
52
|
+
|
53
|
+
# The structure containing index definitions
|
54
|
+
IndexEntry = Struct.new('DeplateIndexEntry', :name, :synonymes,
|
55
|
+
:label, :file, :level_as_string)
|
56
|
+
|
57
|
+
# If true, don't load user configuration files.
|
58
|
+
@vanilla = false
|
59
|
+
|
60
|
+
# see #log_valid_condition?
|
61
|
+
@log_treshhold = 4
|
62
|
+
# see #log_valid_condition?
|
63
|
+
@log_events = [:unknown_macro, :newbie]
|
64
|
+
|
65
|
+
# shut up
|
66
|
+
@@quiet = false
|
67
|
+
# color output
|
68
|
+
@@colored_output = false
|
69
|
+
|
70
|
+
# A hash of known modules and their ruby require filenames
|
71
|
+
@@modules = {}
|
72
|
+
# A hash of known formatters and their ruby require filenames
|
73
|
+
@@formatters = {}
|
74
|
+
# A hash of known css files and their ruby file names
|
75
|
+
@@css = {}
|
76
|
+
# A hash of known templates and their ruby file names
|
77
|
+
@@templates = {}
|
78
|
+
# A hash of known encodings and the corresponding module
|
79
|
+
@@symbols = {}
|
80
|
+
# A hash of known input definitions and their ruby require filenames
|
81
|
+
@@input_defs = {}
|
82
|
+
# A hash of known metadata formatters and their ruby require filenames
|
83
|
+
@@metadata_formats = {}
|
84
|
+
|
85
|
+
# This variable (array of strings) can contain some deplate markup that will
|
86
|
+
# be prepended to every file read
|
87
|
+
@@deplate_template = []
|
88
|
+
|
89
|
+
# A hash of {lang => message class}.
|
90
|
+
@@messages = {}
|
91
|
+
# The class of the message catalog that was loaded last
|
92
|
+
@@messages_last = nil
|
93
|
+
# An instance of Deplate::Messages that is used for translating
|
94
|
+
# messages.
|
95
|
+
@@message_object = nil
|
96
|
+
|
97
|
+
# The values for slot names are currently pre-defined in this hash. In the
|
98
|
+
# future they will be calculated dynamically as required.
|
99
|
+
# <+TBD+>This will be subject of change.
|
100
|
+
@@slot_names = {
|
101
|
+
#pre matter
|
102
|
+
:prematter_begin => 0,
|
103
|
+
:doc_def => 5,
|
104
|
+
:doc_beg => 8,
|
105
|
+
:head_beg => 10,
|
106
|
+
:fmt_packages => 13,
|
107
|
+
:mod_packages => 15,
|
108
|
+
:user_packages => 20,
|
109
|
+
:head => 30,
|
110
|
+
:head_meta => 31,
|
111
|
+
:head_identifier => 32,
|
112
|
+
:head_title => 33,
|
113
|
+
:head_extra => 34,
|
114
|
+
:user_head => 35,
|
115
|
+
:mod_head => 40,
|
116
|
+
:user_head => 50,
|
117
|
+
:htmlsite_prev => 55,
|
118
|
+
:htmlsite_up => 56,
|
119
|
+
:htmlsite_next => 57,
|
120
|
+
# Synonyms for the above 3
|
121
|
+
:htmlslides_prev => 55,
|
122
|
+
:htmlslides_up => 56,
|
123
|
+
:htmlslides_next => 57,
|
124
|
+
:html_relations => 58,
|
125
|
+
:css => 60,
|
126
|
+
:javascript => 70,
|
127
|
+
:head_end => 80,
|
128
|
+
:body_beg => 90,
|
129
|
+
:header => 95,
|
130
|
+
:prematter_end => 100,
|
131
|
+
:body_pre => 105,
|
132
|
+
|
133
|
+
#body
|
134
|
+
:body_begin => 0,
|
135
|
+
:navbar_js => 4,
|
136
|
+
:navbar_top => 5,
|
137
|
+
:body_title => 20,
|
138
|
+
:body => 50,
|
139
|
+
:footnotes => 75,
|
140
|
+
:navbar_bottom => 95,
|
141
|
+
:body_end => 100,
|
142
|
+
|
143
|
+
#post matter
|
144
|
+
:body_post => 0,
|
145
|
+
:postmatter_begin => 1,
|
146
|
+
:footer => 5,
|
147
|
+
:html_pageicons_beg => 10,
|
148
|
+
:html_pageicons => 11,
|
149
|
+
:html_pageicons_end => 12,
|
150
|
+
:pre_body_end => 15,
|
151
|
+
:body_end => 20,
|
152
|
+
:doc_end => 50,
|
153
|
+
:postmatter_end => 100,
|
154
|
+
}
|
155
|
+
|
156
|
+
# A hash of formatter names and corresponding classes
|
157
|
+
@@formatter_classes = {}
|
158
|
+
|
159
|
+
# A hash of names of input formats and corresponding classes
|
160
|
+
@@input_classes = {}
|
161
|
+
|
162
|
+
# The IO where to display messages.
|
163
|
+
@@log_destination = $stderr
|
164
|
+
|
165
|
+
class << self
|
166
|
+
# Do what has to be done. This is the method that gets called when
|
167
|
+
# invoking deplate from the command line. It checks the command line
|
168
|
+
# arguments, sets up a Deplate::Core object, and makes it convert the
|
169
|
+
# input files.
|
170
|
+
def deplate(args=ARGV)
|
171
|
+
if ENV['DeplateOptions']
|
172
|
+
for keyval in ENV['DeplateOptions'].split(/\s*;\s*/)
|
173
|
+
key, val = keyval.split(/\s*=\s*/)
|
174
|
+
case key
|
175
|
+
when 'vanilla'
|
176
|
+
@vanilla = true
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
modules, formatters, csss, templates, input_defs, meta_fmts = collect_standard
|
181
|
+
|
182
|
+
options = deplate_options
|
183
|
+
opts = OptionParser.new do |opts|
|
184
|
+
opts.banner = 'Usage: deplate.rb [OPTIONS] FILE [OTHER FILES ...]'
|
185
|
+
opts.separator ''
|
186
|
+
opts.separator 'deplate is a free software with ABSOLUTELY NO WARRANTY under'
|
187
|
+
opts.separator 'the terms of the GNU General Public License version 2.'
|
188
|
+
opts.separator ''
|
189
|
+
|
190
|
+
opts.separator 'General Options:'
|
191
|
+
|
192
|
+
opts.on('-a', '--[no-]ask',
|
193
|
+
'On certain actions, query user before overwriting files') do |bool|
|
194
|
+
log("options.ask_user = #{bool}")
|
195
|
+
options.ask_user = bool
|
196
|
+
end
|
197
|
+
|
198
|
+
opts.on('-A', '--allow ALLOW', 'Allow certain things: x, X, $') do |string|
|
199
|
+
options.allow += string.strip.split(/[,; ]+/)
|
200
|
+
log("options.allow = #{string}")
|
201
|
+
end
|
202
|
+
|
203
|
+
opts.on('-c', '--config FILE', String,
|
204
|
+
'Alternative user cfg file') do |file|
|
205
|
+
log("options.cfg = #{file}")
|
206
|
+
options.cfg = file
|
207
|
+
end
|
208
|
+
|
209
|
+
opts.on('--[no-]clean', 'Clean up temporary files') do |b|
|
210
|
+
log("options.clean = #{b}")
|
211
|
+
options.clean = b
|
212
|
+
end
|
213
|
+
|
214
|
+
opts.on('--color', 'Colored output') do |b|
|
215
|
+
log("options.color = #{b}")
|
216
|
+
enable_color(options)
|
217
|
+
end
|
218
|
+
|
219
|
+
opts.on('--css NAME', csss,
|
220
|
+
'Copy NAME.css to the destination directory, if inexistent') do |file|
|
221
|
+
log("options.css = #{file}")
|
222
|
+
options.css << [file]
|
223
|
+
end
|
224
|
+
|
225
|
+
opts.on('--copy-css NAME', csss,
|
226
|
+
'Copy NAME.css to the destination directory') do |file|
|
227
|
+
log("options.css = #{file}")
|
228
|
+
options.css << [file, true]
|
229
|
+
end
|
230
|
+
|
231
|
+
opts.on('-d', '--dir DIR', String, 'Output directory') do |dir|
|
232
|
+
log("options.dir = #{dir}")
|
233
|
+
if dir.kind_of?(String) and File.directory?(dir)
|
234
|
+
options.dir = dir
|
235
|
+
else
|
236
|
+
log(["Directory doesn't exist", dir], :error)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
opts.on('-D', '--define NAME=VALUE', String,
|
241
|
+
'Define a document option') do |text|
|
242
|
+
m = /^(\w+?)(=(.*))?$/.match(text)
|
243
|
+
if m
|
244
|
+
k = m[1]
|
245
|
+
v = m[3]
|
246
|
+
if v
|
247
|
+
v = remove_backslashes(v.gsub(/(^|[^\\])~/, '\\1 '))
|
248
|
+
else
|
249
|
+
v ='1'
|
250
|
+
end
|
251
|
+
log(%{options.variables[#{k}] = "#{v}"})
|
252
|
+
options.variables[k] = v
|
253
|
+
else
|
254
|
+
log(["Malformed variable definition on command line", text], :error)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
opts.on('-e', '--[no-]each', 'Handle each file separately') do |bool|
|
259
|
+
log("options.each = #{bool}")
|
260
|
+
options.each = bool
|
261
|
+
end
|
262
|
+
|
263
|
+
opts.on('--[no-]force', 'Force output') do |bool|
|
264
|
+
log("options.force = #{bool}")
|
265
|
+
options.force = bool
|
266
|
+
end
|
267
|
+
|
268
|
+
opts.on('-f', '--format FORMAT', String,
|
269
|
+
'Output format (default: html)') do |fmt|
|
270
|
+
log("options.fmt = #{fmt}")
|
271
|
+
if formatters.include?(fmt)
|
272
|
+
options.fmt = fmt
|
273
|
+
else
|
274
|
+
log(["Unknown formatter", fmt, formatters], :error)
|
275
|
+
exit 5
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
opts.on('--[no-]included', 'Output body only') do |bool|
|
280
|
+
log("options.included = #{bool}")
|
281
|
+
options.included = bool
|
282
|
+
end
|
283
|
+
|
284
|
+
opts.on('-i', '--input NAME', String, 'Input definition') do |str|
|
285
|
+
log("options.input_def = #{str}")
|
286
|
+
options.input_def = str
|
287
|
+
end
|
288
|
+
|
289
|
+
opts.on('--list FILE', String,
|
290
|
+
'A file that contains a list of input files') do |file|
|
291
|
+
log("options.list = #{file}")
|
292
|
+
options.list = file
|
293
|
+
end
|
294
|
+
|
295
|
+
opts.on('--log FILE', String,
|
296
|
+
'A file (or - for stdout) where to put the log') do |file|
|
297
|
+
case file
|
298
|
+
when '-'
|
299
|
+
file = $stdout
|
300
|
+
else
|
301
|
+
file = File.expand_path(file)
|
302
|
+
end
|
303
|
+
log("options.log = #{file}")
|
304
|
+
@@log_destination = file
|
305
|
+
end
|
306
|
+
|
307
|
+
opts.on('--[no-]loop', 'Read from stdin forever and ever') do |bool|
|
308
|
+
log("options.loop = #{bool}")
|
309
|
+
options.loop = bool
|
310
|
+
end
|
311
|
+
|
312
|
+
opts.on('--metadata [NAME]', meta_fmts,
|
313
|
+
'Save metadata in this format (default: yaml)') do |str|
|
314
|
+
str ||= 'yaml'
|
315
|
+
log("options.metadata_model = #{str}")
|
316
|
+
unstopable_require(@@metadata_formats[str])
|
317
|
+
end
|
318
|
+
|
319
|
+
opts.on('-m', '--module MODULE', modules, 'Load a module') do |str|
|
320
|
+
log("options.modules << #{str}")
|
321
|
+
options.modules << str
|
322
|
+
end
|
323
|
+
|
324
|
+
opts.on('-o', '--out FILE', String, "Output to file or stdout ('-')") do |file|
|
325
|
+
log("options.out = #{file}")
|
326
|
+
options.out = file
|
327
|
+
options.explicitOut = true
|
328
|
+
end
|
329
|
+
|
330
|
+
opts.on('-p', '--pattern GLOBPATTERN', String, 'File name pattern') do |str|
|
331
|
+
log("options.file_pattern = #{remove_backslashes(str)}")
|
332
|
+
options.file_pattern = remove_backslashes(str)
|
333
|
+
end
|
334
|
+
|
335
|
+
opts.on('-P', '--exclude GLOBPATTERN', String,
|
336
|
+
'Excluded file name pattern') do |str|
|
337
|
+
log("options.file_excl_pattern = #{remove_backslashes(str)}")
|
338
|
+
options.file_excl_pattern = remove_backslashes(str)
|
339
|
+
end
|
340
|
+
|
341
|
+
opts.on('-r', '--[no-]recurse', 'Recurse into directories') do |bool|
|
342
|
+
log("options.recurse = #{bool}")
|
343
|
+
options.recurse = bool
|
344
|
+
end
|
345
|
+
|
346
|
+
opts.on('--reset-filecache', 'Reset the file database') do |bool|
|
347
|
+
log("options.reset_filecache = #{bool}")
|
348
|
+
if File.exist?(FileCache)
|
349
|
+
File.delete(FileCache)
|
350
|
+
log("Deleting file database. Files will be re-scanned on next run.", :anyway)
|
351
|
+
end
|
352
|
+
exit 0
|
353
|
+
end
|
354
|
+
|
355
|
+
opts.on('-R', '--[no-]Recurse', 'Recurse and rebuild hierarchy') do |bool|
|
356
|
+
log("options.recurse_hierarchy = #{bool}")
|
357
|
+
options.recurse = bool
|
358
|
+
options.recurse_hierarchy = bool
|
359
|
+
end
|
360
|
+
|
361
|
+
opts.on('--[no-]simple-names', 'Disable simple wiki names') do |bool|
|
362
|
+
unless bool
|
363
|
+
options.disabled_particles << Deplate::HyperLink::Simple
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
opts.on('--suffix SUFFIX', String, 'Suffix for output files') do |str|
|
368
|
+
log("options.suffix = #{str}")
|
369
|
+
options.suffix = str
|
370
|
+
end
|
371
|
+
|
372
|
+
opts.on('-t', '--template NAME', String, 'Template to use') do |str|
|
373
|
+
if templates.include?(str) or File.exist?(str)
|
374
|
+
log("options.template = #{@@templates[str]}")
|
375
|
+
options.template = str
|
376
|
+
else
|
377
|
+
log(["Template not found", str], :error)
|
378
|
+
exit 5
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
opts.on('--[no-]vanilla', 'Ignore user configuration') do |bool|
|
383
|
+
log("options.vanilla = #{bool}")
|
384
|
+
options.vanilla = bool
|
385
|
+
end
|
386
|
+
|
387
|
+
opts.on('-x', '--allow-ruby [RUBY SAFE]', Integer,
|
388
|
+
'Allow the execution of ruby code') do |level|
|
389
|
+
if level
|
390
|
+
options.allow_ruby = level
|
391
|
+
options.allow << level
|
392
|
+
else
|
393
|
+
options.allow_ruby = true
|
394
|
+
options.allow << 'x'
|
395
|
+
end
|
396
|
+
log("options.allow_ruby = #{options.allow_ruby}")
|
397
|
+
end
|
398
|
+
|
399
|
+
opts.on('-X', '--[no-]allow-exec', '--[no-]external',
|
400
|
+
'Allow the execution of helper applications') do |bool|
|
401
|
+
options.allow_external = bool
|
402
|
+
options.allow << 'X'
|
403
|
+
log("options.allow_external = #{bool}")
|
404
|
+
end
|
405
|
+
|
406
|
+
opts.separator ""
|
407
|
+
opts.separator "LaTeX Formatter:"
|
408
|
+
|
409
|
+
opts.on('--[no-]pdf', 'Prepare for use with pdf(la)tex') do |bool|
|
410
|
+
log("options.pdftex = #{bool}")
|
411
|
+
options.pdftex = bool
|
412
|
+
options.variables['pdfOutput'] = true
|
413
|
+
end
|
414
|
+
|
415
|
+
opts.separator ''
|
416
|
+
opts.separator 'Available input defintions:'
|
417
|
+
opts.separator input_defs.join(', ')
|
418
|
+
|
419
|
+
opts.separator ''
|
420
|
+
opts.separator 'Available formatters:'
|
421
|
+
opts.separator formatters.join(', ')
|
422
|
+
|
423
|
+
opts.separator ''
|
424
|
+
opts.separator 'Available metadata formats:'
|
425
|
+
opts.separator meta_fmts.join(', ')
|
426
|
+
|
427
|
+
opts.separator ''
|
428
|
+
opts.separator 'Available modules:'
|
429
|
+
opts.separator modules.join(', ')
|
430
|
+
|
431
|
+
opts.separator ''
|
432
|
+
opts.separator 'Available css files:'
|
433
|
+
opts.separator csss.join(', ')
|
434
|
+
|
435
|
+
opts.separator ''
|
436
|
+
opts.separator 'Available templates:'
|
437
|
+
opts.separator templates.join(', ')
|
438
|
+
|
439
|
+
opts.separator ''
|
440
|
+
opts.separator 'Other Options:'
|
441
|
+
|
442
|
+
opts.on('--debug [LEVEL]', Integer, 'Show debug messages') do |v|
|
443
|
+
if v
|
444
|
+
@log_treshhold = v
|
445
|
+
end
|
446
|
+
$DEBUG = TRUE
|
447
|
+
$VERBOSE = TRUE
|
448
|
+
end
|
449
|
+
|
450
|
+
opts.on('--[no-]profile', 'Profile execution') do |b|
|
451
|
+
log("profile = #{b}")
|
452
|
+
# require "profile" if b
|
453
|
+
end
|
454
|
+
|
455
|
+
opts.on('--[no-]quiet', 'Be quiet') do |bool|
|
456
|
+
log("quiet = #{bool}")
|
457
|
+
@@quiet = bool
|
458
|
+
end
|
459
|
+
|
460
|
+
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
|
461
|
+
log("verbose = #{v}")
|
462
|
+
$VERBOSE = v
|
463
|
+
end
|
464
|
+
|
465
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
466
|
+
puts opts
|
467
|
+
exit 1
|
468
|
+
end
|
469
|
+
|
470
|
+
opts.on_tail('--version', 'Show version') do
|
471
|
+
puts version
|
472
|
+
exit 0
|
473
|
+
end
|
474
|
+
|
475
|
+
opts.on_tail('--microversion', 'Show version') do
|
476
|
+
puts microversion
|
477
|
+
exit 0
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
@@command_line_args = args.dup
|
482
|
+
options.files = opts.parse!(args)
|
483
|
+
|
484
|
+
if options.list
|
485
|
+
accum = []
|
486
|
+
File.open(options.list) do |io|
|
487
|
+
io.each {|l| accum << l.chomp}
|
488
|
+
end
|
489
|
+
options.files = accum + options.files
|
490
|
+
else
|
491
|
+
if options.files.empty?
|
492
|
+
failhelp(opts, 'No input files given!')
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
options.fmt ||= 'html'
|
497
|
+
require_standard(options)
|
498
|
+
|
499
|
+
formatter_class = @@formatter_classes[options.fmt]
|
500
|
+
|
501
|
+
# if options.multi_file_output
|
502
|
+
# if !options.dir
|
503
|
+
# failhelp(opts, "Output to multiple files requires the --dir option!")
|
504
|
+
# elsif options.each
|
505
|
+
# failhelp(opts, "Cannot use --each and multi-file output at the same time!")
|
506
|
+
# end
|
507
|
+
# end
|
508
|
+
|
509
|
+
if formatter_class
|
510
|
+
f = options.files[0]
|
511
|
+
options.dir ||= '.'
|
512
|
+
case f
|
513
|
+
when '-'
|
514
|
+
options.ext = ''
|
515
|
+
options.srcdir ||= Dir.pwd
|
516
|
+
options.out ||= '-'
|
517
|
+
else
|
518
|
+
options.ext = File.extname(f)
|
519
|
+
options.srcdir ||= File.dirname(f)
|
520
|
+
options.suffix ||= formatter_class.suffix
|
521
|
+
if options.out
|
522
|
+
options.out = options.dir ? File.join(options.dir, options.out) : options.out
|
523
|
+
else
|
524
|
+
options.out = get_out_fullname(f, options.suffix, options)
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
prc = Deplate::Core.new(options.fmt,
|
529
|
+
:formatter => formatter_class,
|
530
|
+
:options => options,
|
531
|
+
:now => true)
|
532
|
+
else
|
533
|
+
log(["Unknown formatter", options.fmt], :error)
|
534
|
+
exit 5
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
# Load the formatter, all required modules, and the user configuration
|
539
|
+
# files.
|
540
|
+
def require_standard(options)
|
541
|
+
require_input(options)
|
542
|
+
|
543
|
+
require_formatter(options)
|
544
|
+
|
545
|
+
for m in options.modules
|
546
|
+
require_module(options, m) if m
|
547
|
+
end
|
548
|
+
|
549
|
+
require 'deplate/mod/en' unless @@messages_last
|
550
|
+
|
551
|
+
# load general user config
|
552
|
+
options.cfg ||= File.join(CfgDir, 'config.rb')
|
553
|
+
unless options.vanilla
|
554
|
+
user_config(options, options.cfg)
|
555
|
+
user_setup(options) if defined?(user_setup)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
# Read CfgDir/deplate.ini
|
560
|
+
#
|
561
|
+
# This file knows the following commands/entries
|
562
|
+
# <tt>mod NAME</tt>:: Load module NAME
|
563
|
+
# <tt>fmt NAME</tt>:: Set the default formatter
|
564
|
+
# <tt>clip NAME=TEXT</tt>:: Set a clip
|
565
|
+
# <tt>wiki NAME.SUFFIX BASEURL</tt>:: Define an interwiki
|
566
|
+
# <tt>wikichars UPPER LOWER</tt>:: Define the set of allowed
|
567
|
+
# character in wiki names
|
568
|
+
# <tt>VAR=VALUE</tt>:: Set the variable VAR to VALUE
|
569
|
+
# <tt>$ENV=VALUE</tt>:: Set the environment variable VAR to VALUE
|
570
|
+
#
|
571
|
+
# Lines beginning with one of ';#%' are considered comments.
|
572
|
+
def read_ini(options)
|
573
|
+
for inifile in [
|
574
|
+
File.join(Dir.pwd, "deplate.rc", 'deplate.ini'),
|
575
|
+
File.join(CfgDir, 'deplate.ini'),
|
576
|
+
]
|
577
|
+
if File.exist?(inifile)
|
578
|
+
ini = File.open(inifile) {|io| io.read}
|
579
|
+
mode = :normal
|
580
|
+
acc = []
|
581
|
+
endm = nil
|
582
|
+
setter = nil
|
583
|
+
for line in ini
|
584
|
+
line.chomp!
|
585
|
+
case mode
|
586
|
+
when :normal
|
587
|
+
if line =~ /^\s*[;#%*]/
|
588
|
+
# comment
|
589
|
+
next
|
590
|
+
elsif line =~ /^\s*mod\s+(\S+)/
|
591
|
+
options.modules << $1
|
592
|
+
elsif line =~ /^\s*fmt\s+(\S+)/
|
593
|
+
options.fmt = $1
|
594
|
+
elsif line =~ /^\s*clip\s+([^\s=]+)\s*=\s*(.+)/
|
595
|
+
options.clips[$1] = $2
|
596
|
+
elsif line =~ /^\s*wiki ([A-Z]+)(\.\w+)?\s*=\s*(.+)/
|
597
|
+
Deplate::InterWiki.add($1, $3, $2)
|
598
|
+
elsif line =~ /^\s*wikichars\s*(\S+)\s*(\S+)/
|
599
|
+
Deplate::HyperLink.setup($1, $2)
|
600
|
+
elsif line =~ /^\s*app\s*([_\w]+)\s*=\s*(.+)/
|
601
|
+
Deplate::External.def_app $1, $2
|
602
|
+
elsif line =~ /^\s*(option\s+|:)([_\w]+)([!~]|\s*([?%])?=\s*(.+))/
|
603
|
+
case $3
|
604
|
+
when '!'
|
605
|
+
val = true
|
606
|
+
when '~'
|
607
|
+
val = false
|
608
|
+
else
|
609
|
+
case $4
|
610
|
+
when '?'
|
611
|
+
case $5
|
612
|
+
when 'true', 'yes', 'on'
|
613
|
+
val = true
|
614
|
+
when 'false', 'no', 'off'
|
615
|
+
val = false
|
616
|
+
else
|
617
|
+
Deplate::Core.log(['Malformed configuration line', line], :error)
|
618
|
+
next
|
619
|
+
end
|
620
|
+
when '%'
|
621
|
+
val = $5.to_i
|
622
|
+
else
|
623
|
+
val = $5
|
624
|
+
end
|
625
|
+
end
|
626
|
+
options.send("#$2=", val)
|
627
|
+
elsif line =~ /^\s*\$(\S+)\s*=\s*(.+)/
|
628
|
+
ENV[$1] = $2
|
629
|
+
elsif line =~ /^\s*(\S+)\s*=<<(.+)/
|
630
|
+
mode = :multiline
|
631
|
+
endm = $2
|
632
|
+
setter = lambda {|val| options.variables[$1] = val}
|
633
|
+
elsif line =~ /^\s*(\S+)\s*=\s*(.+)/
|
634
|
+
options.variables[$1] = $2
|
635
|
+
end
|
636
|
+
when :multiline
|
637
|
+
if line == endm
|
638
|
+
setter.call(acc)
|
639
|
+
acc = []
|
640
|
+
mode = :normal
|
641
|
+
else
|
642
|
+
acc << line
|
643
|
+
end
|
644
|
+
else
|
645
|
+
raise "Invalid mode"
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
# Load the input definition if any.
|
653
|
+
# options:: A OpenStruct as returned by Deplate::Core.deplate_options
|
654
|
+
def require_input(options, input_name=nil)
|
655
|
+
name = input_name || options.input_def
|
656
|
+
rb = @@input_defs[name]
|
657
|
+
if rb
|
658
|
+
require rb
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
# Load the formatter named in options.fmt.
|
663
|
+
# options:: A OpenStruct as returned by Deplate::Core.deplate_options
|
664
|
+
def require_formatter(options)
|
665
|
+
fmt = options.fmt
|
666
|
+
if @@formatter_classes[fmt]
|
667
|
+
Deplate::Core.log(['Formatter already loaded', fmt])
|
668
|
+
else
|
669
|
+
Deplate::Core.log(['Require formatter', fmt])
|
670
|
+
fmtf = @@formatters[fmt]
|
671
|
+
require fmtf
|
672
|
+
end
|
673
|
+
user_config(options, File.join(CfgDir, 'after', 'fmt', '%s.rb' % fmt))
|
674
|
+
user_config(options, File.join(CfgDir, 'after', 'fmt', fmt))
|
675
|
+
end
|
676
|
+
|
677
|
+
# Load a module.
|
678
|
+
# options:: A OpenStruct as returned by Deplate::Core.deplate_options
|
679
|
+
# module_name:: The name of the module to be loaded
|
680
|
+
def require_module(options, module_name)
|
681
|
+
Deplate::Core.log(["Require module", module_name])
|
682
|
+
mf = @@modules[module_name]
|
683
|
+
vsave, $VERBOSE = $VERBOSE, false
|
684
|
+
begin
|
685
|
+
require mf
|
686
|
+
user_config(options, File.join(CfgDir, 'after', 'mod', '%s.rb' % module_name))
|
687
|
+
user_config(options, File.join(CfgDir, 'after', 'mod', module_name))
|
688
|
+
rescue Exception => e
|
689
|
+
Deplate::Core.log(["Loading module failed", module_name, e], :error)
|
690
|
+
ensure
|
691
|
+
$VERBOSE = vsave
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
# require NAME but don't stop if an error occurs; takes an optional block as
|
696
|
+
# argument, which is called when the file was successfully loaded.
|
697
|
+
# name:: A string passed on to +require+
|
698
|
+
def unstopable_require(name)
|
699
|
+
begin
|
700
|
+
require name
|
701
|
+
if block_given?
|
702
|
+
yield
|
703
|
+
end
|
704
|
+
rescue Exception => e
|
705
|
+
log(["Require failed", name, e], :error)
|
706
|
+
end
|
707
|
+
end
|
708
|
+
|
709
|
+
# Load the user configuration for a file/module.
|
710
|
+
# options:: A OpenStruct as returned by Deplate::Core.deplate_options
|
711
|
+
# file:: Either a file name or a directory; if it is a
|
712
|
+
# directory, all ruby files in that directory will be loaded
|
713
|
+
def user_config(options, file)
|
714
|
+
unless options.vanilla
|
715
|
+
if File.exist?(file)
|
716
|
+
if File.stat(file).directory?
|
717
|
+
for f in Dir[File.join(file, '*.rb')]
|
718
|
+
user_config(options, f)
|
719
|
+
end
|
720
|
+
else
|
721
|
+
Deplate::Core.log(["Loading", file])
|
722
|
+
load(file)
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
# Set up the standard options structure.
|
729
|
+
# inherit:: A OpenStruct; if provided, reuse it
|
730
|
+
def deplate_options(inherit=nil)
|
731
|
+
options = inherit || OpenStruct.new
|
732
|
+
options.modules ||= []
|
733
|
+
options.clean ||= true
|
734
|
+
options.force ||= true
|
735
|
+
options.css ||= []
|
736
|
+
options.variables ||= Deplate::Variables.new
|
737
|
+
options.clips ||= {}
|
738
|
+
options.ext ||= ''
|
739
|
+
options.dir ||= '.'
|
740
|
+
options.allow ||= []
|
741
|
+
options.disabled_particles ||= []
|
742
|
+
read_ini(options) unless inherit
|
743
|
+
return options
|
744
|
+
end
|
745
|
+
|
746
|
+
# Return the current version number as string.
|
747
|
+
def version
|
748
|
+
Deplate::Core::Version
|
749
|
+
end
|
750
|
+
|
751
|
+
# Return the current micorversion number as string.
|
752
|
+
def microversion
|
753
|
+
[Deplate::Core::Version, Deplate::Core::MicroRev].join('-')
|
754
|
+
end
|
755
|
+
|
756
|
+
# Enable colored log output
|
757
|
+
# options:: A OpenStruct as returned by Deplate::Core.deplate_options
|
758
|
+
def enable_color(options)
|
759
|
+
unless @@colored_output
|
760
|
+
unstopable_require('term/ansicolor') do
|
761
|
+
eval <<-EOR
|
762
|
+
class Color
|
763
|
+
class << self
|
764
|
+
include Term::ANSIColor
|
765
|
+
end
|
766
|
+
end
|
767
|
+
EOR
|
768
|
+
@@colored_output = options.color = true
|
769
|
+
end
|
770
|
+
end
|
771
|
+
end
|
772
|
+
|
773
|
+
# Collect all available modules, formatters, css files, and templates.
|
774
|
+
def collect_standard
|
775
|
+
if FileCache and File.exist?(FileCache)
|
776
|
+
File.open(FileCache) do |f|
|
777
|
+
data = Marshal.load(f)
|
778
|
+
if data['version'] == Deplate::Core.microversion
|
779
|
+
modules, @@modules = data['modules']
|
780
|
+
formatters, @@formatters = data['formatters']
|
781
|
+
csss, @@css = data['css']
|
782
|
+
templates, @@templates = data['templates']
|
783
|
+
input_defs, @@input_defs = data['input']
|
784
|
+
meta_fmts, @@metadata_formats = data['metadata']
|
785
|
+
Deplate::Core.log(['Using file cache', FileCache])
|
786
|
+
return modules, formatters, csss, templates, input_defs, meta_fmts
|
787
|
+
else
|
788
|
+
Deplate::Core.log(['Old file cache', FileCache])
|
789
|
+
end
|
790
|
+
end
|
791
|
+
end
|
792
|
+
modules, @@modules = collect_deplate_options('modules', 'mod')
|
793
|
+
formatters, @@formatters = collect_deplate_options('formatters','fmt')
|
794
|
+
csss, @@css = collect_deplate_options('css', 'css', '.css')
|
795
|
+
templates, @@templates = collect_deplate_options('templates', 'templates', '')
|
796
|
+
input_defs, @@input_defs = collect_deplate_options('input', 'input')
|
797
|
+
meta_fmts, @@metadata_formats = collect_deplate_options('metadata', 'metadata')
|
798
|
+
if FileCache
|
799
|
+
File.open(FileCache, 'w+') do |f|
|
800
|
+
data = {
|
801
|
+
'version' => Deplate::Core.microversion,
|
802
|
+
'modules' => [modules, @@modules],
|
803
|
+
'formatters' => [formatters, @@formatters],
|
804
|
+
'css' => [csss, @@css],
|
805
|
+
'templates' => [templates, @@templates],
|
806
|
+
'input' => [input_defs, @@input_defs],
|
807
|
+
'metadata' => [meta_fmts, @@metadata_formats],
|
808
|
+
}
|
809
|
+
Marshal.dump(data, f)
|
810
|
+
Deplate::Core.log(['Create file cache', FileCache])
|
811
|
+
end
|
812
|
+
end
|
813
|
+
return modules, formatters, csss, templates, input_defs, meta_fmts
|
814
|
+
end
|
815
|
+
|
816
|
+
# This is the actual logging method. Every log message should pass
|
817
|
+
# through this method.
|
818
|
+
def log(text, condition=nil, source=nil)
|
819
|
+
if log_valid_condition?(condition)
|
820
|
+
text = log_filter(text)
|
821
|
+
if source
|
822
|
+
msg = log_build_message(text, condition, source.file, source.begin, source.end)
|
823
|
+
else
|
824
|
+
msg = log_build_message(text, condition)
|
825
|
+
end
|
826
|
+
log_to(msg)
|
827
|
+
log_to(caller.join("\n")) if $DEBUG and condition == :error
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
# Remove all backslashes from +text+
|
832
|
+
def remove_backslashes(text)
|
833
|
+
return text.gsub(/\\(.)/, '\\1')
|
834
|
+
end
|
835
|
+
|
836
|
+
# Clean +idx+'s (a instance of Deplate::IndexEntry) name from backslashes
|
837
|
+
def get_index_name(idx)
|
838
|
+
# return remove_backslashes(idx.name.split(/\s*\|\s*/)[0])
|
839
|
+
return remove_backslashes(idx.name)
|
840
|
+
end
|
841
|
+
|
842
|
+
# Retrieve field information from a "property list" as used form, e.g.,
|
843
|
+
# specifying column widths in tables
|
844
|
+
def props(proplist, field)
|
845
|
+
if proplist
|
846
|
+
proplist.split(/\s*[,;]\s*/).collect do |c|
|
847
|
+
rv = nil
|
848
|
+
for key, val in c.scan(/(\w+?)[:.](\S+)/)
|
849
|
+
if key == field
|
850
|
+
rv = val
|
851
|
+
break
|
852
|
+
end
|
853
|
+
end
|
854
|
+
rv
|
855
|
+
end
|
856
|
+
else
|
857
|
+
[]
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
# Return the canonic file name for +name+. +maj+ and +min+
|
862
|
+
# correspond to section numbers.
|
863
|
+
def canonic_file_name(name, sfx, maj=nil, min=nil, dir=nil)
|
864
|
+
name = File.basename(name, File.extname(name))
|
865
|
+
name = clean_file_name(name)
|
866
|
+
if !name or (maj and maj != 0)
|
867
|
+
canonic_numbered_file_name(name, sfx, maj, min, dir)
|
868
|
+
elsif min and min != 0
|
869
|
+
# fn = "%s.%02d%s" % [name, min, sfx]
|
870
|
+
canonic_numbered_file_name(name, sfx, 0, min, dir)
|
871
|
+
else
|
872
|
+
fn = name + (sfx || '')
|
873
|
+
dir ? File.join(dir, fn) : fn
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
877
|
+
# Return a valid file name based on +text+
|
878
|
+
def clean_file_name(text, replacement='_')
|
879
|
+
text.gsub(/[[:cntrl:].+*:"?<>|&\\\/]/, replacement) if text
|
880
|
+
end
|
881
|
+
|
882
|
+
# Make sure +dir+ exists
|
883
|
+
def ensure_dir_exists(dir)
|
884
|
+
unless File.exist?(dir) or dir.empty? or dir == '.'
|
885
|
+
parent = File.dirname(dir)
|
886
|
+
unless File.exist?(parent)
|
887
|
+
ensure_dir_exists(parent)
|
888
|
+
end
|
889
|
+
Deplate::Core.log(["Creating directory", dir])
|
890
|
+
Dir.mkdir(dir)
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
# Return the output directory for +fname+
|
895
|
+
def get_out_name_dir(fname, options)
|
896
|
+
if options.recurse_hierarchy
|
897
|
+
# dir = File.dirname(fname).split(Regexp.new(Regexp.escape(File::SEPARATOR)))[1..-1]
|
898
|
+
# dir = File.dirname(fname).split(Regexp.new(Regexp.escape(File::SEPARATOR)))
|
899
|
+
# return File.join(*dir)
|
900
|
+
return File.dirname(fname)
|
901
|
+
else
|
902
|
+
return '.'
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
# Purge *path and return it as sting
|
907
|
+
def get_path(*path)
|
908
|
+
path.compact!
|
909
|
+
path.delete(".")
|
910
|
+
path.delete("")
|
911
|
+
File.join(*path)
|
912
|
+
end
|
913
|
+
|
914
|
+
# Get the canonic output filename for fname.
|
915
|
+
# fname:: The input file name
|
916
|
+
# suffix:: The suffix to use
|
917
|
+
# options:: A OpenStruct as returned by Deplate::Core.deplate_options
|
918
|
+
# maj:: The major section/page number
|
919
|
+
# maj:: The minor section/page number
|
920
|
+
def get_out_fullname(fname, suffix, options, *args)
|
921
|
+
File.join(options.dir, get_out_name(fname, suffix, options, *args))
|
922
|
+
end
|
923
|
+
|
924
|
+
def get_out_name(fname, suffix, options, maj=nil, min=nil)
|
925
|
+
path = []
|
926
|
+
path << get_out_name_dir(fname, options)
|
927
|
+
if suffix
|
928
|
+
fn = File.basename(fname, options.ext)
|
929
|
+
path << canonic_file_name(fn, suffix, maj, min)
|
930
|
+
else
|
931
|
+
path << fname
|
932
|
+
end
|
933
|
+
get_path(*path)
|
934
|
+
end
|
935
|
+
|
936
|
+
def declare_input_format(input_class, name=nil)
|
937
|
+
@@input_classes[name || input_class.myname] = input_class
|
938
|
+
end
|
939
|
+
|
940
|
+
# Make a formatter class publically known.
|
941
|
+
def declare_formatter(formatter_class, name=nil)
|
942
|
+
@@formatter_classes[formatter_class.myname] = formatter_class
|
943
|
+
name = (name || formatter_class.myname).gsub(/\W/, '_')
|
944
|
+
self.class_eval %{
|
945
|
+
def to_#{name}(text, sourcename=nil)
|
946
|
+
format_with_formatter(#{formatter_class}, text, sourcename)
|
947
|
+
end
|
948
|
+
}
|
949
|
+
# class << self
|
950
|
+
# define_method("to_#{name}") do |text, *args|
|
951
|
+
# sourcename, _ = args
|
952
|
+
# format_with_formatter(formatter_class, text, sourcename)
|
953
|
+
# end
|
954
|
+
# end
|
955
|
+
end
|
956
|
+
|
957
|
+
# Collect all available modules/parts/libraries etc. Check the
|
958
|
+
# file system and the "builtin" modules (e.g., when using the
|
959
|
+
# win32 exerb distribution).
|
960
|
+
def collect_deplate_options(id=nil, subdir='', suffix='.rb')
|
961
|
+
hash = {}
|
962
|
+
for d in library_directories(subdir)
|
963
|
+
collect_deplate_options_in_hash(hash, suffix, Dir[File.join(d, '*%s' % suffix)])
|
964
|
+
end
|
965
|
+
|
966
|
+
builtin = "builtin_#{id}"
|
967
|
+
if id and respond_to?(builtin)
|
968
|
+
collect_deplate_options_in_hash(hash, suffix, send(builtin), File.join('deplate', subdir))
|
969
|
+
end
|
970
|
+
|
971
|
+
return hash.keys.sort, hash
|
972
|
+
end
|
973
|
+
|
974
|
+
# Return an array of directorys that could contain deplate
|
975
|
+
# files.
|
976
|
+
def library_directories(*subdirs)
|
977
|
+
@library_directories ||= {}
|
978
|
+
id = File.join(*subdirs)
|
979
|
+
unless @library_directories[id]
|
980
|
+
dirs = []
|
981
|
+
dirs << File.join(CfgDir, *subdirs) unless @vanilla
|
982
|
+
dirs << File.join(DataDir, *subdirs)
|
983
|
+
dirs << File.join(LibDir, *subdirs)
|
984
|
+
dirs.delete_if {|d| !File.exist?(d)}
|
985
|
+
@library_directories[id] = dirs
|
986
|
+
end
|
987
|
+
@library_directories[id]
|
988
|
+
end
|
989
|
+
|
990
|
+
private
|
991
|
+
# Return the localized text.
|
992
|
+
def msg(text)
|
993
|
+
if @@message_object
|
994
|
+
@@message_object.msg(text)
|
995
|
+
else
|
996
|
+
text
|
997
|
+
end
|
998
|
+
end
|
999
|
+
|
1000
|
+
# Return the proper numbered output filename for name.
|
1001
|
+
def canonic_numbered_file_name(name, sfx, maj, min, dir=nil)
|
1002
|
+
name = File.basename(name, File.extname(name))
|
1003
|
+
if min == 0
|
1004
|
+
idx = '%05d' % maj
|
1005
|
+
else
|
1006
|
+
idx = '%05d_%02d' % [maj, min]
|
1007
|
+
end
|
1008
|
+
fn = [name, idx, sfx].join
|
1009
|
+
dir ? File.join(dir, fn) : fn
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
# Collect files in +array+ in +hash+.
|
1013
|
+
def collect_deplate_options_in_hash(hash, suffix, array, subdir=nil)
|
1014
|
+
for m in array
|
1015
|
+
key = File.basename(m, suffix) || m
|
1016
|
+
hash[key] ||= subdir ? File.join(subdir, m) : m
|
1017
|
+
hash[key] ||= subdir ? File.join(subdir, m) : m
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
# Display text and exit.
|
1022
|
+
def failhelp(opts, text)
|
1023
|
+
puts text
|
1024
|
+
puts
|
1025
|
+
puts opts
|
1026
|
+
exit 5
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
# * If @@quiet is true, don't display any messages.
|
1030
|
+
# * If +condition+ is :anyway, :error, or +true+, display the
|
1031
|
+
# message.
|
1032
|
+
# * If +condition+ is :debug, display the message if $DEBUG is
|
1033
|
+
# set.
|
1034
|
+
# * If +condition+ is a numeric, display the message if +condition+
|
1035
|
+
# is less or equal @log_treshhold
|
1036
|
+
# * If +condition+ is a symbol, display the message if @log_events
|
1037
|
+
# contains +condition+.
|
1038
|
+
def log_valid_condition?(condition)
|
1039
|
+
if @@quiet
|
1040
|
+
return false
|
1041
|
+
elsif condition
|
1042
|
+
case condition
|
1043
|
+
when :anyway, :error, true
|
1044
|
+
return true
|
1045
|
+
when :debug
|
1046
|
+
return $DEBUG
|
1047
|
+
when Numeric
|
1048
|
+
return condition <= @log_treshhold
|
1049
|
+
when Symbol
|
1050
|
+
return @log_events.include?(condition)
|
1051
|
+
end
|
1052
|
+
else
|
1053
|
+
return $VERBOSE
|
1054
|
+
end
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
# Convert text into a localized message.
|
1058
|
+
# text:: Either an array or a string.
|
1059
|
+
def log_filter(text)
|
1060
|
+
case text
|
1061
|
+
when Array
|
1062
|
+
m = []
|
1063
|
+
text.each_with_index do |t, i|
|
1064
|
+
if i == 0
|
1065
|
+
m << log_filter(t)
|
1066
|
+
else
|
1067
|
+
case t
|
1068
|
+
when Array
|
1069
|
+
m << t.join("\n")
|
1070
|
+
else
|
1071
|
+
m << t.to_s
|
1072
|
+
end
|
1073
|
+
end
|
1074
|
+
end
|
1075
|
+
if m.size > 1
|
1076
|
+
return "#{m[0]}: #{m[1..-1].join(", ")}"
|
1077
|
+
else
|
1078
|
+
return m[0]
|
1079
|
+
end
|
1080
|
+
when String, Symbol
|
1081
|
+
return msg(text)
|
1082
|
+
else
|
1083
|
+
raise msg(["Internal error: Bad log text", text])
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
# Delegate building the log message to
|
1088
|
+
# #log_build_colored_message or #log_build_monochrom_message
|
1089
|
+
# depending on whether we use colored output (@@colored_output).
|
1090
|
+
def log_build_message(*args)
|
1091
|
+
if @@colored_output
|
1092
|
+
log_build_colored_message(*args)
|
1093
|
+
else
|
1094
|
+
log_build_monochrom_message(*args)
|
1095
|
+
end
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
# Build a non-colored log message.
|
1099
|
+
# text:: A string
|
1100
|
+
# condition:: A condition evaluated by #log_valid_condition?
|
1101
|
+
def log_build_monochrom_message(text, condition, file=nil, line_begin=nil, line_end=nil)
|
1102
|
+
msg = []
|
1103
|
+
if file
|
1104
|
+
msg << file
|
1105
|
+
msg << ':'
|
1106
|
+
if line_begin
|
1107
|
+
msg << line_begin
|
1108
|
+
msg << '-' << line_end if line_end and line_end != line_begin
|
1109
|
+
msg << ':'
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
msg << text
|
1113
|
+
return msg.join
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
# Build a colored log message.
|
1117
|
+
# text:: A string
|
1118
|
+
# condition:: A condition evaluated by #log_valid_condition?
|
1119
|
+
def log_build_colored_message(text, condition, file=nil, line_begin=nil, line_end=nil)
|
1120
|
+
msg = []
|
1121
|
+
if file
|
1122
|
+
msg << Color.green << Color.bold(file)
|
1123
|
+
msg << Color.yellow << ':'
|
1124
|
+
if line_begin
|
1125
|
+
msg << Color.cyan
|
1126
|
+
msg << line_begin
|
1127
|
+
msg << '-' << line_end if line_end and line_end != line_begin
|
1128
|
+
msg << Color.yellow
|
1129
|
+
msg << ':'
|
1130
|
+
end
|
1131
|
+
case condition
|
1132
|
+
when :error, :unknown_macro
|
1133
|
+
msg << Color.red
|
1134
|
+
else
|
1135
|
+
msg << Color.blue
|
1136
|
+
end
|
1137
|
+
msg << text
|
1138
|
+
else
|
1139
|
+
case condition
|
1140
|
+
when :error, :unknown_macro
|
1141
|
+
msg << Color.red
|
1142
|
+
else
|
1143
|
+
msg << Color.magenta
|
1144
|
+
end
|
1145
|
+
msg << text
|
1146
|
+
end
|
1147
|
+
msg << Color.clear
|
1148
|
+
return msg.join
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
# Display text on io unless the @@quiet flag is set.
|
1152
|
+
def log_to(text, io=@@log_destination)
|
1153
|
+
unless @@quiet
|
1154
|
+
if io
|
1155
|
+
case io
|
1156
|
+
when String
|
1157
|
+
File.open(io, 'a') {|io| io.puts text}
|
1158
|
+
else
|
1159
|
+
io.puts text
|
1160
|
+
end
|
1161
|
+
else
|
1162
|
+
$stderr.puts msg('No log destination given!')
|
1163
|
+
end
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
# ???
|
1168
|
+
# def dispatch(src, object, method, modifier, *args)
|
1169
|
+
# disp = '%s_%s' % [method, modifier]
|
1170
|
+
# if object.methods.include?(disp)
|
1171
|
+
# return object.send(disp, *args)
|
1172
|
+
# else
|
1173
|
+
# return nil
|
1174
|
+
# end
|
1175
|
+
# end
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
# A open structure that holds this instance's options.
|
1179
|
+
attr_reader :options
|
1180
|
+
|
1181
|
+
# The formatter this instance of deplate uses.
|
1182
|
+
attr_reader :formatter
|
1183
|
+
|
1184
|
+
# A hash that holds the current document's variables.
|
1185
|
+
attr_accessor :variables
|
1186
|
+
|
1187
|
+
# A hash holding all known document services (names => method).
|
1188
|
+
attr_accessor :doc_services
|
1189
|
+
|
1190
|
+
# Other document specific variables.
|
1191
|
+
# A hash containing the footnotes (id => object)
|
1192
|
+
attr_accessor :footnotes
|
1193
|
+
# An array holding already consumed footnotes IDs.
|
1194
|
+
attr_accessor :footnotes_used
|
1195
|
+
# A running index.
|
1196
|
+
attr_accessor :footnote_last_idx
|
1197
|
+
|
1198
|
+
# A hash (label => [IndexEntry]).
|
1199
|
+
attr_accessor :index
|
1200
|
+
|
1201
|
+
# A hash (level_string => object); currently only used by the
|
1202
|
+
# structured formatter.
|
1203
|
+
attr_accessor :headings
|
1204
|
+
|
1205
|
+
# A hash (id => object)
|
1206
|
+
attr_accessor :clips
|
1207
|
+
|
1208
|
+
# A hash (label => level_string). Use of this hash should be
|
1209
|
+
# replaced with uses of @label_aliases<+TBD+>
|
1210
|
+
attr_accessor :labels
|
1211
|
+
# An array (unused!!!???<+TBD+>)
|
1212
|
+
attr_accessor :labels_named
|
1213
|
+
# An array used to postpone labels until there is some regular
|
1214
|
+
# output.
|
1215
|
+
attr_accessor :labels_floating
|
1216
|
+
# A hash (label => corresponding elements).
|
1217
|
+
attr_accessor :label_aliases
|
1218
|
+
|
1219
|
+
# A hash (slot name => number).
|
1220
|
+
attr_reader :slot_names
|
1221
|
+
|
1222
|
+
# The current input filter.
|
1223
|
+
attr_reader :input
|
1224
|
+
|
1225
|
+
# The current output object (Deplate::Output).
|
1226
|
+
attr_reader :output
|
1227
|
+
# An ordered array of output filenames.
|
1228
|
+
attr_accessor :output_filename
|
1229
|
+
# An ordered array of top level/page headings.
|
1230
|
+
attr_accessor :output_headings
|
1231
|
+
# An array of collected output objects (@output).
|
1232
|
+
attr_accessor :collected_output
|
1233
|
+
# The base output file.
|
1234
|
+
attr_accessor :dest
|
1235
|
+
# An array holding the elements after reading the input files.
|
1236
|
+
attr_accessor :accum_elements
|
1237
|
+
# An array of Proc objects that will be evaluated before processing
|
1238
|
+
# any other elements.
|
1239
|
+
attr_accessor :preprocess
|
1240
|
+
# An array of Proc objects that will be evaluated after printing any
|
1241
|
+
# other elements.
|
1242
|
+
attr_accessor :postponed_print
|
1243
|
+
|
1244
|
+
attr_accessor :current_source, :current_heading, :current_table, :current_figure
|
1245
|
+
attr_accessor :current_source_stats, :current_top
|
1246
|
+
attr_accessor :table_of_contents, :table_of_tables, :table_of_figures
|
1247
|
+
|
1248
|
+
# a stack with if/elseif status; skip input if the top-switch is true
|
1249
|
+
attr_accessor :switches
|
1250
|
+
|
1251
|
+
# formatter_name:: A formatter name
|
1252
|
+
# args:: A hash
|
1253
|
+
def initialize(formatter_name='html', args={})
|
1254
|
+
@args = args
|
1255
|
+
@slot_names = @@slot_names.dup
|
1256
|
+
@options = Deplate::Core.deplate_options(args[:options])
|
1257
|
+
@sources = args[:sources] || @options.files
|
1258
|
+
@dest = args[:dest] || @options.out || ''
|
1259
|
+
@doc_services = args[:doc_services] || initialize_services
|
1260
|
+
@vanilla = args[:vanilla] || false
|
1261
|
+
|
1262
|
+
# set_safe
|
1263
|
+
|
1264
|
+
reset(!args[:inherit_options])
|
1265
|
+
@output = Deplate::Output.new(self)
|
1266
|
+
# @output.destination = File.join(@options.dir, @dest)
|
1267
|
+
@output.destination = @dest
|
1268
|
+
|
1269
|
+
call_methods_matching(self, /^deplate_initialize_/)
|
1270
|
+
|
1271
|
+
formatter_class = args[:formatter] || @@formatter_classes[formatter_name]
|
1272
|
+
if formatter_class
|
1273
|
+
Deplate::Core.log('Initializing formatter')
|
1274
|
+
@formatter = formatter_class.new(self, args)
|
1275
|
+
call_methods_matching(self, /^formatter_initialize_/)
|
1276
|
+
call_methods_matching(@formatter, /^formatter_initialize_/)
|
1277
|
+
|
1278
|
+
Deplate::Core.log('Initializing modules')
|
1279
|
+
call_methods_matching(self, /^module_initialize_/)
|
1280
|
+
|
1281
|
+
Deplate::Core.log('Setting up text scanner')
|
1282
|
+
call_methods_matching(self, /^input_initialize_/)
|
1283
|
+
call_methods_matching(self, /^hook_pre_input_initialize_/)
|
1284
|
+
initialize_input(args)
|
1285
|
+
call_methods_matching(self, /^hook_post_input_initialize_/)
|
1286
|
+
|
1287
|
+
Deplate::Core.log('Setting up formatter')
|
1288
|
+
call_methods_matching(@formatter, /^hook_pre_setup_/)
|
1289
|
+
@formatter.setup
|
1290
|
+
call_methods_matching(@formatter, /^hook_post_setup_/)
|
1291
|
+
|
1292
|
+
Deplate::Core.log('User initialization')
|
1293
|
+
user_initialize if defined?(user_initialize)
|
1294
|
+
|
1295
|
+
reset_output
|
1296
|
+
set_standard_clips
|
1297
|
+
|
1298
|
+
if args[:now]
|
1299
|
+
Deplate::Core.log('Here we go ...')
|
1300
|
+
call_methods_matching(self, /^hook_pre_go_/)
|
1301
|
+
call_methods_matching(@formatter, /^hook_pre_go_/)
|
1302
|
+
go_now
|
1303
|
+
call_methods_matching(self, /^hook_post_go_/)
|
1304
|
+
call_methods_matching(@formatter, /^hook_post_go_/)
|
1305
|
+
end
|
1306
|
+
else
|
1307
|
+
log(['No or unknown formatter', formatter_name], :error)
|
1308
|
+
end
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
def_delegator(:@output, :pre_matter)
|
1312
|
+
def_delegator(:@output, :body)
|
1313
|
+
def_delegator(:@output, :post_matter)
|
1314
|
+
def_delegator(:@output, :destination)
|
1315
|
+
# def_delegator(:@output, :top_heading)
|
1316
|
+
def_delegator(:@output, :body_empty?)
|
1317
|
+
def_delegator(:@output, :add_at)
|
1318
|
+
|
1319
|
+
def_delegator(:@input, :initialize_particles)
|
1320
|
+
def_delegator(:@input, :register_particle)
|
1321
|
+
def_delegator(:@input, :parse_with_particles)
|
1322
|
+
def_delegator(:@input, :parse_with_source)
|
1323
|
+
def_delegator(:@input, :parse)
|
1324
|
+
|
1325
|
+
# (Re-)set @input
|
1326
|
+
# args:: A hash as passed to #initialize
|
1327
|
+
def initialize_input(args=@args)
|
1328
|
+
@input = args[:input] || @options.input
|
1329
|
+
unless @input
|
1330
|
+
input_class = args[:input_class] || @options.input_class || Deplate::Input
|
1331
|
+
@input = input_class.new(self, args)
|
1332
|
+
end
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
def push_input_format(name)
|
1336
|
+
unless name
|
1337
|
+
return false
|
1338
|
+
end
|
1339
|
+
ic = @@input_classes[name]
|
1340
|
+
unless ic
|
1341
|
+
self.class.require_input(@options, name)
|
1342
|
+
ic = @@input_classes[name]
|
1343
|
+
end
|
1344
|
+
if ic
|
1345
|
+
@input_formats << @input
|
1346
|
+
@input = ic.new(self, @args)
|
1347
|
+
return true
|
1348
|
+
else
|
1349
|
+
log(['Unknown input format', name, @@input_classes.keys], :error)
|
1350
|
+
return false
|
1351
|
+
end
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
def pop_input_format(name=nil)
|
1355
|
+
if @input_formats.empty?
|
1356
|
+
return false
|
1357
|
+
else
|
1358
|
+
if name and @input_formats.last.class.myname == name
|
1359
|
+
return false
|
1360
|
+
end
|
1361
|
+
@input = @input_formats.pop
|
1362
|
+
return true
|
1363
|
+
end
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
# Define a new slot or reset the position of an already known slot.
|
1367
|
+
# <+TBD+>There is no information on whether the slot belongs to the
|
1368
|
+
# prematter/postmatter/body.
|
1369
|
+
# key:: The name (string)
|
1370
|
+
# val:: The position (integer)
|
1371
|
+
def set_slot_name(key, val)
|
1372
|
+
slot_names[key] = val
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
# Return a slot position by its name.
|
1376
|
+
def slot_by_name(slot)
|
1377
|
+
if slot.kind_of?(Numeric)
|
1378
|
+
slotlist = slot_names.collect {|k,v| v == slot ? k : nil }.compact.join(", ")
|
1379
|
+
log(["Please refer to slots by their names", slot, slotlist], :error)
|
1380
|
+
log(caller[0..5].join("\n"), :error)
|
1381
|
+
slot
|
1382
|
+
elsif slot.is_a?(Symbol)
|
1383
|
+
slotstr = slot.to_s
|
1384
|
+
if slotstr =~ /^prepend_/
|
1385
|
+
modi = -1
|
1386
|
+
slot = slotstr[8..-1].to_sym
|
1387
|
+
else
|
1388
|
+
modi = 1
|
1389
|
+
end
|
1390
|
+
val = slot_names[slot]
|
1391
|
+
if val
|
1392
|
+
val * modi
|
1393
|
+
else
|
1394
|
+
nil
|
1395
|
+
end
|
1396
|
+
elsif slot.is_a?(String)
|
1397
|
+
pos = 0
|
1398
|
+
operator = "+"
|
1399
|
+
for i in slot.split(/([+-])/)
|
1400
|
+
case i
|
1401
|
+
when "-", "+"
|
1402
|
+
operator = i
|
1403
|
+
else
|
1404
|
+
j = slot_names[i.intern]
|
1405
|
+
unless j
|
1406
|
+
j = i.to_i
|
1407
|
+
end
|
1408
|
+
pos = pos.send(operator, j)
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
pos
|
1412
|
+
end
|
1413
|
+
end
|
1414
|
+
|
1415
|
+
# Initialize symbol related modules.
|
1416
|
+
def initialize_symbols
|
1417
|
+
Deplate::Core.log("Initializing symbols")
|
1418
|
+
call_methods_matching(self, /^initialize_symbols_/)
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
# Call all of obj's methods matching rx
|
1422
|
+
def call_methods_matching(obj, rx, *args)
|
1423
|
+
unless @vanilla
|
1424
|
+
for m in obj.methods.find_all {|m| m =~ rx }
|
1425
|
+
obj.send(m, *args)
|
1426
|
+
end
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
# Reset instance variables.
|
1431
|
+
# all:: reset really all variables (bool)
|
1432
|
+
def reset(all=false)
|
1433
|
+
@current_source = nil
|
1434
|
+
@current_source_stats = nil
|
1435
|
+
@auto_filenames = {}
|
1436
|
+
|
1437
|
+
@variables = @options.variables.dup
|
1438
|
+
@variables.deplate = self
|
1439
|
+
|
1440
|
+
@clips = {}
|
1441
|
+
@index = {}
|
1442
|
+
@index_last_idx = 0
|
1443
|
+
@labels = {}
|
1444
|
+
@label_aliases = {}
|
1445
|
+
@labels_named = []
|
1446
|
+
@labels_floating = []
|
1447
|
+
@ids = {}
|
1448
|
+
@preprocess = []
|
1449
|
+
@postponed_print = []
|
1450
|
+
|
1451
|
+
@footnotes = {}
|
1452
|
+
@footnote_last_idx = 0
|
1453
|
+
@footnotes_used = []
|
1454
|
+
|
1455
|
+
@headings = {}
|
1456
|
+
|
1457
|
+
@table_of_contents = []
|
1458
|
+
@table_of_tables = []
|
1459
|
+
@table_of_figures = []
|
1460
|
+
|
1461
|
+
@current_heading = []
|
1462
|
+
@accum_elements = []
|
1463
|
+
@switches = []
|
1464
|
+
@metadata = {}
|
1465
|
+
|
1466
|
+
@current_figure = 0
|
1467
|
+
@current_table = 0
|
1468
|
+
|
1469
|
+
set_lang(@@messages_last)
|
1470
|
+
set_standard_clips
|
1471
|
+
|
1472
|
+
if all
|
1473
|
+
@allsources = {}
|
1474
|
+
@input_formats = []
|
1475
|
+
@options.citations = []
|
1476
|
+
@options.bib = []
|
1477
|
+
@options.dont_index = []
|
1478
|
+
@options.author = []
|
1479
|
+
@options.heading_names = []
|
1480
|
+
end
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
# Reset output-related variables.
|
1484
|
+
# inherit_null_output:: The new output obj inherits the settings from the
|
1485
|
+
# initial/anonymous output class (bool)
|
1486
|
+
def reset_output(inherit_null_output=true)
|
1487
|
+
log("Reset output", :debug)
|
1488
|
+
@collected_output = []
|
1489
|
+
@output_filename = []
|
1490
|
+
@output_headings = []
|
1491
|
+
@output_maj_min = [0, 0]
|
1492
|
+
@null_output = @output.dup
|
1493
|
+
if @options.multi_file_output
|
1494
|
+
dest = @variables["docBasename"] || @dest
|
1495
|
+
dest &&= File.basename(dest)
|
1496
|
+
# Deplate::Core.canonic_file_name(dest, @options.suffix, 0, 0)
|
1497
|
+
else
|
1498
|
+
dest = nil
|
1499
|
+
end
|
1500
|
+
dest = dest ? Deplate::Core.get_out_fullname(dest, nil, @options) : @dest
|
1501
|
+
heading = Deplate::NullTop.new(self, :destination => dest)
|
1502
|
+
@output_filename[0] = dest
|
1503
|
+
push_top_heading(heading)
|
1504
|
+
if inherit_null_output
|
1505
|
+
new_output(@null_output)
|
1506
|
+
else
|
1507
|
+
Deplate::Output.reset
|
1508
|
+
new_output(nil)
|
1509
|
+
end
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
# Set the localization object.
|
1513
|
+
# lang:: The new language (string)
|
1514
|
+
def set_lang(lang)
|
1515
|
+
case lang
|
1516
|
+
when String
|
1517
|
+
msg_class = @@messages[lang]
|
1518
|
+
else
|
1519
|
+
msg_class = lang
|
1520
|
+
end
|
1521
|
+
if msg_class
|
1522
|
+
@options.messages = msg_class.new(self)
|
1523
|
+
@@message_object ||= @options.messages
|
1524
|
+
else
|
1525
|
+
log(["Bad language definition", lang, "(#{@@messages.keys.join(', ')})"], :error)
|
1526
|
+
end
|
1527
|
+
end
|
1528
|
+
|
1529
|
+
# See Deplate::Core.log
|
1530
|
+
def log(*args)
|
1531
|
+
self.class.log(*args)
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
# See Deplate::Core.require_module.
|
1535
|
+
def require_module(m)
|
1536
|
+
Deplate::Core.require_module(@options, m)
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
# Do something at last.
|
1540
|
+
def go_now
|
1541
|
+
if @options.each
|
1542
|
+
go_each
|
1543
|
+
elsif @options.loop
|
1544
|
+
loop do
|
1545
|
+
go
|
1546
|
+
reset(true)
|
1547
|
+
reset_output(false)
|
1548
|
+
end
|
1549
|
+
else
|
1550
|
+
@sources.uniq!
|
1551
|
+
go
|
1552
|
+
end
|
1553
|
+
end
|
1554
|
+
|
1555
|
+
# Process each in sources.
|
1556
|
+
def go_each(sources=@sources)
|
1557
|
+
rv = nil
|
1558
|
+
begin
|
1559
|
+
saved_sources = @sources
|
1560
|
+
for f in sources
|
1561
|
+
if File.stat(f).file?
|
1562
|
+
if to_be_included?(f)
|
1563
|
+
@sources = [f]
|
1564
|
+
@dest = Deplate::Core.get_out_fullname(f, @options.suffix, @options)
|
1565
|
+
reset(true)
|
1566
|
+
reset_output(false)
|
1567
|
+
if block_given?
|
1568
|
+
yield
|
1569
|
+
else
|
1570
|
+
rv = go
|
1571
|
+
end
|
1572
|
+
end
|
1573
|
+
elsif @options.recurse
|
1574
|
+
go_each(get_dir_listing(f))
|
1575
|
+
else
|
1576
|
+
log(["Is no file", f], :error)
|
1577
|
+
end
|
1578
|
+
end
|
1579
|
+
ensure
|
1580
|
+
@sources = saved_sources
|
1581
|
+
end
|
1582
|
+
rv
|
1583
|
+
end
|
1584
|
+
|
1585
|
+
# Read input file, process, write the output if writeFile is true.
|
1586
|
+
def go(writeFile=true)
|
1587
|
+
read_file
|
1588
|
+
process_document
|
1589
|
+
body_write if writeFile
|
1590
|
+
end
|
1591
|
+
|
1592
|
+
# Return the index for a top heading.
|
1593
|
+
def output_index(top=nil)
|
1594
|
+
if top
|
1595
|
+
top_heading_idx(top)
|
1596
|
+
else
|
1597
|
+
@collected_output.size - 1
|
1598
|
+
end
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
# Should the file be included or not, e.g., because of a -P
|
1602
|
+
# command line option
|
1603
|
+
def to_be_included?(file)
|
1604
|
+
rv = get_dir_listing(File.dirname(file))
|
1605
|
+
rv = rv.include?(file)
|
1606
|
+
if rv
|
1607
|
+
log(["Should be included", file])
|
1608
|
+
else
|
1609
|
+
log(["Should be excluded", file])
|
1610
|
+
end
|
1611
|
+
return rv
|
1612
|
+
end
|
1613
|
+
|
1614
|
+
# Evaluate block in the working directory; take care
|
1615
|
+
# of the auxiliaryDirSuffix variable
|
1616
|
+
def in_working_dir(cwd=nil, &block)
|
1617
|
+
pwd = Dir.pwd
|
1618
|
+
cwd ||= auxiliary_dirname(true, true)
|
1619
|
+
Dir.chdir(cwd) unless cwd.empty?
|
1620
|
+
begin
|
1621
|
+
block.call
|
1622
|
+
ensure
|
1623
|
+
Dir.chdir(pwd)
|
1624
|
+
end
|
1625
|
+
end
|
1626
|
+
|
1627
|
+
# Get the name for automatically generated auxiliary files (e.g.,
|
1628
|
+
# when no ID was provided)
|
1629
|
+
def auxiliary_auto_filename(type, idx, body=nil, suffix=nil)
|
1630
|
+
fn = ["_#{type}_#{idx}"]
|
1631
|
+
fn << '.' << suffix if suffix
|
1632
|
+
return fn.join
|
1633
|
+
end
|
1634
|
+
|
1635
|
+
# Get the proper filename for an auxiliary file, respecting
|
1636
|
+
# the value of auxiliaryDirSuffix
|
1637
|
+
def auxiliary_filename(filename, full_name=false)
|
1638
|
+
sd = auxiliary_dirname(full_name)
|
1639
|
+
if sd
|
1640
|
+
return Deplate::Core.get_path(sd, filename)
|
1641
|
+
else
|
1642
|
+
return filename
|
1643
|
+
end
|
1644
|
+
end
|
1645
|
+
|
1646
|
+
# Get the dir for auxiliary files; take care of the
|
1647
|
+
# auxiliaryDirSuffix variable
|
1648
|
+
# - If auxiliaryDirSuffix isn't defined, return default if non-nil.
|
1649
|
+
# - Create dir if ensure_dir_exists is true.
|
1650
|
+
def auxiliary_dirname(full_name=false, ensure_dir_exists=false)
|
1651
|
+
sdsfx = @variables['auxiliaryDirSuffix']
|
1652
|
+
path = []
|
1653
|
+
path << File.dirname(@dest) if full_name
|
1654
|
+
path << File.basename(@dest, '.*') + sdsfx if sdsfx
|
1655
|
+
rv = Deplate::Core.get_path(*path)
|
1656
|
+
ensure_dir_exists(rv) if ensure_dir_exists
|
1657
|
+
rv
|
1658
|
+
end
|
1659
|
+
|
1660
|
+
# Get a directory listing while respecting the -p and -P command line options
|
1661
|
+
def get_dir_listing(dir)
|
1662
|
+
pwd = Dir.pwd
|
1663
|
+
begin
|
1664
|
+
Dir.chdir(dir)
|
1665
|
+
files = Dir["*"]
|
1666
|
+
match = Dir[@options.file_pattern || "*"]
|
1667
|
+
files.delete_if {|f| File.stat(f).file? and !match.include?(f)}
|
1668
|
+
if @options.file_excl_pattern
|
1669
|
+
antilist = Dir[@options.file_excl_pattern]
|
1670
|
+
for anti in antilist
|
1671
|
+
files.delete(anti)
|
1672
|
+
end
|
1673
|
+
end
|
1674
|
+
log(["DIR", dir])
|
1675
|
+
ensure
|
1676
|
+
Dir.chdir(pwd)
|
1677
|
+
end
|
1678
|
+
unless dir == "."
|
1679
|
+
files.collect! {|f| File.join(dir, f)}
|
1680
|
+
end
|
1681
|
+
return files
|
1682
|
+
end
|
1683
|
+
|
1684
|
+
# Format either text or, if text is nil, the file "sourcename".
|
1685
|
+
# This is the method called by the Deplate::Formatter's
|
1686
|
+
# to_whatsoever methods.
|
1687
|
+
def format_with_formatter(formatter_class, text, sourcename=nil)
|
1688
|
+
if text
|
1689
|
+
format_string_with_formatter(formatter_class, text, sourcename)
|
1690
|
+
elsif sourcename
|
1691
|
+
format_file_with_formatter(formatter_class, sourcename)
|
1692
|
+
end
|
1693
|
+
end
|
1694
|
+
|
1695
|
+
# Format a file by means of formatter_class that is a child of Deplate::Formatter
|
1696
|
+
def format_file_with_formatter(formatter_class, sourcename)
|
1697
|
+
with_formatter(formatter_class) do
|
1698
|
+
go_each([sourcename])
|
1699
|
+
end
|
1700
|
+
end
|
1701
|
+
|
1702
|
+
# Format text by means of formatter_class that is a child of Deplate::Formatter
|
1703
|
+
def format_string_with_formatter(formatter_class, text, sourcename=nil)
|
1704
|
+
with_formatter(formatter_class) do
|
1705
|
+
format_string(text, sourcename)
|
1706
|
+
end
|
1707
|
+
end
|
1708
|
+
|
1709
|
+
# Format text with the current formatter
|
1710
|
+
def format_string(text, sourcename=nil)
|
1711
|
+
reset_output(false)
|
1712
|
+
maintain_current_source(sourcename) do
|
1713
|
+
accum_elements = @accum_elements
|
1714
|
+
@accum_elements = Array.new
|
1715
|
+
include_each(text, @accum_elements, sourcename)
|
1716
|
+
process_document
|
1717
|
+
return body_string
|
1718
|
+
end
|
1719
|
+
end
|
1720
|
+
|
1721
|
+
# Read text from STDIN. End on EOF or due to a matching
|
1722
|
+
# pair of #BEGIN, #END pseudo commands
|
1723
|
+
def include_stdin(array)
|
1724
|
+
if $stdin.eof?
|
1725
|
+
log("No more input on STDIN", :anyway)
|
1726
|
+
exit 1
|
1727
|
+
end
|
1728
|
+
maintain_current_source("") do
|
1729
|
+
log("Including from STDIN")
|
1730
|
+
acc = []
|
1731
|
+
end_tag = nil
|
1732
|
+
$stdin.each_with_index do |l, i|
|
1733
|
+
if i == 0 and l =~ /^#BEGIN:/
|
1734
|
+
end_tag = "#END:" + l[7..-1]
|
1735
|
+
elsif end_tag and l == end_tag
|
1736
|
+
break
|
1737
|
+
else
|
1738
|
+
acc << l
|
1739
|
+
end
|
1740
|
+
end
|
1741
|
+
include_each(acc, array, "STDIN")
|
1742
|
+
end
|
1743
|
+
end
|
1744
|
+
|
1745
|
+
# Read a file and add the parsed elements to array
|
1746
|
+
def include_file(array, filename)
|
1747
|
+
maintain_current_source(filename) do
|
1748
|
+
log(["Including", filename])
|
1749
|
+
filename_abs = File.expand_path(filename)
|
1750
|
+
unless @options.included
|
1751
|
+
filename_label = file_label(filename_abs)
|
1752
|
+
@labels_floating << filename_label
|
1753
|
+
end
|
1754
|
+
File.open(filename, "r") do |io|
|
1755
|
+
include_each(io, array, filename)
|
1756
|
+
end
|
1757
|
+
end
|
1758
|
+
end
|
1759
|
+
|
1760
|
+
# Execute &block while maintaining @current_top
|
1761
|
+
def maintain_current_top(top, &block)
|
1762
|
+
begin
|
1763
|
+
current_top = @current_top
|
1764
|
+
@current_top = top
|
1765
|
+
block.call
|
1766
|
+
ensure
|
1767
|
+
@current_top = current_top
|
1768
|
+
end
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
# Include each line in enum and accumulate parsed elements in array
|
1772
|
+
def include_each(enum, array, sourcename=nil)
|
1773
|
+
@input.include_enum(enum, array, 0)
|
1774
|
+
end
|
1775
|
+
|
1776
|
+
# Include strings as if read from a file and return the resulting array of parsed elements
|
1777
|
+
def parsed_array_from_strings(strings, linenumber=nil, src="[array]")
|
1778
|
+
array = []
|
1779
|
+
include_stringarray(strings, array, linenumber, src)
|
1780
|
+
return array
|
1781
|
+
end
|
1782
|
+
|
1783
|
+
# Include strings as if read from a file and push parsed elements onto array
|
1784
|
+
def include_stringarray(strings, array, linenumber=nil, src="[array]")
|
1785
|
+
maintain_current_source(src) do
|
1786
|
+
@input.include_enum(strings, array, linenumber || 0)
|
1787
|
+
end
|
1788
|
+
end
|
1789
|
+
|
1790
|
+
# Set the current top heading.
|
1791
|
+
# heading:: Heading object
|
1792
|
+
# text:: The output filename base
|
1793
|
+
def set_top_heading(heading, text)
|
1794
|
+
if heading.level == 1
|
1795
|
+
fname = nil
|
1796
|
+
sfx = @options.suffix
|
1797
|
+
dir = @options.dir
|
1798
|
+
if @output_headings.include?(heading)
|
1799
|
+
maj = top_heading_idx(heading)
|
1800
|
+
else
|
1801
|
+
heading.top_heading = heading
|
1802
|
+
push_top_heading(heading)
|
1803
|
+
maj = @output_headings.size - 1
|
1804
|
+
unless text or !@options.multi_file_output
|
1805
|
+
if @variables["autoFileNames"]
|
1806
|
+
fname = Deplate::Core.clean_file_name(heading.get_text)[0..20]
|
1807
|
+
c = @auto_filenames[fname]
|
1808
|
+
if c
|
1809
|
+
fname = Deplate::Core.canonic_file_name(fname, sfx, c, 0)
|
1810
|
+
@auto_filenames[fname] += 1
|
1811
|
+
else
|
1812
|
+
fname = Deplate::Core.canonic_file_name(fname, sfx)
|
1813
|
+
@auto_filenames[fname] = 0
|
1814
|
+
end
|
1815
|
+
else
|
1816
|
+
fname = File.basename(@dest, File.extname(@dest))
|
1817
|
+
fname = Deplate::Core.canonic_file_name(fname, sfx, maj, 0)
|
1818
|
+
end
|
1819
|
+
end
|
1820
|
+
end
|
1821
|
+
if !@options.multi_file_output
|
1822
|
+
# fname = ""
|
1823
|
+
fname = @dest
|
1824
|
+
else
|
1825
|
+
if text
|
1826
|
+
fname = Deplate::Core.canonic_file_name(text, sfx)
|
1827
|
+
end
|
1828
|
+
if fname
|
1829
|
+
fname = Deplate::Core.get_out_fullname(fname, nil, @options)
|
1830
|
+
end
|
1831
|
+
end
|
1832
|
+
# if fname and @options.multi_file_output
|
1833
|
+
if fname
|
1834
|
+
heading.destination = @output_filename[maj] = fname
|
1835
|
+
end
|
1836
|
+
log(["Set top heading", maj, (text||"nil"), fname], :debug)
|
1837
|
+
end
|
1838
|
+
end
|
1839
|
+
|
1840
|
+
# Get a top/page heading by its index.
|
1841
|
+
# idx:: The index (integer)
|
1842
|
+
def top_heading_by_idx(idx)
|
1843
|
+
@output_headings[idx || 0]
|
1844
|
+
end
|
1845
|
+
|
1846
|
+
# Get the top/page heading index (or get the current index if no top
|
1847
|
+
# heading object is provided.
|
1848
|
+
def top_heading_idx(top=nil)
|
1849
|
+
if top
|
1850
|
+
@output_headings.index(top)
|
1851
|
+
else
|
1852
|
+
@output_headings.size - 1
|
1853
|
+
end
|
1854
|
+
end
|
1855
|
+
|
1856
|
+
# Return the number of output pages.
|
1857
|
+
def number_of_outputs
|
1858
|
+
@output_headings.size
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
# Return the nth output filename.
|
1862
|
+
# idx:: Top heading index (integer)
|
1863
|
+
def output_filename_by_idx(idx)
|
1864
|
+
if idx
|
1865
|
+
idx = idx.to_i if idx.kind_of?(String)
|
1866
|
+
@output_filename[idx]
|
1867
|
+
end
|
1868
|
+
end
|
1869
|
+
|
1870
|
+
# accum format elts in pre/body|matter/post
|
1871
|
+
def printable_strings(strings, linenumber=nil, src="[array]")
|
1872
|
+
output = []
|
1873
|
+
accum_elements = []
|
1874
|
+
include_stringarray(strings, accum_elements, linenumber, src)
|
1875
|
+
accum_elements.collect! do |e|
|
1876
|
+
e.doc_type = :array
|
1877
|
+
e.doc_slot = output
|
1878
|
+
e.process
|
1879
|
+
end
|
1880
|
+
accum_elements.compact!
|
1881
|
+
for e in accum_elements
|
1882
|
+
e.doc_type = :array
|
1883
|
+
e.doc_slot = output
|
1884
|
+
e.print
|
1885
|
+
end
|
1886
|
+
return output
|
1887
|
+
end
|
1888
|
+
|
1889
|
+
# Run a "service", i.e., a small, mostly autonomous function/method that
|
1890
|
+
# usually yields some formatted output.
|
1891
|
+
def invoke_service(name, args={}, text="")
|
1892
|
+
method = @doc_services[name]
|
1893
|
+
if method
|
1894
|
+
begin
|
1895
|
+
return send(method, args || {}, text || "")
|
1896
|
+
rescue Exception => e
|
1897
|
+
puts e.backtrace[0..10].join("\n")
|
1898
|
+
Deplate::Core.log(["Calling service failed", name, e], :error)
|
1899
|
+
end
|
1900
|
+
else
|
1901
|
+
Deplate::Core.log(["Unknown service", name], :error)
|
1902
|
+
end
|
1903
|
+
end
|
1904
|
+
|
1905
|
+
# Return whether ruby code may be evaluated.
|
1906
|
+
def allow_ruby
|
1907
|
+
return @options.allow_ruby
|
1908
|
+
end
|
1909
|
+
|
1910
|
+
# Caller requests calling ruby code with some args
|
1911
|
+
def eval_ruby(invoker, args, code)
|
1912
|
+
ar = allow_ruby
|
1913
|
+
case ar
|
1914
|
+
when true
|
1915
|
+
begin
|
1916
|
+
# +++ Run this in a thread and set $SAFE for this thread only
|
1917
|
+
context = (args['context'] || '').downcase
|
1918
|
+
case context
|
1919
|
+
when 'ruby'
|
1920
|
+
return eval(code)
|
1921
|
+
when 'deplate'
|
1922
|
+
return self.instance_eval(code)
|
1923
|
+
when 'self', 'this'
|
1924
|
+
return invoker.instance_eval(code)
|
1925
|
+
else
|
1926
|
+
return Deplate::Void.module_eval(code)
|
1927
|
+
end
|
1928
|
+
rescue Exception => e
|
1929
|
+
src = invoker ? invoker.source : nil
|
1930
|
+
invoker.log(["Error in ruby code", code, e], :error)
|
1931
|
+
end
|
1932
|
+
when 1,2,3,4,5
|
1933
|
+
result, error = Deplate::Safe.safe(ar, code)
|
1934
|
+
if error then
|
1935
|
+
invoker.log(["Error in ruby code", code, error.inspect], :error)
|
1936
|
+
else
|
1937
|
+
return result.to_s
|
1938
|
+
end
|
1939
|
+
else
|
1940
|
+
if args['alt']
|
1941
|
+
return args['alt']
|
1942
|
+
elsif caller
|
1943
|
+
invoker.log(["Disabled ruby command", code], :anyway)
|
1944
|
+
end
|
1945
|
+
end
|
1946
|
+
end
|
1947
|
+
|
1948
|
+
# Set a clip.
|
1949
|
+
# id:: The clip's name
|
1950
|
+
# value:: An instance of either Deplate::Element::Clip or
|
1951
|
+
# Deplate::Regions::Clip.
|
1952
|
+
def set_clip(id, value)
|
1953
|
+
@clips[id] = value
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
# Get a clip.
|
1957
|
+
# id:: The clip's name
|
1958
|
+
def get_clip(id)
|
1959
|
+
@clips[id]
|
1960
|
+
end
|
1961
|
+
|
1962
|
+
# Set all clips.
|
1963
|
+
# clips:: A hash
|
1964
|
+
def set_all_clips(clips)
|
1965
|
+
@clips = clips
|
1966
|
+
end
|
1967
|
+
|
1968
|
+
# Get a hash on yet unprocessed clips. Obsolete?
|
1969
|
+
def get_unprocessed_clips
|
1970
|
+
@clips
|
1971
|
+
end
|
1972
|
+
|
1973
|
+
# Get all css files that are required by the current document.
|
1974
|
+
def collected_css
|
1975
|
+
@@css
|
1976
|
+
end
|
1977
|
+
|
1978
|
+
# Return whether +file+ was already included.
|
1979
|
+
def file_included?(file, dir=nil, try_suffix=nil)
|
1980
|
+
dir = dir || "."
|
1981
|
+
file = File.expand_path(file, dir)
|
1982
|
+
rv = @allsources.keys.include?(file)
|
1983
|
+
if !rv and try_suffix
|
1984
|
+
file = File.expand_path(file + try_suffix, dir)
|
1985
|
+
rv = @allsources.keys.include?(file)
|
1986
|
+
end
|
1987
|
+
return rv
|
1988
|
+
end
|
1989
|
+
|
1990
|
+
# Make +file+ a filename relative to +dir+.
|
1991
|
+
def relative_path(file, dir)
|
1992
|
+
fn1 = Pathname.new(File.expand_path(file))
|
1993
|
+
fn2 = Pathname.new(File.expand_path(dir))
|
1994
|
+
rv = fn1.relative_path_from(fn2).to_s
|
1995
|
+
return rv == "." ? "" : rv
|
1996
|
+
end
|
1997
|
+
|
1998
|
+
def relative_path_by_file(file, base_file)
|
1999
|
+
if base_file
|
2000
|
+
relative_path(file, File.dirname(base_file))
|
2001
|
+
else
|
2002
|
+
File.basename(file)
|
2003
|
+
end
|
2004
|
+
end
|
2005
|
+
|
2006
|
+
# Return the automatically generated label for an included file.
|
2007
|
+
def file_label(filename_abs)
|
2008
|
+
if filename_abs
|
2009
|
+
label = @allsources[filename_abs]
|
2010
|
+
unless label
|
2011
|
+
# rel = relative_path(filename_abs, Dir.pwd)
|
2012
|
+
label = "file%03d" % @allsources.size
|
2013
|
+
label.gsub!(/\W/, "00")
|
2014
|
+
@allsources[filename_abs] = label
|
2015
|
+
end
|
2016
|
+
return label
|
2017
|
+
else
|
2018
|
+
return nil
|
2019
|
+
end
|
2020
|
+
end
|
2021
|
+
|
2022
|
+
# Amend +file+'s suffix.
|
2023
|
+
def file_with_suffix(file, sfx=nil, filename_only=false)
|
2024
|
+
sfx = sfx || ""
|
2025
|
+
sfx0 = File.extname(file)
|
2026
|
+
fn = File.basename(file, sfx0)
|
2027
|
+
if filename_only
|
2028
|
+
return fn + sfx
|
2029
|
+
else
|
2030
|
+
dir = File.dirname(file)
|
2031
|
+
fname = fn + sfx
|
2032
|
+
if dir == "."
|
2033
|
+
return fname
|
2034
|
+
else
|
2035
|
+
return File.join(dir, fname)
|
2036
|
+
end
|
2037
|
+
end
|
2038
|
+
end
|
2039
|
+
|
2040
|
+
# Return the output file according to +level_as_string+.
|
2041
|
+
def file_name_by_level(level_as_string)
|
2042
|
+
if @options.multi_file_output and level_as_string
|
2043
|
+
if level_as_string.kind_of?(String)
|
2044
|
+
las = level_as_string
|
2045
|
+
else
|
2046
|
+
las = level_as_string.to_s
|
2047
|
+
end
|
2048
|
+
if las == ""
|
2049
|
+
top = top_heading_by_idx(0)
|
2050
|
+
else
|
2051
|
+
top = nil
|
2052
|
+
catch(:ok) do
|
2053
|
+
each_heading do |heading, title|
|
2054
|
+
if heading.level_as_string == las
|
2055
|
+
top = heading.top_heading
|
2056
|
+
throw :ok
|
2057
|
+
end
|
2058
|
+
end
|
2059
|
+
raise "Internal error: unknown level: #{level_as_string}"
|
2060
|
+
end
|
2061
|
+
end
|
2062
|
+
return top.destination
|
2063
|
+
elsif level_as_string == "0"
|
2064
|
+
return File.basename(@dest)
|
2065
|
+
else
|
2066
|
+
return ""
|
2067
|
+
end
|
2068
|
+
end
|
2069
|
+
|
2070
|
+
# Return the canonic name for an automatically generated label (e.g.,
|
2071
|
+
# figures, tables ...)
|
2072
|
+
def elt_label(prefix, text, plain=false)
|
2073
|
+
if text
|
2074
|
+
if plain
|
2075
|
+
return "%s00%s" % [prefix, text.sum]
|
2076
|
+
else
|
2077
|
+
return "%s00%s" % [prefix, text.gsub(/\W/, "00")]
|
2078
|
+
end
|
2079
|
+
else
|
2080
|
+
raise msg("No label")
|
2081
|
+
log(["No label", prefix], :error)
|
2082
|
+
return nil
|
2083
|
+
end
|
2084
|
+
end
|
2085
|
+
|
2086
|
+
# Create a new output and push it to @collected_output.
|
2087
|
+
def new_output(inherited_output=nil, args={})
|
2088
|
+
@output = Deplate::Output.new(self, inherited_output)
|
2089
|
+
@collected_output << @output
|
2090
|
+
@output.top_heading = top_heading_by_idx(@collected_output.size - 1)
|
2091
|
+
@output.index = @output_maj_min.dup
|
2092
|
+
increase_maj_min
|
2093
|
+
end
|
2094
|
+
|
2095
|
+
# Insert a page/output break.
|
2096
|
+
def break_output(minor=false)
|
2097
|
+
@output.body_flush
|
2098
|
+
new_output(@output)
|
2099
|
+
end
|
2100
|
+
|
2101
|
+
# Return all the formatted output as string.
|
2102
|
+
def body_string
|
2103
|
+
return @collected_output.collect {|o| o.join("\n")}.join("\n")
|
2104
|
+
end
|
2105
|
+
|
2106
|
+
# Write the output to the disk.
|
2107
|
+
def body_write
|
2108
|
+
rv = nil
|
2109
|
+
method = uri_method_name_with_prefix("body_write_to_", @dest)
|
2110
|
+
log(["Writing output file(s)", @collected_output.size])
|
2111
|
+
if method
|
2112
|
+
for output in @collected_output
|
2113
|
+
rv = send(method, @dest, output)
|
2114
|
+
end
|
2115
|
+
elsif @dest == "-"
|
2116
|
+
sep = @variables["stdoutSeparator"]
|
2117
|
+
for output in @collected_output
|
2118
|
+
puts(output.join("\n"))
|
2119
|
+
puts sep if sep
|
2120
|
+
end
|
2121
|
+
elsif @options.multi_file_output
|
2122
|
+
@collected_output.each_with_index do |output, i|
|
2123
|
+
if output.body_empty?
|
2124
|
+
log("Empty body ... skipping")
|
2125
|
+
else
|
2126
|
+
dest = output.destination
|
2127
|
+
rv ||= dest
|
2128
|
+
log(["Writing file", i, dest])
|
2129
|
+
write_file(dest) do |io|
|
2130
|
+
io.puts(output.join("\n"))
|
2131
|
+
end
|
2132
|
+
call_methods_matching(@formatter, /^hook_post_write_file_/)
|
2133
|
+
output.merge_metadata(@metadata)
|
2134
|
+
if @options.metadata_model and output.metadata_available?
|
2135
|
+
md_dest = File.join(@options.dir, output.metadata_destination)
|
2136
|
+
log(["Saving metadata", md_dest])
|
2137
|
+
write_metadata(md_dest, output)
|
2138
|
+
end
|
2139
|
+
end
|
2140
|
+
end
|
2141
|
+
else
|
2142
|
+
dest = @collected_output.first.destination
|
2143
|
+
# dest = @dest
|
2144
|
+
log(["Writing file", dest])
|
2145
|
+
write_file(dest) do |io|
|
2146
|
+
for output in @collected_output
|
2147
|
+
io.puts(output.join("\n"))
|
2148
|
+
end
|
2149
|
+
io.puts
|
2150
|
+
end
|
2151
|
+
call_methods_matching(@formatter, /^hook_post_write_file_/)
|
2152
|
+
if @options.metadata_model
|
2153
|
+
md = @metadata.dup
|
2154
|
+
for output in @collected_output
|
2155
|
+
if output.metadata_available?
|
2156
|
+
for key, data in output.metadata
|
2157
|
+
for e in data
|
2158
|
+
push_metadata(e, md)
|
2159
|
+
end
|
2160
|
+
end
|
2161
|
+
end
|
2162
|
+
end
|
2163
|
+
unless md.empty?
|
2164
|
+
output = @collected_output.first
|
2165
|
+
md_dest = auxiliary_filename(output.metadata_destination(@dest), true)
|
2166
|
+
log(["Saving metadata", md_dest])
|
2167
|
+
write_metadata(md_dest, output, md)
|
2168
|
+
end
|
2169
|
+
end
|
2170
|
+
end
|
2171
|
+
rv || @dest
|
2172
|
+
end
|
2173
|
+
|
2174
|
+
# Make sure +dir+ exists (create it if it doesn't).
|
2175
|
+
def ensure_dir_exists(dir)
|
2176
|
+
unless File.exist?(dir)
|
2177
|
+
if @options.force
|
2178
|
+
Deplate::Core.ensure_dir_exists(dir)
|
2179
|
+
else
|
2180
|
+
log(["Destination directory doesn't exist", dir, Dir.pwd], :error)
|
2181
|
+
exit 5
|
2182
|
+
end
|
2183
|
+
end
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
# Actually write something to some file.
|
2187
|
+
def write_file(file, &block)
|
2188
|
+
if file
|
2189
|
+
# pwd = Dir.pwd
|
2190
|
+
begin
|
2191
|
+
# Dir.chdir(@options.dir)
|
2192
|
+
ensure_dir_exists(File.dirname(file))
|
2193
|
+
ok = if File.exist?(file) and @options.ask
|
2194
|
+
print "File '#{file}' already exists. Overwrite (y/N)? "
|
2195
|
+
gets.chomp == 'y'
|
2196
|
+
else
|
2197
|
+
true
|
2198
|
+
end
|
2199
|
+
if ok
|
2200
|
+
File.open(file, 'w') do |io|
|
2201
|
+
block.call(io)
|
2202
|
+
end
|
2203
|
+
end
|
2204
|
+
# ensure
|
2205
|
+
# Dir.chdir(pwd)
|
2206
|
+
rescue Exception => e
|
2207
|
+
log(['Error when writing file', file, e], :error)
|
2208
|
+
exit 5
|
2209
|
+
end
|
2210
|
+
else
|
2211
|
+
log(['No output file', file], :error)
|
2212
|
+
end
|
2213
|
+
end
|
2214
|
+
|
2215
|
+
# Increase the heading level.
|
2216
|
+
def increase_current_heading(level)
|
2217
|
+
if @current_heading.size < level
|
2218
|
+
while @current_heading.size < level
|
2219
|
+
@current_heading << 1
|
2220
|
+
end
|
2221
|
+
else
|
2222
|
+
while @current_heading.size > level
|
2223
|
+
@current_heading.pop
|
2224
|
+
end
|
2225
|
+
@current_heading << (@current_heading.pop + 1)
|
2226
|
+
end
|
2227
|
+
end
|
2228
|
+
|
2229
|
+
# Get the current section's level as string.
|
2230
|
+
def get_current_heading
|
2231
|
+
return @current_heading.join(".")
|
2232
|
+
end
|
2233
|
+
|
2234
|
+
# Get the current top heading object.
|
2235
|
+
def get_current_top
|
2236
|
+
top_heading_by_idx(top_heading_idx)
|
2237
|
+
end
|
2238
|
+
|
2239
|
+
# Register a new label.
|
2240
|
+
# invoker:: The labelled object
|
2241
|
+
# label:: The label name
|
2242
|
+
# level_as_string:: The section heading's level as string (redundant???)
|
2243
|
+
def add_label(invoker, label, level_as_string, opts={})
|
2244
|
+
if !opts[:anyway] and (@labels[label] or @label_aliases[label])
|
2245
|
+
invoker.log(["Label already defined", label, level_as_string], :error)
|
2246
|
+
else
|
2247
|
+
@labels[label] = level_as_string
|
2248
|
+
@label_aliases[label] = opts[:container] || invoker
|
2249
|
+
end
|
2250
|
+
end
|
2251
|
+
|
2252
|
+
# <+TBD+>This doesn't work as intended. Elements still have to be labelled in order to
|
2253
|
+
# be referred to by their ID
|
2254
|
+
def get_label_by_id(invoker, id)
|
2255
|
+
o = @ids[id]
|
2256
|
+
if o
|
2257
|
+
l = o.label
|
2258
|
+
l &&= l.first
|
2259
|
+
if l
|
2260
|
+
return l
|
2261
|
+
else
|
2262
|
+
return id
|
2263
|
+
# invoker.log(["Object has no label", id], :error)
|
2264
|
+
end
|
2265
|
+
else
|
2266
|
+
invoker.log(["No object with that id", id], :error)
|
2267
|
+
end
|
2268
|
+
end
|
2269
|
+
|
2270
|
+
# Get the filename of the object marked with +label+.
|
2271
|
+
def get_filename_for_label(invoker, label)
|
2272
|
+
f = @label_aliases[label]
|
2273
|
+
if f
|
2274
|
+
f = f.top_heading.destination
|
2275
|
+
d = invoker.top_heading.destination
|
2276
|
+
if f == d
|
2277
|
+
return ""
|
2278
|
+
else
|
2279
|
+
return relative_path(f, File.dirname(d))
|
2280
|
+
end
|
2281
|
+
else
|
2282
|
+
# puts caller
|
2283
|
+
invoker.log(["Reference to unknown label", label], :error)
|
2284
|
+
end
|
2285
|
+
end
|
2286
|
+
|
2287
|
+
# A dummy method to be overwritten by a metadata module.
|
2288
|
+
def dump_metadata(data)
|
2289
|
+
data
|
2290
|
+
end
|
2291
|
+
|
2292
|
+
# A dummy method to be overwritten by a metadata module.
|
2293
|
+
def put_metadata(io, metadata)
|
2294
|
+
io.puts(metadata)
|
2295
|
+
end
|
2296
|
+
|
2297
|
+
# A dummy method to be overwritten by a metadata module.
|
2298
|
+
def write_metadata(file, output, metadata=nil)
|
2299
|
+
write_file(file) do |io|
|
2300
|
+
md = metadata ? output.format_metadata(metadata) : output.format_metadata
|
2301
|
+
put_metadata(io, md)
|
2302
|
+
end
|
2303
|
+
end
|
2304
|
+
|
2305
|
+
# Return the metadata as hash.
|
2306
|
+
def get_metadata(source, metadata)
|
2307
|
+
if @options.metadata_model
|
2308
|
+
if source
|
2309
|
+
metadata['source_file'] = source.file
|
2310
|
+
metadata['source_begin'] = source.begin
|
2311
|
+
metadata['source_end'] = source.end
|
2312
|
+
if (stats = source.stats)
|
2313
|
+
if (mtime = stats.mtime)
|
2314
|
+
metadata['source_mtime'] = mtime
|
2315
|
+
end
|
2316
|
+
end
|
2317
|
+
end
|
2318
|
+
metadata
|
2319
|
+
else
|
2320
|
+
nil
|
2321
|
+
end
|
2322
|
+
end
|
2323
|
+
|
2324
|
+
# Register a new metadata entry.
|
2325
|
+
# source:: The related source filename
|
2326
|
+
# metadata:: A hash
|
2327
|
+
def register_metadata(source, metadata)
|
2328
|
+
if @options.metadata_model
|
2329
|
+
push_metadata(get_metadata(source, metadata))
|
2330
|
+
end
|
2331
|
+
end
|
2332
|
+
|
2333
|
+
# Actually save the metadata in some variable for later use.
|
2334
|
+
def push_metadata(data, array=@metadata)
|
2335
|
+
if @options.metadata_model
|
2336
|
+
type = data["type"]
|
2337
|
+
@metadata[type] ||= []
|
2338
|
+
@metadata[type] << data
|
2339
|
+
end
|
2340
|
+
end
|
2341
|
+
|
2342
|
+
# Register an object's ID.
|
2343
|
+
# <+TBD+>Not systematically used yet.
|
2344
|
+
def register_id(hash, obj)
|
2345
|
+
id = hash["id"]
|
2346
|
+
xid = hash["xid"]
|
2347
|
+
if id and xid
|
2348
|
+
log(["Option clash: both id and xid provided", id, xid], :error, obj.source)
|
2349
|
+
else
|
2350
|
+
id ||= xid
|
2351
|
+
end
|
2352
|
+
if id and !id.empty?
|
2353
|
+
if @ids[id]
|
2354
|
+
# obj.log(["ID with the same name already exists", id, @ids[id].level_as_string], :error)
|
2355
|
+
else
|
2356
|
+
obj.log(["Register id", id, obj.class], :debug)
|
2357
|
+
@ids[id] = obj
|
2358
|
+
end
|
2359
|
+
end
|
2360
|
+
end
|
2361
|
+
|
2362
|
+
# Register a new index entry.
|
2363
|
+
def add_index(container, names, level_as_string="")
|
2364
|
+
@index_last_idx += 1
|
2365
|
+
id = "idx00#{@index_last_idx}"
|
2366
|
+
words = names.split(/\s*\|\s*/)
|
2367
|
+
lname = Deplate::Core.remove_backslashes(words[0])
|
2368
|
+
level_as_string = container ? container.level_as_string : level_as_string
|
2369
|
+
if @options.dont_index.delete(lname)
|
2370
|
+
return nil
|
2371
|
+
else
|
2372
|
+
i = @index[lname]
|
2373
|
+
unless i
|
2374
|
+
i = @index.find do |k, a|
|
2375
|
+
a.find do |i|
|
2376
|
+
i.synonymes.find {|j| words.include?(j)}
|
2377
|
+
end
|
2378
|
+
end
|
2379
|
+
if i
|
2380
|
+
lname = i[0]
|
2381
|
+
i = @index[lname]
|
2382
|
+
end
|
2383
|
+
end
|
2384
|
+
if @options.each
|
2385
|
+
f = file_with_suffix(File.basename(@current_source), @options.suffix, true)
|
2386
|
+
elsif @options.multi_file_output
|
2387
|
+
f = nil
|
2388
|
+
else
|
2389
|
+
f = @dest
|
2390
|
+
end
|
2391
|
+
d = IndexEntry.new(lname, words, id, f, level_as_string)
|
2392
|
+
if i
|
2393
|
+
i << d
|
2394
|
+
else
|
2395
|
+
@index[lname] = [d]
|
2396
|
+
end
|
2397
|
+
return d
|
2398
|
+
end
|
2399
|
+
end
|
2400
|
+
|
2401
|
+
# Remove a registered index entry.
|
2402
|
+
def remove_index(containes, names)
|
2403
|
+
lname = Deplate::Core.remove_backslashes(names.split(/\s*\|\s*/)[0])
|
2404
|
+
i = @index[lname]
|
2405
|
+
if i
|
2406
|
+
i.pop
|
2407
|
+
@index.delete(lname) if i.empty?
|
2408
|
+
end
|
2409
|
+
end
|
2410
|
+
|
2411
|
+
# Return a localized version of text. (delegated)
|
2412
|
+
def msg(text)
|
2413
|
+
@options.messages.msg(text)
|
2414
|
+
end
|
2415
|
+
|
2416
|
+
# Class variable accessor.
|
2417
|
+
def symbols
|
2418
|
+
@@symbols
|
2419
|
+
end
|
2420
|
+
|
2421
|
+
# Class variable accessor.
|
2422
|
+
def templates
|
2423
|
+
@@templates
|
2424
|
+
end
|
2425
|
+
|
2426
|
+
# Join an array of particles into a string.
|
2427
|
+
def join_particles(particles)
|
2428
|
+
particles.join
|
2429
|
+
end
|
2430
|
+
|
2431
|
+
# Return an array of unprocessed particles as string.
|
2432
|
+
def format_particles(particles)
|
2433
|
+
return join_particles(particles.collect{|e| e.process; e.elt})
|
2434
|
+
end
|
2435
|
+
|
2436
|
+
# Parse +text+ and return a formatted string.
|
2437
|
+
def parse_and_format(container, text, alt=true, excluded=[])
|
2438
|
+
t = parse(container, text, alt, excluded)
|
2439
|
+
return format_particles(t)
|
2440
|
+
end
|
2441
|
+
|
2442
|
+
def parse_and_format_without_wikinames(container, text, alt=true)
|
2443
|
+
excluded = [
|
2444
|
+
Deplate::HyperLink::Simple,
|
2445
|
+
]
|
2446
|
+
return parse_and_format(container, text, alt, excluded)
|
2447
|
+
end
|
2448
|
+
|
2449
|
+
# Evaluate block (args: heading, caption) with each heading.
|
2450
|
+
def each_heading(depth=nil, &block)
|
2451
|
+
case depth
|
2452
|
+
when :top
|
2453
|
+
arr = @output_headings
|
2454
|
+
depth = false
|
2455
|
+
else
|
2456
|
+
arr = @table_of_contents
|
2457
|
+
end
|
2458
|
+
for section in arr
|
2459
|
+
if !depth or (section and section.level <= depth)
|
2460
|
+
unless section and section.args["noList"]
|
2461
|
+
if section.kind_of?(Deplate::NullTop)
|
2462
|
+
v = section.caption
|
2463
|
+
else
|
2464
|
+
v = section.description.gsub(/<\/?[^>]*>/, "")
|
2465
|
+
v = [section.level_as_string, v].join(" ") unless section.plain_caption?
|
2466
|
+
end
|
2467
|
+
block.call(section, v)
|
2468
|
+
end
|
2469
|
+
end
|
2470
|
+
end
|
2471
|
+
end
|
2472
|
+
|
2473
|
+
# <+TBD+>
|
2474
|
+
def serve_object_by_id(args, text)
|
2475
|
+
if args["id"]
|
2476
|
+
o = @ids[args["id"]]
|
2477
|
+
if o
|
2478
|
+
return o
|
2479
|
+
else
|
2480
|
+
log(["Serving object failed because of unknown object", id], :error)
|
2481
|
+
end
|
2482
|
+
elsif args["array"]
|
2483
|
+
text = args["array"]
|
2484
|
+
sep = args["sep"]
|
2485
|
+
if sep
|
2486
|
+
sep = sep ? Regexp.escape(sep) : "\\s+"
|
2487
|
+
else
|
2488
|
+
sep = args["rx"]
|
2489
|
+
end
|
2490
|
+
if sep
|
2491
|
+
return text.split(Regexp.new(sep))
|
2492
|
+
else
|
2493
|
+
log("Serving array failed because no separator was defined", :error)
|
2494
|
+
end
|
2495
|
+
end
|
2496
|
+
end
|
2497
|
+
|
2498
|
+
# <+TBD+>
|
2499
|
+
def serve_formatted_object(args, text)
|
2500
|
+
id = args["id"] || text
|
2501
|
+
o = @variables[id]
|
2502
|
+
if o
|
2503
|
+
return o.format_as_string
|
2504
|
+
else
|
2505
|
+
log(["Formatting object failed because of unknown variable", id], :error)
|
2506
|
+
end
|
2507
|
+
end
|
2508
|
+
|
2509
|
+
private
|
2510
|
+
def initialize_services
|
2511
|
+
services = {}
|
2512
|
+
services['object'] = :serve_object_by_id
|
2513
|
+
services['format'] = :serve_formatted_object
|
2514
|
+
return services
|
2515
|
+
end
|
2516
|
+
|
2517
|
+
def set_safe
|
2518
|
+
if @options.allow_ruby and @options.allow_ruby.kind_of?(Integer)
|
2519
|
+
$SAFE = @options.allow_ruby
|
2520
|
+
end
|
2521
|
+
end
|
2522
|
+
|
2523
|
+
def set_standard_clips
|
2524
|
+
if defined?(@input) and @input
|
2525
|
+
for id, text in @options.clips
|
2526
|
+
src = '[clip]'
|
2527
|
+
text = parse_with_source(src, text, false)
|
2528
|
+
@options.clips[id] = Deplate::Element::Clip.new(text, self, src)
|
2529
|
+
end
|
2530
|
+
options.clips_initialized = true
|
2531
|
+
end
|
2532
|
+
@clips = @options.clips.dup if options.clips_initialized
|
2533
|
+
end
|
2534
|
+
|
2535
|
+
def reset_footones
|
2536
|
+
@footnote_last_idx = 0
|
2537
|
+
@footnotes_used = []
|
2538
|
+
end
|
2539
|
+
|
2540
|
+
# Execute block but make sure that @current_source remains unchanged
|
2541
|
+
def maintain_current_source(source, &block)
|
2542
|
+
begin
|
2543
|
+
current_source = @current_source
|
2544
|
+
current_source_stats = @current_source_stats
|
2545
|
+
@current_source = source
|
2546
|
+
if source and File.exist?(source)
|
2547
|
+
@current_source_stats = File.stat(source)
|
2548
|
+
else
|
2549
|
+
@current_source_stats = nil
|
2550
|
+
end
|
2551
|
+
block.call
|
2552
|
+
ensure
|
2553
|
+
@current_source = current_source
|
2554
|
+
@current_source_stats = current_source_stats
|
2555
|
+
end
|
2556
|
+
end
|
2557
|
+
|
2558
|
+
# Set @formatter to an instance of formatter_class, call block, and
|
2559
|
+
# restore the old @formatter
|
2560
|
+
def with_formatter(formatter_class, &block)
|
2561
|
+
if @formatter.instance_of?(formatter_class)
|
2562
|
+
block.call
|
2563
|
+
else
|
2564
|
+
formatter_orig = @formatter
|
2565
|
+
begin
|
2566
|
+
@formatter = formatter_class.new(self)
|
2567
|
+
block.call
|
2568
|
+
ensure
|
2569
|
+
@formatter = formatter_orig
|
2570
|
+
end
|
2571
|
+
end
|
2572
|
+
end
|
2573
|
+
|
2574
|
+
# Read the file
|
2575
|
+
def read_file(sources=@sources)
|
2576
|
+
for f in sources
|
2577
|
+
method = uri_method_name_with_prefix("read_from_", f)
|
2578
|
+
if method
|
2579
|
+
send(method, f)
|
2580
|
+
elsif f == "-"
|
2581
|
+
include_stringarray(@@deplate_template, @accum_elements, nil, "@@deplate_template")
|
2582
|
+
include_stdin(@accum_elements)
|
2583
|
+
elsif File.exists?(f)
|
2584
|
+
if File.stat(f).file?
|
2585
|
+
include_stringarray(@@deplate_template, @accum_elements, nil, "@@deplate_template")
|
2586
|
+
include_file(@accum_elements, f)
|
2587
|
+
elsif @options.recurse
|
2588
|
+
read_file(get_dir_listing(f))
|
2589
|
+
else
|
2590
|
+
log(['Is no file', f], :error)
|
2591
|
+
end
|
2592
|
+
else
|
2593
|
+
log(['File not found', f], :error)
|
2594
|
+
end
|
2595
|
+
end
|
2596
|
+
end
|
2597
|
+
|
2598
|
+
def process_document
|
2599
|
+
@formatter.pre_process
|
2600
|
+
process_etc
|
2601
|
+
process
|
2602
|
+
print_prepare
|
2603
|
+
print_etc
|
2604
|
+
print
|
2605
|
+
body_finish
|
2606
|
+
end
|
2607
|
+
|
2608
|
+
def process_etc
|
2609
|
+
unless @output_headings.empty?
|
2610
|
+
@output_headings.first.first_top = true
|
2611
|
+
@output_headings.last.last_top = true
|
2612
|
+
end
|
2613
|
+
|
2614
|
+
for p in preprocess
|
2615
|
+
p.call
|
2616
|
+
end
|
2617
|
+
|
2618
|
+
for fn in @footnotes.keys
|
2619
|
+
e = @footnotes[fn]
|
2620
|
+
@current_top = e.top_heading
|
2621
|
+
@footnotes[fn] = e.process
|
2622
|
+
end
|
2623
|
+
|
2624
|
+
for clp in @clips.keys
|
2625
|
+
e = @clips[clp]
|
2626
|
+
@current_top = e.top_heading
|
2627
|
+
@clips[clp] = e.process
|
2628
|
+
end
|
2629
|
+
end
|
2630
|
+
|
2631
|
+
# process elts
|
2632
|
+
def process
|
2633
|
+
log("Processing elements")
|
2634
|
+
call_methods_matching(@formatter, /^hook_pre_process_/)
|
2635
|
+
@accum_elements.collect! do |e|
|
2636
|
+
@current_top = e.top_heading
|
2637
|
+
e.process
|
2638
|
+
end
|
2639
|
+
@accum_elements.flatten!
|
2640
|
+
@accum_elements.compact!
|
2641
|
+
end
|
2642
|
+
|
2643
|
+
def print_prepare
|
2644
|
+
log("Preparing formatting")
|
2645
|
+
call_methods_matching(@formatter, /^hook_pre_prepare_/)
|
2646
|
+
@formatter.prepare
|
2647
|
+
call_methods_matching(@formatter, /^prepare_/)
|
2648
|
+
call_methods_matching(@formatter, /^hook_post_prepare_/)
|
2649
|
+
end
|
2650
|
+
|
2651
|
+
def print_etc
|
2652
|
+
reset_footones
|
2653
|
+
end
|
2654
|
+
|
2655
|
+
def print
|
2656
|
+
log("Formatting elements")
|
2657
|
+
for e in @accum_elements
|
2658
|
+
@current_top = e.top_heading
|
2659
|
+
e.print
|
2660
|
+
end
|
2661
|
+
for p in @postponed_print
|
2662
|
+
p.call
|
2663
|
+
end
|
2664
|
+
|
2665
|
+
end
|
2666
|
+
|
2667
|
+
def body_finish
|
2668
|
+
@output.body_flush
|
2669
|
+
end
|
2670
|
+
|
2671
|
+
def uri_method_name_with_prefix(prefix, uri)
|
2672
|
+
begin
|
2673
|
+
uri = URI.parse(uri)
|
2674
|
+
scheme = uri.scheme
|
2675
|
+
if scheme
|
2676
|
+
name = prefix + scheme
|
2677
|
+
if respond_to?(name)
|
2678
|
+
return name
|
2679
|
+
end
|
2680
|
+
end
|
2681
|
+
rescue URI::InvalidURIError => e
|
2682
|
+
end
|
2683
|
+
return nil
|
2684
|
+
end
|
2685
|
+
|
2686
|
+
# Set heading as the current top heading.
|
2687
|
+
def push_top_heading(heading)
|
2688
|
+
@output_headings << heading
|
2689
|
+
@current_top = heading
|
2690
|
+
end
|
2691
|
+
|
2692
|
+
def increase_maj_min(minor=false)
|
2693
|
+
if minor
|
2694
|
+
@output_maj_min[1] += 1
|
2695
|
+
else
|
2696
|
+
@output_maj_min[0] += 1
|
2697
|
+
end
|
2698
|
+
end
|
2699
|
+
|
2700
|
+
end
|
2701
|
+
|
2702
|
+
# vim: ff=unix
|
2703
|
+
# Local Variables:
|
2704
|
+
# revisionRx: MicroRev\s\+=\s\+\'
|
2705
|
+
# End:
|