rex-text 0.1.1 → 0.1.2

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
  SHA1:
3
- metadata.gz: c21a0eef12185e5d77b4ab24904765c42f464004
4
- data.tar.gz: 6f588d580a7b84a6eddfcf4a07cf0ec617e16d21
3
+ metadata.gz: a213d45ce77b442cf80cc9e8c302d705c9f63128
4
+ data.tar.gz: 739aabf270a14239b68f9bdc3f394eebaa949efc
5
5
  SHA512:
6
- metadata.gz: 91d1531cc4a260903fa59357a6b4aeea72ddce6b3018953e6084f1943149ddedfcd2f7378d6638992020c9e2244a44ca2ab63a48809b4648230388d1d3601304
7
- data.tar.gz: 2cab8cb2862e871ea5cdde228266a1796c23ffaadb46f71118976108a7bff008d6aebd11e09a7e3f43825790a856d8ee3502b4a139a454c7f617ee1d3c9b2403
6
+ metadata.gz: cd856ab7b00383f19e189e4da9f55e8b078a6ef1429535b4e6c223ce853dd83bbdd8fb562cb0f7fe295db52de89c538e11fa1b677f0d97a15684271de22671d7
7
+ data.tar.gz: e77455056b8e1ffa80af71886deae1576a21b8f92bc2450f296dc4430ec6cd5c9a229d03c95fc3cc6cb788aeef5aa9e829103d88116d751bc6046c20b5e1dd33
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/lib/rex/text.rb CHANGED
@@ -25,6 +25,10 @@ require 'rex/text/ebcdic'
25
25
  require 'rex/text/pattern'
26
26
  require 'rex/text/badchars'
27
27
 
28
+ require 'rex/text/color'
29
+ require 'rex/text/table'
30
+
31
+
28
32
  module Rex
29
33
 
30
34
  ###
