rex-text 0.2.41 → 0.2.44

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99760857150edd0c9d0c3147b6529fa51244895878cf8aabb265cc17054931ed
4
- data.tar.gz: 0bf2431de8fbdd5618a65925972e09e768f1e5b3c960a63cd4764d4bbc1252e5
3
+ metadata.gz: b88966aea3c2991faabd788caf7b9141968550554530da585e6cb46207c33d64
4
+ data.tar.gz: 233562b44e969d9a210695571a3aa84fcd4c4d84a6b0d22846696f84ff67a1d5
5
5
  SHA512:
6
- metadata.gz: 10dc8566dc93c40022a13b03cb0f2e4ebde49e1bba0a7133d7048512635be71a084e54c4e68e39a7b3b36f44da57a81db6896c9cd739b314b49133cce4d9bcb3
7
- data.tar.gz: 92daf4cf40ba727164f354be16b7193b0c84dc2bf87f89f4dd59ea2d6ac1172ac58993ad12dc30216a9e490d24f1c8aaad7083d75f01033f461fe0628cd0c6c8
6
+ metadata.gz: 6cd18682c4ffcaedc3c3177f27c42e93b4009cce7316090d3a64f84207895af48393f8cd431bd062fcc5c14f10241ad5537b464e2c5484bc1c513a4ed7d76502
7
+ data.tar.gz: 29cc99e565a56b899e43f8f9312ab9520abb3838ee68d8d3702de6ee13f080bc1929401837a0a32df3e00e29f673ae3d5aeee880e765fcf2f1b5727f60dfea76
checksums.yaml.gz.sig CHANGED
Binary file
@@ -10,6 +10,7 @@ module Text
10
10
  #
11
11
  ###
12
12
  module Color
13
+ SUPPORTED_FORMAT_CODES = %w[%cya %red %grn %blu %yel %whi %mag %blk %dred %dgrn %dblu %dyel %dcya %dwhi %dmag %und %bld %clr %bgblu %bgyel %bggrn %bgmag %bgblk %bgred %bgcyn %bgwhi]
13
14
 
14
15
  AnsiAttributes =
