Cut-and-paste cleanup: unified the ErrorList functionality.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 03 Nov 2010 22:54:17 -0400
changeset 939 64cc93ee5a56
parent 938 fc5240c4f197
child 940 bc2a5efade5e
Cut-and-paste cleanup: unified the ErrorList functionality. Also removed the NULL checks from most free() calls: they make the code ugly, and the app should really do this check itself anyhow (the docs say it behaves like ANSI C's free(), which does check for this. I think.)
mojoshader.c
mojoshader.h
mojoshader_assembler.c
mojoshader_common.c
mojoshader_compiler.c
mojoshader_internal.h
mojoshader_preprocessor.c
--- a/mojoshader.c	Tue Nov 02 14:58:31 2010 -0400
+++ b/mojoshader.c	Wed Nov 03 22:54:17 2010 -0400
@@ -75,6 +75,7 @@
     const VariableList *relative_array;
 } SourceArgInfo;
 
+// !!! FIXME: can we get rid of this nonsense?
 #define SCRATCH_BUFFER_SIZE 128
 #define SCRATCH_BUFFERS 32
 
@@ -143,7 +144,6 @@
     int last_address_reg_component;
     RegisterList used_registers;
     RegisterList defined_registers;
-    int error_count;
     ErrorList *errors;
     int constant_count;
     ConstantsList *constants;
@@ -298,10 +298,19 @@
 
 static inline void Free(Context *ctx, void *ptr)
 {
-    if (ptr != NULL)  // check for NULL in case of dumb free() impl.
-        ctx->free(ptr, ctx->malloc_data);
+    ctx->free(ptr, ctx->malloc_data);
 } // Free
 
+static void *MallocBridge(int bytes, void *data)
+{
+    return Malloc((Context *) data, (size_t) bytes);
+} // MallocBridge
+
+static void FreeBridge(void *ptr, void *data)
+{
+    Free((Context *) data, ptr);
+} // FreeBridge
+
 
 // jump between output sections in the context...
 
@@ -381,68 +390,28 @@
     if (ctx->out_of_memory)
         return;
 
-    int error_position = 0;
+    int errpos = 0;
     switch (ctx->parse_phase)
     {
         case MOJOSHADER_PARSEPHASE_NOTSTARTED:
-            error_position = -2;
+            errpos = -2;
             break;
         case MOJOSHADER_PARSEPHASE_WORKING:
-            error_position = (ctx->tokens - ctx->orig_tokens) * sizeof (uint32);
+            errpos = (ctx->tokens - ctx->orig_tokens) * sizeof (uint32);
             break;
         case MOJOSHADER_PARSEPHASE_DONE:
-            error_position = -1;
+            errpos = -1;
             break;
         default:
             assert(0 && "Unexpected value");
             return;
     } // switch
 
-    ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList));
-    if (error == NULL)
-        return;
-
-    char *scratch = get_scratch_buffer(ctx);
+    // no filename at this level (we pass a NULL to errorlist_add_va()...)
     va_list ap;
     va_start(ap, fmt);
-    const int len = vsnprintf(scratch, SCRATCH_BUFFER_SIZE, fmt, ap);
+    errorlist_add_va(ctx->errors, NULL, errpos, fmt, ap);
     va_end(ap);
-
-    char *failstr = (char *) Malloc(ctx, len + 1);
-    if (failstr == NULL)
-        Free(ctx, error);
-    else
-    {
-        // see comments about scratch buffer overflow in output_line().
-        if (len < SCRATCH_BUFFER_SIZE)
-            strcpy(failstr, scratch);  // copy it over.
-        else
-        {
-            va_start(ap, fmt);
-            vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
-            va_end(ap);
-        } // else
-
-        error->error.error = failstr;
-        error->error.filename = NULL;  // no filename at this level.
-        error->error.error_position = error_position;
-        error->next = NULL;
-
-        ErrorList *prev = NULL;
-        ErrorList *item = ctx->errors;
-        while (item != NULL)
-        {
-            prev = item;
-            item = item->next;
-        } // while
-
-        if (prev == NULL)
-            ctx->errors = error;
-        else
-            prev->next = error;
-
-        ctx->error_count++;
-    } // else
 } // failf
 
 
@@ -6940,6 +6909,13 @@
     ctx->last_address_reg_component = -1;
     ctx->parse_phase = MOJOSHADER_PARSEPHASE_NOTSTARTED;
 
+    ctx->errors = errorlist_create(MallocBridge, FreeBridge, ctx);
+    if (ctx->errors == NULL)
+    {
+        f(ctx, d);
+        return NULL;
+    } // if
+
     const int profileid = find_profile_id(profile);
     ctx->profileid = profileid;
     if (profileid >= 0)
@@ -6986,19 +6962,6 @@
 } // free_variable_list
 
 
-static void free_error_list(MOJOSHADER_free f, void *d, ErrorList *item)
-{
-    while (item != NULL)
-    {
-        ErrorList *next = item->next;
-        f((void *) item->error.error, d);
-        f((void *) item->error.filename, d);
-        f(item, d);
-        item = next;
-    } // while
-} // free_error_list
-
-
 static void destroy_context(Context *ctx)
 {
     if (ctx != NULL)
@@ -7021,7 +6984,7 @@
         free_reglist(f, d, ctx->attributes.next);
         free_reglist(f, d, ctx->samplers.next);
         free_variable_list(f, d, ctx->variables);
-        free_error_list(f, d, ctx->errors);
+        errorlist_destroy(ctx->errors);
         f(ctx, d);
     } // if
 } // destroy_context
@@ -7257,31 +7220,6 @@
 } // build_samplers
 
 
