@@ -41,7 +41,7 @@ macro_rules! t {
41
41
}
42
42
43
43
fn main ( ) {
44
- let docs = env:: args ( ) . nth ( 1 ) . unwrap ( ) ;
44
+ let docs = env:: args_os ( ) . nth ( 1 ) . unwrap ( ) ;
45
45
let docs = env:: current_dir ( ) . unwrap ( ) . join ( docs) ;
46
46
let mut errors = false ;
47
47
walk ( & mut HashMap :: new ( ) , & docs, & docs, & mut errors) ;
@@ -65,15 +65,14 @@ enum Redirect {
65
65
struct FileEntry {
66
66
source : String ,
67
67
ids : HashSet < String > ,
68
- names : HashSet < String > ,
69
68
}
70
69
71
70
type Cache = HashMap < PathBuf , FileEntry > ;
72
71
73
72
impl FileEntry {
74
73
fn parse_ids ( & mut self , file : & Path , contents : & str , errors : & mut bool ) {
75
74
if self . ids . is_empty ( ) {
76
- with_attrs_in_source ( contents, " id" , |fragment, i| {
75
+ with_attrs_in_source ( contents, " id" , |fragment, i, _ | {
77
76
let frag = fragment. trim_left_matches ( "#" ) . to_owned ( ) ;
78
77
if !self . ids . insert ( frag) {
79
78
* errors = true ;
@@ -82,15 +81,6 @@ impl FileEntry {
82
81
} ) ;
83
82
}
84
83
}
85
-
86
- fn parse_names ( & mut self , contents : & str ) {
87
- if self . names . is_empty ( ) {
88
- with_attrs_in_source ( contents, " name" , |fragment, _| {
89
- let frag = fragment. trim_left_matches ( "#" ) . to_owned ( ) ;
90
- self . names . insert ( frag) ;
91
- } ) ;
92
- }
93
- }
94
84
}
95
85
96
86
fn walk ( cache : & mut Cache , root : & Path , dir : & Path , errors : & mut bool ) {
@@ -116,15 +106,8 @@ fn check(cache: &mut Cache,
116
106
file : & Path ,
117
107
errors : & mut bool )
118
108
-> Option < PathBuf > {
119
- // ignore js files as they are not prone to errors as the rest of the
120
- // documentation is and they otherwise bring up false positives.
121
- if file. extension ( ) . and_then ( |s| s. to_str ( ) ) == Some ( "js" ) {
122
- return None ;
123
- }
124
-
125
- // ignore handlebars files as they use {{}} to build links, we only
126
- // want to test the generated files
127
- if file. extension ( ) . and_then ( |s| s. to_str ( ) ) == Some ( "hbs" ) {
109
+ // Ignore none HTML files.
110
+ if file. extension ( ) . and_then ( |s| s. to_str ( ) ) != Some ( "html" ) {
128
111
return None ;
129
112
}
130
113
@@ -147,13 +130,7 @@ fn check(cache: &mut Cache,
147
130
return None ;
148
131
}
149
132
150
- // mdbook uses the HTML <base> tag to handle links for subdirectories, which
151
- // linkchecker doesn't support
152
- if file. to_str ( ) . unwrap ( ) . contains ( "unstable-book" ) {
153
- return None ;
154
- }
155
-
156
- let res = load_file ( cache, root, PathBuf :: from ( file) , SkipRedirect ) ;
133
+ let res = load_file ( cache, root, file, SkipRedirect ) ;
157
134
let ( pretty_file, contents) = match res {
158
135
Ok ( res) => res,
159
136
Err ( _) => return None ,
@@ -162,13 +139,10 @@ fn check(cache: &mut Cache,
162
139
cache. get_mut ( & pretty_file)
163
140
. unwrap ( )
164
141
. parse_ids ( & pretty_file, & contents, errors) ;
165
- cache. get_mut ( & pretty_file)
166
- . unwrap ( )
167
- . parse_names ( & contents) ;
168
142
}
169
143
170
144
// Search for anything that's the regex 'href[ ]*=[ ]*".*?"'
171
- with_attrs_in_source ( & contents, " href" , |url, i| {
145
+ with_attrs_in_source ( & contents, " href" , |url, i, base | {
172
146
// Ignore external URLs
173
147
if url. starts_with ( "http:" ) || url. starts_with ( "https:" ) ||
174
148
url. starts_with ( "javascript:" ) || url. starts_with ( "ftp:" ) ||
@@ -184,9 +158,9 @@ fn check(cache: &mut Cache,
184
158
// Once we've plucked out the URL, parse it using our base url and
185
159
// then try to extract a file path.
186
160
let mut path = file. to_path_buf ( ) ;
187
- if !url. is_empty ( ) {
161
+ if !base . is_empty ( ) || ! url. is_empty ( ) {
188
162
path. pop ( ) ;
189
- for part in Path :: new ( url) . components ( ) {
163
+ for part in Path :: new ( base ) . join ( url) . components ( ) {
190
164
match part {
191
165
Component :: Prefix ( _) |
192
166
Component :: RootDir => panic ! ( ) ,
@@ -197,13 +171,6 @@ fn check(cache: &mut Cache,
197
171
}
198
172
}
199
173
200
- if let Some ( extension) = path. extension ( ) {
201
- // don't check these files
202
- if extension == "png" {
203
- return ;
204
- }
205
- }
206
-
207
174
// Alright, if we've found a file name then this file had better
208
175
// exist! If it doesn't then we register and print an error.
209
176
if path. exists ( ) {
@@ -218,11 +185,17 @@ fn check(cache: &mut Cache,
218
185
pretty_path. display( ) ) ;
219
186
return ;
220
187
}
221
- let res = load_file ( cache, root, path. clone ( ) , FromRedirect ( false ) ) ;
188
+ if let Some ( extension) = path. extension ( ) {
189
+ // Ignore none HTML files.
190
+ if extension != "html" {
191
+ return ;
192
+ }
193
+ }
194
+ let res = load_file ( cache, root, & path, FromRedirect ( false ) ) ;
222
195
let ( pretty_path, contents) = match res {
223
196
Ok ( res) => res,
224
197
Err ( LoadError :: IOError ( err) ) => {
225
- panic ! ( format! ( "error loading {}: {}" , path. display( ) , err) ) ;
198
+ panic ! ( "error loading {}: {}" , path. display( ) , err) ;
226
199
}
227
200
Err ( LoadError :: BrokenRedirect ( target, _) ) => {
228
201
* errors = true ;
@@ -245,11 +218,10 @@ fn check(cache: &mut Cache,
245
218
246
219
let entry = & mut cache. get_mut ( & pretty_path) . unwrap ( ) ;
247
220
entry. parse_ids ( & pretty_path, & contents, errors) ;
248
- entry. parse_names ( & contents) ;
249
221
250
- if !( entry. ids . contains ( * fragment) || entry . names . contains ( * fragment ) ) {
222
+ if !entry. ids . contains ( * fragment) {
251
223
* errors = true ;
252
- print ! ( "{}:{}: broken link fragment " ,
224
+ print ! ( "{}:{}: broken link fragment " ,
253
225
pretty_file. display( ) ,
254
226
i + 1 ) ;
255
227
println ! ( "`#{}` pointing to `{}`" , fragment, pretty_path. display( ) ) ;
@@ -267,7 +239,7 @@ fn check(cache: &mut Cache,
267
239
268
240
fn load_file ( cache : & mut Cache ,
269
241
root : & Path ,
270
- mut file : PathBuf ,
242
+ file : & Path ,
271
243
redirect : Redirect )
272
244
-> Result < ( PathBuf , String ) , LoadError > {
273
245
let mut contents = String :: new ( ) ;
@@ -279,9 +251,9 @@ fn load_file(cache: &mut Cache,
279
251
None
280
252
}
281
253
Entry :: Vacant ( entry) => {
282
- let mut fp = File :: open ( file. clone ( ) ) . map_err ( |err| {
254
+ let mut fp = File :: open ( file) . map_err ( |err| {
283
255
if let FromRedirect ( true ) = redirect {
284
- LoadError :: BrokenRedirect ( file. clone ( ) , err)
256
+ LoadError :: BrokenRedirect ( file. to_path_buf ( ) , err)
285
257
} else {
286
258
LoadError :: IOError ( err)
287
259
}
@@ -297,17 +269,14 @@ fn load_file(cache: &mut Cache,
297
269
entry. insert ( FileEntry {
298
270
source : contents. clone ( ) ,
299
271
ids : HashSet :: new ( ) ,
300
- names : HashSet :: new ( ) ,
301
272
} ) ;
302
273
}
303
274
maybe
304
275
}
305
276
} ;
306
- file. pop ( ) ;
307
- match maybe_redirect. map ( |url| file. join ( url) ) {
277
+ match maybe_redirect. map ( |url| file. parent ( ) . unwrap ( ) . join ( url) ) {
308
278
Some ( redirect_file) => {
309
- let path = PathBuf :: from ( redirect_file) ;
310
- load_file ( cache, root, path, FromRedirect ( true ) )
279
+ load_file ( cache, root, & redirect_file, FromRedirect ( true ) )
311
280
}
312
281
None => Ok ( ( pretty_file, contents) ) ,
313
282
}
@@ -329,10 +298,14 @@ fn maybe_redirect(source: &str) -> Option<String> {
329
298
} )
330
299
}
331
300
332
- fn with_attrs_in_source < F : FnMut ( & str , usize ) > ( contents : & str , attr : & str , mut f : F ) {
301
+ fn with_attrs_in_source < F : FnMut ( & str , usize , & str ) > ( contents : & str , attr : & str , mut f : F ) {
302
+ let mut base = "" ;
333
303
for ( i, mut line) in contents. lines ( ) . enumerate ( ) {
334
304
while let Some ( j) = line. find ( attr) {
335
305
let rest = & line[ j + attr. len ( ) ..] ;
306
+ // The base tag should always be the first link in the document so
307
+ // we can get away with using one pass.
308
+ let is_base = line[ ..j] . ends_with ( "<base" ) ;
336
309
line = rest;
337
310
let pos_equals = match rest. find ( "=" ) {
338
311
Some ( i) => i,
@@ -358,7 +331,11 @@ fn with_attrs_in_source<F: FnMut(&str, usize)>(contents: &str, attr: &str, mut f
358
331
Some ( i) => & rest[ ..i] ,
359
332
None => continue ,
360
333
} ;
361
- f ( url, i)
334
+ if is_base {
335
+ base = url;
336
+ continue ;
337
+ }
338
+ f ( url, i, base)
362
339
}
363
340
}
364
341
}
0 commit comments