asciidoctor 1.5.6.2 → 1.5.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of asciidoctor might be problematic. Click here for more details.

Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +330 -143
  3. data/README-fr.adoc +441 -0
  4. data/README-jp.adoc +418 -0
  5. data/README-zh_CN.adoc +430 -0
  6. data/README.adoc +454 -0
  7. data/Rakefile +57 -0
  8. data/asciidoctor.gemspec +7 -1
  9. data/data/locale/attributes-ar.adoc +22 -0
  10. data/data/locale/attributes-bg.adoc +22 -0
  11. data/data/locale/attributes-ca.adoc +22 -0
  12. data/data/locale/attributes-cs.adoc +22 -0
  13. data/data/locale/attributes-da.adoc +22 -0
  14. data/data/locale/attributes-de.adoc +22 -0
  15. data/data/locale/attributes-en.adoc +23 -0
  16. data/data/locale/attributes-es.adoc +22 -0
  17. data/data/locale/attributes-fa.adoc +22 -0
  18. data/data/locale/attributes-fi.adoc +22 -0
  19. data/data/locale/attributes-fr.adoc +22 -0
  20. data/data/locale/attributes-hu.adoc +22 -0
  21. data/data/locale/attributes-id.adoc +22 -0
  22. data/data/locale/attributes-it.adoc +22 -0
  23. data/data/locale/attributes-ja.adoc +22 -0
  24. data/data/locale/attributes-kr.adoc +22 -0
  25. data/data/locale/attributes-nb.adoc +22 -0
  26. data/data/locale/attributes-nl.adoc +22 -0
  27. data/data/locale/attributes-nn.adoc +22 -0
  28. data/data/locale/attributes-pl.adoc +22 -0
  29. data/data/locale/attributes-pt.adoc +22 -0
  30. data/data/locale/attributes-pt_BR.adoc +22 -0
  31. data/data/locale/attributes-ro.adoc +22 -0
  32. data/data/locale/attributes-ru.adoc +22 -0
  33. data/data/locale/attributes-sr.adoc +22 -0
  34. data/data/locale/attributes-sr_Latn.adoc +22 -0
  35. data/data/locale/attributes-tr.adoc +22 -0
  36. data/data/locale/attributes-uk.adoc +22 -0
  37. data/data/locale/attributes-zh_CN.adoc +22 -0
  38. data/data/locale/attributes-zh_TW.adoc +22 -0
  39. data/data/locale/attributes.adoc +8 -649
  40. data/data/stylesheets/asciidoctor-default.css +77 -72
  41. data/features/xref.feature +366 -7
  42. data/lib/asciidoctor.rb +107 -93
  43. data/lib/asciidoctor/abstract_block.rb +247 -239
  44. data/lib/asciidoctor/abstract_node.rb +56 -58
  45. data/lib/asciidoctor/block.rb +3 -3
  46. data/lib/asciidoctor/callouts.rb +1 -1
  47. data/lib/asciidoctor/cli/invoker.rb +36 -9
  48. data/lib/asciidoctor/cli/options.rb +63 -25
  49. data/lib/asciidoctor/converter.rb +23 -13
  50. data/lib/asciidoctor/converter/base.rb +4 -0
  51. data/lib/asciidoctor/converter/docbook45.rb +16 -9
  52. data/lib/asciidoctor/converter/docbook5.rb +115 -97
  53. data/lib/asciidoctor/converter/factory.rb +29 -31
  54. data/lib/asciidoctor/converter/html5.rb +229 -192
  55. data/lib/asciidoctor/converter/manpage.rb +72 -50
  56. data/lib/asciidoctor/converter/template.rb +12 -12
  57. data/lib/asciidoctor/core_ext.rb +5 -1
  58. data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +6 -0
  59. data/lib/asciidoctor/document.rb +168 -77
  60. data/lib/asciidoctor/extensions.rb +79 -47
  61. data/lib/asciidoctor/helpers.rb +33 -11
  62. data/lib/asciidoctor/inline.rb +3 -2
  63. data/lib/asciidoctor/list.rb +2 -1
  64. data/lib/asciidoctor/logging.rb +122 -0
  65. data/lib/asciidoctor/parser.rb +406 -382
  66. data/lib/asciidoctor/path_resolver.rb +169 -162
  67. data/lib/asciidoctor/reader.rb +166 -121
  68. data/lib/asciidoctor/section.rb +45 -28
  69. data/lib/asciidoctor/stylesheets.rb +13 -5
  70. data/lib/asciidoctor/substitutors.rb +328 -254
  71. data/lib/asciidoctor/table.rb +105 -48
  72. data/lib/asciidoctor/timings.rb +34 -6
  73. data/lib/asciidoctor/version.rb +1 -1
  74. data/man/asciidoctor.1 +41 -23
  75. data/man/asciidoctor.adoc +14 -8
  76. data/test/api_test.rb +1004 -0
  77. data/test/attributes_test.rb +241 -50
  78. data/test/blocks_test.rb +549 -124
  79. data/test/converter_test.rb +170 -78
  80. data/test/document_test.rb +208 -767
  81. data/test/extensions_test.rb +188 -53
  82. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +1 -1
  83. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +1 -1
  84. data/test/fixtures/file-with-missing-include.adoc +1 -0
  85. data/test/fixtures/include-file.jsx +8 -0
  86. data/test/fixtures/lists.adoc +96 -0
  87. data/test/fixtures/other-chapters.adoc +11 -0
  88. data/test/fixtures/outer-include.adoc +5 -0
  89. data/test/fixtures/sample.asciidoc +5 -1
  90. data/test/fixtures/subdir/index.adoc +3 -0
  91. data/test/fixtures/subdir/inner-include.adoc +3 -0
  92. data/test/fixtures/subdir/middle-include.adoc +5 -0
  93. data/test/fixtures/tagged-class-enclosed.rb +0 -1
  94. data/test/fixtures/unclosed-tag.adoc +3 -0
  95. data/test/fixtures/unexpected-end-tag.adoc +4 -0
  96. data/test/invoker_test.rb +101 -40
  97. data/test/links_test.rb +266 -72
  98. data/test/lists_test.rb +243 -45
  99. data/test/logger_test.rb +211 -0
  100. data/test/manpage_test.rb +124 -6
  101. data/test/options_test.rb +46 -1
  102. data/test/paragraphs_test.rb +23 -10
  103. data/test/parser_test.rb +30 -1
  104. data/test/paths_test.rb +115 -33
  105. data/test/preamble_test.rb +1 -1
  106. data/test/reader_test.rb +337 -81
  107. data/test/sections_test.rb +656 -72
  108. data/test/substitutions_test.rb +182 -57
  109. data/test/tables_test.rb +324 -57
  110. data/test/test_helper.rb +77 -32
  111. data/test/text_test.rb +7 -7
  112. metadata +67 -3