-static MOJOSHADER_error *build_errors(Context *ctx)
-{
-    int total = 0;
-    MOJOSHADER_error *retval = (MOJOSHADER_error *)
-            Malloc(ctx, sizeof (MOJOSHADER_error) * ctx->error_count);
-    if (retval == NULL)
-        return NULL;
-
-    ErrorList *item = ctx->errors;
-    while (item != NULL)
-    {
-        ErrorList *next = item->next;
-        // reuse the string allocations
-        memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error));
-        Free(ctx, item);
-        item = next;
-        total++;
-    } // while
-    ctx->errors = NULL;
-
-    assert(total == ctx->error_count);
-    return retval;
-} // build_errors
-
-
 static MOJOSHADER_attribute *build_attributes(Context *ctx, int *_count)
 {
     int count = 0;
@@ -7390,7 +7328,8 @@
     if (!isfail(ctx))
         samplers = build_samplers(ctx);
 
-    errors = build_errors(ctx);
+    const int error_count = ctx->errors->count;
+    errors = errorlist_flatten(ctx->errors);
 
     if (!isfail(ctx))
     {
@@ -7435,12 +7374,12 @@
 
         if (ctx->out_of_memory)
         {
-            for (i = 0; i < ctx->sampler_count; i++)
+            for (i = 0; i < error_count; i++)
             {
                 Free(ctx, (void *) errors[i].filename);
                 Free(ctx, (void *) errors[i].error);
             } // for
-            Free(ctx, ctx->errors);
+            Free(ctx, errors);
             Free(ctx, retval);
             return &MOJOSHADER_out_of_mem_data;
         } // if
@@ -7466,7 +7405,7 @@
         retval->swizzles = swizzles;
     } // else
 
-    retval->error_count = ctx->error_count;
+    retval->error_count = error_count;
     retval->errors = errors;
     retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
     retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
@@ -7749,68 +7688,35 @@
 
     // we don't f(data->profile), because that's internal static data.
 
-    if (data->output != NULL)  // check for NULL in case of dumb free() impl.
-        f((void *) data->output, d);
-
-    if (data->constants != NULL)
-        f((void *) data->constants, d);
-
-    if (data->swizzles != NULL)
-        f((void *) data->swizzles, d);
-
-    if (data->errors != NULL)
-    {
-        for (i = 0; i < data->error_count; i++)
-        {
-            if (data->errors[i].error != NULL)
-                f((void *) data->errors[i].error, d);
-            if (data->errors[i].filename != NULL)
-                f((void *) data->errors[i].filename, d);
-        } // for
-        f((void *) data->errors, d);
-    } // if
-
-    if (data->uniforms != NULL)
-    {
-        for (i = 0; i < data->uniform_count; i++)
-        {
-            if (data->uniforms[i].name != NULL)
-                f((void *) data->uniforms[i].name, d);
-        } // for
-        f((void *) data->uniforms, d);
-    } // if
-
-    if (data->attributes != NULL)
-    {
-        for (i = 0; i < data->attribute_count; i++)
-        {
-            if (data->attributes[i].name != NULL)
-                f((void *) data->attributes[i].name, d);
-        } // for
-        f((void *) data->attributes, d);
-    } // if
-
-    if (data->samplers != NULL)
-    {
-        for (i = 0; i < data->sampler_count; i++)
-        {
-            if (data->samplers[i].name != NULL)
-                f((void *) data->samplers[i].name, d);
-        } // for
-        f((void *) data->samplers, d);
-    } // if
-
-    if (data->symbols != NULL)
-    {
-        for (i = 0; i < data->symbol_count; i++)
-        {
-            if (data->symbols[i].name != NULL)
-                f((void *) data->symbols[i].name, d);
-            if (data->symbols[i].default_value != NULL)
-                f((void *) data->symbols[i].default_value, d);
-        } // for
-        f((void *) data->symbols, d);
-    } // if
+    f((void *) data->output, d);
+    f((void *) data->constants, d);
+    f((void *) data->swizzles, d);
+
+    for (i = 0; i < data->error_count; i++)
+    {
+        f((void *) data->errors[i].error, d);
+        f((void *) data->errors[i].filename, d);
+    } // for
+    f((void *) data->errors, d);
+
+    for (i = 0; i < data->uniform_count; i++)
+        f((void *) data->uniforms[i].name, d);
+    f((void *) data->uniforms, d);
+
+    for (i = 0; i < data->attribute_count; i++)
+        f((void *) data->attributes[i].name, d);
+    f((void *) data->attributes, d);
+
+    for (i = 0; i < data->sampler_count; i++)
+        f((void *) data->samplers[i].name, d);
+    f((void *) data->samplers, d);
+
+    for (i = 0; i < data->symbol_count; i++)
+    {
+        f((void *) data->symbols[i].name, d);
+        f((void *) data->symbols[i].default_value, d);
+    } // for
+    f((void *) data->symbols, d);
 
     f(data, d);
 } // MOJOSHADER_freeParseData
--- a/mojoshader.h	Tue Nov 02 14:58:31 2010 -0400
+++ b/mojoshader.h	Wed Nov 03 22:54:17 2010 -0400
@@ -1521,7 +1521,7 @@
     /*
      * This is internal data, and not for the application to touch.
      */
-    void *strcache;
+    void *opaque;
 } MOJOSHADER_astData;
 
 
--- a/mojoshader_assembler.c	Tue Nov 02 14:58:31 2010 -0400
+++ b/mojoshader_assembler.c	Wed Nov 03 22:54:17 2010 -0400
@@ -40,7 +40,6 @@
     MOJOSHADER_malloc malloc;
     MOJOSHADER_free free;
     void *malloc_data;
-    int error_count;
     ErrorList *errors;
     Preprocessor *preprocessor;
     MOJOSHADER_parsePhase parse_phase;
@@ -90,75 +89,51 @@
 
 static inline void Free(Context *ctx, void *ptr)
 {
-    if (ptr != NULL)  // check for NULL in case of dumb free() impl.
-        ctx->free(ptr, ctx->malloc_data);
+    ctx->free(ptr, ctx->malloc_data);
 } // Free
 