15
16
  {
data/lib/rex/text/hex.rb CHANGED
@@ -127,51 +127,22 @@ module Rex
127
127
  # Converts a string to a hex version with wrapping support
128
128
  #
129
129
  def self.hexify(str, col = DefaultWrap, line_start = '', line_end = '', buf_start = '', buf_end = '')
130
- output = buf_start
131
- cur = 0
132
- count = 0
133
- new_line = true
134
-
135
- # Go through each byte in the string
136
- str.each_byte { |byte|
137
- count += 1
138
- append = ''
139
-
140
- # If this is a new line, prepend with the
141
- # line start text
142
- if (new_line == true)
143
- append << line_start
144
- new_line = false
145
- end
146
-
147
- # Append the hexified version of the byte
148
- append << sprintf("\\x%.2x", byte)
149
- cur += append.length
150
-
151
- # If we're about to hit the column or have gone past it,
152
- # time to finish up this line
153
- if ((cur + line_end.length >= col) or (cur + buf_end.length >= col))
154
- new_line = true
155
- cur = 0
130
+ if col < line_start.length + 4 + line_end.length
131
+ # raise an exception
132
+ raise ArgumentError.new('insufficient column width')
133
+ end
156
134
 
157
- # If this is the last byte, use the buf_end instead of
158
- # line_end
159
- if (count == str.length)
160
- append << buf_end + "\n"
161
- else
162
- append << line_end + "\n"
163
- end
135
+ ret = buf_start.dup
136
+ ret << line_start if ret.end_with?("\n")
137
+ str.each_char do |char|
138
+ # "\x##".length is 4, check if we're going over the wrap boundary
139
+ if (ret.split("\n").last || '').length + 4 + line_end.length > col
140
+ ret << "#{line_end}\n#{line_start}"
164
141
  end
165
-
166
- output << append
167
- }
168
-
169
- # If we were in the middle of a line, finish the buffer at this point
170
- if (new_line == false)
171
- output << buf_end + "\n"
142
+ ret << "\\x" << char.unpack('H*')[0]
172
143
  end
173
-
174
- return output
144
+ ret << "\n" if ret.split("\n").last.length + buf_end.length > col
145
+ ret << "#{buf_end}\n"
175
146
  end
176
147
 
177
148
  #
data/lib/rex/text/lang.rb CHANGED
@@ -29,13 +29,14 @@ module Rex
29
29
 
30
30
  def self.to_csharp(str, wrap = DefaultWrap, name = "buf")
31
31
  ret = "byte[] #{name} = new byte[#{str.length}] {"
32
- i = -1;
33
- while (i += 1) < str.length
34
- ret << "\n" if i%(wrap/4) == 0
35
- ret << "0x" << str[i].unpack("H*")[0] << ","
32
+ str.each_char do |char|
33
+ # "0x##,".length is 5, check if we're going over the wrap boundary
34
+ ret << "\n" if ret.split("\n").last.length + 5 > wrap
35
+ ret << "0x" << char.unpack('H*')[0] << ","
36
36
  end
37
- ret = ret[0..ret.length-2] #cut off last comma
38
- ret << " };\n"
37
+ ret = ret[0..ret.length - 2] unless str.empty? # cut off last comma
38
+ ret << "\n" if ret.split("\n").last.length + 2 > wrap
39
+ ret << "};\n"
39
40
  end
40
41
 
41
42
  #
@@ -43,21 +44,21 @@ module Rex
43
44
  #
44
45
  def self.to_golang(str, wrap = DefaultWrap, name = "buf")
45
46
  ret = "#{name} := []byte{"
46
- i = -1;
47
- while (i += 1) < str.length
48
- ret << "\n" if i%(wrap/4) == 0
49
- ret << "0x" << str[i].unpack("H*")[0] << ", "
47
+ str.each_char do |char|
48
+ # "0x##,".length is 5, check if we're going over the wrap boundary
49
+ ret << "\n" if ret.split("\n").last.length + 5 > wrap
50
+ ret << "0x" << char.unpack('H*')[0] << ","
50
51
  end
51
- ret = ret[0..ret.length-3] #cut off last comma
52
- ret << " }\n"
53
-
52
+ ret = ret[0..ret.length - 2] unless str.empty? # cut off last comma
53
+ ret << "\n" if ret.split("\n").last.length + 2 > wrap
54
+ ret << "};\n"
54
55
  end
55
-
56
+
56
57
  #
57
58
  # Creates a golang style comment
58
59
  #
59
60
  def self.to_golang_comment(str, wrap = DefaultWrap)
60
- return "/*\n" + wordwrap(str, 0, wrap, '', '') + "*/\n"
61
+ return "/*\n" + wordwrap(str, 0, wrap, '', '') + "*/\n"
61
62
  end
62
63
 
63
64
  #
@@ -181,6 +182,5 @@ module Rex
181
182
  return wordwrap(str, 0, wrap, '', '# ')
182
183
  end
183
184
 
184
-
185
185
  end
186
186
  end
@@ -1,5 +1,5 @@
1
1
  module Rex
2
2
  module Text
3
- VERSION = "0.2.41"
3
+ VERSION = "0.2.44"
4
4
  end
5
5
  end
@@ -390,6 +390,7 @@ protected
390
390
  # Converts a row to a string.
391
391
  #
392
392
  def row_to_s(row) # :nodoc:
393
+ row = row.each_with_index.map { |cell, index| style_table_field(cell, index) }
393
394
  optimal_widths = calculate_optimal_widths
394
395
  values_as_chunks = chunk_values(row, optimal_widths)
395
396
  chunks_to_s(values_as_chunks, optimal_widths)
@@ -401,17 +402,134 @@ protected
401
402
  # widths. For now it simply returns the string's length.
402
403
  #
403
404
  def display_width(str)
404
- str.length
405
+ Rex::Text.display_width(str)
405
406
  end
406
407
 
408
+ #
409
+ # Returns a string of color/formatting codes made up of the previously stored color_state
410
+ # e.g. if a `%blu` color code spans multiple lines this will return a string of `%blu` to be appended to
411
+ # the beginning of each row
412
+ #
413
+ # @param [Hash<String, String>] color_state tracks current color/formatting codes within table row
414
+ # @returns [String] Color code string such as `%blu%grn'
415
+ def color_code_string_for(color_state)
416
+ result = ''.dup
417
+ color_state.each do |_format, value|
418
+ if value.is_a?(Array)
419
+ result << value.uniq.join
420
+ else
421
+ result << value
422
+ end
423
+ end
424
+ result
425
+ end
426
+
427
+ # @returns [Hash<String, String>] The supported color codes from {Rex::Text::Color} grouped into sections
428
+ def color_code_groups
429
+ return @color_code_groups if @color_code_groups
430
+
431
+ @color_code_groups = {
432
+ foreground: %w[
433
+ %cya %red %grn %blu %yel %whi %mag %blk
434
+ %dred %dgrn %dblu %dyel %dcya %dwhi %dmag
435
+ ],
436
+ background: %w[
437
+ %bgblu %bgyel %bggrn %bgmag %bgblk %bgred %bgcyn %bgwhi
438
+ ],
439
+ decoration: %w[
440
+ %und %bld
441
+ ],
442
+ clear: %w[
443
+ %clr
444
+ ]
445
+ }
446
+
447
+ # Developer exception raised to ensure all color codes are accounted for. Verified via tests.
448
+ missing_color_codes = (Rex::Text::Color::SUPPORTED_FORMAT_CODES - @color_code_groups.values.flatten)
449
+ raise "Unsupported color codes #{missing_color_codes.join(', ')}" if missing_color_codes.any?
450
+
451
+ @color_code_groups
452
+ end
453
+
454
+ # Find the preceding color type and value of a given string
455
+ # @param [String] string A string such as '%bgyel etc'
456
+ # @returns [Array,nil] A tuple with the color type and value, or nil
457
+ def find_color_type_and_value(string)
458
+ color_code_groups.each do |color_type, color_values|
459
+ color_value = color_values.find { |color_value| string.start_with?(color_value) }
460
+ if color_value
461
+ return [color_type, color_value]
462
+ end
463
+ end
464
+
465
+ nil
466
+ end
467
+
468
+ #
469
+ # Takes an array of row values and an integer of optimal column width, loops over array and parses
470
+ # each string to gather color/formatting tags and handles those appropriately while not increasing the column width
471
+ #
472
+ # e.g. if a formatting "%blu" spans across multiple lines it needs to be added to the beginning off every following
473
+ # line, and each line will have a "%clr" added to the end of each row
474
+ #
475
+ # @param [Array<String>] values
476
+ # @param [Integer] optimal_widths
407
477
  def chunk_values(values, optimal_widths)
408
478
  # First split long strings into an array of chunks, where each chunk size is the calculated column width
409
479
  values_as_chunks = values.each_with_index.map do |value, idx|
480
+ color_state = {}
410
481
  column_width = optimal_widths[idx]
411
- value
412
- .chars
413
- .each_slice(column_width)
414
- .map(&:join)
482
+ chunks = []
483
+ current_chunk = nil
484
+ chars = value.chars
485
+ char_index = 0
486
+
487
+ # Check if any color code(s) from previous the string need appended
488
+ while char_index < chars.length do
489
+ # If a new chunk has started, start the chunk with any previous color codes
490
+ if current_chunk.nil? && color_state.any?
491
+ current_chunk = color_code_string_for(color_state)
492
+ end
493
+ current_chunk ||= ''.dup
494
+
495
+ # Check if the remaining chars start with a color code such as %blu
496
+ color_type_and_value = chars[char_index] == '%' ? find_color_type_and_value(chars[char_index..].join) : nil
497
+ if color_type_and_value.nil?
498
+ current_chunk << chars[char_index]
499
+ char_index += 1
500
+ else
501
+ color_type, color_code = color_type_and_value
502
+
503
+ if color_type == :clear
504
+ color_state.clear
505
+ elsif color_type == :decoration
506
+ # Multiple decorations can be enabled
507
+ color_state[:decoration] ||= []
508
+ color_state[:decoration] << color_code
509
+ else
510
+ # There can only be one foreground or background color
511
+ color_state[color_type] = color_code
512
+ end
513
+
514
+ current_chunk << color_code
515
+ char_index += color_code.length
516
+ end
517
+
518
+ # If we've reached the final character of the string, or need to word wrap
519
+ # it's time to push the current chunk, and start a new row. Also discard
520
+ # any values that are purely colors and have no display_width
521
+ is_final_character = char_index >= chars.length
522
+ display_width = display_width(current_chunk)
523
+ if (is_final_character && display_width != 0) || display_width == column_width
524
+ if color_state.any? && !current_chunk.end_with?('%clr')
525
+ current_chunk << '%clr'
526
+ end
527
+ chunks.push(current_chunk)
528
+ current_chunk = nil
529
+ end
530
+ end
531
+
532
+ chunks
415
533
  end
416
534
 
417
535
  values_as_chunks
@@ -424,12 +542,13 @@ protected
424
542
  line = ""
425
543
  row_chunks.each_with_index do |chunk, idx|
426
544
  column_width = optimal_widths[idx]
545
+ chunk_length_with_padding = column_width + (chunk.to_s.length - display_width(chunk.to_s))
427
546
 
428
547
  if idx == 0
429
548
  line << ' ' * indent
430
549
  end
431
550
 
432
- line << chunk.to_s.ljust(column_width)
551
+ line << chunk.to_s.ljust(chunk_length_with_padding)
433
552
  line << ' ' * cellpad
434
553
  end
435
554
 
@@ -519,13 +638,12 @@ protected
519
638
  str_cp
520
639
  end
521
640
 
522
- def style_table_field(str, _idx)
641
+ def style_table_field(str, idx)
523
642
  str_cp = str.dup
524
643
 
525
- # Not invoking as color currently conflicts with the wrapping of tables
526
- # colprops[idx]['Stylers'].each do |s|
527
- # str_cp = s.style(str_cp)
528
- # end
644
+ colprops[idx]['Stylers'].each do |s|
645
+ str_cp = s.style(str_cp)
646
+ end
529
647
 
530
648
  str_cp
531
649
  end
data/lib/rex/text.rb CHANGED
@@ -98,6 +98,21 @@ module Rex
98
98
  end
99
99
 
100
100
 
101
+ # @return [Regexp] Matches a valid color code, i.e. "%blu,%yel,...etc"
102
+ COLOR_CODES_REGEX = /#{Regexp.union(Rex::Text::Color::SUPPORTED_FORMAT_CODES.compact).source}/
103
+ private_constant :COLOR_CODES_REGEX
104
+
105
+ #
106
+ # Function that aims to calculate the display width of the given string.
107
+ # In the future this will be aware of East Asian characters having different display
108
+ # widths. For now it simply returns the string's length ignoring color codes.
109
+ #
110
+ # @param [String] str
111
+ # @return [Integer]
112
+ def self.display_width(str)
113
+ str.gsub(COLOR_CODES_REGEX, '').length
114
+ end
115
+
101
116
  #
102
117
  # Convert 16-byte string to a GUID string
103
118
  #
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rex-text
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.41
4
+ version: 0.2.44
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
@@ -93,7 +93,7 @@ cert_chain:
93
93
  EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
94
94
  9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
95
95
  -----END CERTIFICATE-----
96
- date: 2022-08-31 00:00:00.000000000 Z
96
+ date: 2022-09-02 00:00:00.000000000 Z
97
97
  dependencies:
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: rake
metadata.gz.sig CHANGED
Binary file