@@ -4,26 +4,26 @@ module Asciidoctor
4
4
  # node of AsciiDoc content. The state and methods on this class are comment to
5
5
  # all content segments in an AsciiDoc document.
6
6
  class AbstractNode
7
-
7
+ include Logging
8
8
  include Substitutors
9
9
 
10
- # Public: Get the element which is the parent of this node
11
- attr_reader :parent
12
-
13
- # Public: Get the Asciidoctor::Document to which this node belongs
14
- attr_reader :document
10
+ # Public: Get the Hash of attributes for this node
11
+ attr_reader :attributes
15
12
 
16
13
  # Public: Get the Symbol context for this node
17
14
  attr_reader :context
18
15
 
19
- # Public: Get the String name of this node
20
- attr_reader :node_name
16
+ # Public: Get the Asciidoctor::Document to which this node belongs
17
+ attr_reader :document
21
18
 
22
19
  # Public: Get/Set the id of this node
23
20
  attr_accessor :id
24
21
 
25
- # Public: Get the Hash of attributes for this node
26
- attr_reader :attributes
22
+ # Public: Get the String name of this node
23
+ attr_reader :node_name
24
+
25
+ # Public: Get the element which is the parent of this node
26
+ attr_reader :parent
27
27
 
28
28
  def initialize parent, context, opts = {}
29
29
  if context == :document
@@ -42,22 +42,6 @@ class AbstractNode
42
42
  @passthroughs = {}
43
43
  end
44
44
 