+static void *MallocBridge(int bytes, void *data)
+{
+    return Malloc((Context *) data, (size_t) bytes);
+} // MallocBridge
+
+static void FreeBridge(void *ptr, void *data)
+{
+    Free((Context *) data, ptr);
+} // FreeBridge
+
 static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
 static void failf(Context *ctx, const char *fmt, ...)
 {
     const char *fname = NULL;
     unsigned int linenum = 0;
-    int error_position = 0;
 
     ctx->isfail = 1;
+    if (ctx->out_of_memory)
+        return;
 
+    int errpos = 0;
     switch (ctx->parse_phase)
     {
         case MOJOSHADER_PARSEPHASE_NOTSTARTED:
-            error_position = -2;
+            errpos = -2;
             break;
         case MOJOSHADER_PARSEPHASE_WORKING:
             fname = preprocessor_sourcepos(ctx->preprocessor, &linenum);
-            error_position = (int) linenum;
+            errpos = (int) linenum;
             break;
         case MOJOSHADER_PARSEPHASE_DONE:
-            error_position = -1;
+            errpos = -1;
             break;
         default:
             assert(0 && "Unexpected value");
             return;
     } // switch
 
-    ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList));
-    if (error == NULL)
-        return;
-
-    char scratch = 0;
     va_list ap;
     va_start(ap, fmt);
-    const int len = vsnprintf(&scratch, sizeof (scratch), fmt, ap);
+    errorlist_add_va(ctx->errors, fname, errpos, fmt, ap);
     va_end(ap);
-
-    char *failstr = (char *) Malloc(ctx, len + 1);
-    if (failstr == NULL)
-        Free(ctx, error);
-    else
-    {
-        va_start(ap, fmt);
-        vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
-        va_end(ap);
-
-        error->error.error = failstr;
-        error->error.filename = fname ? StrDup(ctx, fname) : NULL;
-        error->error.error_position = error_position;
-        error->next = NULL;
-
-        ErrorList *prev = NULL;
-        ErrorList *item = ctx->errors;
-        while (item != NULL)
-        {
-            prev = item;
-            item = item->next;
-        } // while
-
-        if (prev == NULL)
-            ctx->errors = error;
-        else
-            prev->next = error;
-
-        ctx->error_count++;
-    } // else
 } // failf
 
 static inline void fail(Context *ctx, const char *reason)
@@ -1468,12 +1443,22 @@
     ctx->free = f;
     ctx->malloc_data = d;
     ctx->parse_phase = MOJOSHADER_PARSEPHASE_NOTSTARTED;
+
+    ctx->errors = errorlist_create(MallocBridge, FreeBridge, ctx);
+    if (ctx->errors == NULL)
+    {
+        f(ctx, d);
+        return NULL;
+    } // if
+
     ctx->preprocessor = preprocessor_start(filename, source, sourcelen,
                                            include_open, include_close,
-                                           defines, define_count, 1, m, f, d);
+                                           defines, define_count, 1,
+                                           MallocBridge, FreeBridge, ctx);
 
     if (ctx->preprocessor == NULL)
     {
+        errorlist_destroy(ctx->errors);
         f(ctx, d);
         return NULL;
     } // if
@@ -1482,26 +1467,14 @@
 } // build_context
 
 
-static void free_error_list(MOJOSHADER_free f, void *d, ErrorList *item)
-{
-    while (item != NULL)
-    {
-        ErrorList *next = item->next;
-        f((void *) item->error.error, d);
-        f((void *) item->error.filename, d);
-        f(item, d);
-        item = next;
-    } // while
-} // free_error_list
-
-
 static void destroy_context(Context *ctx)
 {
     if (ctx != NULL)
     {
         MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : MOJOSHADER_internal_free);
         void *d = ctx->malloc_data;
-        free_error_list(f, d, ctx->errors);
+        if (ctx->errors != NULL)
+            errorlist_destroy(ctx->errors);
         if (ctx->preprocessor != NULL)
             preprocessor_end(ctx->preprocessor);
         if (ctx->output != NULL)
@@ -1515,31 +1488,6 @@
 } // destroy_context
 
 
