В библиотеке libxml2
есть несколько функций с помощью которых можно задать пользовательские callback-функции для обработки общих ошибок. Эти функции будут вызваны библиотекой при обнаружении ошибки во время обработки XML
документа.
Первая функция initGenericErrorDefaultFunc()
устанавливает или сбрасывает, ранее установленный, обработчик. Для сброса обработчика в качестве значения необходимо передать NULL
, в качестве обработчика будет использована внутрення функция. Прототип функции:
void initGenericErrorDefaultFunc(xmlGenericErrorFunc * handler)
Функция xmlSetGenericErrorFunc()
- устанавливает или сбрасывает, ранее установленный, обработчик, а также позволяет передать пользовательские данные в callback
-функцию. Пользовательские данные будут переданы в callback-функцию первым параметром:
void xmlSetGenericErrorFunc(void * ctx, xmlGenericErrorFunc handler)
По умолчанию сообщения об ошибках выводятся на stderr
, c помощью этой функции можно изменить устройство вывода. Для этого необходимо первым параметром передать указатель на файловый дескриптор FILE
, а в качестве второго параметра NULL
:
FILE *fd = fopen("./debug.txt", "a+");
//..
if(fd) {
xmlSetGenericErrorFunc((void *)fd, NULL);
}
//...
Теперь все сообщения об ошибках будут записываться в файл debug.txt
. Если в качестве значения второго параметра передать NULL
, то это приведет к сбросу, ранее установленного, обработчика и в качестве обработчика будет использована внутрення функция.
Прототип callback-функции:
void xmlGenericErrorFunc (void * ctx, const char * msg, ...)
Функция принимает указатель на пользовательские данные(ctx
), человеко-понятное сообщение об ошибке(msg
) и список дополнительных пераметров. Сообщение об ошибке, предаваемое вторым аргументом в функцию, представляет из себя шаблон со спецификаторами формата:
Entity: line %d:
Последующими парметрами в функцию передаются значения спецификаторов шаблона.
Пример функции обработки ошибок:
//...
static void my_libxml2_err(void * ctx, const char * msg, ...) {
va_list args;
va_start(args, msg);
vfprintf((FILE *)ctx, msg, args);
va_end(args);
}
//...
FILE *fd;
fd = fopen("./debug.txt", "w+");
if(fd != NULL) {
xmlSetGenericErrorFunc((void *)fd, (xmlGenericErrorFunc)my_libxml2_err);
} else {
xmlSetGenericErrorFunc((void *)stderr, (xmlGenericErrorFunc)my_libxml2_err);
}
//...
Для запрета вывода сообщений оставляем тело функции пустым:
//...
static void my_libxml2_err(void * ctx, const char * msg, ...) {
(void) ctx;
(void) msg;
return;
}
xmlGenericErrorFunc handle = (xmlGenericErrorFunc)my_libxml2_err;
initGenericErrorDefaultFunc(&handle;);
//...
На практике часто встречаются ситуации при которых сообщение о возникшей ошибке, переданное в callback-функцию, заданную с помощью xmlSetGenericErrorFunc()
или initGenericErrorDefaultFunc()
, является мало информативным или непригодным к использованию в данном контексте. В таких случаях вместо выше описанных функций необходимо использовать функцию xmlSetStructuredErrorFunc()
Функция xmlSetStructuredErrorFunc()
- устанавливает или сбрасывает, ранее установленный, обработчик, а также позволяет передать пользовательские данные в callback-функцию. Пользовательские данные будут переданы в callback-функцию первым параметром. Прототип функции:
void xmlSetStructuredErrorFunc (void *userData, xmlStructuredErrorFunc handler)
Первым параметром функция принимает пользовательские данные, вторым - указатель на структуру xmlErrorPtr с информацией об ошибке.
Структура xmlErrorPtr
:
struct _xmlError {
int domain; /* Код, из перечисления xmlErrorDomain, модуля библиотеки, который обнаружил ошибку. */
int code; /* Код ошибки из перечисления xmlParserErrors */
char *message; /* Человеко-понятное сообщение об ошибке */
xmlErrorLevel level; /* Код уровня ошибки из перечисления xmlErrorLevel */
char *file; /* Имя файла, если доступно, при обработке которого произошла ошибка, иначе NULL */
int line; /* Номер линии в файле, если доступно, на которой обнаружена ошибка, иначе 0 */
char *str1; /* Дополнительная строковая информация */
char *str2; /* Дополнительная строковая информация */
char *str3; /* Дополнительная строковая информация */
int int1; /* Дополнительная числовая информация */
int int2; /* Номер столбца или 0, если не доступно */
void *ctxt; /* Указатель (xmlParserCtxt) на контекст парсера */
void *node; /* Указатель (xmlNodePtr) на элемент в дереве при обработке которого произошла ошибка */
};
Структура xmlErrorDomain
:
typedef enum {
XML_FROM_NONE = 0,
XML_FROM_PARSER, /* The XML parser */
XML_FROM_TREE, /* The tree module */
XML_FROM_NAMESPACE, /* The XML Namespace module */
XML_FROM_DTD, /* The XML DTD validation with parser context*/
XML_FROM_HTML, /* The HTML parser */
XML_FROM_MEMORY, /* The memory allocator */
XML_FROM_OUTPUT, /* The serialization code */
XML_FROM_IO, /* The Input/Output stack */
XML_FROM_FTP, /* The FTP module */
XML_FROM_HTTP, /* The HTTP module */
XML_FROM_XINCLUDE, /* The XInclude processing */
XML_FROM_XPATH, /* The XPath module */
XML_FROM_XPOINTER, /* The XPointer module */
XML_FROM_REGEXP, /* The regular expressions module */
XML_FROM_DATATYPE, /* The W3C XML Schemas Datatype module */
XML_FROM_SCHEMASP, /* The W3C XML Schemas parser module */
XML_FROM_SCHEMASV, /* The W3C XML Schemas validation module */
XML_FROM_RELAXNGP, /* The Relax-NG parser module */
XML_FROM_RELAXNGV, /* The Relax-NG validator module */
XML_FROM_CATALOG, /* The Catalog module */
XML_FROM_C14N, /* The Canonicalization module */
XML_FROM_XSLT, /* The XSLT engine from libxslt */
XML_FROM_VALID, /* The XML DTD validation with valid context */
XML_FROM_CHECK, /* The error checking module */
XML_FROM_WRITER, /* The xmlwriter module */
XML_FROM_MODULE, /* The dynamically loaded module module*/
XML_FROM_I18N, /* The module handling character conversion */
XML_FROM_SCHEMATRONV /* The Schematron validator module */
} xmlErrorDomain;
Структура xmlErrorLevel
:
typedef enum {
XML_ERR_NONE = 0,
XML_ERR_WARNING = 1, /* Простое предупреждение */
XML_ERR_ERROR = 2, /* Устранимая ошибка */
XML_ERR_FATAL = 3 /* Фатальная ошибка */
} xmlErrorLevel;
Прототип callback-функции:
void xmlStructuredErrorFunc (void * userData, xmlErrorPtr error)
Пример функции обработки ошибок:
void *my_malloc(size_t);
void *my_realloc(void *, size_t);
unsigned short my_str_rtrim(char *, size_t);
void *my_malloc(size_t size) {
void *mem = malloc(size);
if (mem == NULL) {
return NULL;
}
if (memset(mem, '\0', size)) {
return mem;
}
free(mem);
return NULL;
}
void *my_realloc(void *ptr, size_t size) {
void *new_ptr = NULL;
if (size == 0) {
free(ptr);
} else if (ptr) {
new_ptr = realloc(ptr, size);
if (new_ptr == NULL) {
free(ptr);
}
return new_ptr;
}
return my_malloc(size);
}
unsigned short my_str_rtrim(char *line, size_t line_len) {
char *p = line;
size_t i = 0;
if (!p) {
return 1;
}
while (*p++ != '\0' && i < line_len) {
i++;
}
for (i+=1; i > 0; i--) {
p--;
if (isspace(*p) || *p == '\0' ) {
*p = '\0';
continue;
}
break;
}
return 0;
}
void my_strcat(char * dest, const char *src, size_t size) {
size_t tmp_src_size = 0;
char *buff = dest;
const char *s = src;
if (!dest || !src || size == 0) {
return;
}
while (*buff != '\0') {
buff++;
}
tmp_src_size = size;
if (tmp_src_size > 0) {
while (--tmp_src_size != 0) {
if ((*buff++ = *s++) == '\0') {
return;
}
}
}
*buff = '\0';
}
//...
static void my_libxml2_err(void *ctx, xmlErrorPtr error) {
char *level_str = NULL, *buff = NULL, *message = NULL;
const xmlChar *node_name = NULL;
size_t alloc_mem = 1, len = 0;
int line = 0, column = 0;
if (!error || error->code == XML_ERR_OK) {
return;
}
line = error->line; // Номер линии
column = error->int2; // Номер колонки
message = error->message; // Сообщение об ошибке сформированное парсером
// Удаляем пробельные символы с конца строки
(void)my_str_rtrim(message, strlen(message));
// Получаем уровень возникшей ошибки в виде строки
switch (error->level) {
case XML_ERR_NONE:
level_str = "";
break;
case XML_ERR_WARNING:
level_str = "Warning: ";
break;
case XML_ERR_ERROR:
level_str = "Error: ";
break;
case XML_ERR_FATAL:
level_str = "Fatal error: ";
break;
}
// Рассчитываем размер памяти, который необходимо выделить: длинна строки + 1 для '\0'
alloc_mem += strlen(level_str);
// Выделяем память
buff = my_malloc(alloc_mem);
if (!buff) {
return;
}
// Помещаем в буфер строку
my_strcat(buff, level_str, alloc_mem);
// Определяем где произошла ошибка (Файл, линиия, колонка) и
// Добавляем информацию в буфер
if (error->file != NULL) {
alloc_mem += strlen(error->file);
buff = my_realloc(buff, alloc_mem);
if (!buff) {
return;
}
my_strcat(buff, error->file, alloc_mem);
} else if ((line != 0) && (error->domain == XML_FROM_PARSER)) {
len = snprintf(NULL, 0, "Entity: line %d, column: %d", line, column);
buff = my_realloc(buff, alloc_mem + len);
if (!buff) {
return;
}
snprintf(buff + alloc_mem - 1, len + 1, "Entity: line %d, column: %d", line, column);
alloc_mem += len;
}
// Определяем название элемента в котором произошла ошибка и
// Добавляем информацию в буфер
if ((error->node != NULL) && ((xmlNodePtr) error->node)->type == XML_ELEMENT_NODE) {
node_name = ((xmlNodePtr) error->node)->name;
len = snprintf(NULL, 0, ", element %s: ", node_name);
buff = my_realloc(buff, alloc_mem + len);
if (!buff) {
return;
}
snprintf(buff + alloc_mem - 1, len + 1, ", element %s: ", node_name);
alloc_mem += len;
} else {
alloc_mem += 2;
buff = my_realloc(buff, alloc_mem);
if (!buff) {
return;
}
my_strcat(buff, ": ", alloc_mem);
}
// Добавляем в буфер сообщение об ощибке
alloc_mem += strlen(message);
buff = my_realloc(buff, alloc_mem);
if (!buff) {
return;
}
my_strcat(buff, message, alloc_mem);
// Если есть дополнительная информация,
// то также помещаем ее в буфер
if ((error->domain == XML_FROM_XPATH) && (error->str1 != NULL)) {
alloc_mem += strlen(error->str1) + 2;
buff = dwavdapi_realloc(buff, alloc_mem);
if (!buff) {
return;
}
my_strcat(buff, ": ", alloc_mem);
my_strcat(buff, error->str1, alloc_mem);
}
fprintf((FILE *)ctx, "LibXML2: %s\n", buff);
}
//..
FILE *fd;
fd = fopen("./debug.txt", "w+");
if(fd != NULL) {
xmlSetStructuredErrorFunc((void *)fd, (xmlStructuredErrorFunc)my_libxml2_err);
} else {
xmlSetGenericErrorFunc((void *)stderr, (xmlStructuredErrorFunc)my_libxml2_err);
}
Вывод:
LibXML2: Fatal error: Entity: line 1, column: 589: error parsing attribute name
LibXML2: Fatal error: Entity: line 1, column: 589: attributes construct error
LibXML2: Fatal error: Entity: line 1, column: 589: Couldn't find end of Start Tag processing line 1
LibXML2: Fatal error: Entity: line 1, column: 614: expected '>'
LibXML2: Fatal error: Entity: line 1, column: 614: Opening and ending tag mismatch: infections line 1 and processing
LibXML2: Fatal error: Entity: line 1, column: 661: Opening and ending tag mismatch: server-statistics line 1 and infections
Если в коде будут одновременно вызвана функция xmlSetStructuredErrorFunc()
и xmlSetGenericErrorFunc()
, то приоритет будет у функции xmlSetStructuredErrorFunc()
Для многопоточных приложений функция xmlSetStructuredErrorFunc()
/xmlSetGenericErrorFunc()
должна быть вызвана отдельно для каждого потока.
Описанные функции и структуры объявлены в заголовочном файле libxml/xmlerror.h. Также информацию об описанных функциях и структурах можно найти в документации к модулю xmlerror: http://xmlsoft.org/html/libxml-parser.html