1 // stb_include.h - v0.02 - parse and process #include directives - public domain
3 // To build this, in one source file that includes this file do
4 // #define STB_INCLUDE_IMPLEMENTATION
6 // This program parses a string and replaces lines of the form
8 // with the contents of a file named "foo". It also embeds the
9 // appropriate #line directives. Note that all include files must
10 // reside in the location specified in the path passed to the API;
11 // it does not check multiple directories.
13 // If the string contains a line of the form
15 // then it will be replaced with the contents of the string 'inject' passed to the API.
19 // Define STB_INCLUDE_LINE_GLSL to get GLSL-style #line directives
20 // which use numbers instead of filenames.
22 // Define STB_INCLUDE_LINE_NONE to disable output of #line directives.
24 // Standard libraries:
26 // stdio.h FILE, fopen, fclose, fseek, ftell
27 // stdlib.h malloc, realloc, free
28 // string.h strcpy, strncmp, memcpy
32 // Written by Sean Barrett.
37 #ifndef STB_INCLUDE_STB_INCLUDE_H
38 #define STB_INCLUDE_STB_INCLUDE_H
40 // Do include-processing on the string 'str'. To free the return value, pass it to free()
41 char *stb_include_string(char *str
, char *inject
, char *path_to_includes
, char *filename_for_line_directive
, char error
[256]);
43 // Concatenate the strings 'strs' and do include-processing on the result. To free the return value, pass it to free()
44 char *stb_include_strings(char **strs
, int count
, char *inject
, char *path_to_includes
, char *filename_for_line_directive
, char error
[256]);
46 // Load the file 'filename' and do include-processing on the string therein. note that
47 // 'filename' is opened directly; 'path_to_includes' is not used. To free the return value, pass it to free()
48 char *stb_include_file(char *filename
, char *inject
, char *path_to_includes
, char error
[256]);
53 #ifdef STB_INCLUDE_IMPLEMENTATION
60 #define STB_MALLOC malloc
70 #define STB_REALLOC realloc
77 static char *stb_include_load_file(char *filename
, size_t *plen
)
81 FILE *f
= fopen(filename
, "rb");
83 fseek(f
, 0, SEEK_END
);
84 len
= (size_t) ftell(f
);
85 if (plen
) *plen
= len
;
86 text
= (char *) STB_MALLOC(len
+1);
87 if (text
== 0) return 0;
88 fseek(f
, 0, SEEK_SET
);
89 fread(text
, 1, len
, f
);
103 static include_info
*stb_include_append_include(include_info
*array
, int len
, int offset
, int end
, char *filename
, int next_line
)
105 include_info
*z
= (include_info
*)STB_REALLOC(array
, sizeof(*z
) * (len
+1));
106 z
[len
].offset
= offset
;
108 z
[len
].filename
= filename
;
109 z
[len
].next_line_after
= next_line
;
113 static void stb_include_free_includes(include_info
*array
, int len
)
116 for (i
=0; i
< len
; ++i
)
117 STB_FREE(array
[i
].filename
);
121 static int stb_include_isspace(int ch
)
123 return (ch
== ' ' || ch
== '\t' || ch
== '\r' || ch
== '\n');
126 // find location of all #include and #inject
127 static int stb_include_find_includes(char *text
, include_info
**plist
)
131 char *s
= text
, *start
;
132 include_info
*list
= NULL
;
134 // parse is always at start of line when we reach here
136 while (*s
== ' ' || *s
== '\t')
140 while (*s
== ' ' || *s
== '\t')
142 if (0==strncmp(s
, "include", 7) && stb_include_isspace(s
[7])) {
144 while (*s
== ' ' || *s
== '\t')
148 while (*t
!= '"' && *t
!= '\n' && *t
!= '\r' && *t
!= 0)
151 char *filename
= (char *) STB_MALLOC(t
-s
+1);
152 memcpy(filename
, s
, t
-s
);
155 while (*s
!= '\r' && *s
!= '\n' && *s
!= 0)
157 // s points to the newline, so s-start is everything except the newline
158 list
= stb_include_append_include(list
, inc_count
++, start
-text
, s
-text
, filename
, line_count
+1);
161 } else if (0==strncmp(s
, "inject", 6) && (stb_include_isspace(s
[6]) || s
[6]==0)) {
162 while (*s
!= '\r' && *s
!= '\n' && *s
!= 0)
164 list
= stb_include_append_include(list
, inc_count
++, start
-text
, s
-text
, NULL
, line_count
+1);
167 while (*s
!= '\r' && *s
!= '\n' && *s
!= 0)
169 if (*s
== '\r' || *s
== '\n') {
170 s
= s
+ (s
[0] + s
[1] == '\r' + '\n' ? 2 : 1);
178 // avoid dependency on sprintf()
179 static void stb_include_itoa(char str
[9], int n
)
182 for (i
=0; i
< 8; ++i
)
186 for (i
=1; i
< 8; ++i
) {
187 str
[7-i
] = '0' + (n
% 10);
194 static char *stb_include_append(char *str
, size_t *curlen
, char *addstr
, size_t addlen
)
196 str
= (char *)STB_REALLOC(str
, *curlen
+ addlen
);
197 memcpy(str
+ *curlen
, addstr
, addlen
);
202 char *stb_include_string(char *str
, char *inject
, char *path_to_includes
, char *filename
, char error
[256])
205 include_info
*inc_list
;
206 int i
, num
= stb_include_find_includes(str
, &inc_list
);
207 size_t source_len
= strlen(str
);
209 size_t textlen
=0, last
=0;
210 for (i
=0; i
< num
; ++i
) {
211 text
= stb_include_append(text
, &textlen
, str
+last
, inc_list
[i
].offset
- last
);
212 // write out line directive for the include
213 #ifndef STB_INCLUDE_LINE_NONE
214 #ifdef STB_INCLUDE_LINE_GLSL
215 if (textlen
!= 0) // GLSL #version must appear first, so don't put a #line at the top
218 strcpy(temp
, "#line ");
219 stb_include_itoa(temp
+6, 1);
221 #ifdef STB_INCLUDE_LINE_GLSL
222 stb_include_itoa(temp
+15, i
+1);
225 if (inc_list
[i
].filename
== 0)
226 strcmp(temp
, "INJECT");
228 strcat(temp
, inc_list
[i
].filename
);
232 text
= stb_include_append(text
, &textlen
, temp
, strlen(temp
));
235 if (inc_list
[i
].filename
== 0) {
237 text
= stb_include_append(text
, &textlen
, inject
, strlen(inject
));
240 strcpy(temp
, path_to_includes
);
242 strcat(temp
, inc_list
[i
].filename
);
243 inc
= stb_include_file(temp
, inject
, path_to_includes
, error
);
245 stb_include_free_includes(inc_list
, num
);
248 text
= stb_include_append(text
, &textlen
, inc
, strlen(inc
));
251 // write out line directive
252 #ifndef STB_INCLUDE_LINE_NONE
253 strcpy(temp
, "\n#line ");
254 stb_include_itoa(temp
+6, inc_list
[i
].next_line_after
);
256 #ifdef STB_INCLUDE_LINE_GLSL
257 stb_include_itoa(temp
+15, 0);
259 strcat(temp
, filename
!= 0 ? filename
: "source-file");
261 text
= stb_include_append(text
, &textlen
, temp
, strlen(temp
));
262 // no newlines, because we kept the #include newlines, which will get appended next
264 last
= inc_list
[i
].end
;
266 text
= stb_include_append(text
, &textlen
, str
+last
, source_len
- last
+ 1); // append '\0'
267 stb_include_free_includes(inc_list
, num
);
271 char *stb_include_strings(char **strs
, int count
, char *inject
, char *path_to_includes
, char *filename
, char error
[256])
277 for (i
=0; i
< count
; ++i
)
278 length
+= strlen(strs
[i
]);
279 text
= (char *) STB_MALLOC(length
+1);
281 for (i
=0; i
< count
; ++i
) {
282 strcpy(text
+ length
, strs
[i
]);
283 length
+= strlen(strs
[i
]);
285 result
= stb_include_string(text
, inject
, path_to_includes
, filename
, error
);
290 char *stb_include_file(char *filename
, char *inject
, char *path_to_includes
, char error
[256])
294 char *text
= stb_include_load_file(filename
, &len
);
296 strcpy(error
, "Error: couldn't load '");
297 strcat(error
, filename
);
301 result
= stb_include_string(text
, inject
, path_to_includes
, filename
, error
);
306 #if 0 // @TODO, GL_ARB_shader_language_include-style system that doesn't touch filesystem
307 char *stb_include_preloaded(char *str
, char *inject
, char *includes
[][2], char error
[256])
313 #endif // STB_INCLUDE_IMPLEMENTATION