commit
						eddae8cc98
					
				
				 2 changed files with 302 additions and 5 deletions
			
			
		| @ -0,0 +1,288 @@ | |||||||
|  | // stb_include.h - v0.01 - parse and process #include directives - public domain
 | ||||||
|  | //
 | ||||||
|  | // To build this, in one source file that includes this file do
 | ||||||
|  | //      #define STB_INCLUDE_IMPLEMENTATION
 | ||||||
|  | //
 | ||||||
|  | // This program parses a string and replaces lines of the form
 | ||||||
|  | //         #include "foo"
 | ||||||
|  | // with the contents of a file named "foo". It also embeds the
 | ||||||
|  | // appropriate #line directives. Note that all include files must
 | ||||||
|  | // reside in the location specified in the path passed to the API;
 | ||||||
|  | // it does not check multiple directories.
 | ||||||
|  | //
 | ||||||
|  | // If the string contains a line of the form
 | ||||||
|  | //         #inject
 | ||||||
|  | // then it will be replaced with the contents of the string 'inject' passed to the API.
 | ||||||
|  | //
 | ||||||
|  | // Options:
 | ||||||
|  | //
 | ||||||
|  | //      Define STB_INCLUDE_LINE_GLSL to get GLSL-style #line directives
 | ||||||
|  | //      which use numbers instead of filenames.
 | ||||||
|  | //
 | ||||||
|  | //      Define STB_INCLUDE_LINE_NONE to disable output of #line directives.
 | ||||||
|  | //
 | ||||||
|  | // Standard libraries:
 | ||||||
|  | //
 | ||||||
|  | //      stdio.h     FILE, fopen, fclose, fseek, ftell
 | ||||||
|  | //      stdlib.h    malloc, realloc, free
 | ||||||
|  | //      string.h    strcpy, strncmp, memcpy
 | ||||||
|  | 
 | ||||||
|  | #ifndef STB_INCLUDE_STB_INCLUDE_H | ||||||
|  | #define STB_INCLUDE_STB_INCLUDE_H | ||||||
|  | 
 | ||||||
|  | // Do include-processing on the string 'str'. To free the return value, pass it to free()
 | ||||||
|  | char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]); | ||||||
|  | 
 | ||||||
|  | // Concatenate the strings 'strs' and do include-processing on the result. To free the return value, pass it to free()
 | ||||||
|  | char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]); | ||||||
|  | 
 | ||||||
|  | // Load the file 'filename' and do include-processing on the string therein. note that
 | ||||||
|  | // 'filename' is opened directly; 'path_to_includes' is not used. To free the return value, pass it to free()
 | ||||||
|  | char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256]); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef STB_INCLUDE_IMPLEMENTATION | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | static char *stb_include_load_file(char *filename, size_t *plen) | ||||||
|  | { | ||||||
|  |    char *text; | ||||||
|  |    size_t len; | ||||||
|  |    FILE *f = fopen(filename, "rb"); | ||||||
|  |    if (f == 0) return 0; | ||||||
|  |    fseek(f, 0, SEEK_END); | ||||||
|  |    len = (size_t) ftell(f); | ||||||
|  |    if (plen) *plen = len; | ||||||
|  |    text = (char *) malloc(len+1); | ||||||
|  |    if (text == 0) return 0; | ||||||
|  |    fseek(f, 0, SEEK_SET); | ||||||
|  |    fread(text, 1, len, f); | ||||||
|  |    fclose(f); | ||||||
|  |    text[len] = 0; | ||||||
|  |    return text; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |    int offset; | ||||||
|  |    int end; | ||||||
|  |    char *filename; | ||||||
|  |    int next_line_after; | ||||||
|  | } include_info; | ||||||
|  | 
 | ||||||
