mojoshader_preprocessor.c
changeset 555 940821555fda
child 557 ef6a607a5618
equal deleted inserted replaced
554:42dd28107cd8 555:940821555fda
       
     1 /**
       
     2  * MojoShader; generate shader programs from bytecode of compiled
       
     3  *  Direct3D shaders.
       
     4  *
       
     5  * Please see the file LICENSE.txt in the source's root directory.
       
     6  *
       
     7  *  This file written by Ryan C. Gordon.
       
     8  */
       
     9 
       
    10 #define __MOJOSHADER_INTERNAL__ 1
       
    11 #include "mojoshader_internal.h"
       
    12 
       
    13 typedef struct DefineHash
       
    14 {
       
    15     MOJOSHADER_preprocessorDefine define;
       
    16     struct DefineHash *next;
       
    17 } DefineHash;
       
    18 
       
    19 typedef struct Context
       
    20 {
       
    21     int isfail;
       
    22     int out_of_memory;
       
    23     char failstr[128];
       
    24     IncludeState *include_stack;
       
    25     DefineHash *define_hashtable[256];
       
    26     int pushedback;
       
    27     const char *token;
       
    28     unsigned int tokenlen;
       
    29     MOJOSHADER_includeOpen open_callback;
       
    30     MOJOSHADER_includeClose close_callback;
       
    31     MOJOSHADER_malloc malloc;
       
    32     MOJOSHADER_free free;
       
    33     void *malloc_data;
       
    34 } Context;
       
    35 
       
    36 
       
    37 // Convenience functions for allocators...
       
    38 
       
    39 static inline void out_of_memory(Context *ctx)
       
    40 {
       
    41     ctx->isfail = ctx->out_of_memory = 1;
       
    42 } // out_of_memory
       
    43 
       
    44 static inline void *Malloc(Context *ctx, const size_t len)
       
    45 {
       
    46     void *retval = ctx->malloc((int) len, ctx->malloc_data);
       
    47     if (retval == NULL)
       
    48         out_of_memory(ctx);
       
    49     return retval;
       
    50 } // Malloc
       
    51 
       
    52 static inline void Free(Context *ctx, void *ptr)
       
    53 {
       
    54     if (ptr != NULL)  // check for NULL in case of dumb free() impl.
       
    55         ctx->free(ptr, ctx->malloc_data);
       
    56 } // Free
       
    57 
       
    58 static inline char *StrDup(Context *ctx, const char *str)
       
    59 {
       
    60     char *retval = (char *) Malloc(ctx, strlen(str) + 1);
       
    61     if (retval != NULL)
       
    62         strcpy(retval, str);
       
    63     return retval;
       
    64 } // StrDup
       
    65 
       
    66 static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
       
    67 static void failf(Context *ctx, const char *fmt, ...)
       
    68 {
       
    69     ctx->isfail = 1;
       
    70     va_list ap;
       
    71     va_start(ap, fmt);
       
    72     vsnprintf(ctx->failstr, sizeof (ctx->failstr), fmt, ap);  // rebuild it.
       
    73     va_end(ap);
       
    74 } // failf
       
    75 
       
    76 static inline void fail(Context *ctx, const char *reason)
       
    77 {
       
    78     failf(ctx, "%s", reason);
       
    79 } // fail
       
    80 
       
    81 
       
    82 // Preprocessor define hashtable stuff...
       
    83 
       
    84 static unsigned char hash_define(const char *sym)
       
    85 {
       
    86     unsigned char retval = 0;
       
    87     while (sym)
       
    88         retval += *(sym++);
       
    89     return retval;
       
    90 } // hash_define
       
    91 
       
    92 
       
    93 static int add_define(Context *ctx, const char *sym, const char *val)
       
    94 {
       
    95     char *identifier = NULL;
       
    96     char *definition = NULL;
       
    97     const unsigned char hash = hash_define(sym);
       
    98     DefineHash *bucket = ctx->define_hashtable[hash];
       
    99     while (bucket)
       
   100     {
       
   101         if (strcmp(bucket->define.identifier, sym) == 0)
       
   102         {
       
   103             failf(ctx, "'%s' already defined", sym);
       
   104             return 0;
       
   105         } // if
       
   106         bucket = bucket->next;
       
   107     } // while
       
   108 
       
   109     bucket = (DefineHash *) Malloc(ctx, sizeof (DefineHash));
       
   110     if (bucket == NULL)
       
   111         return 0;
       
   112 
       
   113     identifier = (char *) Malloc(ctx, strlen(sym) + 1);
       
   114     definition = (char *) Malloc(ctx, strlen(val) + 1);
       
   115     if ((identifier == NULL) || (definition == NULL))
       
   116     {
       
   117         Free(ctx, identifier);
       
   118         Free(ctx, definition);
       
   119         Free(ctx, bucket);
       
   120         return 0;
       
   121     } // if
       
   122 
       
   123     strcpy(identifier, sym);
       
   124     strcpy(definition, val);
       
   125     bucket->define.identifier = identifier;
       
   126     bucket->define.definition = definition;
       
   127     bucket->next = ctx->define_hashtable[hash];
       
   128     ctx->define_hashtable[hash] = bucket;
       
   129     return 1;
       
   130 } // add_define
       
   131 
       
   132 
       
   133 static int remove_define(Context *ctx, const char *sym)
       
   134 {
       
   135     const unsigned char hash = hash_define(sym);
       
   136     DefineHash *bucket = ctx->define_hashtable[hash];
       
   137     DefineHash *prev = NULL;
       
   138     while (bucket)
       
   139     {
       
   140         if (strcmp(bucket->define.identifier, sym) == 0)
       
   141         {
       
   142             if (prev == NULL)
       
   143                 ctx->define_hashtable[hash] = bucket->next;
       
   144             else
       
   145                 prev->next = bucket->next;
       
   146             Free(ctx, (void *) bucket->define.identifier);
       
   147             Free(ctx, (void *) bucket->define.definition);
       
   148             Free(ctx, bucket);
       
   149             return 1;
       
   150         } // if
       
   151         prev = bucket;
       
   152         bucket = bucket->next;
       
   153     } // while
       
   154 
       
   155     failf(ctx, "'%s' not defined", sym);
       
   156     return 0;
       
   157 } // remove_define
       
   158 
       
   159 
       
   160 static const char *find_define(Context *ctx, const char *sym)
       
   161 {
       
   162     const unsigned char hash = hash_define(sym);
       
   163     DefineHash *bucket = ctx->define_hashtable[hash];
       
   164     while (bucket)
       
   165     {
       
   166         if (strcmp(bucket->define.identifier, sym) == 0)
       
   167             return bucket->define.definition;
       
   168         bucket = bucket->next;
       
   169     } // while
       
   170     return NULL;
       
   171 } // find_define
       
   172 
       
   173 
       
   174 static void free_all_defines(Context *ctx)
       
   175 {
       
   176     int i;
       
   177     for (i = 0; i < STATICARRAYLEN(ctx->define_hashtable); i++)
       
   178     {
       
   179         DefineHash *bucket = ctx->define_hashtable[i];
       
   180         ctx->define_hashtable[i] = NULL;
       
   181         while (bucket)
       
   182         {
       
   183             DefineHash *next = bucket->next;
       
   184             Free(ctx, (void *) bucket->define.identifier);
       
   185             Free(ctx, (void *) bucket->define.definition);
       
   186             Free(ctx, bucket);
       
   187             bucket = next;
       
   188         } // while
       
   189     } // for
       
   190 } // find_define
       
   191 
       
   192 
       
   193 static int push_source(Context *ctx, const char *fname, const char *source,
       
   194                        unsigned int srclen, int included)
       
   195 {
       
   196     IncludeState *state = (IncludeState *) Malloc(ctx, sizeof (IncludeState));
       
   197     if (state == NULL)
       
   198         return 0;
       
   199     memset(state, '\0', sizeof (IncludeState));
       
   200 
       
   201     state->filename = StrDup(ctx, fname);
       
   202     if (state->filename == NULL)
       
   203     {
       
   204         Free(ctx, state);
       
   205         return 0;
       
   206     } // if
       
   207 
       
   208     state->included = included;
       
   209     state->source_base = source;
       
   210     state->source = source;
       
   211     state->token = source;
       
   212     state->insert_token = TOKEN_UNKNOWN;
       
   213     state->insert_token2 = TOKEN_UNKNOWN;
       
   214     state->bytes_left = srclen;
       
   215     state->line = 1;
       
   216     state->next = ctx->include_stack;
       
   217 
       
   218     ctx->include_stack = state;
       
   219 
       
   220     return 1;
       
   221 } // push_source
       
   222 
       
   223 
       
   224 static void pop_source(Context *ctx)
       
   225 {
       
   226     IncludeState *state = ctx->include_stack;
       
   227     if (state == NULL)
       
   228         return;
       
   229 
       
   230     if (state->included)
       
   231     {
       
   232         ctx->close_callback(state->source_base, ctx->malloc,
       
   233                             ctx->free, ctx->malloc_data);
       
   234     } // if
       
   235 
       
   236     ctx->include_stack = state->next;
       
   237     Free(ctx, state->filename);
       
   238     Free(ctx, state);
       
   239 } // pop_source
       
   240 
       
   241 
       
   242 Preprocessor *preprocessor_start(const char *fname, const char *source,
       
   243                             unsigned int sourcelen,
       
   244                             MOJOSHADER_includeOpen open_callback,
       
   245                             MOJOSHADER_includeClose close_callback,
       
   246                             const MOJOSHADER_preprocessorDefine **defines,
       
   247                             unsigned int define_count,
       
   248                             MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
       
   249 {
       
   250     int okay = 1;
       
   251     int i = 0;
       
   252 
       
   253     // the preprocessor is internal-only, so we verify all these are != NULL.
       
   254     assert(m != NULL);
       
   255     assert(f != NULL);
       
   256     assert(open_callback != NULL);
       
   257     assert(close_callback != NULL);
       
   258 
       
   259     Context *ctx = (Context *) m(sizeof (Context), d);
       
   260     if (ctx == NULL)
       
   261         return 0;
       
   262 
       
   263     memset(ctx, '\0', sizeof (Context));
       
   264     ctx->malloc = m;
       
   265     ctx->free = f;
       
   266     ctx->malloc_data = d;
       
   267     ctx->open_callback = open_callback;
       
   268     ctx->close_callback = close_callback;
       
   269 
       
   270     for (i = 0; i < define_count; i++)
       
   271     {
       
   272         if (!add_define(ctx, defines[i]->identifier, defines[i]->definition))
       
   273         {
       
   274             okay = 0;
       
   275             break;
       
   276         } // if
       
   277     } // for
       
   278 
       
   279     if ((okay) && (!push_source(ctx, fname, source, sourcelen, 0)))
       
   280         okay = 0;
       
   281 
       
   282     if (!okay)
       
   283     {
       
   284         preprocessor_end((Preprocessor *) ctx);
       
   285         return NULL;
       
   286     } // if
       
   287 
       
   288     return (Preprocessor *) ctx;
       
   289 } // preprocessor_start
       
   290 
       
   291 
       
   292 void preprocessor_end(Preprocessor *_ctx)
       
   293 {
       
   294     Context *ctx = (Context *) _ctx;
       
   295     if (ctx == NULL)
       
   296         return;
       
   297 
       
   298     while (ctx->include_stack != NULL)
       
   299         pop_source(ctx);
       
   300 
       
   301     free_all_defines(ctx);
       
   302 
       
   303     Free(ctx, ctx);
       
   304 } // preprocessor_end
       
   305 
       
   306 
       
   307 void preprocessor_clearerror(Preprocessor *_ctx)
       
   308 {
       
   309     Context *ctx = (Context *) _ctx;
       
   310     ctx->isfail = 0;
       
   311 } // preprocessor_clearerror
       
   312 
       
   313 
       
   314 const char *preprocessor_error(Preprocessor *_ctx)
       
   315 {
       
   316     Context *ctx = (Context *) _ctx;
       
   317     return ctx->isfail ? ctx->failstr : NULL;
       
   318 } // preprocessor_error
       
   319 
       
   320 
       
   321 int preprocessor_outofmemory(Preprocessor *_ctx)
       
   322 {
       
   323     Context *ctx = (Context *) _ctx;
       
   324     return ctx->out_of_memory;
       
   325 } // preprocessor_outofmemory
       
   326 
       
   327 
       
   328 const char *preprocessor_nexttoken(Preprocessor *_ctx, unsigned int *_len,
       
   329                                     Token *_token)
       
   330 {
       
   331     Context *ctx = (Context *) _ctx;
       
   332 
       
   333     while (1)
       
   334     {
       
   335         IncludeState *state = ctx->include_stack;
       
   336         if (state == NULL)
       
   337             return NULL;  // we're done!
       
   338 
       
   339         if (state->insert_token != TOKEN_UNKNOWN)
       
   340         {
       
   341             state->insert_tokchar = (char) state->insert_token;
       
   342             *_token = state->insert_token;
       
   343             *_len = 1;
       
   344             state->insert_token = TOKEN_UNKNOWN;
       
   345             return &state->insert_tokchar;
       
   346         } // if
       
   347 
       
   348         else if (state->insert_token2 != TOKEN_UNKNOWN)
       
   349         {
       
   350             state->insert_tokchar = (char) state->insert_token2;
       
   351             *_token = state->insert_token2;
       
   352             *_len = 1;
       
   353             state->insert_token2 = TOKEN_UNKNOWN;
       
   354             return &state->insert_tokchar;
       
   355         } // if
       
   356 
       
   357         Token token = preprocessor_internal_lexer(state);
       
   358         if (token == TOKEN_EOI)
       
   359         {
       
   360             assert(state->bytes_left == 0);
       
   361             pop_source(ctx);
       
   362             continue;  // pick up again after parent's #include line.
       
   363         } // if
       
   364 
       
   365         // Microsoft's preprocessor is weird.
       
   366         // It ignores newlines, and then inserts its own around certain
       
   367         //  tokens. For example, after a semicolon. This allows HLSL code to
       
   368         //  be mostly readable, and lets the ';' work as a single line comment
       
   369         //  in the assembler.
       
   370         if ( (token == ((Token) ';')) || (token == ((Token) '}')) )
       
   371             state->insert_token = (Token) '\n';
       
   372         else if (token == ((Token) '{'))
       
   373         {
       
   374             state->insert_token = (Token) '{';
       
   375             state->insert_token2 = (Token) '\n';
       
   376             state->insert_tokchar = '\n';
       
   377             *_token = (Token) '\n';
       
   378             *_len = 1;
       
   379             return &state->insert_tokchar;
       
   380         } // else if
       
   381 
       
   382         *_token = token;
       
   383         *_len = (unsigned int) (state->source - state->token);
       
   384         return state->token;
       
   385     } // while
       
   386 } // preprocessor_nexttoken
       
   387 
       
   388 
       
   389 const char *preprocessor_sourcepos(Preprocessor *_ctx, unsigned int *pos)
       
   390 {
       
   391     Context *ctx = (Context *) _ctx;
       
   392     if (ctx->include_stack == NULL)
       
   393     {
       
   394         *pos = 0;
       
   395         return NULL;
       
   396     } // if
       
   397 
       
   398     *pos = ctx->include_stack->line;
       
   399     return ctx->include_stack->filename;
       
   400 } // preprocessor_sourcepos
       
   401 
       
   402 
       
   403 // public API...
       
   404 
       
   405 static const MOJOSHADER_preprocessData out_of_mem_data_preprocessor = {
       
   406     1, &MOJOSHADER_out_of_mem_error, 0, 0, 0, 0, 0
       
   407 };
       
   408 
       
   409 #define BUFFER_LEN (64 * 1024)
       
   410 typedef struct BufferList
       
   411 {
       
   412     char buffer[BUFFER_LEN];
       
   413     size_t bytes;
       
   414     struct BufferList *next;
       
   415 } BufferList;
       
   416 
       
   417 typedef struct Buffer
       
   418 {
       
   419     size_t total_bytes;
       
   420     BufferList head;
       
   421     BufferList *tail;
       
   422 } Buffer;
       
   423 
       
   424 static void buffer_init(Buffer *buffer)
       
   425 {
       
   426     buffer->total_bytes = 0;
       
   427     buffer->head.bytes = 0;
       
   428     buffer->head.next = NULL;
       
   429     buffer->tail = &buffer->head;
       
   430 } // buffer_init
       
   431 
       
   432 
       
   433 static int add_to_buffer(Buffer *buffer, const char *data,
       
   434                          size_t len, MOJOSHADER_malloc m, void *d)
       
   435 {
       
   436     buffer->total_bytes += len;
       
   437     while (len > 0)
       
   438     {
       
   439         const size_t avail = BUFFER_LEN - buffer->tail->bytes;
       
   440         const size_t cpy = (avail > len) ? len : avail;
       
   441         memcpy(buffer->tail->buffer + buffer->tail->bytes, data, cpy);
       
   442         len -= cpy;
       
   443         data += cpy;
       
   444         buffer->tail->bytes += cpy;
       
   445         assert(buffer->tail->bytes <= BUFFER_LEN);
       
   446         if (buffer->tail->bytes == BUFFER_LEN)
       
   447         {
       
   448             BufferList *item = (BufferList *) m(sizeof (BufferList), d);
       
   449             if (item == NULL)
       
   450                 return 0;
       
   451             item->bytes = 0;
       
   452             item->next = NULL;
       
   453             buffer->tail->next = item;
       
   454             buffer->tail = item;
       
   455         } // if
       
   456     } // while
       
   457 
       
   458     return 1;
       
   459 } // add_to_buffer
       
   460 
       
   461 
       
   462 static int indent_buffer(Buffer *buffer, int n, int newline,
       
   463                          MOJOSHADER_malloc m, void *d)
       
   464 {
       
   465     static char spaces[4] = { ' ', ' ', ' ', ' ' };
       
   466     if (newline)
       
   467     {
       
   468         while (n--)
       
   469         {
       
   470             if (!add_to_buffer(buffer, spaces, sizeof (spaces), m, d))
       
   471                 return 0;
       
   472         } // while
       
   473     } // if
       
   474     else
       
   475     {
       
   476         if (!add_to_buffer(buffer, spaces, 1, m, d))
       
   477             return 0;
       
   478     } // else
       
   479     return 1;
       
   480 } // indent_buffer
       
   481 
       
   482 
       
   483 static char *flatten_buffer(Buffer *buffer, MOJOSHADER_malloc m, void *d)
       
   484 {
       
   485     char *retval = m(buffer->total_bytes + 1, d);
       
   486     if (retval == NULL)
       
   487         return NULL;
       
   488     BufferList *item = &buffer->head;
       
   489     char *ptr = retval;
       
   490     while (item != NULL)
       
   491     {
       
   492         BufferList *next = item->next;
       
   493         memcpy(ptr, item->buffer, item->bytes);
       
   494         ptr += item->bytes;
       
   495         item = next;
       
   496     } // while
       
   497     *ptr = '\0';
       
   498 
       
   499     assert(ptr == (retval + buffer->total_bytes));
       
   500     return retval;
       
   501 } // flatten_buffer
       
   502 
       
   503 
       
   504 static void free_buffer(Buffer *buffer, MOJOSHADER_free f, void *d)
       
   505 {
       
   506     // head is statically allocated, so start with head.next...
       
   507     BufferList *item = buffer->head.next;
       
   508     while (item != NULL)
       
   509     {
       
   510         BufferList *next = item->next;
       
   511         f(item, d);
       
   512         item = next;
       
   513     } // while
       
   514     buffer_init(buffer);
       
   515 } // free_buffer
       
   516 
       
   517 
       
   518 // !!! FIXME: cut and paste.
       
   519 static void free_error_list(ErrorList *item, MOJOSHADER_free f, void *d)
       
   520 {
       
   521     while (item != NULL)
       
   522     {
       
   523         ErrorList *next = item->next;
       
   524         f((void *) item->error.error, d);
       
   525         f((void *) item->error.filename, d);
       
   526         f(item, d);
       
   527         item = next;
       
   528     } // while
       
   529 } // free_error_list
       
   530 
       
   531 
       
   532 // !!! FIXME: cut and paste.
       
   533 static MOJOSHADER_error *build_errors(ErrorList **errors, const int count,
       
   534                          MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
       
   535 {
       
   536     int total = 0;
       
   537     MOJOSHADER_error *retval = (MOJOSHADER_error *)
       
   538                                 m(sizeof (MOJOSHADER_error) * count, d);
       
   539     if (retval == NULL)
       
   540         return NULL;
       
   541 
       
   542     ErrorList *item = *errors;
       
   543     while (item != NULL)
       
   544     {
       
   545         ErrorList *next = item->next;
       
   546         // reuse the string allocations
       
   547         memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error));
       
   548         f(item, d);
       
   549         item = next;
       
   550         total++;
       
   551     } // while
       
   552     *errors = NULL;
       
   553 
       
   554     assert(total == count);
       
   555     return retval;
       
   556 } // build_errors
       
   557 
       
   558 
       
   559 const MOJOSHADER_preprocessData *MOJOSHADER_preprocess(const char *source,
       
   560                              unsigned int sourcelen,
       
   561                              const MOJOSHADER_preprocessorDefine **defines,
       
   562                              unsigned int define_count,
       
   563                              MOJOSHADER_includeOpen include_open,
       
   564                              MOJOSHADER_includeClose include_close,
       
   565                              MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
       
   566 {
       
   567     ErrorList *errors = NULL;
       
   568     int error_count = 0;
       
   569 
       
   570     if (m == NULL) m = MOJOSHADER_internal_malloc;
       
   571     if (f == NULL) f = MOJOSHADER_internal_free;
       
   572 
       
   573 include_open = (MOJOSHADER_includeOpen) 0x1;
       
   574 include_close = (MOJOSHADER_includeClose) 0x1;
       
   575 
       
   576     const char *fname = "*";  // !!! FIXME
       
   577     Preprocessor *pp = preprocessor_start(fname, source, sourcelen,
       
   578                                           include_open, include_close,
       
   579                                           defines, define_count, m, f, d);
       
   580 
       
   581     if (pp == NULL)
       
   582         return &out_of_mem_data_preprocessor;
       
   583 
       
   584     Token token = TOKEN_UNKNOWN;
       
   585     const char *tokstr = NULL;
       
   586     const char *err = NULL;
       
   587 
       
   588     Buffer buffer;
       
   589     buffer_init(&buffer);
       
   590 
       
   591     int nl = 1;
       
   592     int indent = 0;
       
   593     unsigned int len = 0;
       
   594     while ((tokstr = preprocessor_nexttoken(pp, &len, &token)) != NULL)
       
   595     {
       
   596         #ifdef _WINDOWS
       
   597         static const char endline[] = { '\r', '\n' };
       
   598         #else
       
   599         static const char endline[] = { '\n' };
       
   600         #endif
       
   601 
       
   602         const int isnewline = (token == ((Token) '\n'));
       
   603         if (isnewline)
       
   604         {
       
   605             tokstr = endline;  // convert to platform-specific.
       
   606             len = sizeof (endline);
       
   607         } // if
       
   608 
       
   609         if ((token == ((Token) '}')) && (indent > 0))
       
   610             indent--;
       
   611 
       
   612         int out_of_memory = preprocessor_outofmemory(pp);
       
   613 
       
   614         if ((!out_of_memory) && (!isnewline))
       
   615             out_of_memory = !indent_buffer(&buffer, indent, nl, m, d);
       
   616 
       
   617         if (!out_of_memory)
       
   618             out_of_memory = !add_to_buffer(&buffer, tokstr, len, m, d);
       
   619 
       
   620         if (token == ((Token) '{'))
       
   621             indent++;
       
   622 
       
   623         nl = isnewline;
       
   624 
       
   625         if ((!out_of_memory) && ((err = preprocessor_error(pp)) != NULL))
       
   626         {
       
   627             ErrorList *error = (ErrorList *) m(sizeof (ErrorList), d);
       
   628             unsigned int pos = 0;
       
   629             char *fname = NULL;
       
   630             const char *str = preprocessor_sourcepos(pp, &pos);
       
   631             if (str != NULL)
       
   632             {
       
   633                 fname = (char *) m(strlen(str) + 1, d);
       
   634                 if (fname != NULL)
       
   635                     strcpy(fname, str);
       
   636             } // if
       
   637 
       
   638             // !!! FIXME: cut and paste with other error handlers.
       
   639             char *errstr = (char *) m(strlen(err) + 1, d);
       
   640             if (errstr != NULL)
       
   641                 strcpy(errstr, err);
       
   642 
       
   643             out_of_memory = ((!error) || ((!fname) && (str)) || (!errstr));
       
   644             if (out_of_memory)
       
   645             {
       
   646                 if (errstr) f(errstr, d);
       
   647                 if (fname) f(fname, d);
       
   648                 if (error) f(error, d);
       
   649             } // if
       
   650             else
       
   651             {
       
   652                 error->error.error = errstr;
       
   653                 error->error.filename = fname;
       
   654                 error->error.error_position = pos;
       
   655                 error->next = NULL;
       
   656 
       
   657                 ErrorList *prev = NULL;
       
   658                 ErrorList *item = errors;
       
   659                 while (item != NULL)
       
   660                 {
       
   661                     prev = item;
       
   662                     item = error->next;
       
   663                 } // while
       
   664 
       
   665                 if (prev == NULL)
       
   666                     errors = error;
       
   667                 else
       
   668                     prev->next = error;
       
   669 
       
   670                 error_count++;
       
   671             } // else
       
   672 
       
   673             preprocessor_clearerror(pp);
       
   674             continue;
       
   675         } // if
       
   676 
       
   677         if (out_of_memory)
       
   678         {
       
   679             preprocessor_end(pp);
       
   680             free_buffer(&buffer, f, d);
       
   681             free_error_list(errors, f, d);
       
   682             return &out_of_mem_data_preprocessor;
       
   683         } // if
       
   684     } // while
       
   685     
       
   686     preprocessor_end(pp);
       
   687 
       
   688     const size_t total_bytes = buffer.total_bytes;
       
   689     char *output = flatten_buffer(&buffer, m, d);
       
   690     free_buffer(&buffer, f, d);
       
   691     if (output == NULL)
       
   692     {
       
   693         free_error_list(errors, f, d);
       
   694         return &out_of_mem_data_preprocessor;
       
   695     } // if
       
   696 
       
   697     MOJOSHADER_preprocessData *retval = (MOJOSHADER_preprocessData *)
       
   698                                     m(sizeof (MOJOSHADER_preprocessData), d);
       
   699     if (retval == NULL)
       
   700     {
       
   701         free_error_list(errors, f, d);
       
   702         f(output, d);
       
   703         return &out_of_mem_data_preprocessor;
       
   704     } // if
       
   705 
       
   706     retval->errors = build_errors(&errors, error_count, m, f, d);
       
   707     if (retval->errors == NULL)
       
   708     {
       
   709         free_error_list(errors, f, d);
       
   710         f(retval, d);
       
   711         f(output, d);
       
   712         return &out_of_mem_data_preprocessor;
       
   713     } // if
       
   714 
       
   715     retval->error_count = error_count;
       
   716     retval->output = output;
       
   717     retval->output_len = total_bytes;
       
   718     retval->malloc = m;
       
   719     retval->free = f;
       
   720     retval->malloc_data = d;
       
   721     return retval;
       
   722 } // MOJOSHADER_preprocess
       
   723 
       
   724 
       
   725 void MOJOSHADER_freePreprocessData(const MOJOSHADER_preprocessData *_data)
       
   726 {
       
   727     MOJOSHADER_preprocessData *data = (MOJOSHADER_preprocessData *) _data;
       
   728     if ((data == NULL) || (data == &out_of_mem_data_preprocessor))
       
   729         return;
       
   730 
       
   731     MOJOSHADER_free f = (data->free == NULL) ? MOJOSHADER_internal_free : data->free;
       
   732     void *d = data->malloc_data;
       
   733     int i;
       
   734 
       
   735     if (data->output != NULL)
       
   736         f((void *) data->output, d);
       
   737 
       
   738     if (data->errors != NULL)
       
   739     {
       
   740         for (i = 0; i < data->error_count; i++)
       
   741         {
       
   742             if (data->errors[i].error != NULL)
       
   743                 f((void *) data->errors[i].error, d);
       
   744             if (data->errors[i].filename != NULL)
       
   745                 f((void *) data->errors[i].filename, d);
       
   746         } // for
       
   747         f(data->errors, d);
       
   748     } // if
       
   749 
       
   750     f(data, d);
       
   751 } // MOJOSHADER_freePreprocessData
       
   752 
       
   753 
       
   754 // end of mojoshader_preprocessor.c ...
       
   755