dbc 1.2.1 → 1.2.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.
- data/bin/dbcparse.rb +46 -37
- data/lib/dbc/ctokenizer.rb +5 -4
- data/lib/dbc/ctype.rb +3 -3
- data/lib/dbc/define.rb +2 -2
- data/lib/dbc/ocl.rb +2 -2
- data/lib/dbc/parameters.rb +2 -2
- data/lib/dbc/preprocessor.rb +53 -24
- data/lib/dbc/searchpath.rb +33 -15
- metadata +2 -2
data/bin/dbcparse.rb
CHANGED
|
@@ -42,7 +42,7 @@ opts = GetoptLong.new(
|
|
|
42
42
|
[ "--output", "-o", GetoptLong::REQUIRED_ARGUMENT ],
|
|
43
43
|
# cc compatibility options
|
|
44
44
|
[ "--define", "-D", GetoptLong::REQUIRED_ARGUMENT ],
|
|
45
|
-
[ "--include", "-I", GetoptLong::
|
|
45
|
+
[ "--include", "-I", GetoptLong::OPTIONAL_ARGUMENT ]
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
# initialize with defaults
|
|
@@ -50,6 +50,7 @@ dest_file = nil
|
|
|
50
50
|
src_file = nil
|
|
51
51
|
search_path = SearchPath.new
|
|
52
52
|
defines = []
|
|
53
|
+
search_usr_include = true
|
|
53
54
|
|
|
54
55
|
always_output = false
|
|
55
56
|
check_level = nil
|
|
@@ -70,10 +71,15 @@ begin
|
|
|
70
71
|
end
|
|
71
72
|
when "--include"
|
|
72
73
|
# should support including file in the future
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
if arg
|
|
75
|
+
begin
|
|
76
|
+
search_path.unshift(arg)
|
|
77
|
+
rescue
|
|
78
|
+
opts.error($!)
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
# -I with no argument means don't search /usr/include
|
|
82
|
+
search_usr_include = false
|
|
77
83
|
end
|
|
78
84
|
when "--define"
|
|
79
85
|
unless arg =~ /\A([A-Za-z_]\w*)(?:\(([\w,\s]*)\))?(?:=(.*))?\Z/
|
|
@@ -168,12 +174,32 @@ else
|
|
|
168
174
|
end
|
|
169
175
|
|
|
170
176
|
### Compatibility with various compilers ###
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
|
|
178
|
+
# the following are added to the end of the search path
|
|
179
|
+
if search_usr_include
|
|
180
|
+
if File.exists?('/usr/include')
|
|
181
|
+
search_path << '/usr/include'
|
|
182
|
+
# search for /usr/lib/gcc*/<system>/<version>/include
|
|
183
|
+
Dir['/usr/lib/gcc*/*/*/include'].each do |gcc_inc|
|
|
184
|
+
search_path << gcc_inc if File.directory?(gcc_inc)
|
|
185
|
+
end
|
|
186
|
+
else
|
|
187
|
+
if env_include = ENV['INCLUDE']
|
|
188
|
+
env_include.split(';').each do |p|
|
|
189
|
+
# any anci c installation should have stdlib.h or stdio.h
|
|
190
|
+
if File.exists?(File.join(p, 'stdlib.h'))
|
|
191
|
+
search_path << p
|
|
192
|
+
end
|
|
193
|
+
end # each
|
|
194
|
+
end
|
|
195
|
+
end # if File.exists?('/usr/include')
|
|
176
196
|
end
|
|
197
|
+
|
|
198
|
+
search_path_limited = search_path.dup
|
|
199
|
+
# Add directory of file to the begining of the search_path
|
|
200
|
+
# search_path_limited does not include the directory of the file being parsed
|
|
201
|
+
search_path.unshift( src_file ? File.dirname(src_file) : '.' )
|
|
202
|
+
|
|
177
203
|
case RUBY_PLATFORM
|
|
178
204
|
when /i[0-9]86|cygwin|mingw/
|
|
179
205
|
defines << ['__i386__', nil, nil]
|
|
@@ -181,8 +207,11 @@ case RUBY_PLATFORM
|
|
|
181
207
|
# experiance may vary :)
|
|
182
208
|
defines << ['__GLIBC_HAVE_LONG_LONG', nil, '1']
|
|
183
209
|
defines << ['__extension__', nil, ' ']
|
|
210
|
+
defines << ['__inline__', nil, ' ']
|
|
184
211
|
when /powerpc|darwin/
|
|
185
212
|
defines << ['__ppc__', nil, nil]
|
|
213
|
+
else
|
|
214
|
+
warn "unrecognized platform: #{RUBY_PLATFORM}"
|
|
186
215
|
end
|
|
187
216
|
# for gcc 3.4+ compatibility
|
|
188
217
|
defines << ['__builtin_va_list', nil, 'int *']
|
|
@@ -191,9 +220,6 @@ defines << ['SHLIB_COMPAT', ['arg'], '(0)']
|
|
|
191
220
|
defines << ['__STDC__', nil, '1']
|
|
192
221
|
#############################################
|
|
193
222
|
|
|
194
|
-
# included files
|
|
195
|
-
includes = {}
|
|
196
|
-
|
|
197
223
|
# Cache Tokens => Preprocessor => Parse OCL => Parse C Types
|
|
198
224
|
# Cached tokens are output.
|
|
199
225
|
if not docs and check_level == DBC::NONE
|
|
@@ -204,17 +230,10 @@ else
|
|
|
204
230
|
out_str = DBC.parse_docs(CTokenizer::Lexer.new(text, src_file))
|
|
205
231
|
elsif preprocess_only
|
|
206
232
|
out_str = ''
|
|
207
|
-
preproc = Preprocessor::Parser.new(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
begin
|
|
212
|
-
File.open(search_path.find(f)) { |in_f| includes[f] = in_f.read }
|
|
213
|
-
rescue ArgumentError
|
|
214
|
-
preproc.error($!)
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
end
|
|
233
|
+
preproc = Preprocessor::Parser.new( search_path,
|
|
234
|
+
search_path_limited,
|
|
235
|
+
text, src_file )
|
|
236
|
+
|
|
218
237
|
defines.each { |d,p,v| preproc.define(d, p, v) }
|
|
219
238
|
preproc.each do |t|
|
|
220
239
|
out_str << t[1]
|
|
@@ -224,20 +243,10 @@ else
|
|
|
224
243
|
# cache statements
|
|
225
244
|
cache = DBC::Cache.new(text, src_file)
|
|
226
245
|
# preprocesses all tokens
|
|
227
|
-
preproc = Preprocessor::Parser.new(cache)
|
|
228
|
-
|
|
229
|
-
if inc_text = includes[f]
|
|
230
|
-
inc_text
|
|
231
|
-
else
|
|
232
|
-
begin
|
|
233
|
-
File.open(search_path.find(f)) { |in_f| includes[f] = in_f.read }
|
|
234
|
-
rescue ArgumentError
|
|
235
|
-
preproc.error($!)
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
end
|
|
246
|
+
preproc = Preprocessor::Parser.new(search_path, search_path_limited, cache)
|
|
247
|
+
|
|
239
248
|
# define tokens passed
|
|
240
|
-
defines.each { |d
|
|
249
|
+
defines.each { |d| preproc.define(*d) }
|
|
241
250
|
|
|
242
251
|
# extracts DBC condtions
|
|
243
252
|
source = DBC::OCLParser.new(preproc)
|
data/lib/dbc/ctokenizer.rb
CHANGED
|
@@ -40,10 +40,10 @@ module CTokenizer
|
|
|
40
40
|
#FLOAT = %r(#{FLOAT_1}|#{FLOAT_2}|#{FLOAT_3})
|
|
41
41
|
FLOAT = %r(#{FLOAT_1}|#{FLOAT_2})
|
|
42
42
|
|
|
43
|
-
i_s = /[
|
|
44
|
-
INTEGER_1 = /0[xX][0-9a-fA-F]+#{i_s}
|
|
45
|
-
INTEGER_2 = /0[0-7]+#{i_s}
|
|
46
|
-
INTEGER_3 = /[0-9]+#{i_s}
|
|
43
|
+
i_s = /[uUlL]*/
|
|
44
|
+
INTEGER_1 = /0[xX][0-9a-fA-F]+#{i_s}/
|
|
45
|
+
INTEGER_2 = /0[0-7]+#{i_s}/
|
|
46
|
+
INTEGER_3 = /[0-9]+#{i_s}/
|
|
47
47
|
INTEGER = %r(#{INTEGER_1}|#{INTEGER_2}|#{INTEGER_3})
|
|
48
48
|
end # Expression
|
|
49
49
|
|
|
@@ -146,6 +146,7 @@ module CTokenizer
|
|
|
146
146
|
when m = scan(Expression::COMMENT)
|
|
147
147
|
@line += CTokenizer.line_count(m)
|
|
148
148
|
[:COMMENT, m]
|
|
149
|
+
# SYMBOL should come before INTEGER and FLOAT
|
|
149
150
|
when m = scan(Expression::SYMBOL)
|
|
150
151
|
[:SYMBOL, m]
|
|
151
152
|
when m = scan(Expression::NEWLINE)
|
data/lib/dbc/ctype.rb
CHANGED
|
@@ -69,7 +69,7 @@ module CType
|
|
|
69
69
|
|
|
70
70
|
def CType.[]=(val, new_val)
|
|
71
71
|
raise "expecting a String got #{val.class}" if val.class != String
|
|
72
|
-
raise "type #{val} already defined" if @@typedefs.include?(val)
|
|
72
|
+
raise ParseError, "type #{val} already defined" if @@typedefs.include?(val)
|
|
73
73
|
@@typedefs[val] = new_val
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -601,7 +601,7 @@ module CType
|
|
|
601
601
|
|
|
602
602
|
class Parser < Racc::Parser
|
|
603
603
|
|
|
604
|
-
module_eval <<'..end src/ctype.y modeval..
|
|
604
|
+
module_eval <<'..end src/ctype.y modeval..idcee92c23da', 'src/ctype.y', 1137
|
|
605
605
|
|
|
606
606
|
def parse(str, file=nil, line=1)
|
|
607
607
|
@tokens = CTokenizer::CLexer.new(str, file, line)
|
|
@@ -640,7 +640,7 @@ protected
|
|
|
640
640
|
t
|
|
641
641
|
end
|
|
642
642
|
|
|
643
|
-
..end src/ctype.y modeval..
|
|
643
|
+
..end src/ctype.y modeval..idcee92c23da
|
|
644
644
|
|
|
645
645
|
##### racc 1.4.4 generates ###
|
|
646
646
|
|
data/lib/dbc/define.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Preprocessor
|
|
|
16
16
|
|
|
17
17
|
class Define < Racc::Parser
|
|
18
18
|
|
|
19
|
-
module_eval <<'..end src/define.y modeval..
|
|
19
|
+
module_eval <<'..end src/define.y modeval..idbc0a52f69c', 'src/define.y', 75
|
|
20
20
|
|
|
21
21
|
def initialize(params, tokens)
|
|
22
22
|
if params and not params.class == Parameters
|
|
@@ -87,7 +87,7 @@ protected
|
|
|
87
87
|
@q.shift
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
..end src/define.y modeval..
|
|
90
|
+
..end src/define.y modeval..idbc0a52f69c
|
|
91
91
|
|
|
92
92
|
##### racc 1.4.4 generates ###
|
|
93
93
|
|
data/lib/dbc/ocl.rb
CHANGED
|
@@ -413,7 +413,7 @@ module OCL
|
|
|
413
413
|
|
|
414
414
|
class Parser < Racc::Parser
|
|
415
415
|
|
|
416
|
-
module_eval <<'..end src/ocl.y modeval..
|
|
416
|
+
module_eval <<'..end src/ocl.y modeval..ida1d027bef5', 'src/ocl.y', 849
|
|
417
417
|
|
|
418
418
|
def Parser.reserved_word?(str)
|
|
419
419
|
str =~ /\A(?:context|forall|exists|in|and|or|implies|not|xor)\Z/
|
|
@@ -511,7 +511,7 @@ protected
|
|
|
511
511
|
t
|
|
512
512
|
end
|
|
513
513
|
|
|
514
|
-
..end src/ocl.y modeval..
|
|
514
|
+
..end src/ocl.y modeval..ida1d027bef5
|
|
515
515
|
|
|
516
516
|
##### racc 1.4.4 generates ###
|
|
517
517
|
|
data/lib/dbc/parameters.rb
CHANGED
|
@@ -72,7 +72,7 @@ module Preprocessor
|
|
|
72
72
|
|
|
73
73
|
class ArgumentParser < Racc::Parser
|
|
74
74
|
|
|
75
|
-
module_eval <<'..end src/parameters.y modeval..
|
|
75
|
+
module_eval <<'..end src/parameters.y modeval..id008fb4e411', 'src/parameters.y', 144
|
|
76
76
|
|
|
77
77
|
def ArgumentParser.parse(tokens)
|
|
78
78
|
self.new.parse(tokens)
|
|
@@ -103,7 +103,7 @@ protected
|
|
|
103
103
|
end # case
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
..end src/parameters.y modeval..
|
|
106
|
+
..end src/parameters.y modeval..id008fb4e411
|
|
107
107
|
|
|
108
108
|
##### racc 1.4.4 generates ###
|
|
109
109
|
|
data/lib/dbc/preprocessor.rb
CHANGED
|
@@ -118,22 +118,34 @@ module Preprocessor
|
|
|
118
118
|
end
|
|
119
119
|
end # Resolve
|
|
120
120
|
|
|
121
|
+
module FileMacro
|
|
122
|
+
def FileMacro.takes_args?; false end
|
|
123
|
+
def FileMacro.value(source); source.file.dup end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
module LineMacro
|
|
127
|
+
def LineMacro.takes_args?; false end
|
|
128
|
+
def LineMacro.value(source); source.line.to_s end
|
|
129
|
+
end
|
|
130
|
+
|
|
121
131
|
class Parser < CTokenizer::LexerBase
|
|
122
132
|
|
|
123
133
|
include CTokenizer
|
|
124
134
|
include Resolve
|
|
125
135
|
|
|
126
|
-
def initialize(source, file=nil, line=1
|
|
127
|
-
@upon_include = upon_include
|
|
136
|
+
def initialize(search_path, search_path_limited, source, file=nil, line=1)
|
|
128
137
|
if source.class <= String
|
|
129
138
|
source = CTokenizer::Lexer.new(source, file, line)
|
|
130
139
|
end
|
|
140
|
+
|
|
141
|
+
@search_path = search_path
|
|
142
|
+
@search_path_limited = search_path_limited
|
|
131
143
|
|
|
132
144
|
@cond_comp = [true] # conditional compile is true at base
|
|
133
145
|
|
|
134
146
|
@source = Tokens.new(source)
|
|
135
147
|
@macro_tokens = CTokenizer::Lexer.new('', file, line)
|
|
136
|
-
@defines = {}
|
|
148
|
+
@defines = { '__FILE__' => FileMacro, '__LINE__' => LineMacro }
|
|
137
149
|
@resolving = {}
|
|
138
150
|
|
|
139
151
|
@parser = StatementParser.new(@defines)
|
|
@@ -190,7 +202,24 @@ module Preprocessor
|
|
|
190
202
|
end
|
|
191
203
|
|
|
192
204
|
protected
|
|
193
|
-
EMPTY_TOKEN = [:SPACE, ''].freeze
|
|
205
|
+
EMPTY_TOKEN = [:SPACE, ''.freeze].freeze
|
|
206
|
+
|
|
207
|
+
def include_file(file)
|
|
208
|
+
path = begin
|
|
209
|
+
if file =~ /\A"(.*)"\Z/
|
|
210
|
+
yield($1, @search_path)
|
|
211
|
+
elsif file =~ /\A<(.*)>\Z/
|
|
212
|
+
yield($1, @search_path_limited)
|
|
213
|
+
else
|
|
214
|
+
raise "impossible"
|
|
215
|
+
end
|
|
216
|
+
rescue ArgumentError
|
|
217
|
+
self.error($!)
|
|
218
|
+
end
|
|
219
|
+
str = File.open(path) { |f| f.read }
|
|
220
|
+
# could use *path* instead of *file* to give more info
|
|
221
|
+
@source.add_source( CTokenizer::Lexer.new(str, file) )
|
|
222
|
+
end
|
|
194
223
|
|
|
195
224
|
# @macro_tokens is empty when we parse a statement
|
|
196
225
|
def parse_statement
|
|
@@ -218,12 +247,13 @@ protected
|
|
|
218
247
|
self.undef(result[1])
|
|
219
248
|
when :INCLUDE
|
|
220
249
|
# note: we don't keep track of recursive includes
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
250
|
+
include_file(result[1]) do |f, search_path|
|
|
251
|
+
search_path.find(f)
|
|
252
|
+
end
|
|
253
|
+
when :INCLUDE_NEXT
|
|
254
|
+
include_file(result[1]) do |f, search_path|
|
|
255
|
+
search_path.find_next(f)
|
|
225
256
|
end
|
|
226
|
-
@source.add_source(str) if str
|
|
227
257
|
when :PRAGMA
|
|
228
258
|
# ignore
|
|
229
259
|
when :ERROR
|
|
@@ -259,10 +289,6 @@ protected
|
|
|
259
289
|
end # if
|
|
260
290
|
when :CLOSE
|
|
261
291
|
@cond_comp.pop
|
|
262
|
-
# for debugging
|
|
263
|
-
#when :IGNORE
|
|
264
|
-
#else
|
|
265
|
-
# raise "impossible"
|
|
266
292
|
end # case
|
|
267
293
|
|
|
268
294
|
# eat rest of line unless token is a newline
|
|
@@ -283,7 +309,7 @@ module Preprocessor
|
|
|
283
309
|
|
|
284
310
|
class StatementParser < Racc::Parser
|
|
285
311
|
|
|
286
|
-
module_eval <<'..end src/preprocessor.y modeval..
|
|
312
|
+
module_eval <<'..end src/preprocessor.y modeval..idb767efdc5e', 'src/preprocessor.y', 734
|
|
287
313
|
|
|
288
314
|
include Resolve
|
|
289
315
|
|
|
@@ -301,6 +327,14 @@ module_eval <<'..end src/preprocessor.y modeval..id895fa9d1fa', 'src/preprocesso
|
|
|
301
327
|
@defines.member?(t)
|
|
302
328
|
end
|
|
303
329
|
|
|
330
|
+
def file
|
|
331
|
+
@source.file
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def line
|
|
335
|
+
@source.line
|
|
336
|
+
end
|
|
337
|
+
|
|
304
338
|
def parse_ignore(tokens, ignore_all)
|
|
305
339
|
@source = CTokenizer::CPLexer.new(tokens)
|
|
306
340
|
|
|
@@ -349,7 +383,7 @@ protected
|
|
|
349
383
|
@macro_tokens
|
|
350
384
|
end .scan(/.*?>/) # NOT multiline
|
|
351
385
|
raise ParseError, "expecting '>'" unless f
|
|
352
|
-
f
|
|
386
|
+
"<#{f}"
|
|
353
387
|
end
|
|
354
388
|
|
|
355
389
|
# next_ignore and next_ignore_all are used to output the first token
|
|
@@ -414,7 +448,7 @@ protected
|
|
|
414
448
|
t
|
|
415
449
|
end
|
|
416
450
|
|
|
417
|
-
..end src/preprocessor.y modeval..
|
|
451
|
+
..end src/preprocessor.y modeval..idb767efdc5e
|
|
418
452
|
|
|
419
453
|
##### racc 1.4.4 generates ###
|
|
420
454
|
|
|
@@ -482,7 +516,7 @@ racc_reduce_table = [
|
|
|
482
516
|
2, 67, :_reduce_60,
|
|
483
517
|
1, 87, :_reduce_61,
|
|
484
518
|
1, 87, :_reduce_62,
|
|
485
|
-
1, 88, :
|
|
519
|
+
1, 88, :_reduce_none,
|
|
486
520
|
1, 88, :_reduce_64,
|
|
487
521
|
2, 63, :_reduce_65,
|
|
488
522
|
3, 63, :_reduce_66,
|
|
@@ -1569,7 +1603,7 @@ module_eval <<'.,.,', 'src/preprocessor.y', 150
|
|
|
1569
1603
|
|
|
1570
1604
|
module_eval <<'.,.,', 'src/preprocessor.y', 155
|
|
1571
1605
|
def _reduce_60( val, _values, result )
|
|
1572
|
-
result = val[0,2]
|
|
1606
|
+
result = val[0,2]
|
|
1573
1607
|
result
|
|
1574
1608
|
end
|
|
1575
1609
|
.,.,
|
|
@@ -1588,12 +1622,7 @@ module_eval <<'.,.,', 'src/preprocessor.y', 166
|
|
|
1588
1622
|
end
|
|
1589
1623
|
.,.,
|
|
1590
1624
|
|
|
1591
|
-
|
|
1592
|
-
def _reduce_63( val, _values, result )
|
|
1593
|
-
val[0] =~ /\A"(.*)"\Z/; result = $1
|
|
1594
|
-
result
|
|
1595
|
-
end
|
|
1596
|
-
.,.,
|
|
1625
|
+
# reduce 63 omitted
|
|
1597
1626
|
|
|
1598
1627
|
module_eval <<'.,.,', 'src/preprocessor.y', 171
|
|
1599
1628
|
def _reduce_64( val, _values, result )
|
data/lib/dbc/searchpath.rb
CHANGED
|
@@ -5,36 +5,54 @@
|
|
|
5
5
|
|
|
6
6
|
class SearchPath
|
|
7
7
|
def SearchPath.check(p)
|
|
8
|
-
|
|
9
|
-
raise "invalid search path: #{p}"
|
|
10
|
-
end
|
|
8
|
+
raise "invalid search path: #{p}" unless File.directory?(p)
|
|
11
9
|
end
|
|
12
10
|
def initialize
|
|
13
11
|
@paths = []
|
|
14
|
-
|
|
15
|
-
def add(path, check=true)
|
|
16
|
-
SearchPath.check(path)
|
|
17
|
-
@paths << path
|
|
12
|
+
@next_path = {}
|
|
18
13
|
end
|
|
19
14
|
def unshift(path)
|
|
20
15
|
SearchPath.check(path)
|
|
21
16
|
@paths.unshift(path)
|
|
22
17
|
end
|
|
23
18
|
def <<(path)
|
|
24
|
-
|
|
19
|
+
SearchPath.check(path)
|
|
20
|
+
@paths << path
|
|
25
21
|
end
|
|
26
22
|
|
|
27
|
-
def
|
|
23
|
+
def find(file)
|
|
24
|
+
raise ArgumentError, "'#{file}' not found" \
|
|
25
|
+
unless f = self.find_in(@paths, file)
|
|
26
|
+
f
|
|
27
|
+
end
|
|
28
|
+
def find_next(file)
|
|
29
|
+
if next_paths = @next_path[file]
|
|
30
|
+
f = self.find_in(next_paths, file)
|
|
31
|
+
return f if f
|
|
32
|
+
end
|
|
33
|
+
# start the search over
|
|
34
|
+
self.find(file)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def dup
|
|
38
|
+
d = SearchPath.new
|
|
28
39
|
@paths.each do |p|
|
|
29
|
-
|
|
40
|
+
d << p
|
|
30
41
|
end
|
|
42
|
+
d
|
|
31
43
|
end
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
|
|
45
|
+
protected
|
|
46
|
+
def find_in(paths, file)
|
|
47
|
+
paths.each_with_index do |dir, i|
|
|
48
|
+
path = File.join(dir, file)
|
|
49
|
+
if File.exists?(path)
|
|
50
|
+
i += 1
|
|
51
|
+
@next_path[file] = @paths[i, @paths.length - i]
|
|
52
|
+
return path
|
|
53
|
+
end
|
|
35
54
|
end
|
|
36
|
-
|
|
37
|
-
File.join(dir, file)
|
|
55
|
+
return nil
|
|
38
56
|
end
|
|
39
57
|
end
|
|
40
58
|
|