|  | static include_info *stb_include_append_include(include_info *array, int len, int offset, int end, char *filename, int next_line) | ||||||
|  | { | ||||||
|  |    include_info *z = (include_info *) realloc(array, sizeof(*z) * (len+1)); | ||||||
|  |    z[len].offset   = offset; | ||||||
|  |    z[len].end      = end; | ||||||
|  |    z[len].filename = filename; | ||||||
|  |    z[len].next_line_after = next_line; | ||||||
|  |    return z; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void stb_include_free_includes(include_info *array, int len) | ||||||
|  | { | ||||||
|  |    int i; | ||||||
|  |    for (i=0; i < len; ++i) | ||||||
|  |       free(array[i].filename); | ||||||
|  |    free(array); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int stb_include_isspace(int ch) | ||||||
|  | { | ||||||
|  |    return (ch == ' ' || ch == '\t' || ch == '\r' || ch == 'n'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // find location of all #include and #inject
 | ||||||
|  | static int stb_include_find_includes(char *text, include_info **plist) | ||||||
|  | { | ||||||
|  |    int line_count = 1; | ||||||
|  |    int inc_count = 0; | ||||||
|  |    char *s = text, *start; | ||||||
|  |    include_info *list = NULL; | ||||||
|  |    while (*s) { | ||||||
|  |       // parse is always at start of line when we reach here
 | ||||||
|  |       start = s; | ||||||
|  |       while (*s == ' ' || *s == '\t') | ||||||
|  |          ++s; | ||||||
|  |       if (*s == '#') { | ||||||
|  |          ++s; | ||||||
|  |          while (*s == ' ' || *s == '\t') | ||||||
|  |             ++s; | ||||||
|  |          if (0==strncmp(s, "include", 7) && stb_include_isspace(s[7])) { | ||||||
|  |             s += 7; | ||||||
|  |             while (*s == ' ' || *s == '\t') | ||||||
|  |                ++s; | ||||||
|  |             if (*s == '"') { | ||||||
|  |                char *t = ++s; | ||||||
|  |                while (*t != '"' && *t != '\n' && *t != '\r' && *t != 0) | ||||||
|  |                   ++t; | ||||||
|  |                if (*t == '"') { | ||||||
|  |                   char *filename = (char *) malloc(t-s+1); | ||||||
|  |                   memcpy(filename, s, t-s); | ||||||
|  |                   filename[t-s] = 0; | ||||||
|  |                   s=t; | ||||||
|  |                   while (*s != '\r' && *s != '\n' && *s != 0) | ||||||
|  |                      ++s; | ||||||
|  |                   // s points to the newline, so s-start is everything except the newline
 | ||||||
|  |                   list = stb_include_append_include(list, inc_count++, start-text, s-text, filename, line_count+1); | ||||||
|  |                } | ||||||
|  |             } | ||||||
|  |          } else if (0==strncmp(s, "inject", 6) && (stb_include_isspace(s[6]) || s[6]==0)) { | ||||||
|  |             while (*s != '\r' && *s != '\n' && *s != 0) | ||||||
|  |                ++s; | ||||||
|  |             list = stb_include_append_include(list, inc_count++, start-text, s-text, NULL, line_count+1); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |       while (*s != '\r' && *s != '\n' && *s != 0) | ||||||
|  |          ++s; | ||||||
|  |       if (*s == '\r' || *s == '\n') { | ||||||
|  |          s = s + (s[0] + s[1] == '\r' + '\n' ? 2 : 1); | ||||||
|  |       } | ||||||
|  |       ++line_count; | ||||||
|  |    } | ||||||
|  |    *plist = list; | ||||||
|  |    return inc_count; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // avoid dependency on sprintf()
 | ||||||
|  | static void stb_include_itoa(char str[9], int n) | ||||||
|  | { | ||||||
|  |    int i; | ||||||
|  |    for (i=0; i < 8; ++i) | ||||||
|  |       str[i] = ' '; | ||||||
|  |    str[i] = 0; | ||||||
|  | 
 | ||||||
|  |    for (i=1; i < 8; ++i) { | ||||||
|  |       str[7-i] = '0' + (n % 10); | ||||||
|  |       n /= 10; | ||||||
|  |       if (n == 0) | ||||||
|  |          break; | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static char *stb_include_append(char *str, size_t *curlen, char *addstr, size_t addlen) | ||||||
|  | { | ||||||
|  |    str = (char *) realloc(str, *curlen + addlen); | ||||||
|  |    memcpy(str + *curlen, addstr, addlen); | ||||||
|  |    *curlen += addlen; | ||||||
|  |    return str; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename, char error[256]) | ||||||
|  | { | ||||||
|  |    char temp[4096]; | ||||||
|  |    include_info *inc_list; | ||||||
|  |    int i, num = stb_include_find_includes(str, &inc_list); | ||||||
|  |    size_t source_len = strlen(str); | ||||||
|  |    char *text=0; | ||||||
|  |    size_t textlen=0, last=0; | ||||||
|  |    for (i=0; i < num; ++i) { | ||||||
|  |       text = stb_include_append(text, &textlen, str+last, inc_list[i].offset - last); | ||||||
|  |       // write out line directive for the include
 | ||||||
|  |       #ifndef STB_INCLUDE_LINE_NONE | ||||||
|  |       #ifdef STB_INCLUDE_LINE_GLSL | ||||||
|  |       if (textlen != 0)  // GLSL #version must appear first, so don't put a #line at the top
 | ||||||
|  |       #endif | ||||||
|  |       { | ||||||
|  |          strcpy(temp, "#line "); | ||||||
|  |          stb_include_itoa(temp+6, 1); | ||||||
|  |          strcat(temp, " "); | ||||||
|  |          #ifdef STB_INCLUDE_LINE_GLSL | ||||||
|  |          stb_include_itoa(temp+15, i+1); | ||||||
|  |          #else | ||||||
|  |          strcat(temp, "\""); | ||||||
|  |          if (inc_list[i].filename == 0) | ||||||
|  |             strcmp(temp, "INJECT"); | ||||||
|  |          else | ||||||
|  |             strcat(temp, inc_list[i].filename); | ||||||
|  |          strcat(temp, "\""); | ||||||
|  |          #endif | ||||||
|  |          strcat(temp, "\n"); | ||||||
|  |          text = stb_include_append(text, &textlen, temp, strlen(temp)); | ||||||
|  |       } | ||||||
|  |       #endif | ||||||
|  |       if (inc_list[i].filename == 0) { | ||||||
|  |          if (inject != 0) | ||||||
|  |             text = stb_include_append(text, &textlen, inject, strlen(inject)); | ||||||
|  |       } else { | ||||||
|  |          char *inc; | ||||||
|  |          strcpy(temp, path_to_includes); | ||||||
|  |          strcat(temp, "/"); | ||||||
|  |          strcat(temp, inc_list[i].filename); | ||||||
|  |          inc = stb_include_file(temp, inject, path_to_includes, error); | ||||||
|  |          if (inc == NULL) { | ||||||
|  |             stb_include_free_includes(inc_list, num); | ||||||
|  |             return NULL; | ||||||
|  |          } | ||||||
|  |          text = stb_include_append(text, &textlen, inc, strlen(inc)); | ||||||
|  |          free(inc); | ||||||
|  |       } | ||||||
|  |       // write out line directive
 | ||||||
|  |       #ifndef STB_INCLUDE_LINE_NONE | ||||||
|  |       strcpy(temp, "\n#line "); | ||||||
|  |       stb_include_itoa(temp+6, inc_list[i].next_line_after); | ||||||
|  |       strcat(temp, " "); | ||||||
|  |       #ifdef STB_INCLUDE_LINE_GLSL | ||||||
|  |       stb_include_itoa(temp+15, 0); | ||||||
|  |       #else | ||||||
|  |       strcat(temp, filename != 0 ? filename : "source-file"); | ||||||
|  |       #endif | ||||||
|  |       text = stb_include_append(text, &textlen, temp, strlen(temp)); | ||||||
|  |       // no newlines, because we kept the #include newlines, which will get appended next
 | ||||||
|  |       #endif | ||||||
|  |       last = inc_list[i].end; | ||||||
|  |    } | ||||||
|  |    text = stb_include_append(text, &textlen, str+last, source_len - last + 1); // append '\0'
 | ||||||
|  |    stb_include_free_includes(inc_list, num); | ||||||
|  |    return text; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename, char error[256]) | ||||||
|  | { | ||||||
|  |    char *text; | ||||||
|  |    char *result; | ||||||
|  |    int i; | ||||||
|  |    size_t length=0; | ||||||
|  |    for (i=0; i < count; ++count) | ||||||
|  |       length += strlen(strs[i]); | ||||||
|  |    text = (char *) malloc(length+1); | ||||||
|  |    length = 0; | ||||||
|  |    for (i=0; i < count; ++count) { | ||||||
|  |       strcpy(text + length, strs[i]); | ||||||
|  |       length += strlen(strs[i]); | ||||||
|  |    } | ||||||
|  |    result = stb_include_string(text, inject, path_to_includes, filename, error); | ||||||
|  |    free(text); | ||||||
|  |    return result;   
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256]) | ||||||
|  | { | ||||||
|  |    size_t len; | ||||||
|  |    char *result; | ||||||
|  |    char *text = stb_include_load_file(filename, &len); | ||||||
|  |    if (text == NULL) { | ||||||
|  |       strcpy(error, "Error: couldn't load '"); | ||||||
|  |       strcat(error, filename); | ||||||
|  |       strcat(error, "'"); | ||||||
|  |       return 0; | ||||||
|  |    } | ||||||
|  |    result = stb_include_string(text, inject, path_to_includes, filename, error); | ||||||
|  |    free(text); | ||||||
|  |    return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #if 0 // @TODO, GL_ARB_shader_language_include-style system that doesn't touch filesystem
 | ||||||
|  | char *stb_include_preloaded(char *str, char *inject, char *includes[][2], char error[256]) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif // STB_INCLUDE_IMPLEMENTATION
 | ||||||
					Loading…
					
					
				
		Reference in New Issue