-static MOJOSHADER_error *build_errors(Context *ctx)
-{
-    int total = 0;
-    MOJOSHADER_error *retval = (MOJOSHADER_error *)
-            Malloc(ctx, sizeof (MOJOSHADER_error) * ctx->error_count);
-    if (retval == NULL)
-        return NULL;
-
-    ErrorList *item = ctx->errors;
-    while (item != NULL)
-    {
-        ErrorList *next = item->next;
-        // reuse the string allocations
-        memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error));
-        Free(ctx, item);
-        item = next;
-        total++;
-    } // while
-    ctx->errors = NULL;
-
-    assert(total == ctx->error_count);
-    return retval;
-} // build_errors
-
-
 static const MOJOSHADER_parseData *build_failed_assembly(Context *ctx)
 {
     assert(isfail(ctx));
@@ -1557,9 +1505,9 @@
     retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
     retval->malloc_data = ctx->malloc_data;
 
-    retval->error_count = ctx->error_count;
-    retval->errors = build_errors(ctx);
-    if ((retval->errors == NULL) && (ctx->error_count > 0))
+    retval->error_count = ctx->errors->count;
+    retval->errors = errorlist_flatten(ctx->errors);
+    if (ctx->out_of_memory)
     {
         Free(ctx, retval);
         return &MOJOSHADER_out_of_mem_data;
--- a/mojoshader_common.c	Tue Nov 02 14:58:31 2010 -0400
+++ b/mojoshader_common.c	Wed Nov 03 22:54:17 2010 -0400
@@ -365,6 +365,9 @@
 
 void stringcache_destroy(StringCache *cache)
 {
+    if (cache == NULL)
+        return;
+
     MOJOSHADER_free f = cache->f;
     void *d = cache->d;
     size_t i;
@@ -386,5 +389,149 @@
     f(cache, d);
 } // stringcache_destroy
 
+
+
+ErrorList *errorlist_create(MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
+{
+    ErrorList *retval = (ErrorList *) m(sizeof (ErrorList), d);
+    if (retval != NULL)
+    {
+        memset(retval, '\0', sizeof (ErrorList));
+        retval->tail = &retval->head;
+        retval->m = m;
+        retval->f = f;
+        retval->d = d;
+    } // if
+
+    return retval;
+} // errorlist_create
+
+
+int errorlist_add(ErrorList *list, const char *fname,
+                  const int errpos, const char *str)
+{
+    return errorlist_add_fmt(list, fname, errpos, "%s", str);
+} // errorlist_add
+
+
+int errorlist_add_fmt(ErrorList *list, const char *fname,
+                      const int errpos, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    const int retval = errorlist_add_va(list, fname, errpos, fmt, ap);
+    va_end(ap);
+    return retval;
+} // errorlist_add_fmt
+
+
+int errorlist_add_va(ErrorList *list, const char *_fname,
+                     const int errpos, const char *fmt, va_list va)
+{
+    ErrorItem *error = (ErrorItem *) list->m(sizeof (ErrorItem), list->d);
+    if (error == NULL)
+        return 0;
+
+    char *fname = NULL;
+    if (_fname != NULL)
+    {
+        fname = (char *) list->m(strlen(_fname) + 1, list->d);
+        if (fname == NULL)
+        {
+            list->f(error, list->d);
+            return 0;
+        } // if
+        strcpy(fname, _fname);
+    } // if
+
+    char scratch[128];
+    va_list ap;
+    va_copy(ap, va);
+    const int len = vsnprintf(scratch, sizeof (scratch), fmt, ap);
+    va_end(ap);
+
+    char *failstr = (char *) list->m(len + 1, list->d);
+    if (failstr == NULL)
+    {
+        list->f(error, list->d);
+        list->f(fname, list->d);
+        return 0;
+    } // if
+
+    // If we overflowed our scratch buffer, that's okay. We were going to
+    //  allocate anyhow...the scratch buffer just lets us avoid a second
+    //  run of vsnprintf().
+    if (len < sizeof (scratch))
+        strcpy(failstr, scratch);  // copy it over.
+    else
+    {
+        va_copy(ap, va);
+        vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
+        va_end(ap);
+    } // else
+
+    error->error.error = failstr;
+    error->error.filename = fname;
+    error->error.error_position = errpos;
+    error->next = NULL;
+
+    list->tail->next = error;
+    list->tail = error;
+
+    list->count++;
+    return 1;
+} // errorlist_add_va
+
+
+MOJOSHADER_error *errorlist_flatten(ErrorList *list)
+{
+    if (list->count == 0)
+        return NULL;
+
+    int total = 0;
+    MOJOSHADER_error *retval = (MOJOSHADER_error *)
+            list->m(sizeof (MOJOSHADER_error) * list->count, list->d);
+    if (retval == NULL)
+        return NULL;
+
+    ErrorItem *item = list->head.next;
+    while (item != NULL)
+    {
+        ErrorItem *next = item->next;
+        // reuse the string allocations
+        memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error));
+        list->f(item, list->d);
+        item = next;
+        total++;
+    } // while
+
+    assert(total == list->count);
+    list->count = 0;
+    list->head.next = NULL;
+    list->tail = &list->head;
+    return retval;
+} // errorlist_flatten
+
+
+void errorlist_destroy(ErrorList *list)
+{
+    if (list == NULL)
+        return;
+
+    MOJOSHADER_free f = list->f;
+    void *d = list->d;
+    ErrorItem *item = list->head.next;
+    while (item != NULL)
+    {
+        ErrorItem *next = item->next;
+        f((void *) item->error.error, d);
+        f((void *) item->error.filename, d);
+        f(item, d);
+        item = next;
+    } // while
+    f(list, d);
+} // errorlist_destroy
+
+
 // end of mojoshader_common.c ...
 
--- a/mojoshader_compiler.c	Tue Nov 02 14:58:31 2010 -0400
+++ b/mojoshader_compiler.c	Wed Nov 03 22:54:17 2010 -0400
@@ -86,9 +86,7 @@
     MOJOSHADER_malloc malloc;
     MOJOSHADER_free free;
     void *malloc_data;
-    int error_count;
     ErrorList *errors;
-    int warning_count;
     ErrorList *warnings;
     StringCache *strcache;
     const char *sourcefile;  // current source file that we're parsing.
@@ -150,63 +148,30 @@
 
 static inline void Free(Context *ctx, void *ptr)
 {
-    if (ptr != NULL)  // check for NULL in case of dumb free() impl.
-        ctx->free(ptr, ctx->malloc_data);
+    ctx->free(ptr, ctx->malloc_data);
 } // Free
 
+static void *MallocBridge(int bytes, void *data)
+{
+    return Malloc((Context *) data, (size_t) bytes);
+} // MallocBridge
+
+static void FreeBridge(void *ptr, void *data)
+{
+    Free((Context *) data, ptr);
+} // FreeBridge
+
 static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
 static void failf(Context *ctx, const char *fmt, ...)
 {
-    const char *fname = ctx->sourcefile;
-    const unsigned int error_position = ctx->sourceline;
-
     ctx->isfail = 1;
-
-    const int MAX_ERROR_COUNT = 128;
-    if (ctx->error_count == (MAX_ERROR_COUNT-1))
-        fmt = "Too many errors, not reporting any more.";
-    else if (ctx->error_count >= MAX_ERROR_COUNT)
+    if (ctx->out_of_memory)
         return;
 
-    ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList));
-    if (error == NULL)
-        return;
-
-    char scratch = 0;
     va_list ap;
     va_start(ap, fmt);
-    const int len = vsnprintf(&scratch, sizeof (scratch), fmt, ap);
+    errorlist_add_va(ctx->errors, ctx->sourcefile, ctx->sourceline, fmt, ap);
     va_end(ap);
-
-    char *failstr = (char *) Malloc(ctx, len + 1);
-    if (failstr == NULL)
-        Free(ctx, error);
-    else
-    {
-        va_start(ap, fmt);
-        vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
-        va_end(ap);
-
-        error->error.error = failstr;
-        error->error.filename = fname ? StrDup(ctx, fname) : NULL;
-        error->error.error_position = error_position;
-        error->next = NULL;
-
-        ErrorList *prev = NULL;
-        ErrorList *item = ctx->errors;
-        while (item != NULL)
-        {
-            prev = item;
-            item = item->next;
-        } // while
-
-        if (prev == NULL)
-            ctx->errors = error;
-        else
-            prev->next = error;
-
-        ctx->error_count++;
-    } // else
 } // failf
 
 static inline void fail(Context *ctx, const char *reason)