45
- # Public: Associate this Block with a new parent Block
46
- #
47
- # parent - The Block to set as the parent of this Block
48
- #
49
- # Returns nothing
50
- def parent=(parent)
51
- @parent, @document = parent, parent.document
52
- nil
53
- end
54
-
55
- # Public: Get the Asciidoctor::Converter instance being used to convert the
56
- # current Asciidoctor::Document.
57
- def converter
58
- @document.converter
59
- end
60
-
61
45
  # Public: Returns whether this {AbstractNode} is an instance of {Block}
62
46
  #
63
47
  # Returns [Boolean]
@@ -76,6 +60,21 @@ class AbstractNode
76
60
  # :nocov:
77
61
  end
78
62
 
63
+ # Public: Get the Asciidoctor::Converter instance being used to convert the
64
+ # current Asciidoctor::Document.
65
+ def converter
66
+ @document.converter
67
+ end
68
+
69
+ # Public: Associate this Block with a new parent Block
70
+ #
71
+ # parent - The Block to set as the parent of this Block
72
+ #
73
+ # Returns the new parent Block associated with this Block
74
+ def parent= parent
75
+ @parent, @document = parent, parent.document
76
+ end
77
+
79
78
  # Public: Get the value of the specified attribute
80
79
  #
81
80
  # Get the value for the specified attribute. First look in the attributes on
@@ -212,7 +211,7 @@ class AbstractNode
212
211
  # in the list of roles on this node
213
212
  def has_role?(name)
214
213
  # NOTE center + include? is faster than split + include?
