libclamav/yara_compiler.c
c329e3c7
 /*
 Copyright (c) 2013. The YARA Authors. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
    http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
 
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
 
b289385d
 #ifdef _WIN32
 #include <stdint.h>
 #endif
 
c329e3c7
 #ifdef REAL_YARA
 #include <yara/utils.h>
 #include <yara/compiler.h>
 #include <yara/exec.h>
 #include <yara/error.h>
 #include <yara/mem.h>
 #include <yara/object.h>
 #include <yara/lexer.h>
 #else
 #include "yara_clam.h"
 #include "yara_compiler.h"
 #include "yara_exec.h"
 #include "yara_grammar.h"
 #include "yara_lexer.h"
 #endif
 
 #ifdef REAL_YARA
 int yr_compiler_create(
     YR_COMPILER** compiler)
 {
288057e9
     int result;
     YR_COMPILER* new_compiler;
 
     new_compiler = (YR_COMPILER*)yr_malloc(sizeof(YR_COMPILER));
 
     if (new_compiler == NULL)
         return ERROR_INSUFICIENT_MEMORY;
 
     new_compiler->errors                 = 0;
     new_compiler->callback               = NULL;
     new_compiler->last_error             = ERROR_SUCCESS;
     new_compiler->last_error_line        = 0;
     new_compiler->error_line             = 0;
     new_compiler->last_result            = ERROR_SUCCESS;
     new_compiler->file_stack_ptr         = 0;
     new_compiler->file_name_stack_ptr    = 0;
     new_compiler->current_rule_flags     = 0;
     new_compiler->allow_includes         = 1;
     new_compiler->loop_depth             = 0;
     new_compiler->loop_for_of_mem_offset = -1;
     new_compiler->compiled_rules_arena   = NULL;
     new_compiler->namespaces_count       = 0;
     new_compiler->current_rule_strings   = NULL;
 
     result = yr_hash_table_create(10007, &new_compiler->rules_table);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_hash_table_create(10007, &new_compiler->objects_table);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->sz_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->rules_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->strings_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->code_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->re_code_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->automaton_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->externals_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->namespaces_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_create(65536, 0, &new_compiler->metas_arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_ac_create_automaton(
             new_compiler->automaton_arena,
             &new_compiler->automaton);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS) {
         *compiler = new_compiler;
     } else // if error, do cleanup
     {
         yr_compiler_destroy(new_compiler);
     }
c329e3c7
 
288057e9
     return result;
c329e3c7
 }
 
 void yr_compiler_destroy(
     YR_COMPILER* compiler)
 {
288057e9
     int i;
c329e3c7
 
288057e9
     if (compiler->compiled_rules_arena != NULL)
         yr_arena_destroy(compiler->compiled_rules_arena);
c329e3c7
 
288057e9
     if (compiler->sz_arena != NULL)
         yr_arena_destroy(compiler->sz_arena);
c329e3c7
 
288057e9
     if (compiler->rules_arena != NULL)
         yr_arena_destroy(compiler->rules_arena);
c329e3c7
 
288057e9
     if (compiler->strings_arena != NULL)
         yr_arena_destroy(compiler->strings_arena);
c329e3c7
 
288057e9
     if (compiler->code_arena != NULL)
         yr_arena_destroy(compiler->code_arena);
c329e3c7
 
288057e9
     if (compiler->re_code_arena != NULL)
         yr_arena_destroy(compiler->re_code_arena);
c329e3c7
 
288057e9
     if (compiler->automaton_arena != NULL)
         yr_arena_destroy(compiler->automaton_arena);
c329e3c7
 
288057e9
     if (compiler->externals_arena != NULL)
         yr_arena_destroy(compiler->externals_arena);
c329e3c7
 
288057e9
     if (compiler->namespaces_arena != NULL)
         yr_arena_destroy(compiler->namespaces_arena);
c329e3c7
 
288057e9
     if (compiler->metas_arena != NULL)
         yr_arena_destroy(compiler->metas_arena);
c329e3c7
 
288057e9
     yr_hash_table_destroy(
         compiler->rules_table,
         NULL);
c329e3c7
 
288057e9
     yr_hash_table_destroy(
         compiler->objects_table,
         (YR_HASH_TABLE_FREE_VALUE_FUNC)yr_object_destroy);
c329e3c7
 
288057e9
     for (i = 0; i < compiler->file_name_stack_ptr; i++)
         yr_free(compiler->file_name_stack[i]);
c329e3c7
 
288057e9
     yr_free(compiler);
c329e3c7
 }
 
 void yr_compiler_set_callback(
     YR_COMPILER* compiler,
     YR_COMPILER_CALLBACK_FUNC callback)
 {
288057e9
     compiler->callback = callback;
c329e3c7
 }
 #endif
 
 int _yr_compiler_push_file(
     YR_COMPILER* compiler,
     FILE* fh)
 {
288057e9
     if (compiler->file_stack_ptr < MAX_INCLUDE_DEPTH) {
         compiler->file_stack[compiler->file_stack_ptr] = fh;
         compiler->file_stack_ptr++;
         return ERROR_SUCCESS;
     } else {
         compiler->last_result = ERROR_INCLUDE_DEPTH_EXCEEDED;
         return ERROR_INCLUDE_DEPTH_EXCEEDED;
     }
c329e3c7
 }
 
 FILE* _yr_compiler_pop_file(
     YR_COMPILER* compiler)
 {
288057e9
     FILE* result = NULL;
c329e3c7
 
288057e9
     if (compiler->file_stack_ptr > 0) {
         compiler->file_stack_ptr--;
         result = compiler->file_stack[compiler->file_stack_ptr];
     }
c329e3c7
 
288057e9
     return result;
c329e3c7
 }
 
 int _yr_compiler_push_file_name(
     YR_COMPILER* compiler,
     const char* file_name)
 {
288057e9
     char* str;
     int i;
 
     for (i = 0; i < compiler->file_name_stack_ptr; i++) {
         if (strcmp(file_name, compiler->file_name_stack[i]) == 0) {
             compiler->last_result = ERROR_INCLUDES_CIRCULAR_REFERENCE;
             return ERROR_INCLUDES_CIRCULAR_REFERENCE;
         }
c329e3c7
     }
 
288057e9
     if (compiler->file_name_stack_ptr < MAX_INCLUDE_DEPTH) {
         str = yr_strdup(file_name);
c329e3c7
 
288057e9
         if (str == NULL)
             return ERROR_INSUFICIENT_MEMORY;
c329e3c7
 
288057e9
         compiler->file_name_stack[compiler->file_name_stack_ptr] = str;
         compiler->file_name_stack_ptr++;
c329e3c7
 
288057e9
         return ERROR_SUCCESS;
     } else {
         compiler->last_result = ERROR_INCLUDE_DEPTH_EXCEEDED;
         return ERROR_INCLUDE_DEPTH_EXCEEDED;
     }
c329e3c7
 }
 
 void _yr_compiler_pop_file_name(
     YR_COMPILER* compiler)
 {
288057e9
     if (compiler->file_name_stack_ptr > 0) {
         compiler->file_name_stack_ptr--;
         yr_free(compiler->file_name_stack[compiler->file_name_stack_ptr]);
         compiler->file_name_stack[compiler->file_name_stack_ptr] = NULL;
     }
c329e3c7
 }
 
 char* yr_compiler_get_current_file_name(
     YR_COMPILER* context)
 {
288057e9
     if (context->file_name_stack_ptr > 0) {
         return context->file_name_stack[context->file_name_stack_ptr - 1];
     } else {
         return NULL;
     }
c329e3c7
 }
 
 #ifdef REAL_YARA
 int _yr_compiler_set_namespace(
     YR_COMPILER* compiler,
     const char* namespace_)
 {
288057e9
     YR_NAMESPACE* ns;
 
     char* ns_name;
     int result;
     int i;
     int found;
 
     ns    = yr_arena_base_address(compiler->namespaces_arena);
     found = FALSE;
 
     for (i = 0; i < compiler->namespaces_count; i++) {
         if (strcmp(ns->name, namespace_) == 0) {
             found = TRUE;
             break;
         }
 
         ns = yr_arena_next_address(
             compiler->namespaces_arena,
             ns,
             sizeof(YR_NAMESPACE));
c329e3c7
     }
 
288057e9
     if (!found) {
         result = yr_arena_write_string(
             compiler->sz_arena,
             namespace_,
             &ns_name);
c329e3c7
 
288057e9
         if (result == ERROR_SUCCESS)
             result = yr_arena_allocate_struct(
                 compiler->namespaces_arena,
                 sizeof(YR_NAMESPACE),
                 (void*)&ns,
                 offsetof(YR_NAMESPACE, name),
                 EOL);
c329e3c7
 
288057e9
         if (result != ERROR_SUCCESS)
             return result;
c329e3c7
 
288057e9
         ns->name = ns_name;
c329e3c7
 
288057e9
         for (i = 0; i < MAX_THREADS; i++)
             ns->t_flags[i] = 0;
c329e3c7
 
288057e9
         compiler->namespaces_count++;
     }
c329e3c7
 
288057e9
     compiler->current_namespace = ns;
     return ERROR_SUCCESS;
c329e3c7
 }
 
 int yr_compiler_add_file(
     YR_COMPILER* compiler,
     FILE* rules_file,
     const char* namespace_,
     const char* file_name)
 {
288057e9
     // Don't allow yr_compiler_add_file() after
     // yr_compiler_get_rules() has been called.
c329e3c7
 
288057e9
     assert(compiler->compiled_rules_arena == NULL);
c329e3c7
 
288057e9
     if (file_name != NULL)
         _yr_compiler_push_file_name(compiler, file_name);
c329e3c7
 
288057e9
     if (namespace_ != NULL)
         _yr_compiler_set_namespace(compiler, namespace_);
     else
         _yr_compiler_set_namespace(compiler, "default");
c329e3c7
 
288057e9
     return yr_lex_parse_rules_file(rules_file, compiler);
c329e3c7
 }
 
 int yr_compiler_add_string(
     YR_COMPILER* compiler,
     const char* rules_string,
     const char* namespace_)
 {
288057e9
     // Don't allow yr_compiler_add_string() after
     // yr_compiler_get_rules() has been called.
c329e3c7
 
288057e9
     assert(compiler->compiled_rules_arena == NULL);
c329e3c7
 
288057e9
     if (namespace_ != NULL)
         _yr_compiler_set_namespace(compiler, namespace_);
     else
         _yr_compiler_set_namespace(compiler, "default");
c329e3c7
 
288057e9
     return yr_lex_parse_rules_string(rules_string, compiler);
c329e3c7
 }
 
 int _yr_compiler_compile_rules(
288057e9
     YR_COMPILER* compiler)
c329e3c7
 {
288057e9
     YARA_RULES_FILE_HEADER* rules_file_header = NULL;
     YR_ARENA* arena;
     YR_RULE null_rule;
     YR_EXTERNAL_VARIABLE null_external;
 
     int8_t halt = OP_HALT;
     int result;
 
     // Write halt instruction at the end of code.
     yr_arena_write_data(
         compiler->code_arena,
         &halt,
         sizeof(int8_t),
         NULL);
 
     // Write a null rule indicating the end.
     memset(&null_rule, 0xFA, sizeof(YR_RULE));
     null_rule.g_flags = RULE_GFLAGS_NULL;
 
     yr_arena_write_data(
         compiler->rules_arena,
         &null_rule,
         sizeof(YR_RULE),
         NULL);
 
     // Write a null external the end.
     memset(&null_external, 0xFA, sizeof(YR_EXTERNAL_VARIABLE));
     null_external.type = EXTERNAL_VARIABLE_TYPE_NULL;
 
     yr_arena_write_data(
         compiler->externals_arena,
         &null_external,
         sizeof(YR_EXTERNAL_VARIABLE),
         NULL);
 
     // Create Aho-Corasick automaton's failure links.
     yr_ac_create_failure_links(
         compiler->automaton_arena,
         compiler->automaton);
 
     result = yr_arena_create(1024, 0, &arena);
c329e3c7
 
288057e9
     if (result == ERROR_SUCCESS)
         result = yr_arena_allocate_struct(
             arena,
             sizeof(YARA_RULES_FILE_HEADER),
             (void**)&rules_file_header,
             offsetof(YARA_RULES_FILE_HEADER, rules_list_head),
             offsetof(YARA_RULES_FILE_HEADER, externals_list_head),
             offsetof(YARA_RULES_FILE_HEADER, code_start),
             offsetof(YARA_RULES_FILE_HEADER, automaton),
             EOL);
 
     if (result == ERROR_SUCCESS) {
         rules_file_header->rules_list_head = yr_arena_base_address(
             compiler->rules_arena);
 
         rules_file_header->externals_list_head = yr_arena_base_address(
             compiler->externals_arena);
 
         rules_file_header->code_start = yr_arena_base_address(
             compiler->code_arena);
 
         rules_file_header->automaton = yr_arena_base_address(
             compiler->automaton_arena);
     }
 
     if (result == ERROR_SUCCESS)
         result = yr_arena_append(
             arena,
             compiler->automaton_arena);
 
     if (result == ERROR_SUCCESS) {
         compiler->automaton_arena = NULL;
         result                    = yr_arena_append(
             arena,
             compiler->code_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->code_arena = NULL;
         result               = yr_arena_append(
             arena,
             compiler->re_code_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->re_code_arena = NULL;
         result                  = yr_arena_append(
             arena,
             compiler->rules_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->rules_arena = NULL;
         result                = yr_arena_append(
             arena,
             compiler->strings_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->strings_arena = NULL;
         result                  = yr_arena_append(
             arena,
             compiler->externals_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->externals_arena = NULL;
         result                    = yr_arena_append(
             arena,
             compiler->namespaces_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->namespaces_arena = NULL;
         result                     = yr_arena_append(
             arena,
             compiler->metas_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->metas_arena = NULL;
         result                = yr_arena_append(
             arena,
             compiler->sz_arena);
     }
 
     if (result == ERROR_SUCCESS) {
         compiler->sz_arena             = NULL;
         compiler->compiled_rules_arena = arena;
         result                         = yr_arena_coalesce(arena);
     }
 
     return result;
 }
c329e3c7
 
 int yr_compiler_get_rules(
     YR_COMPILER* compiler,
     YR_RULES** rules)
 {
288057e9
     YR_RULES* yara_rules;
     YARA_RULES_FILE_HEADER* rules_file_header;
c329e3c7
 
288057e9
     *rules = NULL;
c329e3c7
 
288057e9
     if (compiler->compiled_rules_arena == NULL)
         FAIL_ON_ERROR(_yr_compiler_compile_rules(compiler));
c329e3c7
 
288057e9
     yara_rules = yr_malloc(sizeof(YR_RULES));
c329e3c7
 
288057e9
     if (yara_rules == NULL)
         return ERROR_INSUFICIENT_MEMORY;
c329e3c7
 
288057e9
     FAIL_ON_ERROR_WITH_CLEANUP(
         yr_arena_duplicate(compiler->compiled_rules_arena, &yara_rules->arena),
         yr_free(yara_rules));
c329e3c7
 
288057e9
     rules_file_header = (YARA_RULES_FILE_HEADER*)yr_arena_base_address(
         yara_rules->arena);
c329e3c7
 
288057e9
     yara_rules->externals_list_head = rules_file_header->externals_list_head;
     yara_rules->rules_list_head     = rules_file_header->rules_list_head;
     yara_rules->automaton           = rules_file_header->automaton;
     yara_rules->code_start          = rules_file_header->code_start;
     yara_rules->tidx_mask           = 0;
c329e3c7
 
288057e9
 #if _WIN32
     yara_rules->mutex = CreateMutex(NULL, FALSE, NULL);
 #else
     pthread_mutex_init(&yara_rules->mutex, NULL);
 #endif
c329e3c7
 
288057e9
     *rules = yara_rules;
c329e3c7
 
288057e9
     return ERROR_SUCCESS;
c329e3c7
 }
 
 int yr_compiler_define_integer_variable(
     YR_COMPILER* compiler,
     const char* identifier,
     int64_t value)
 {
288057e9
     YR_EXTERNAL_VARIABLE* external;
     YR_OBJECT* object;
c329e3c7
 
288057e9
     char* id;
 
     compiler->last_result = ERROR_SUCCESS;
 
     FAIL_ON_COMPILER_ERROR(yr_arena_write_string(
         compiler->sz_arena,
         identifier,
         &id));
 
     FAIL_ON_COMPILER_ERROR(yr_arena_allocate_struct(
         compiler->externals_arena,
         sizeof(YR_EXTERNAL_VARIABLE),
         (void**)&external,
         offsetof(YR_EXTERNAL_VARIABLE, identifier),
         offsetof(YR_EXTERNAL_VARIABLE, string),
         EOL));
 
     external->type       = EXTERNAL_VARIABLE_TYPE_INTEGER;
     external->identifier = id;
     external->integer    = value;
     external->string     = NULL;
 
     FAIL_ON_COMPILER_ERROR(yr_object_from_external_variable(
         external,
         &object));
 
     FAIL_ON_COMPILER_ERROR(yr_hash_table_add(
         compiler->objects_table,
         external->identifier,
         NULL,
         (void*)object));
 
     return ERROR_SUCCESS;
 }
c329e3c7
 
 int yr_compiler_define_boolean_variable(
     YR_COMPILER* compiler,
     const char* identifier,
     int value)
 {
288057e9
     return yr_compiler_define_integer_variable(
         compiler,
         identifier,
         value);
c329e3c7
 }
 
 int yr_compiler_define_string_variable(
     YR_COMPILER* compiler,
     const char* identifier,
     const char* value)
 {
288057e9
     YR_OBJECT* object;
     YR_EXTERNAL_VARIABLE* external;
 
     char* id  = NULL;
     char* val = NULL;
 
     compiler->last_result = ERROR_SUCCESS;
 
     FAIL_ON_COMPILER_ERROR(yr_arena_write_string(
         compiler->sz_arena,
         identifier,
         &id));
 
     FAIL_ON_COMPILER_ERROR(yr_arena_write_string(
         compiler->sz_arena,
         value,
         &val));
 
     FAIL_ON_COMPILER_ERROR(yr_arena_allocate_struct(
         compiler->externals_arena,
         sizeof(YR_EXTERNAL_VARIABLE),
         (void**)&external,
         offsetof(YR_EXTERNAL_VARIABLE, identifier),
         offsetof(YR_EXTERNAL_VARIABLE, string),
         EOL));
 
     external->type       = EXTERNAL_VARIABLE_TYPE_STRING;
     external->identifier = id;
     external->integer    = 0;
     external->string     = val;
 
     FAIL_ON_COMPILER_ERROR(yr_object_from_external_variable(
         external,
         &object));
 
     FAIL_ON_COMPILER_ERROR(yr_hash_table_add(
         compiler->objects_table,
         external->identifier,
         NULL,
         (void*)object));
 
     return compiler->last_result;
c329e3c7
 }
 #endif
 
 char* yr_compiler_get_error_message(
     YR_COMPILER* compiler,
     char* buffer,
     int buffer_size)
 {
288057e9
     switch (compiler->last_error) {
         case ERROR_INSUFICIENT_MEMORY:
             snprintf(buffer, buffer_size, "not enough memory");
             break;
         case ERROR_DUPLICATE_IDENTIFIER:
             snprintf(
                 buffer,
                 buffer_size,
                 "duplicate identifier \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_DUPLICATE_STRING_IDENTIFIER:
             snprintf(
                 buffer,
                 buffer_size,
                 "duplicate string identifier \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_DUPLICATE_TAG_IDENTIFIER:
             snprintf(
                 buffer,
                 buffer_size,
                 "duplicate tag identifier \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_DUPLICATE_META_IDENTIFIER:
             snprintf(
                 buffer,
                 buffer_size,
                 "duplicate metadata identifier \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_DUPLICATE_LOOP_IDENTIFIER:
             snprintf(
                 buffer,
                 buffer_size,
                 "duplicate loop identifier \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_UNDEFINED_STRING:
             snprintf(
                 buffer,
                 buffer_size,
                 "undefined string \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_UNDEFINED_IDENTIFIER:
             snprintf(
                 buffer,
                 buffer_size,
                 "undefined identifier \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_UNREFERENCED_STRING:
             snprintf(
                 buffer,
                 buffer_size,
                 "unreferenced string \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_NOT_A_STRUCTURE:
             snprintf(
                 buffer,
                 buffer_size,
                 "\"%s\" is not a structure",
                 compiler->last_error_extra_info);
             break;
         case ERROR_NOT_AN_ARRAY:
             snprintf(
                 buffer,
                 buffer_size,
                 "\"%s\" is not a array",
                 compiler->last_error_extra_info);
             break;
         case ERROR_INVALID_FIELD_NAME:
             snprintf(
                 buffer,
                 buffer_size,
                 "invalid field name \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_MISPLACED_ANONYMOUS_STRING:
             snprintf(
                 buffer,
                 buffer_size,
                 "wrong use of anonymous string");
             break;
         case ERROR_INCLUDES_CIRCULAR_REFERENCE:
             snprintf(
                 buffer,
                 buffer_size,
                 "include circular reference");
             break;
         case ERROR_INCLUDE_DEPTH_EXCEEDED:
             snprintf(buffer,
                      buffer_size,
                      "too many levels of included rules");
             break;
         case ERROR_LOOP_NESTING_LIMIT_EXCEEDED:
             snprintf(buffer,
                      buffer_size,
                      "loop nesting limit exceeded");
             break;
         case ERROR_NESTED_FOR_OF_LOOP:
             snprintf(buffer,
                      buffer_size,
                      "'for <quantifier> of <string set>' loops can't be nested");
             break;
         case ERROR_UNKNOWN_MODULE:
             snprintf(
                 buffer,
                 buffer_size,
                 "unknown module \"%s\"",
                 compiler->last_error_extra_info);
             break;
         case ERROR_INVALID_HEX_STRING:
         case ERROR_INVALID_REGULAR_EXPRESSION:
         case ERROR_SYNTAX_ERROR:
         case ERROR_WRONG_TYPE:
         case ERROR_WRONG_NUMBER_OF_ARGUMENTS:
             snprintf(
                 buffer,
                 buffer_size,
                 "%s",
                 compiler->last_error_extra_info);
             break;
         case ERROR_INTERNAL_FATAL_ERROR:
             snprintf(
                 buffer,
                 buffer_size,
                 "internal fatal error");
             break;
     }
 
     return buffer;
c329e3c7
 }