@@ -214,6 +179,23 @@
     failf(ctx, "%s", reason);
 } // fail
 
+static void warnf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
+static void warnf(Context *ctx, const char *fmt, ...)
+{
+    if (ctx->out_of_memory)
+        return;
+
+    va_list ap;
+    va_start(ap, fmt);
+    errorlist_add_va(ctx->warnings, ctx->sourcefile, ctx->sourceline, fmt, ap);
+    va_end(ap);
+} // warnf
+
+static inline void warn(Context *ctx, const char *reason)
+{
+    warnf(ctx, "%s", reason);
+} // warn
+
 static inline int isfail(const Context *ctx)
 {
     return ctx->isfail;
@@ -227,15 +209,8 @@
     // !!! FIXME: should compare string pointer, with string in cache.
     map->scope = NULL;
     map->hash = hash_create(ctx, hash_hash_string, hash_keymatch_string,
-                            symbolmap_nuke, 1, ctx->malloc, ctx->free,
-                            ctx->malloc_data);
-    if (!map->hash)
-    {
-        out_of_memory(ctx);
-        return 0;
-    } // if
-
-    return 1;
+                            symbolmap_nuke, 1, MallocBridge, FreeBridge, ctx);
+    return (map->hash != NULL);
 } // create_symbolmap
 
 
@@ -2164,13 +2139,12 @@
         MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : MOJOSHADER_internal_free);
         void *d = ctx->malloc_data;
 
-        // !!! FIXME: free ctx->errors
         delete_compilation_unit(ctx, (MOJOSHADER_astCompilationUnit*)ctx->ast);
         destroy_symbolmap(ctx, &ctx->usertypes);
         destroy_symbolmap(ctx, &ctx->variables);
-
-        if (ctx->strcache)
-            stringcache_destroy(ctx->strcache);
+        stringcache_destroy(ctx->strcache);
+        errorlist_destroy(ctx->errors);
+        errorlist_destroy(ctx->warnings);
 
         // !!! FIXME: more to clean up here, now.
 
@@ -2194,7 +2168,9 @@
     //ctx->parse_phase = MOJOSHADER_PARSEPHASE_NOTSTARTED;
     create_symbolmap(ctx, &ctx->usertypes); // !!! FIXME: check for failure.
     create_symbolmap(ctx, &ctx->variables); // !!! FIXME: check for failure.
-    ctx->strcache = stringcache_create(m, f, d);  // !!! FIXME: check for failure.
+    ctx->strcache = stringcache_create(MallocBridge, FreeBridge, ctx);  // !!! FIXME: check for failure.
+    ctx->errors = errorlist_create(MallocBridge, FreeBridge, ctx);  // !!! FIXME: check for failure.
+    ctx->warnings = errorlist_create(MallocBridge, FreeBridge, ctx);  // !!! FIXME: check for failure.
 
     // fill in some common strings we'll want to use without further hashing.
     ctx->str_b = stringcache(ctx->strcache, "b");
@@ -2242,7 +2218,7 @@
 
     pp = preprocessor_start(filename, source, sourcelen, include_open,
                             include_close, defines, define_count, 0,
-                            ctx->malloc, ctx->free, ctx->malloc_data);
+                            MallocBridge, FreeBridge, ctx);
 
     // !!! FIXME: check if (pp == NULL)...
 
@@ -2282,11 +2258,8 @@
     do {
         token = preprocessor_nexttoken(pp, &tokenlen, &tokenval);
 
-        if (preprocessor_outofmemory(pp))
-        {
-            out_of_memory(ctx);
+        if (ctx->out_of_memory)
             break;
-        } // if
 
         fname = preprocessor_sourcepos(pp, &ctx->sourceline);
         ctx->sourcefile = fname ? stringcache(ctx->strcache, fname) : 0;
@@ -2368,33 +2341,6 @@
     1, &MOJOSHADER_out_of_mem_error, 0, 0, 0, 0, 0, 0
 };
 
-// !!! FIXME: cut and paste from assembler.
-// !!! FIXME: make ErrorList into something with a head/tail and count
-// !!! FIXME:  inherent.
-static MOJOSHADER_error *build_errors(Context *ctx, ErrorList **list, const int count)
-{
-    int total = 0;
-    MOJOSHADER_error *retval = (MOJOSHADER_error *)
-            Malloc(ctx, sizeof (MOJOSHADER_error) * count);
-    if (retval == NULL)
-        return NULL;
-
-    ErrorList *item = *list;
-    while (item != NULL)
-    {
-        ErrorList *next = item->next;
-        // reuse the string allocations
-        memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error));
-        Free(ctx, item);
-        item = next;
-        total++;
-    } // while
-    *list = NULL;
-
-    assert(total == count);
-    return retval;
-} // build_errors
-
 
 // !!! FIXME: cut and paste from assembler.
 static const MOJOSHADER_astData *build_failed_ast(Context *ctx)
@@ -2414,10 +2360,10 @@
     retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
     retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
     retval->malloc_data = ctx->malloc_data;
-
-    retval->error_count = ctx->error_count;
-    retval->errors = build_errors(ctx, &ctx->errors, ctx->error_count);
-    if ((retval->errors == NULL) && (ctx->error_count > 0))
+    retval->error_count = ctx->errors->count;
+    retval->errors = errorlist_flatten(ctx->errors);
+
+    if (ctx->out_of_memory)
     {
         Free(ctx, retval);
         return &MOJOSHADER_out_of_mem_ast_data;
@@ -2439,28 +2385,25 @@
         return &MOJOSHADER_out_of_mem_ast_data;
 
     memset(retval, '\0', sizeof (MOJOSHADER_astData));
+    retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
+    retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
+    retval->malloc_data = ctx->malloc_data;
 
     if (!isfail(ctx))
     {
         retval->source_profile = ctx->source_profile;
         retval->ast = ctx->ast;
-        ctx->ast = NULL;  // don't free this with the context, now.
     } // if
 
-    retval->error_count = ctx->error_count;
-    retval->errors = build_errors(ctx, &ctx->errors, ctx->error_count);
-    if (retval->errors == NULL)
+    retval->error_count = ctx->errors->count;
+    retval->errors = errorlist_flatten(ctx->errors);
+    if (ctx->out_of_memory)
     {
         Free(ctx, retval);
         return &MOJOSHADER_out_of_mem_ast_data;
     } // if
 
-    retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
-    retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
-    retval->malloc_data = ctx->malloc_data;
-
-    retval->strcache = ctx->strcache;
-    ctx->strcache = NULL;
+    retval->opaque = ctx;
 
     return retval;
 } // build_astdata