@@ -0,0 +1,106 @@
1
+ # -*- coding: binary -*-
2
+ module Rex
3
+ module Text
4
+
5
+ ###
6
+ #
7
+ # This module provides an interface to getting ANSI color codes.
8
+ # It's taken mostly from perl's Term::ANSIColor by Russ Allbery
9
+ # <rra@stanford.edu> and Zenin <zenin@best.com>.
10
+ #
11
+ ###
12
+ module Color
13
+
14
+ AnsiAttributes =
15
+ {
16
+ 'clear' => 0,
17
+ 'reset' => 0,
18
+ 'bold' => 1,
19
+ 'dark' => 2,
20
+ 'underline' => 4,
21
+ 'underscore' => 4,
22
+ 'blink' => 5,
23
+ 'reverse' => 7,
24
+ 'concealed' => 8,
25
+ 'black' => 30, 'on_black' => 40,
26
+ 'red' => 31, 'on_red' => 41,
27
+ 'green' => 32, 'on_green' => 42,
28
+ 'yellow' => 33, 'on_yellow' => 43,
29
+ 'blue' => 34, 'on_blue' => 44,
30
+ 'magenta' => 35, 'on_magenta' => 45,
31
+ 'cyan' => 36, 'on_cyan' => 46,
32
+ 'white' => 37, 'on_white' => 47
33
+ }
34
+
35
+ #
36
+ # Return a string with ANSI codes substituted. Derived from code
37
+ # written by The FaerieMUD Consortium.
38
+ #
39
+ def ansi(*attrs)
40
+ attr = attrs.collect {|a| AnsiAttributes[a] ? AnsiAttributes[a] : nil}.compact.join(';')
41
+ attr = "\e[%sm" % attr if (attr.empty? == false)
42
+ return attr
43
+ end
44
+
45
+ #
46
+ # Colorize if this shell supports it
47
+ #
48
+ def colorize(*color)
49
+ supports_color?() ? ansi(*color) : ''
50
+ end
51
+
52
+ def substitute_colors(msg, in_prompt = nil)
53
+ str = msg.dup
54
+ pre_color = post_color = ''
55
+ if (in_prompt)
56
+ pre_color = "\x01" # RL_PROMPT_START_IGNORE
57
+ post_color = "\x02" # RL_PROMPT_END_IGNORE
58
+ end
59
+ str.gsub!(/%cya/, pre_color+colorize('cyan')+post_color)
60
+ str.gsub!(/%red/, pre_color+colorize('red')+post_color)
61
+ str.gsub!(/%grn/, pre_color+colorize('green')+post_color)
62
+ str.gsub!(/%blu/, pre_color+colorize('blue')+post_color)
63
+ str.gsub!(/%yel/, pre_color+colorize('yellow')+post_color)
64
+ str.gsub!(/%whi/, pre_color+colorize('white')+post_color)
65
+ str.gsub!(/%mag/, pre_color+colorize('magenta')+post_color)
66
+ str.gsub!(/%blk/, pre_color+colorize('black')+post_color)
67
+ str.gsub!(/%dred/, pre_color+colorize('dark', 'red')+post_color)
68
+ str.gsub!(/%dgrn/, pre_color+colorize('dark', 'green')+post_color)
69
+ str.gsub!(/%dblu/, pre_color+colorize('dark', 'blue')+post_color)
70
+ str.gsub!(/%dyel/, pre_color+colorize('dark', 'yellow')+post_color)
71
+ str.gsub!(/%dcya/, pre_color+colorize('dark', 'cyan')+post_color)
72
+ str.gsub!(/%dwhi/, pre_color+colorize('dark', 'white')+post_color)
73
+ str.gsub!(/%dmag/, pre_color+colorize('dark', 'magenta')+post_color)
74
+ str.gsub!(/%und/, pre_color+colorize('underline')+post_color)
75
+ str.gsub!(/%bld/, pre_color+colorize('bold')+post_color)
76
+ str.gsub!(/%clr/, pre_color+colorize('clear')+post_color)
77
+ # Background Color
78
+ str.gsub!(/%bgblu/, pre_color+colorize('on_blue')+post_color)
79
+ str.gsub!(/%bgyel/, pre_color+colorize('on_yellow')+post_color)
80
+ str.gsub!(/%bggrn/, pre_color+colorize('on_green')+post_color)
81
+ str.gsub!(/%bgmag/, pre_color+colorize('on_magenta')+post_color)
82
+ str.gsub!(/%bgblk/, pre_color+colorize('on_black')+post_color)
83
+ str.gsub!(/%bgred/, pre_color+colorize('on_red')+post_color)
84
+ str.gsub!(/%bgcyn/, pre_color+colorize('on_cyan')+post_color)
85
+ str.gsub!(/%bgwhi/, pre_color+colorize('on_white')+post_color)
86
+
87
+ str
88
+ end
89
+
90
+ #
91
+ # Resets coloring so that it's back to normal.
92
+ #
93
+ def reset_color
94
+ return if not supports_color?
95
+ print(colorize('clear'))
96
+ end
97
+
98
+ #
99
+ # Colorize if this shell supports it
100
+ #
101
+ def do_colorize(*color)
102
+ supports_color?() ? ansi(*color) : ''
103
+ end
104
+ end
105
+
106
+ end end
@@ -0,0 +1,418 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module Text
5
+
6
+ ###
7
+ #
8
+ # Prints text in a tablized format. Pretty lame at the moment, but
9
+ # whatever.
10
+ #
11
+ ###
12
+ class Table
13
+
14
+ #
15
+ # Initializes a text table instance using the supplied properties. The
16
+ # Table class supports the following hash attributes:
17
+ #
18
+ # Header
19
+ #
20
+ # The string to display as a heading above the table. If none is
21
+ # specified, no header will be displayed.
22
+ #
23
+ # HeaderIndent
24
+ #
25
+ # The amount of space to indent the header. The default is zero.
26
+ #
27
+ # Columns
28
+ #
29
+ # The array of columns that will exist within the table.
30
+ #
31
+ # Rows
32
+ #
33
+ # The array of rows that will exist.
34
+ #
35
+ # Width
36
+ #
37
+ # The maximum width of the table in characters.
38
+ #
39
+ # Indent
40
+ #
41
+ # The number of characters to indent the table.
42
+ #
43
+ # CellPad
44
+ #
45
+ # The number of characters to put between each horizontal cell.
46
+ #
47
+ # Prefix
48
+ #
49
+ # The text to prefix before the table.
50
+ #
51
+ # Postfix
52
+ #
53
+ # The text to affix to the end of the table.
54
+ #
55
+ # Sortindex
56
+ #
57
+ # The column to sort the table on, -1 disables sorting.
58
+ #
59
+ def initialize(opts = {})
60
+ self.header = opts['Header']
61
+ self.headeri = opts['HeaderIndent'] || 0
62
+ self.columns = opts['Columns'] || []
63
+ # updated below if we got a "Rows" option
64
+ self.rows = []
65
+
66
+ self.width = opts['Width'] || 80
67
+ self.indent = opts['Indent'] || 0
68
+ self.cellpad = opts['CellPad'] || 2
69
+ self.prefix = opts['Prefix'] || ''
70
+ self.postfix = opts['Postfix'] || ''
71
+ self.colprops = []
72
+ self.scterm = /#{opts['SearchTerm']}/mi if opts['SearchTerm']
73
+
74
+ self.sort_index = opts['SortIndex'] || 0
75
+ self.sort_order = opts['SortOrder'] || :forward
76
+
77
+ # Default column properties
78
+ self.columns.length.times { |idx|
79
+ self.colprops[idx] = {}
80
+ self.colprops[idx]['MaxWidth'] = self.columns[idx].length
81
+ }
82
+
83
+ # ensure all our internal state gets updated with the given rows by
84
+ # using add_row instead of just adding them to self.rows. See #3825.
85
+ opts['Rows'].each { |row| add_row(row) } if opts['Rows']
86
+
87
+ # Merge in options
88
+ if (opts['ColProps'])
89
+ opts['ColProps'].each_key { |col|
90
+ idx = self.columns.index(col)
91
+
92
+ if (idx)
93
+ self.colprops[idx].merge!(opts['ColProps'][col])
94
+ end
95
+ }
96
+ end
97
+
98
+ end
99
+
100
+ #
101
+ # Converts table contents to a string.
102
+ #
103
+ def to_s
104
+ str = prefix.dup
105
+ str << header_to_s || ''
106
+ str << columns_to_s || ''
107
+ str << hr_to_s || ''
108
+
109
+ sort_rows
110
+ rows.each { |row|
111
+ if (is_hr(row))
112
+ str << hr_to_s
113
+ else
114
+ str << row_to_s(row) if row_visible(row)
115
+ end
116
+ }
117
+
118
+ str << postfix
119
+
120
+ return str
121
+ end
122
+
123
+ #
124
+ # Converts table contents to a csv
125
+ #
126
+ def to_csv
127
+ str = ''
128
+ str << ( columns.join(",") + "\n" )
129
+ rows.each { |row|
130
+ next if is_hr(row) || !row_visible(row)
131
+ str << ( row.map{|x|
132
+ x = x.to_s
133
+ x.gsub(/[\r\n]/, ' ').gsub(/\s+/, ' ').gsub('"', '""')
134
+ }.map{|x| "\"#{x}\"" }.join(",") + "\n" )
135
+ }
136
+ str
137
+ end
138
+
139
+ #
140
+ #
141
+ # Returns the header string.
142
+ #
143
+ def header_to_s # :nodoc:
144
+ if (header)
145
+ pad = " " * headeri
146
+
147
+ return pad + header + "\n" + pad + "=" * header.length + "\n\n"
148
+ end
149
+
150
+ return ''
151
+ end
152
+
153
+ #
154
+ # Prints the contents of the table.
155
+ #
156
+ def print
157
+ puts to_s
158
+ end
159
+
160
+ #
161
+ # Adds a row using the supplied fields.
162
+ #
163
+ def <<(fields)
164
+ add_row(fields)
165
+ end
166
+
167
+ #
168
+ # Adds a row with the supplied fields.
169
+ #
170
+ def add_row(fields = [])
171
+ if fields.length != self.columns.length
172
+ raise RuntimeError, 'Invalid number of columns!'
173
+ end
174
+ fields.each_with_index { |field, idx|
175
+ # Remove whitespace and ensure String format
176
+ field = field.to_s.strip
177
+ if (colprops[idx]['MaxWidth'] < field.to_s.length)
178
+ old = colprops[idx]['MaxWidth']
179
+ colprops[idx]['MaxWidth'] = field.to_s.length
180
+ end
181
+ }
182
+
183
+ rows << fields
184
+ end
185
+
186
+ #
187
+ # Sorts the rows based on the supplied index of sub-arrays
188
+ # If the supplied index is an IPv4 address, handle it differently, but
189
+ # avoid actually resolving domain names.
190
+ #
191
+ def sort_rows(index = sort_index, order = sort_order)
192
+ return if index == -1
193
+ return unless rows
194
+ rows.sort! do |a,b|
195
+ if a[index].nil?
196
+ cmp = -1
197
+ elsif b[index].nil?
198
+ cmp = 1
199
+ elsif valid_ip?(a[index]) and valid_ip?(b[index])
200
+ cmp = IPAddr.new(a[index]) <=> IPAddr.new(b[index])
201
+ elsif a[index] =~ /^[0-9]+$/ and b[index] =~ /^[0-9]+$/
202
+ cmp = a[index].to_i <=> b[index].to_i
203
+ elsif a[index].kind_of?(IPAddr) && a[index].kind_of?(IPAddr) && a[index].ipv6? && b[index].ipv4?
204
+ cmp = 1
205
+ elsif a[index].kind_of?(IPAddr) && b[index].kind_of?(IPAddr) && a[index].ipv4? && b[index].ipv6?
206
+ cmp = -1
207
+ else
208
+ cmp = a[index] <=> b[index] # assumes otherwise comparable.
209
+ end
210
+ order == :forward ? cmp : -cmp
211
+ end
212
+ end
213
+
214
+ #
215
+ # Adds a horizontal line.
216
+ #
217
+ def add_hr
218
+ rows << '__hr__'
219
+ end
220
+
221
+ #
222
+ # Returns new sub-table with headers and rows maching column names submitted
223
+ #
224
+ #
225
+ # Flips table 90 degrees left
226
+ #
227
+ def drop_left
228
+ tbl = self.class.new(
229
+ 'Columns' => Array.new(self.rows.count+1,' '),
230
+ 'Header' => self.header,
231
+ 'Indent' => self.indent)
232
+ (self.columns.count+1).times do |ti|
233
+ row = self.rows.map {|r| r[ti]}.unshift(self.columns[ti]).flatten
234
+ # insert our col|row break. kind of hackish
235
+ row[1] = "| #{row[1]}" unless row.all? {|e| e.nil? || e.empty?}
236
+ tbl << row
237
+ end
238
+ return tbl
239
+ end
240
+
241
+ def valid_ip?(value)
242
+ begin
243
+ IPAddr.new value
244
+ true
245
+ rescue IPAddr::InvalidAddressError
246
+ false
247
+ end
248
+ end
249
+
250
+ #
251
+ # Build table from CSV dump
252
+ #
253
+ def self.new_from_csv(csv)
254
+ # Read in or keep data, get CSV or die
255
+ if csv.is_a?(String)
256
+ csv = File.file?(csv) ? CSV.read(csv) : CSV.parse(csv)
257
+ end
258
+ # Adjust for skew
259
+ if csv.first == ["Keys", "Values"]
260
+ csv.shift # drop marker
261
+ cols = []
262
+ rows = []
263
+ csv.each do |row|
264
+ cols << row.shift
265
+ rows << row
266
+ end
267
+ tbl = self.new('Columns' => cols)
268
+ rows.in_groups_of(cols.count) {|r| tbl << r.flatten}
269
+ else
270
+ tbl = self.new('Columns' => csv.shift)
271
+ while !csv.empty? do
272
+ tbl << csv.shift
273
+ end
274
+ end
275
+ return tbl
276
+ end
277
+
278
+ def [](*col_names)
279
+ tbl = self.class.new('Indent' => self.indent,
280
+ 'Header' => self.header,
281
+ 'Columns' => col_names)
282
+ indexes = []
283
+
284
+ col_names.each do |col_name|
285
+ index = self.columns.index(col_name)
286
+ raise RuntimeError, "Invalid column name #{col_name}" if index.nil?
287
+ indexes << index
288
+ end
289
+
290
+ self.rows.each do |old_row|
291
+ new_row = []
292
+ indexes.map {|i| new_row << old_row[i]}
293
+ tbl << new_row
294
+ end
295
+
296
+ return tbl
297
+ end
298
+
299
+
300
+ alias p print
301
+
302
+ attr_accessor :header, :headeri # :nodoc:
303
+ attr_accessor :columns, :rows, :colprops # :nodoc:
304
+ attr_accessor :width, :indent, :cellpad # :nodoc:
305
+ attr_accessor :prefix, :postfix # :nodoc:
306
+ attr_accessor :sort_index, :sort_order, :scterm # :nodoc:
307
+
308
+ protected
309
+
310
+ #
311
+ # Returns if a row should be visible or not
312
+ #
313
+ def row_visible(row)
314
+ return true if self.scterm.nil?
315
+ row_to_s(row).match(self.scterm)
316
+ end
317
+
318
+ #
319
+ # Defaults cell widths and alignments.
320
+ #
321
+ def defaults # :nodoc:
322
+ self.columns.length.times { |idx|
323
+ }
324
+ end
325
+
326
+ #
327
+ # Checks to see if the row is an hr.
328
+ #
329
+ def is_hr(row) # :nodoc:
330
+ return ((row.kind_of?(String)) && (row == '__hr__'))
331
+ end
332
+
333
+ #
334
+ # Converts the columns to a string.
335
+ #
336
+ def columns_to_s # :nodoc:
337
+ nameline = ' ' * indent
338
+ barline = nameline.dup
339
+ last_col = nil
340
+ last_idx = nil
341
+ columns.each_with_index { |col,idx|
342
+ if (last_col)
343
+ # This produces clean to_s output without truncation
344
+ # Preserves full string in cells for to_csv output
345
+ padding = pad(' ', last_col, last_idx)
346
+ nameline << padding
347
+ remainder = padding.length - cellpad
348
+ remainder = 0 if remainder < 0
349
+ barline << (' ' * (cellpad + remainder))
350
+ end
351
+
352
+ nameline << col
353
+ barline << ('-' * col.length)
354
+
355
+ last_col = col
356
+ last_idx = idx
357
+ }
358
+
359
+ return "#{nameline}\n#{barline}"
360
+ end
361
+
362
+ #
363
+ # Converts an hr to a string.
364
+ #
365
+ def hr_to_s # :nodoc:
366
+ return "\n"
367
+ end
368
+
369
+ #
370
+ # Converts a row to a string.
371
+ #
372
+ def row_to_s(row) # :nodoc:
373
+ line = ' ' * indent
374
+ last_cell = nil
375
+ last_idx = nil
376
+ row.each_with_index { |cell, idx|
377
+ if (idx != 0)
378
+ line << pad(' ', last_cell.to_s, last_idx)
379
+ end
380
+ # Limit wide cells
381
+ if colprops[idx]['MaxChar']
382
+ last_cell = cell.to_s[0..colprops[idx]['MaxChar'].to_i]
383
+ line << last_cell
384
+ else
385
+ line << cell.to_s
386
+ last_cell = cell
387
+ end
388
+ last_idx = idx
389
+ }
390
+
391
+ return line + "\n"
392
+ end
393
+
394
+ #
395
+ # Pads out with the supplied character for the remainder of the space given
396
+ # some text and a column index.
397
+ #
398
+ def pad(chr, buf, colidx, use_cell_pad = true) # :nodoc:
399
+ # Ensure we pad the minimum required amount
400
+ max = colprops[colidx]['MaxChar'] || colprops[colidx]['MaxWidth']
401
+ max = colprops[colidx]['MaxWidth'] if max.to_i > colprops[colidx]['MaxWidth'].to_i
402
+ remainder = max - buf.length
403
+ remainder = 0 if remainder < 0
404
+ val = chr * remainder
405
+
406
+ if (use_cell_pad)
407
+ val << ' ' * cellpad
408
+ end
409
+
410
+ return val
411
+ end
412
+
413
+
414
+ end
415
+
416
+ end
417
+ end
418
+
@@ -1,5 +1,5 @@
1
1
  module Rex