215
- (val = @attributes['role'] || @document.attributes['role']).nil_or_empty? ? false : %( #{val} ).include?(%( #{name} ))
214
+ (val = @attributes['role'] || @document.attributes['role']) ? %( #{val} ).include?(%( #{name} )) : false
216
215
  end
217
216
 
218
217
  # Public: A convenience method that adds the given role directly to this node
@@ -279,15 +278,14 @@ class AbstractNode
279
278
  # Returns A String reference or data URI for an icon image
280
279
  def icon_uri name
281
280
  if attr? 'icon'
282
- # QUESTION should we add extension if resolved value is an absolute URI?
283
- if ::File.extname(uri = (image_uri attr('icon'), 'iconsdir')).empty?
284
- %(#{uri}.#{@document.attr 'icontype', 'png'})
285
- else
286
- uri
281
+ if ::File.extname(icon = (attr 'icon')).empty?
282
+ # QUESTION should we be adding the extension if the icon is an absolute URI?
283
+ icon = %(#{icon}.#{@document.attr 'icontype', 'png'})
287
284
  end
288
285
  else
289
- image_uri %(#{name}.#{@document.attr 'icontype', 'png'}), 'iconsdir'
286
+ icon = %(#{name}.#{@document.attr 'icontype', 'png'})
290
287
  end
288
+ image_uri icon, 'iconsdir'
291
289
  end
292
290
 
293
291
  # Public: Construct a URI reference or data URI to the target image.
@@ -367,15 +365,15 @@ class AbstractNode
367
365
  image_path = normalize_system_path(target_image)
368
366
  end
369
367
 
370
- unless ::File.readable? image_path
371
- warn %(asciidoctor: WARNING: image to embed not found or not readable: #{image_path})
372
- return %(data:#{mimetype};base64,)
368
+ if ::File.readable? image_path
369
+ # NOTE base64 is autoloaded by reference to ::Base64
370
+ %(data:#{mimetype};base64,#{::Base64.strict_encode64 ::IO.binread image_path})
371
+ else
372
+ logger.warn %(image to embed not found or not readable: #{image_path})
373
+ %(data:#{mimetype};base64,)
373
374
  # uncomment to return 1 pixel white dot instead
374
- #return 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
375
+ #'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
375
376
  end
376
-
377
- # NOTE base64 is autoloaded by reference to ::Base64
378
- %(data:#{mimetype};base64,#{::Base64.encode64(::IO.binread image_path).delete LF})
379
377
  end
380
378
 
381
379
  # Public: Read the image data from the specified URI and generate a data URI
@@ -402,14 +400,14 @@ class AbstractNode
402
400
 
403
401
  begin
404
402
  mimetype = nil
405
- bindata = open(image_uri, 'rb') {|fd|
403
+ bindata = open image_uri, 'rb' do |fd|
406
404
  mimetype = fd.content_type
407
405
  fd.read
408
- }
406
+ end
409
407
  # NOTE base64 is autoloaded by reference to ::Base64
410
- %(data:#{mimetype};base64,#{::Base64.encode64(bindata).delete LF})
408
+ %(data:#{mimetype};base64,#{::Base64.strict_encode64 bindata})
411
409
  rescue
412
- warn %(asciidoctor: WARNING: could not retrieve image data from URI: #{image_uri})
410
+ logger.warn %(could not retrieve image data from URI: #{image_uri})
413
411
  image_uri
414
412
  # uncomment to return empty data (however, mimetype needs to be resolved)
415
413
  #%(data:#{mimetype}:base64,)
@@ -442,8 +440,8 @@ class AbstractNode
442
440
  # start - the String start (i.e., parent) path
443
441
  # jail - the String jail path to confine the resolved path
444
442
  # opts - an optional Hash of options to control processing (default: {}):
445
- # * :recover is used to control whether the processor should auto-recover
446
- # when an illegal path is encountered
443
+ # * :recover is used to control whether the processor should
444
+ # automatically recover when an illegal path is encountered
447
445
  # * :target_name is used in messages to refer to the path being resolved
448
446
  #
449
447
  # raises a SecurityError if a jail is specified and the resolved path is
@@ -453,10 +451,9 @@ class AbstractNode
453
451
  # parent references resolved and self references removed. If a jail is provided,
454
452
  # this path will be guaranteed to be contained within the jail.
455
453
  def normalize_system_path target, start = nil, jail = nil, opts = {}
456
- path_resolver = (@path_resolver ||= PathResolver.new)
457
454
  if (doc = @document).safe < SafeMode::SAFE
458
455
  if start
459
- start = ::File.join doc.base_dir, start unless path_resolver.root? start
456
+ start = ::File.join doc.base_dir, start unless doc.path_resolver.root? start
460
457
  else
461
458
  start = doc.base_dir
462
459
  end
@@ -464,7 +461,7 @@ class AbstractNode
464
461
  start = doc.base_dir unless start
465
462
  jail = doc.base_dir unless jail
466
463
  end
467
- path_resolver.system_path target, start, jail, opts
464
+ doc.path_resolver.system_path target, start, jail, opts
468
465
  end
469
466
 
470
467
  # Public: Normalize the web path using the PathResolver.
@@ -480,7 +477,7 @@ class AbstractNode
480
477
  if preserve_uri_target && (Helpers.uriish? target)
481
478
  uri_encode_spaces target
482
479
  else
483
- (@path_resolver ||= PathResolver.new).web_path target, start
480
+ @document.path_resolver.web_path target, start
484
481
  end
485
482
  end
486
483
 
@@ -508,7 +505,8 @@ class AbstractNode
508
505
  ::IO.read path
509
506
  end
510
507
  elsif opts[:warn_on_failure]
511
- warn %(asciidoctor: WARNING: #{(attr 'docfile') || '<stdin>'}: #{opts[:label] || 'file'} does not exist or cannot be read: #{path})
508
+ logger.warn %(#{(attr 'docfile') || '<stdin>'}: #{opts[:label] || 'file'} does not exist or cannot be read: #{path})
509
+ nil
512
510
  end
513
511
  end
514
512
 
@@ -531,25 +529,25 @@ class AbstractNode
531
529
  def read_contents target, opts = {}
532
530
  doc = @document
533
531
  if (Helpers.uriish? target) || ((start = opts[:start]) && (Helpers.uriish? start) &&
534
- (target = (@path_resolver ||= PathResolver.new).web_path target, start))
532
+ (target = doc.path_resolver.web_path target, start))
535
533
  if doc.attr? 'allow-uri-read'
536
534
  Helpers.require_library 'open-uri/cached', 'open-uri-cached' if doc.attr? 'cache-uri'
537
535
  begin
538
536
  data = ::OpenURI.open_uri(target) {|fd| fd.read }
539
537
  data = (Helpers.normalize_lines_from_string data) * LF if opts[:normalize]
538
+ return data
540
539
  rescue
541
- warn %(asciidoctor: WARNING: could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
542
- data = nil
540
+ logger.warn %(could not retrieve contents of #{opts[:label] || 'asset'} at URI: #{target}) if opts.fetch :warn_on_failure, true
541
+ return
543
542
  end
544
543
  else
545
- warn %(asciidoctor: WARNING: cannot retrieve contents of #{opts[:label] || 'asset'} at URI: #{target} (allow-uri-read attribute not enabled)) if opts.fetch :warn_on_failure, true
546
- data = nil
544
+ logger.warn %(cannot retrieve contents of #{opts[:label] || 'asset'} at URI: #{target} (allow-uri-read attribute not enabled)) if opts.fetch :warn_on_failure, true
545
+ return
547
546
  end
548
547
  else
549
548
  target = normalize_system_path target, opts[:start], nil, :target_name => (opts[:label] || 'asset')
550
- data = read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true), :label => opts[:label]
549
+ return read_asset target, :normalize => opts[:normalize], :warn_on_failure => (opts.fetch :warn_on_failure, true), :label => opts[:label]
551
550
  end
552
- data
553
551
  end
554
552
 
555
553
  # Internal: URI encode spaces in a String
@@ -124,15 +124,15 @@ class Block < AbstractBlock
124
124
  result * LF
125
125
  end
126
126
  else
127
- warn %(Unknown content model '#{@content_model}' for block: #{to_s}) unless @content_model == :empty
127
+ logger.warn %(Unknown content model '#{@content_model}' for block: #{to_s}) unless @content_model == :empty
128
128
  nil
129
129
  end
130
130
  end
131
131
 
132
132
  # Public: Returns the preprocessed source of this block
133
133
  #
134
- # Returns the a String containing the lines joined together or nil if there
135
- # are no lines
134
+ # Returns the a String containing the lines joined together or empty string
135
+ # if there are no lines
136
136
  def source
137
137
  @lines * LF
138
138
  end
@@ -59,7 +59,7 @@ class Callouts
59
59
  #
60
60
  # Returns A space-separated String of callout ids associated with the specified list item
61
61
  def callout_ids li_ordinal
62
- current_list.map {|element| element[:ordinal] == li_ordinal ? %(#{element[:id]} ) : nil }.join.chop
62
+ current_list.map {|it| it[:ordinal] == li_ordinal ? %(#{it[:id]} ) : '' }.join.chop
63
63
  end
64
64
 
65
65
  # Public: The current list for which callouts are being collected
@@ -3,6 +3,7 @@ module Asciidoctor
3
3
  module Cli
4
4
  # Public Invocation class for starting Asciidoctor via CLI
5
5
  class Invoker
6
+
6
7
  attr_reader :options
7
8
  attr_reader :documents
8
9
  attr_reader :code
@@ -29,13 +30,15 @@ module Asciidoctor
29
30
  end
30
31
 
31
32
  def invoke!
32
- old_verbose = -1
33
33
  return unless @options
34
34
 
35
35
  old_verbose = $VERBOSE
36
+ old_logger = old_logger_level = nil
36
37
  opts = {}
37
38
  infiles = []
38
39
  outfile = nil
40
+ abs_srcdir_posix = nil
41
+ non_posix_env = ::File::ALT_SEPARATOR == RS
39
42
  err = @err || $stderr
40
43
  show_timings = false
41
44
 
@@ -45,6 +48,11 @@ module Asciidoctor
45
48
  infiles = val
46
49
  when :output_file
47
50
  outfile = val
51
+ when :source_dir
52
+ if val
53
+ abs_srcdir_posix = ::File.expand_path val
54
+ abs_srcdir_posix = abs_srcdir_posix.tr RS, FS if non_posix_env && (abs_srcdir_posix.include? RS)
55
+ end
48
56
  when :destination_dir
49
57
  opts[:to_dir] = val if val
50
58
  when :attributes
@@ -58,34 +66,36 @@ module Asciidoctor
58
66
  case val
59
67
  when 0
60
68
  $VERBOSE = nil
69
+ old_logger = LoggerManager.logger
70
+ LoggerManager.logger = NullLogger.new
61
71
  when 1
62
72
  $VERBOSE = false
63
73
  when 2
64
74
  $VERBOSE = true
75
+ old_logger_level, LoggerManager.logger.level = LoggerManager.logger.level, ::Logger::Severity::DEBUG
65
76
  end
66
77
  else
67
78
  opts[key] = val unless val.nil?
68
79
  end
69
80
  end
70
81
 
71
- stdin = if infiles.size == 1
82
+ if infiles.size == 1
72
83
  if (infile0 = infiles[0]) == '-'
73
84
  outfile ||= infile0
74
- true
85
+ stdin = true
75
86
  elsif ::File.pipe? infile0
76
87
  outfile ||= '-'
77
- nil
78
88
  end
79
89
  end
80
90
 
81
- tofile = if outfile == '-'
82
- @out || $stdout
91
+ if outfile == '-'
92
+ tofile = @out || $stdout
83
93
  elsif outfile
84
94
  opts[:mkdirs] = true
85
- outfile
95
+ tofile = outfile
86
96
  else
87
97
  opts[:mkdirs] = true
88
- nil # automatically calculate outfile based on infile
98
+ # automatically calculate outfile based on infile
89
99
  end
90
100
 
91
101
  if stdin
@@ -101,6 +111,17 @@ module Asciidoctor
101
111
  else
102
112
  infiles.each do |infile|
103
113
  input_opts = opts.merge :to_file => tofile
114
+ if abs_srcdir_posix && (input_opts.key? :to_dir)
115
+ abs_indir = ::File.dirname ::File.expand_path infile
116
+ if non_posix_env
117
+ abs_indir_posix = (abs_indir.include? RS) ? (abs_indir.tr RS, FS) : abs_indir
118
+ else
119
+ abs_indir_posix = abs_indir
120
+ end
121
+ if abs_indir_posix.start_with? %(#{abs_srcdir_posix}/)
122
+ input_opts[:to_dir] += abs_indir.slice abs_srcdir_posix.length, abs_indir.length
123
+ end
124
+ end
104
125
  if show_timings
105
126
  @documents << (::Asciidoctor.convert_file infile, (input_opts.merge :timings => (timings = Timings.new)))
106
127
  timings.print_report err, infile
@@ -109,6 +130,7 @@ module Asciidoctor
109
130
  end
110
131
  end
111
132
  end
133
+ @code = 1 if ((logger = LoggerManager.logger).respond_to? :max_severity) && logger.max_severity && logger.max_severity >= opts[:failure_level]
112
134
  rescue ::Exception => e
113
135
  if ::SignalException === e
114
136
  @code = e.signo
@@ -129,7 +151,12 @@ module Asciidoctor
129
151
  end
130
152
  nil
131
153
  ensure
132
- $VERBOSE = old_verbose unless old_verbose == -1
154
+ $VERBOSE = old_verbose
155
+ if old_logger
156
+ LoggerManager.logger = old_logger
157
+ elsif old_logger_level
158
+ LoggerManager.logger.level = old_logger_level
159
+ end
133
160
  end
134
161
 
135
162
  def document
@@ -1,6 +1,8 @@
1
1
  # encoding: UTF-8
2
2
  module Asciidoctor
3
3
  module Cli
4
+ FS = '/'
5
+ RS = '\\'
4
6
 
5
7
  # Public: List of options that can be specified on the command line
6
8
  class Options < ::Hash
@@ -24,7 +26,9 @@ module Asciidoctor
24
26
  self[:load_paths] = options[:load_paths] || nil
25
27
  self[:requires] = options[:requires] || nil
26
28
  self[:base_dir] = options[:base_dir]
29
+ self[:source_dir] = options[:source_dir] || nil
27
30
  self[:destination_dir] = options[:destination_dir] || nil
31
+ self[:failure_level] = ::Logger::Severity::FATAL
28
32
  self[:trace] = false
29
33
  self[:timings] = false
30
34
  end
@@ -75,8 +79,6 @@ Example: asciidoctor -b html5 source.asciidoc
75
79
  'specify eRuby implementation to use when rendering custom ERB templates: [erb, erubis] (default: erb)') do |eruby|
76
80
  self[:eruby] = eruby
77
81
  end
78
- opts.on('-C', '--compact', 'compact the output by removing blank lines. (No longer in use)') do
79
- end
80
82
  opts.on('-a', '--attribute key[=value]', 'a document attribute to set in the form of key, key! or key=value pair',
81
83
  'unless @ is appended to the value, this attributes takes precedence over attributes',
82
84
  'defined in the source document') do |attr|
@@ -104,6 +106,9 @@ Example: asciidoctor -b html5 source.asciidoc
104
106
  opts.on('-B', '--base-dir DIR', 'base directory containing the document and resources (default: directory of source file)') do |base_dir|
105
107
  self[:base_dir] = base_dir
106
108
  end
109
+ opts.on('-R', '--source-dir DIR', 'source root directory (used for calculating path in destination directory)') do |src_dir|
110
+ self[:source_dir] = src_dir
111
+ end
107
112
  opts.on('-D', '--destination-dir DIR', 'destination output directory (default: directory of source file)') do |dest_dir|
108
113
  self[:destination_dir] = dest_dir
109
114
  end
@@ -115,6 +120,10 @@ Example: asciidoctor -b html5 source.asciidoc
115
120
  'may be specified more than once') do |path|
116
121
  (self[:requires] ||= []).concat(path.split ',')
117
122
  end
123
+ opts.on('--failure-level LEVEL', %w(warning WARNING error ERROR), 'set minimum logging level that triggers a non-zero exit code: [WARN, ERROR] (default: FATAL)') do |level|
124
+ level = 'WARN' if (level = level.upcase) == 'WARNING'
125
+ self[:failure_level] = ::Logger::Severity.const_get level
126
+ end
118
127
  opts.on('-q', '--quiet', 'suppress warnings (default: false)') do |verbose|
119
128
  self[:verbose] = 0
120
129
  end
@@ -132,11 +141,32 @@ Example: asciidoctor -b html5 source.asciidoc
132
141
  'show the command usage if TOPIC is not specified (or not recognized)',
133
142
  'dump the Asciidoctor man page (in troff/groff format) if TOPIC is manpage') do |topic|
134
143
  if topic == 'manpage'
135
- if ::File.exist?(manpage_path = (::File.join ::Asciidoctor::ROOT_PATH, 'man', 'asciidoctor.1'))
136
- $stdout.puts(::IO.read manpage_path)
144
+ if (manpage_path = ENV['ASCIIDOCTOR_MANPAGE_PATH'])
145
+ if ::File.exist? manpage_path
146
+ if manpage_path.end_with? '.gz'
147
+ require 'zlib' unless defined? ::Zlib::GzipReader
148
+ $stdout.puts ::Zlib::GzipReader.open(manpage_path) {|gz| gz.read }
149
+ else
150
+ $stdout.puts ::IO.read manpage_path
151
+ end
152
+ else
153
+ $stderr.puts %(asciidoctor: FAILED: manual page not found: #{manpage_path})
154
+ return 1
155
+ end
156
+ elsif ::File.exist?(manpage_path = (::File.join ::Asciidoctor::ROOT_PATH, 'man', 'asciidoctor.1'))
157
+ $stdout.puts ::IO.read manpage_path
137
158
  else
138
- $stderr.puts 'asciidoctor: FAILED: man page not found; try `man asciidoctor`'
139
- return 1
159
+ require 'open3' unless defined? ::Open3.popen3
160
+ manpage_path = ::Open3.popen3('man -w asciidoctor') {|_, out| out.read }.chop rescue ''
161
+ if manpage_path.empty?
162
+ $stderr.puts 'asciidoctor: FAILED: manual page not found; try `man asciidoctor`'
163
+ return 1
164
+ elsif manpage_path.end_with? '.gz'
165
+ require 'zlib' unless defined? ::Zlib::GzipReader
166
+ $stdout.puts ::Zlib::GzipReader.open(manpage_path) {|gz| gz.read }
167
+ else
168
+ $stdout.puts ::IO.read manpage_path
169
+ end
140
170
  end
141
171
  else
142
172
  $stdout.puts opts
@@ -171,31 +201,39 @@ Example: asciidoctor -b html5 source.asciidoc
171
201
  # warn, but don't panic; we may have enough to proceed, so we won't force a failure
172
202
  $stderr.puts %(asciidoctor: WARNING: extra arguments detected (unparsed arguments: '#{args * "', '"}') or incorrect usage of stdin)
173
203
  else
174
- if ::File.readable? file
175
- matches = [file]
204
+ if ::File.file? file
205
+ infiles << file
206
+ # NOTE only attempt to glob if file is not found
176
207
  else
177
208
  # Tilt backslashes in Windows paths the Ruby-friendly way
178
- if ::File::ALT_SEPARATOR == '\\' && (file.include? '\\')
179
- file = file.tr '\\', '/'
209
+ if ::File::ALT_SEPARATOR == RS && (file.include? RS)
210
+ file = file.tr RS, FS
180
211
  end
181
212
  if (matches = ::Dir.glob file).empty?
182
- $stderr.puts %(asciidoctor: FAILED: input file #{file} missing or cannot be read)
183
- return 1
213
+ # NOTE if no matches, assume it's just a missing file and proceed
214
+ infiles << file
215
+ else
216
+ infiles.concat matches
184
217
  end
185
218
  end
186
-
187
- infiles.concat matches
188
219
  end
189
220
  end
190
221
  end
191
222
 
192
- infiles.each do |file|
193
- unless file == '-' || (::File.file? file) || (::File.pipe? file)
194
- if ::File.readable? file
195
- $stderr.puts %(asciidoctor: FAILED: input path #{file} is a #{(::File.stat file).ftype}, not a file)
223
+ infiles.reject {|file| file == '-' }.each do |file|
224
+ begin
225
+ fstat = ::File.stat file
226
+ if fstat.file? || fstat.pipe?
227
+ unless fstat.readable?
228
+ $stderr.puts %(asciidoctor: FAILED: input file #{file} is not readable)
229
+ return 1
230
+ end
196
231
  else
197
- $stderr.puts %(asciidoctor: FAILED: input file #{file} missing or cannot be read)
232
+ $stderr.puts %(asciidoctor: FAILED: input path #{file} is a #{fstat.ftype}, not a file)
233
+ return 1
198
234
  end
235
+ rescue ::Errno::ENOENT
236
+ $stderr.puts %(asciidoctor: FAILED: input file #{file} is missing)
199
237
  return 1
200
238
  end
201
239
  end
@@ -206,7 +244,7 @@ Example: asciidoctor -b html5 source.asciidoc
206
244
 
207
245
  if self[:template_dirs]
208
246
  begin
209
- require 'tilt' unless defined? ::Tilt
247
+ require 'tilt' unless defined? ::Tilt::VERSION
210
248
  rescue ::LoadError
211
249
  raise $! if self[:trace]
212
250
  $stderr.puts 'asciidoctor: FAILED: \'tilt\' could not be loaded'
@@ -220,7 +258,7 @@ Example: asciidoctor -b html5 source.asciidoc
220
258
 
221
259
  if (load_paths = self[:load_paths])
222
260
  (self[:load_paths] = load_paths.uniq).reverse_each do |path|
223
- $:.unshift File.expand_path(path)
261
+ $:.unshift ::File.expand_path(path)
224
262
  end
225
263
  end
226
264
 
@@ -251,10 +289,10 @@ Example: asciidoctor -b html5 source.asciidoc
251
289
  end
252
290
 
253
291
  def print_version os = $stdout
254
- os.puts %(Asciidoctor #{::Asciidoctor::VERSION} [http://asciidoctor.org])
255
- if RUBY_VERSION >= '1.9.3'
256
- encoding_info = {'lc' => 'locale', 'fs' => 'filesystem', 'in' => 'internal', 'ex' => 'external'}.map do |k,v|
257
- %(#{k}:#{::Encoding.find(v) || '-'})
292
+ os.puts %(Asciidoctor #{::Asciidoctor::VERSION} [https://asciidoctor.org])
293
+ if RUBY_MIN_VERSION_1_9
294
+ encoding_info = { 'lc' => 'locale', 'fs' => 'filesystem', 'in' => 'internal', 'ex' => 'external' }.map do |k, v|
295
+ %(#{k}:#{v == 'internal' ? (::File.open(__FILE__) {|f| f.getc }).encoding : (::Encoding.find v)})
258
296
  end
259
297
  os.puts %(Runtime Environment (#{RUBY_DESCRIPTION}) (#{encoding_info * ' '}))
260
298
  else