@@ -2501,9 +2444,6 @@
 {
     assert(isfail(ctx));
 
-    if (ctx->out_of_memory)
-        return &MOJOSHADER_out_of_mem_compile_data;
-        
     MOJOSHADER_compileData *retval = NULL;
     retval = (MOJOSHADER_compileData *) Malloc(ctx, sizeof (MOJOSHADER_compileData));
     if (retval == NULL)
@@ -2513,20 +2453,13 @@
     retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
     retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
     retval->malloc_data = ctx->malloc_data;
-
     retval->source_profile = ctx->source_profile;
-
-    retval->error_count = ctx->error_count;
-    retval->errors = build_errors(ctx, &ctx->errors, ctx->error_count);
-    if ((retval->errors == NULL) && (retval->error_count > 0))
-    {
-        MOJOSHADER_freeCompileData(retval);
-        return &MOJOSHADER_out_of_mem_compile_data;
-    } // if
-
-    retval->warning_count = ctx->warning_count;
-    retval->warnings = build_errors(ctx, &ctx->warnings, ctx->warning_count);
-    if ((retval->warnings == NULL) && (retval->warning_count > 0))
+    retval->error_count = ctx->errors->count;
+    retval->errors = errorlist_flatten(ctx->errors);
+    retval->warning_count = ctx->warnings->count;
+    retval->warnings = errorlist_flatten(ctx->warnings);
+
+    if (ctx->out_of_memory)  // in case something failed up there.
     {
         MOJOSHADER_freeCompileData(retval);
         return &MOJOSHADER_out_of_mem_compile_data;
@@ -2538,11 +2471,10 @@
 
 static const MOJOSHADER_compileData *build_compiledata(Context *ctx)
 {
+    assert(!isfail(ctx));
+
     MOJOSHADER_compileData *retval = NULL;
 
-    if (ctx->out_of_memory)
-        return &MOJOSHADER_out_of_mem_compile_data;
-
     retval = (MOJOSHADER_compileData *) Malloc(ctx, sizeof (MOJOSHADER_compileData));
     if (retval == NULL)
         return &MOJOSHADER_out_of_mem_compile_data;
@@ -2551,7 +2483,6 @@
     retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
     retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
     retval->malloc_data = ctx->malloc_data;
-
     retval->source_profile = ctx->source_profile;
 
     if (!isfail(ctx))
@@ -2564,26 +2495,15 @@
         // !!! FIXME: build symbols and symbol_count here.
     } // if
 
-    if (!isfail(ctx))
+    retval->error_count = ctx->errors->count;
+    retval->errors = errorlist_flatten(ctx->errors);
+    retval->warning_count = ctx->warnings->count;
+    retval->warnings = errorlist_flatten(ctx->warnings);
+
+    if (ctx->out_of_memory)  // in case something failed up there.
     {
-        retval->error_count = ctx->error_count;
-        retval->errors = build_errors(ctx, &ctx->errors, ctx->error_count);
-        if ((retval->errors == NULL) && (retval->error_count > 0))
-        {
-            MOJOSHADER_freeCompileData(retval);
-            return &MOJOSHADER_out_of_mem_compile_data;
-        } // if
-    } // if
-
-    if (!isfail(ctx))
-    {
-        retval->warning_count = ctx->warning_count;
-        retval->warnings = build_errors(ctx, &ctx->warnings, ctx->warning_count);
-        if ((retval->warnings == NULL) && (retval->warning_count > 0))
-        {
-            MOJOSHADER_freeCompileData(retval);
-            return &MOJOSHADER_out_of_mem_compile_data;
-        } // if
+        MOJOSHADER_freeCompileData(retval);
+        return &MOJOSHADER_out_of_mem_compile_data;
     } // if
 
     return retval;
@@ -2621,12 +2541,14 @@
                      include_open, include_close);
     } // if
 
-    if (isfail(ctx))
-        retval = (MOJOSHADER_astData *) build_failed_ast(ctx);
+    if (!isfail(ctx))
+        retval = build_astdata(ctx);  // ctx isn't destroyed yet!
     else
-        retval = build_astdata(ctx);
-
-    destroy_context(ctx);
+    {
+        retval = (MOJOSHADER_astData *) build_failed_ast(ctx);
+        destroy_context(ctx);
+    } // else
+
     return retval;
 } // MOJOSHADER_parseAst
 
@@ -2637,41 +2559,25 @@
     if ((data == NULL) || (data == &MOJOSHADER_out_of_mem_ast_data))
         return;  // no-op.
 
+    // !!! FIXME: this needs to live for deleting the stringcache and the ast.
+    Context *ctx = (Context *) data->opaque;
     MOJOSHADER_free f = (data->free == NULL) ? MOJOSHADER_internal_free : data->free;
     void *d = data->malloc_data;
     int i;
 
     // we don't f(data->source_profile), because that's internal static data.
 
-    // check for NULL in case of dumb free() impl.
-    if (data->errors != NULL)
-    {
-        for (i = 0; i < data->error_count; i++)
-        {
-            if (data->errors[i].error != NULL)
-                f((void *) data->errors[i].error, d);
-            if (data->errors[i].filename != NULL)
-                f((void *) data->errors[i].filename, d);
-        } // for
-        f((void *) data->errors, d);
-    } // if
-
-    if (data->ast != NULL)
+    for (i = 0; i < data->error_count; i++)
     {
-        // !!! FIXME: make this not require a Context.
-        Context ctx;
-        memset(&ctx, '\0', sizeof (Context));
-        ctx.malloc = data->malloc;
-        ctx.free = f;
-        ctx.malloc_data = d;
-        delete_compilation_unit(&ctx,
-                    (MOJOSHADER_astCompilationUnit *) &data->ast->compunit);
-    } // if
-
-    if (data->strcache != NULL)
-        stringcache_destroy((StringCache *) data->strcache);
-
+        f((void *) data->errors[i].error, d);
+        f((void *) data->errors[i].filename, d);
+    } // for
+    f((void *) data->errors, d);
+
+    // don't delete data->ast (it'll delete with the context).
     f(data, d);
