@@ -9,6 +9,7 @@ use super::{Preprocessor, PreprocessorContext};
9
9
use book:: { Book , BookItem } ;
10
10
11
11
const ESCAPE_CHAR : char = '\\' ;
12
+ const MAX_LINK_NESTED_DEPTH : usize = 10 ;
12
13
13
14
/// A preprocessor for expanding the `{{# playpen}}` and `{{# include}}`
14
15
/// helpers in a chapter.
@@ -36,7 +37,7 @@ impl Preprocessor for LinkPreprocessor {
36
37
. map ( |dir| src_dir. join ( dir) )
37
38
. expect ( "All book items have a parent" ) ;
38
39
39
- let content = replace_all ( & ch. content , base) ;
40
+ let content = replace_all ( & ch. content , base, & ch . path , 0 ) ;
40
41
ch. content = content;
41
42
}
42
43
} ) ;
@@ -45,11 +46,12 @@ impl Preprocessor for LinkPreprocessor {
45
46
}
46
47
}
47
48
48
- fn replace_all < P : AsRef < Path > > ( s : & str , path : P ) -> String {
49
+ fn replace_all < P : AsRef < Path > > ( s : & str , path : P , source : & P , depth : usize ) -> String {
49
50
// When replacing one thing in a string by something with a different length,
50
51
// the indices after that will not correspond,
51
52
// we therefore have to store the difference to correct this
52
53
let path = path. as_ref ( ) ;
54
+ let source = source. as_ref ( ) ;
53
55
let mut previous_end_index = 0 ;
54
56
let mut replaced = String :: new ( ) ;
55
57
@@ -58,7 +60,15 @@ fn replace_all<P: AsRef<Path>>(s: &str, path: P) -> String {
58
60
59
61
match playpen. render_with_path ( & path) {
60
62
Ok ( new_content) => {
61
- replaced. push_str ( & new_content) ;
63
+ if depth < MAX_LINK_NESTED_DEPTH {
64
+ if let Some ( rel_path) = playpen. link . relative_path ( path) {
65
+ replaced. push_str ( & replace_all ( & new_content, rel_path, & source. to_path_buf ( ) , depth + 1 ) ) ;
66
+ }
67
+ }
68
+ else {
69
+ error ! ( "Stack depth exceeded in {}. Check for cyclic includes" ,
70
+ source. display( ) ) ;
71
+ }
62
72
previous_end_index = playpen. end_index ;
63
73
}
64
74
Err ( e) => {
@@ -84,6 +94,27 @@ enum LinkType<'a> {
84
94
Playpen ( PathBuf , Vec < & ' a str > ) ,
85
95
}
86
96
97
+ impl < ' a > LinkType < ' a > {
98
+ fn relative_path < P : AsRef < Path > > ( self , base : P ) -> Option < PathBuf > {
99
+ let base = base. as_ref ( ) ;
100
+ match self {
101
+ LinkType :: Escaped => None ,
102
+ LinkType :: IncludeRange ( p, _) => Some ( return_relative_path ( base, & p) ) ,
103
+ LinkType :: IncludeRangeFrom ( p, _) => Some ( return_relative_path ( base, & p) ) ,
104
+ LinkType :: IncludeRangeTo ( p, _) => Some ( return_relative_path ( base, & p) ) ,
105
+ LinkType :: IncludeRangeFull ( p, _) => Some ( return_relative_path ( base, & p) ) ,
106
+ LinkType :: Playpen ( p, _) => Some ( return_relative_path ( base, & p) )
107
+ }
108
+ }
109
+ }
110
+ fn return_relative_path < P : AsRef < Path > > ( base : P , relative : P ) -> PathBuf {
111
+ base. as_ref ( )
112
+ . join ( relative)
113
+ . parent ( )
114
+ . expect ( "Included file should not be /" )
115
+ . to_path_buf ( )
116
+ }
117
+
87
118
fn parse_include_path ( path : & str ) -> LinkType < ' static > {
88
119
let mut parts = path. split ( ':' ) ;
89
120
let path = parts. next ( ) . unwrap ( ) . into ( ) ;
0 commit comments