stb_include.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // stb_include.h - v0.02 - parse and process #include directives - public domain
  2. //
  3. // To build this, in one source file that includes this file do
  4. // #define STB_INCLUDE_IMPLEMENTATION
  5. //
  6. // This program parses a string and replaces lines of the form
  7. // #include "foo"
  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.
  12. //
  13. // If the string contains a line of the form
  14. // #inject
  15. // then it will be replaced with the contents of the string 'inject' passed to the API.
  16. //
  17. // Options:
  18. //
  19. // Define STB_INCLUDE_LINE_GLSL to get GLSL-style #line directives
  20. // which use numbers instead of filenames.
  21. //
  22. // Define STB_INCLUDE_LINE_NONE to disable output of #line directives.
  23. //
  24. // Standard libraries:
  25. //
  26. // stdio.h FILE, fopen, fclose, fseek, ftell
  27. // stdlib.h malloc, realloc, free
  28. // string.h strcpy, strncmp, memcpy
  29. //
  30. // Credits:
  31. //
  32. // Written by Sean Barrett.
  33. //
  34. // Fixes:
  35. // Michal Klos
  36. #ifndef STB_INCLUDE_STB_INCLUDE_H
  37. #define STB_INCLUDE_STB_INCLUDE_H
  38. // Do include-processing on the string 'str'. To free the return value, pass it to free()
  39. char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]);
  40. // Concatenate the strings 'strs' and do include-processing on the result. To free the return value, pass it to free()
  41. char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]);
  42. // Load the file 'filename' and do include-processing on the string therein. note that
  43. // 'filename' is opened directly; 'path_to_includes' is not used. To free the return value, pass it to free()
  44. char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256]);
  45. #endif
  46. #ifdef STB_INCLUDE_IMPLEMENTATION
  47. #include <stdio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. static char *stb_include_load_file(char *filename, size_t *plen)
  51. {
  52. char *text;
  53. size_t len;
  54. FILE *f = fopen(filename, "rb");
  55. if (f == 0) return 0;
  56. fseek(f, 0, SEEK_END);
  57. len = (size_t) ftell(f);
  58. if (plen) *plen = len;
  59. text = (char *) malloc(len+1);
  60. if (text == 0) return 0;
  61. fseek(f, 0, SEEK_SET);
  62. fread(text, 1, len, f);
  63. fclose(f);
  64. text[len] = 0;
  65. return text;
  66. }
  67. typedef struct
  68. {
  69. int offset;
  70. int end;
  71. char *filename;
  72. int next_line_after;
  73. } include_info;
  74. static include_info *stb_include_append_include(include_info *array, int len, int offset, int end, char *filename, int next_line)
  75. {
  76. include_info *z = (include_info *) realloc(array, sizeof(*z) * (len+1));
  77. z[len].offset = offset;
  78. z[len].end = end;
  79. z[len].filename = filename;
  80. z[len].next_line_after = next_line;
  81. return z;
  82. }
  83. static void stb_include_free_includes(include_info *array, int len)
  84. {
  85. int i;
  86. for (i=0; i < len; ++i)
  87. free(array[i].filename);
  88. free(array);
  89. }
  90. static int stb_include_isspace(int ch)
  91. {
  92. return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
  93. }
  94. // find location of all #include and #inject
  95. static int stb_include_find_includes(char *text, include_info **plist)
  96. {
  97. int line_count = 1;
  98. int inc_count = 0;
  99. char *s = text, *start;
  100. include_info *list = NULL;
  101. while (*s) {
  102. // parse is always at start of line when we reach here
  103. start = s;
  104. while (*s == ' ' || *s == '\t')
  105. ++s;
  106. if (*s == '#') {
  107. ++s;
  108. while (*s == ' ' || *s == '\t')
  109. ++s;
  110. if (0==strncmp(s, "include", 7) && stb_include_isspace(s[7])) {
  111. s += 7;
  112. while (*s == ' ' || *s == '\t')
  113. ++s;
  114. if (*s == '"') {
  115. char *t = ++s;
  116. while (*t != '"' && *t != '\n' && *t != '\r' && *t != 0)
  117. ++t;
  118. if (*t == '"') {
  119. char *filename = (char *) malloc(t-s+1);
  120. memcpy(filename, s, t-s);
  121. filename[t-s] = 0;
  122. s=t;
  123. while (*s != '\r' && *s != '\n' && *s != 0)
  124. ++s;
  125. // s points to the newline, so s-start is everything except the newline
  126. list = stb_include_append_include(list, inc_count++, start-text, s-text, filename, line_count+1);
  127. }
  128. }
  129. } else if (0==strncmp(s, "inject", 6) && (stb_include_isspace(s[6]) || s[6]==0)) {
  130. while (*s != '\r' && *s != '\n' && *s != 0)
  131. ++s;
  132. list = stb_include_append_include(list, inc_count++, start-text, s-text, NULL, line_count+1);
  133. }
  134. }
  135. while (*s != '\r' && *s != '\n' && *s != 0)
  136. ++s;
  137. if (*s == '\r' || *s == '\n') {
  138. s = s + (s[0] + s[1] == '\r' + '\n' ? 2 : 1);
  139. }
  140. ++line_count;
  141. }
  142. *plist = list;
  143. return inc_count;
  144. }
  145. // avoid dependency on sprintf()
  146. static void stb_include_itoa(char str[9], int n)
  147. {
  148. int i;
  149. for (i=0; i < 8; ++i)
  150. str[i] = ' ';
  151. str[i] = 0;
  152. for (i=1; i < 8; ++i) {
  153. str[7-i] = '0' + (n % 10);
  154. n /= 10;
  155. if (n == 0)
  156. break;
  157. }
  158. }
  159. static char *stb_include_append(char *str, size_t *curlen, char *addstr, size_t addlen)
  160. {
  161. str = (char *) realloc(str, *curlen + addlen);
  162. memcpy(str + *curlen, addstr, addlen);
  163. *curlen += addlen;
  164. return str;
  165. }
  166. char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename, char error[256])
  167. {
  168. char temp[4096];
  169. include_info *inc_list;
  170. int i, num = stb_include_find_includes(str, &inc_list);
  171. size_t source_len = strlen(str);
  172. char *text=0;
  173. size_t textlen=0, last=0;
  174. for (i=0; i < num; ++i) {
  175. text = stb_include_append(text, &textlen, str+last, inc_list[i].offset - last);
  176. // write out line directive for the include
  177. #ifndef STB_INCLUDE_LINE_NONE
  178. #ifdef STB_INCLUDE_LINE_GLSL
  179. if (textlen != 0) // GLSL #version must appear first, so don't put a #line at the top
  180. #endif
  181. {
  182. strcpy(temp, "#line ");
  183. stb_include_itoa(temp+6, 1);
  184. strcat(temp, " ");
  185. #ifdef STB_INCLUDE_LINE_GLSL
  186. stb_include_itoa(temp+15, i+1);
  187. #else
  188. strcat(temp, "\"");
  189. if (inc_list[i].filename == 0)
  190. strcmp(temp, "INJECT");
  191. else
  192. strcat(temp, inc_list[i].filename);
  193. strcat(temp, "\"");
  194. #endif
  195. strcat(temp, "\n");
  196. text = stb_include_append(text, &textlen, temp, strlen(temp));
  197. }
  198. #endif
  199. if (inc_list[i].filename == 0) {
  200. if (inject != 0)
  201. text = stb_include_append(text, &textlen, inject, strlen(inject));
  202. } else {
  203. char *inc;
  204. strcpy(temp, path_to_includes);
  205. strcat(temp, "/");
  206. strcat(temp, inc_list[i].filename);
  207. inc = stb_include_file(temp, inject, path_to_includes, error);
  208. if (inc == NULL) {
  209. stb_include_free_includes(inc_list, num);
  210. return NULL;
  211. }
  212. text = stb_include_append(text, &textlen, inc, strlen(inc));
  213. free(inc);
  214. }
  215. // write out line directive
  216. #ifndef STB_INCLUDE_LINE_NONE
  217. strcpy(temp, "\n#line ");
  218. stb_include_itoa(temp+6, inc_list[i].next_line_after);
  219. strcat(temp, " ");
  220. #ifdef STB_INCLUDE_LINE_GLSL
  221. stb_include_itoa(temp+15, 0);
  222. #else
  223. strcat(temp, filename != 0 ? filename : "source-file");
  224. #endif
  225. text = stb_include_append(text, &textlen, temp, strlen(temp));
  226. // no newlines, because we kept the #include newlines, which will get appended next
  227. #endif
  228. last = inc_list[i].end;
  229. }
  230. text = stb_include_append(text, &textlen, str+last, source_len - last + 1); // append '\0'
  231. stb_include_free_includes(inc_list, num);
  232. return text;
  233. }
  234. char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename, char error[256])
  235. {
  236. char *text;
  237. char *result;
  238. int i;
  239. size_t length=0;
  240. for (i=0; i < count; ++i)
  241. length += strlen(strs[i]);
  242. text = (char *) malloc(length+1);
  243. length = 0;
  244. for (i=0; i < count; ++i) {
  245. strcpy(text + length, strs[i]);
  246. length += strlen(strs[i]);
  247. }
  248. result = stb_include_string(text, inject, path_to_includes, filename, error);
  249. free(text);
  250. return result;
  251. }
  252. char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256])
  253. {
  254. size_t len;
  255. char *result;
  256. char *text = stb_include_load_file(filename, &len);
  257. if (text == NULL) {
  258. strcpy(error, "Error: couldn't load '");
  259. strcat(error, filename);
  260. strcat(error, "'");
  261. return 0;
  262. }
  263. result = stb_include_string(text, inject, path_to_includes, filename, error);
  264. free(text);
  265. return result;
  266. }
  267. #if 0 // @TODO, GL_ARB_shader_language_include-style system that doesn't touch filesystem
  268. char *stb_include_preloaded(char *str, char *inject, char *includes[][2], char error[256])
  269. {
  270. }
  271. #endif
  272. #endif // STB_INCLUDE_IMPLEMENTATION