+
+    destroy_context(ctx);  // finally safe to destroy this.
 } // MOJOSHADER_freeAstData
 
 
@@ -2729,46 +2635,28 @@
 
     // we don't f(data->source_profile), because that's internal static data.
 
-    // check for NULL in case of dumb free() impl.
-    if (data->errors != NULL)
+    for (i = 0; i < data->error_count; i++)
     {
-        for (i = 0; i < data->error_count; i++)
-        {
-            if (data->errors[i].error != NULL)
-                f((void *) data->errors[i].error, d);
-            if (data->errors[i].filename != NULL)
-                f((void *) data->errors[i].filename, d);
-        } // for
-        f((void *) data->errors, d);
-    } // if
-
-    if (data->warnings != NULL)
+        f((void *) data->errors[i].error, d);
+        f((void *) data->errors[i].filename, d);
+    } // for
+    f((void *) data->errors, d);
+
+    for (i = 0; i < data->warning_count; i++)
     {
-        for (i = 0; i < data->warning_count; i++)
-        {
-            if (data->warnings[i].error != NULL)
-                f((void *) data->warnings[i].error, d);
-            if (data->warnings[i].filename != NULL)
-                f((void *) data->warnings[i].filename, d);
-        } // for
-        f((void *) data->warnings, d);
-    } // if
-
-    if (data->symbols != NULL)
+        f((void *) data->warnings[i].error, d);
+        f((void *) data->warnings[i].filename, d);
+    } // for
+    f((void *) data->warnings, d);
+
+    for (i = 0; i < data->symbol_count; i++)
     {
-        for (i = 0; i < data->symbol_count; i++)
-        {
-            if (data->symbols[i].name != NULL)
-                f((void *) data->symbols[i].name, d);
-            if (data->symbols[i].default_value != NULL)
-                f((void *) data->symbols[i].default_value, d);
-        } // for
-        f((void *) data->symbols, d);
-    } // if
-
-    if (data->output != NULL)
-        f((void *) data->output, d);
-
+        f((void *) data->symbols[i].name, d);
+        f((void *) data->symbols[i].default_value, d);
+    } // for
+    f((void *) data->symbols, d);
+
+    f((void *) data->output, d);
     f(data, d);
 } // MOJOSHADER_freeCompileData
 
--- a/mojoshader_internal.h	Tue Nov 02 14:58:31 2010 -0400
+++ b/mojoshader_internal.h	Wed Nov 03 22:54:17 2010 -0400
@@ -83,6 +83,7 @@
 
 #ifdef _MSC_VER
 #include <malloc.h>
+#define va_copy(a, b) a = b
 #define snprintf _snprintf
 #define strcasecmp stricmp
 typedef unsigned __int8 uint8;
@@ -193,6 +194,35 @@
 void stringcache_destroy(StringCache *cache);
 
 
+// We chain errors as a linked list with a head/tail for easy appending.
+//  These get flattened before passing to the application.
+typedef struct ErrorItem
+{
+    MOJOSHADER_error error;
+    struct ErrorItem *next;
+} ErrorItem;
+
+typedef struct ErrorList
+{
+    ErrorItem head;
+    ErrorItem *tail;
+    int count;
+    MOJOSHADER_malloc m;
+    MOJOSHADER_free f;
+    void *d;
+} ErrorList;
+
+ErrorList *errorlist_create(MOJOSHADER_malloc m, MOJOSHADER_free f, void *d);
+int errorlist_add(ErrorList *list, const char *fname,
+                      const int errpos, const char *str);
+int errorlist_add_fmt(ErrorList *list, const char *fname,
+                      const int errpos, const char *fmt, ...) ISPRINTF(4,5);
+int errorlist_add_va(ErrorList *list, const char *_fname,
+                     const int errpos, const char *fmt, va_list va);
+MOJOSHADER_error *errorlist_flatten(ErrorList *list); // resets the list!
+void errorlist_destroy(ErrorList *list);
+
+
 // This is the ID for a D3DXSHADER_CONSTANTTABLE in the bytecode comments.
 #define CTAB_ID 0x42415443  // 0x42415443 == 'CTAB'
 #define CTAB_SIZE 28  // sizeof (D3DXSHADER_CONSTANTTABLE).
@@ -367,13 +397,6 @@
 extern MOJOSHADER_error MOJOSHADER_out_of_mem_error;
 extern MOJOSHADER_parseData MOJOSHADER_out_of_mem_data;
 
-// !!! FIXME: unify all the routines in various modules that deal with these.
-typedef struct ErrorList
-{
-    MOJOSHADER_error error;
-    struct ErrorList *next;
-} ErrorList;
-
 
 // preprocessor stuff.
 
--- a/mojoshader_preprocessor.c	Tue Nov 02 14:58:31 2010 -0400
+++ b/mojoshader_preprocessor.c	Wed Nov 03 22:54:17 2010 -0400
@@ -78,8 +78,7 @@
 
 static inline void Free(Context *ctx, void *ptr)
 {
-    if (ptr != NULL)  // check for NULL in case of dumb free() impl.
-        ctx->free(ptr, ctx->malloc_data);
+    ctx->free(ptr, ctx->malloc_data);
 } // Free
 
 static inline char *StrDup(Context *ctx, const char *str)
@@ -2253,47 +2252,6 @@
 } // preprocessor_sourcepos
 
 
