1 # depends on: module.rb class.rb
9 RADIXES = {"b" => 2, "o" => 8, "d" => 10, "x" => 16}
10 ALTERNATIVES = {"o" => "0", "b" => "0b", "B" => "0B", "x" => "0x", "X" => "0X"}
11 PrecisionMax = 1048576 # Totally random value
13 def initialize(fmt, *args)
14 @tainted = fmt.tainted?
15 @fmt, @args, @arg_position = fmt.to_str, args, 0
27 while (match = /%/.match_from(fmt, start))
29 @flags = {:space => nil, :position => nil, :alternative => nil, :plus => nil,
30 :minus => nil, :zero => nil, :star => nil}
31 @width = @precision = @type = nil
33 ret << match.pre_match_from(start)
34 start = match.begin(0) + 1
36 # Special case: %% prints out as "%"
37 if [?\n, 0].include?(@fmt[start])
38 ret << "%" << @fmt[start]
41 elsif [?%, nil].include?(@fmt[start])
45 elsif @fmt[start, 3] =~ /[1-9]\$/ && !@fmt[start + 2]
52 while token = /\G( |[1-9]\$|#|\+|\-|0|\*)/.match_from(fmt, start)
54 # Special case: if we get two [1-9]\$, it means that we're outside of flag-land
56 raise ArgumentError, "value given twice - #{token[0]}" if flags[:position]
57 @flags[:position] = token[0][0].chr.to_i
62 @flags[:alternative] = true
70 raise ArgumentError, "width given twice" if flags[:star]
71 if width_dollar_match = /\G[1-9]\$/.match_from(fmt, start + 1)
72 @width = Slot.new('*' << width_dollar_match[0])
81 if !flags[:star] && width_match = /\G([1-9]\$|\*|\d+)/.match_from(fmt, start)
82 @width = Slot.new(width_match[0])
83 start += width_match[0].size
86 # PRECISION DETERMINATION STATE
87 if /\G\./.match_from(fmt, start)
90 if /\G\*/.match_from(fmt, start)
91 if precision_dollar_match = /\G[1-9]\$/.match_from(fmt, start + 1)
92 @precision = Slot.new('*' << precision_dollar_match[0])
95 @precision = Slot.new('*')
98 elsif precision_match = /\G([1-9]\$|\d+)/.match_from(fmt, start)
99 @precision = Slot.new(precision_match[0])
100 start += precision_match[0].size
102 @precision = Slot.new("0")
105 # check for positional value again, after the optional '.'
106 if positional_match = /\G[1-9]\$/.match_from(fmt, start)
107 raise ArgumentError, "value given twice - #{token[0]}" if flags[:position]
108 @flags[:position] = positional_match[0][0].chr.to_i
114 unless type = /\G[bcdEefGgiopsuXx]/i.match_from(fmt, start)
115 raise ArgumentError, "malformed format string - missing field type"
121 # Next: Use the parsed values to format some stuff :)
125 if $DEBUG == true && !@positional
126 raise ArgumentError, "you need to use all the arguments" unless @arg_position == args.size
128 ret << @fmt[start..-1] if start < @fmt.size
129 ret.taint if @tainted
136 val = Slot.new("#{flags[:position]}$")
141 @width = Slot.new("*") if flags[:star] && !@width
142 width = get_arg(@width)
143 width = width.to_int if width.respond_to?(:to_int)
144 if width && width < 0
150 precision = get_arg(@precision)
151 precision = precision.to_int if precision.respond_to?(:to_int)
153 unless flags[:position]
159 when "e", "E", "f", "g", "G"
160 if @type.downcase == "g" && flags[:alternative]
163 precision = 4 unless precision
167 ret = val.send(:to_s_formatted, build_format_string(width, precision))
168 ret = plus_char + ret if val >= 0 && @old_type
170 ret = (val < 0 ? "-Inf" : "Inf") if val.infinite?
171 ret = "NaN" if val.nan?
172 ret = plus_char + ret if val > 0
173 flags[:zero] = flags[:space] = flags[:plus] = nil
174 ret = pad(ret, width, precision)
177 val = get_number(val)
179 unless val.kind_of?(Fixnum)
180 raise ArgumentError, "invalid type (only Fixnum allowed)"
183 val = (2**(2.size * 8)) + val
184 if !flags[:zero] and !precision
185 ret = "..#{pad(val, width, precision)}"
187 ret = pad(val, width, precision)
190 ret = pad(val, width, precision)
193 val = get_number(val)
194 ret = pad(val, width, precision)
196 val = val.to_int if val.respond_to?(:to_int)
197 raise TypeError, "cannot convert #{val.class} into Integer" unless val.respond_to?(:chr) && val.respond_to?(:%)
198 val = (val % 256).chr
199 ret = pad(val, width, precision)
201 flags[:zero] = flags[:space] = flags[:plus] = nil
202 ret = pad(val, width, precision)
203 ret.taint if val.tainted?
205 flags[:zero] = flags[:space] = flags[:plus] = nil
206 ret = pad(val.inspect, width, precision)
207 when "o", "x", "X", "b", "B"
208 val = get_number(val)
209 unless flags[:space] || flags[:plus]
210 ret = Number.new(val, RADIXES[@type.downcase]).rep
211 chr = val < 0 ? (RADIXES[@type.downcase] - 1).to_s(RADIXES[@type.downcase]) : 0.to_s
212 ret = pad(ret, width, precision, chr)
213 ret = ALTERNATIVES[@type].to_s + ret if flags[:alternative]
215 flags[:plus] = nil if val < 0
216 ret = val.to_s(RADIXES[@type.downcase])
217 ret.gsub!(/^(\-?)/, "\1#{ALTERNATIVES[@type]}") if flags[:alternative]
218 ret = pad(ret, width, precision)
219 ret.gsub!(/ \-/, "-")
221 ret = ret.downcase if @type == "x"
222 ret = ret.upcase if @type == "X"
228 unless val.respond_to?(:full_to_i)
229 if val.respond_to?(:to_int)
231 elsif val.respond_to?(:to_i)
235 val = val.full_to_i if val.respond_to?(:full_to_i)
240 def build_format_string(width, precision)
241 ret = "%#{make_flags}#{width}"
242 ret << ".#{precision}" if precision
249 ret << " " if flags[:space]
250 ret << "#" if flags[:alternative]
251 ret << "+" if flags[:plus]
252 ret << "-" if flags[:minus]
253 ret << "0" if flags[:zero]
258 return nil unless slot
259 if slot.position == :next
260 raise ArgumentError, "unnumbered mixed with numbered" if @positional
262 raise ArgumentError, "you ran out of arguments" if @arg_position >= args.size
263 ret = args[@arg_position]
266 raise ArgumentError, "unnumbered mixed with numbered" if @relative
268 ret = args[slot.position - 1]
270 raise ArgumentError, "unnumbered mixed with numbered" if @positional
274 raise ArgumentError, "you specified an argument position that did not exist: #{slot.str}" unless defined?(ret)
278 def pad(val, width, precision, pad_override = nil)
279 direction = flags[:minus] ? :ljust : :rjust
281 modded_width = width.to_i + (flags[:plus] ? 1 : 0)
282 width = nil if modded_width <= val.to_s.size
283 if precision || flags[:zero]
287 if precision > PrecisionMax
288 raise ArgumentError, "precision too big"
290 ret = plus_char + ret.send(direction, precision, pad_override || "0")
291 flags[:zero] = flags[:plus] = flags[:space] = nil
294 ret = ret.send(direction, width, pad_char)
295 ret[0] = plus_char unless plus_char.empty?
297 ret = plus_char + ret
303 flags[:zero] ? "0" : " "
307 return "+" if flags[:plus]
308 return " " if flags[:space]
314 # pos means it got a N$ position
316 attr_reader :position
323 if str.size == 3 && /\*\d\$/.match(str)
325 @position = str[1..1].to_i
326 elsif str.size == 2 && str[1] == ?$
328 @position = str[0..0].to_i
339 def initialize(number, radix)
342 @pad = (radix - 1).to_s(radix)
346 return @number.to_s(@radix) if(@number >= 0) || @radix == 10
347 strlen = (@number.to_s(@radix)).size
348 max = (@pad * strlen).to_i(@radix)
349 ".." + (max + @number + 1).to_s(@radix)