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
59 static char *stb_include_load_file(char *filename
, size_t *plen
)
63 FILE *f
= fopen(filename
, "rb");
65 fseek(f
, 0, SEEK_END
);
66 len
= (size_t) ftell(f
);
67 if (plen
) *plen
= len
;
68 text
= (char *) malloc(len
+1);
69 if (text
== 0) return 0;
70 fseek(f
, 0, SEEK_SET
);
71 fread(text
, 1, len
, f
);
85 static include_info
*stb_include_append_include(include_info
*array
, int len
, int offset
, int end
, char *filename
, int next_line
)
87 include_info
*z
= (include_info
*) realloc(array
, sizeof(*z
) * (len
+1));
88 z
[len
].offset
= offset
;
90 z
[len
].filename
= filename
;
91 z
[len
].next_line_after
= next_line
;
95 static void stb_include_free_includes(include_info
*array
, int len
)
98 for (i
=0; i
< len
; ++i
)
99 free(array
[i
].filename
);
103 static int stb_include_isspace(int ch
)
105 return (ch
== ' ' || ch
== '\t' || ch
== '\r' || ch
== '\n');
108 // find location of all #include and #inject
109 static int stb_include_find_includes(char *text
, include_info
**plist
)
113 char *s
= text
, *start
;
114 include_info
*list
= NULL
;
116 // parse is always at start of line when we reach here
118 while (*s
== ' ' || *s
== '\t')
122 while (*s
== ' ' || *s
== '\t')
124 if (0==strncmp(s
, "include", 7) && stb_include_isspace(s
[7])) {
126 while (*s
== ' ' || *s
== '\t')
130 while (*t
!= '"' && *t
!= '\n' && *t
!= '\r' && *t
!= 0)
133 char *filename
= (char *) malloc(t
-s
+1);
134 memcpy(filename
, s
, t
-s
);
137 while (*s
!= '\r' && *s
!= '\n' && *s
!= 0)
139 // s points to the newline, so s-start is everything except the newline
140 list
= stb_include_append_include(list
, inc_count
++, start
-text
, s
-text
, filename
, line_count
+1);
143 } else if (0==strncmp(s
, "inject", 6) && (stb_include_isspace(s
[6]) || s
[6]==0)) {
144 while (*s
!= '\r' && *s
!= '\n' && *s
!= 0)
146 list
= stb_include_append_include(list
, inc_count
++, start
-text
, s
-text
, NULL
, line_count
+1);
149 while (*s
!= '\r' && *s
!= '\n' && *s
!= 0)
151 if (*s
== '\r' || *s
== '\n') {
152 s
= s
+ (s
[0] + s
[1] == '\r' + '\n' ? 2 : 1);
160 // avoid dependency on sprintf()
161 static void stb_include_itoa(char str
[9], int n
)
164 for (i
=0; i
< 8; ++i
)
168 for (i
=1; i
< 8; ++i
) {
169 str
[7-i
] = '0' + (n
% 10);
176 static char *stb_include_append(char *str
, size_t *curlen
, char *addstr
, size_t addlen
)
178 str
= (char *) realloc(str
, *curlen
+ addlen
);
179 memcpy(str
+ *curlen
, addstr
, addlen
);
184 char *stb_include_string(char *str
, char *inject
, char *path_to_includes
, char *filename
, char error
[256])
187 include_info
*inc_list
;
188 int i
, num
= stb_include_find_includes(str
, &inc_list
);
189 size_t source_len
= strlen(str
);
191 size_t textlen
=0, last
=0;
192 for (i
=0; i
< num
; ++i
) {
193 text
= stb_include_append(text
, &textlen
, str
+last
, inc_list
[i
].offset
- last
);
194 // write out line directive for the include
195 #ifndef STB_INCLUDE_LINE_NONE
196 #ifdef STB_INCLUDE_LINE_GLSL
197 if (textlen
!= 0) // GLSL #version must appear first, so don't put a #line at the top
200 strcpy(temp
, "#line ");
201 stb_include_itoa(temp
+6, 1);
203 #ifdef STB_INCLUDE_LINE_GLSL
204 stb_include_itoa(temp
+15, i
+1);
207 if (inc_list
[i
].filename
== 0)
208 strcmp(temp
, "INJECT");
210 strcat(temp
, inc_list
[i
].filename
);
214 text
= stb_include_append(text
, &textlen
, temp
, strlen(temp
));
217 if (inc_list
[i
].filename
== 0) {
219 text
= stb_include_append(text
, &textlen
, inject
, strlen(inject
));
222 strcpy(temp
, path_to_includes
);
224 strcat(temp
, inc_list
[i
].filename
);
225 inc
= stb_include_file(temp
, inject
, path_to_includes
, error
);
227 stb_include_free_includes(inc_list
, num
);
230 text
= stb_include_append(text
, &textlen
, inc
, strlen(inc
));
233 // write out line directive
234 #ifndef STB_INCLUDE_LINE_NONE
235 strcpy(temp
, "\n#line ");
236 stb_include_itoa(temp
+6, inc_list
[i
].next_line_after
);
238 #ifdef STB_INCLUDE_LINE_GLSL
239 stb_include_itoa(temp
+15, 0);
241 strcat(temp
, filename
!= 0 ? filename
: "source-file");
243 text
= stb_include_append(text
, &textlen
, temp
, strlen(temp
));
244 // no newlines, because we kept the #include newlines, which will get appended next
246 last
= inc_list
[i
].end
;
248 text
= stb_include_append(text
, &textlen
, str
+last
, source_len
- last
+ 1); // append '\0'
249 stb_include_free_includes(inc_list
, num
);
253 char *stb_include_strings(char **strs
, int count
, char *inject
, char *path_to_includes
, char *filename
, char error
[256])
259 for (i
=0; i
< count
; ++i
)
260 length
+= strlen(strs
[i
]);
261 text
= (char *) malloc(length
+1);
263 for (i
=0; i
< count
; ++i
) {
264 strcpy(text
+ length
, strs
[i
]);
265 length
+= strlen(strs
[i
]);
267 result
= stb_include_string(text
, inject
, path_to_includes
, filename
, error
);
272 char *stb_include_file(char *filename
, char *inject
, char *path_to_includes
, char error
[256])
276 char *text
= stb_include_load_file(filename
, &len
);
278 strcpy(error
, "Error: couldn't load '");
279 strcat(error
, filename
);
283 result
= stb_include_string(text
, inject
, path_to_includes
, filename
, error
);
288 #if 0 // @TODO, GL_ARB_shader_language_include-style system that doesn't touch filesystem
289 char *stb_include_preloaded(char *str
, char *inject
, char *includes
[][2], char error
[256])
295 #endif // STB_INCLUDE_IMPLEMENTATION