-// !!! FIXME: cut and paste.
-static void free_error_list(ErrorList *item, MOJOSHADER_free f, void *d)
-{
-    while (item != NULL)
-    {
-        ErrorList *next = item->next;
-        f((void *) item->error.error, d);
-        f((void *) item->error.filename, d);
-        f(item, d);
-        item = next;
-    } // while
-} // free_error_list
-
-
-// !!! FIXME: cut and paste.
-static MOJOSHADER_error *build_errors(ErrorList **errors, const int count,
-                         MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
-{
-    int total = 0;
-    MOJOSHADER_error *retval = (MOJOSHADER_error *)
-                                m(sizeof (MOJOSHADER_error) * count, d);
-    if (retval == NULL)
-        return NULL;
-
-    ErrorList *item = *errors;
-    while (item != NULL)
-    {
-        ErrorList *next = item->next;
-        // reuse the string allocations
-        memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error));
-        f(item, d);
-        item = next;
-        total++;
-    } // while
-    *errors = NULL;
-
-    assert(total == count);
-    return retval;
-} // build_errors
-
-
 static int indent_buffer(Buffer *buffer, int n, int newline,
                          MOJOSHADER_malloc m, void *d)
 {
@@ -2336,23 +2294,24 @@
     static const char endline[] = { '\n' };
     #endif
 
-    ErrorList *errors = NULL;
-    int error_count = 0;
-
     if (!m) m = MOJOSHADER_internal_malloc;
     if (!f) f = MOJOSHADER_internal_free;
-
-#if !MOJOSHADER_FORCE_INCLUDE_CALLBACKS
     if (!include_open) include_open = MOJOSHADER_internal_include_open;
     if (!include_close) include_close = MOJOSHADER_internal_include_close;
-#endif
+
+    ErrorList *errors = errorlist_create(m, f, d);
+    if (errors == NULL)
+        return &out_of_mem_data_preprocessor;
 
     Preprocessor *pp = preprocessor_start(filename, source, sourcelen,
                                           include_open, include_close,
                                           defines, define_count, 0, m, f, d);
 
     if (pp == NULL)
+    {
+        errorlist_destroy(errors);
         return &out_of_mem_data_preprocessor;
+    } // if
 
     Token token = TOKEN_UNKNOWN;
     const char *tokstr = NULL;
@@ -2421,51 +2380,10 @@
         {
             if (!out_of_memory)
             {
-                ErrorList *error = (ErrorList *) m(sizeof (ErrorList), d);
                 unsigned int pos = 0;
-                char *fname = NULL;
-                const char *str = preprocessor_sourcepos(pp, &pos);
-                if (str != NULL)
-                {
-                    fname = (char *) m(strlen(str) + 1, d);
-                    if (fname != NULL)
-                        strcpy(fname, str);
-                } // if
-
-                // !!! FIXME: cut and paste with other error handlers.
-                char *errstr = (char *) m(len + 1, d);
-                if (errstr != NULL)
-                    strcpy(errstr, tokstr);
-
-                out_of_memory = ((!error) || ((!fname) && (str)) || (!errstr));
-                if (out_of_memory)
-                {
-                    if (errstr) f(errstr, d);
-                    if (fname) f(fname, d);
-                    if (error) f(error, d);
-                } // if
-                else
-                {
-                    error->error.error = errstr;
-                    error->error.filename = fname;
-                    error->error.error_position = pos;
-                    error->next = NULL;
-
-                    ErrorList *prev = NULL;
-                    ErrorList *item = errors;
-                    while (item != NULL)
-                    {
-                        prev = item;
-                        item = item->next;
-                    } // while
-
-                    if (prev == NULL)
-                        errors = error;
-                    else
-                        prev->next = error;
-
-                    error_count++;
-                } // else
+                const char *fname = preprocessor_sourcepos(pp, &pos);
+                if (!errorlist_add(errors, fname, (int) pos, tokstr))
+                    out_of_memory = 1;
             } // if
         } // else if
 
@@ -2485,7 +2403,7 @@
         {
             preprocessor_end(pp);
             free_buffer(&buffer, f, d);
-            free_error_list(errors, f, d);
+            errorlist_destroy(errors);
             return &out_of_mem_data_preprocessor;
         } // if
     } // while
@@ -2499,7 +2417,7 @@
     free_buffer(&buffer, f, d);
     if (output == NULL)
     {
-        free_error_list(errors, f, d);
+        errorlist_destroy(errors);
         return &out_of_mem_data_preprocessor;
     } // if
 
@@ -2507,21 +2425,27 @@
                                     m(sizeof (MOJOSHADER_preprocessData), d);
     if (retval == NULL)
     {
-        free_error_list(errors, f, d);
+        errorlist_destroy(errors);
         f(output, d);
         return &out_of_mem_data_preprocessor;
     } // if
 
-    retval->errors = build_errors(&errors, error_count, m, f, d);
-    if (retval->errors == NULL)
+    memset(retval, '\0', sizeof (*retval));
+    if (errors->count > 0)
     {
-        free_error_list(errors, f, d);
-        f(retval, d);
-        f(output, d);
-        return &out_of_mem_data_preprocessor;
+        retval->error_count = errors->count;
+        retval->errors = errorlist_flatten(errors);
+        if (retval->errors == NULL)
+        {
+            errorlist_destroy(errors);
+            f(retval, d);
+            f(output, d);
+            return &out_of_mem_data_preprocessor;
+        } // if
     } // if
 
-    retval->error_count = error_count;
+    errorlist_destroy(errors);
+
     retval->output = output;
     retval->output_len = total_bytes;
     retval->malloc = m;
@@ -2541,20 +2465,14 @@
     void *d = data->malloc_data;
     int i;
 
-    if (data->output != NULL)
-        f((void *) data->output, d);
+    f((void *) data->output, d);
 
-    if (data->errors != NULL)
+    for (i = 0; i < data->error_count; i++)
     {
-        for (i = 0; i < data->error_count; i++)
-        {
-            if (data->errors[i].error != NULL)
-                f((void *) data->errors[i].error, d);
-            if (data->errors[i].filename != NULL)
-                f((void *) data->errors[i].filename, d);
-        } // for
-        f(data->errors, d);
-    } // if
+        f((void *) data->errors[i].error, d);
+        f((void *) data->errors[i].filename, d);
+    } // for
+    f(data->errors, d);
 
     f(data, d);
 } // MOJOSHADER_freePreprocessData