2
2
  module Text
3
- VERSION = "0.1.1"
3
+ VERSION = "0.1.2"
4
4
  end
5
5
  end
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.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David 'thelightcosine' Maloney
@@ -85,7 +85,7 @@ cert_chain:
85
85
  2SpuQH+SWteq3NXkAmFEEqvLJQ4sbptZt8OP8ghL3pVAvZNFmww/YVszSkShSzcg
86
86
  QdihYCSEL2drS2cFd50jBeq71sxUtxbv82DUa2b+
87
87
  -----END CERTIFICATE-----
88
- date: 2016-06-21 00:00:00.000000000 Z
88
+ date: 2016-08-10 00:00:00.000000000 Z
89
89
  dependencies:
90
90
  - !ruby/object:Gem::Dependency
91
91
  name: bundler
@@ -155,6 +155,7 @@ files:
155
155
  - lib/rex/text/binary_manipulation.rb
156
156
  - lib/rex/text/block_api.rb
157
157
  - lib/rex/text/checksum.rb
158
+ - lib/rex/text/color.rb
158
159
  - lib/rex/text/compress.rb
159
160
  - lib/rex/text/ebcdic.rb
160
161
  - lib/rex/text/encode.rb
@@ -166,6 +167,7 @@ files:
166
167
  - lib/rex/text/rand.rb
167
168
  - lib/rex/text/randomize.rb
168
169
  - lib/rex/text/silly.rb
170
+ - lib/rex/text/table.rb
169
171
  - lib/rex/text/unicode.rb
170
172
  - lib/rex/text/version.rb
171
173
  - rex-text.gemspec
metadata.gz.sig CHANGED
Binary file