| | Зачем нужна библиотека XML
Главной проблемой с какой может столкнуться разработчик офисных приложений в ОС Debian GNU/Linux это хранение текстовых сообщений списком или иерархической структурой, например оглавление прайс-листа. С одной стороны можно использовать двухмерный массив, но для этого потребуется выделять большое количество памяти в стеке, что не есть хорошо, даже хочу сказать отвратительно. Поэтому мы будем использовать библиотеку XML, которая позволит сразу убить двух зайцев — не раздувать размер стека и получить гибкость в использовании и управлении списком или иерархической структурой символьных сообщений к которым может быть причисленно — "Оглавление документа", "Списка текстовых сообщений", "Иерархическая структура прайс-листа"(или прейскуранта цен) . В качестве, примера мы с вами рассмотрим файл ldapsi-msg.xml, который хранит текстовые сообщения для кодов ошибок, возвращаемые API-функции библиотеки LDAP в переменной ldap_errno. Поэтому начнём с структуры XML-файла. Структура XML-файла Консорциумом W3C принят стандарт eXtensible Markup Language (XML), который является расширяемым языком разметки. Он весьма прост в использовании в отличии от своего предшественника SGM, что мы увидим ниже. Файл, который содержит XML-документ должен начинаться с тега в который называется XML-декларацией без которого не один XML-парсер не будет разбирать файл, а просто вернет ошибку. Поэтому этот тег является обязательным. Тег XML-декларации <?xml version="1.0">
Кроме, того мы должны указать в XML-декларации кодировку, чтобы избежать неправильного отображения текста кириллицей, поэтому добавляем в тег свойство encoding="UTF-8". Тогда XML-декларация будет у нас выглядеть следующим образом. Тег XML-декларации с кодировокой <?xml version="1.0" encoding="UTF-8" ?>
Затем, мы должны указать корневой узел, который будет вершиной иерархической структуры XML-документа. В рассматриваемом файле ldapsi-msg.xml уровень вложенности является небольшим и равен единице. Потому что корневой элемент содержит дочерние элементы с сообщениями, имеющие свойства, которые указываются после названия тега <message />. Они состоят из пары — ключевого слова, являющееся именем свойства и значением, указанного в кавычках, после ключевого слова через знак равно('='). Формат тега LDAP-сообщения <message id="0" msg="Error Code: 0x00. The request was successful"/>
Свойство id — идентификатор LDAP-сообщения, эквивалентен коду ошибки LDAP. Множество которых содержаться в заголовочном файле <ldap.h> библиотеки LDAP в виде макросов c численными значениями в шестнадцатеричной системе счисления(HEX). Поэтому значение свойства id должено не иметь повторений и быть уникально. Свойство msg — содержит текст сообщения, которые были взяты из LDAP_ERROR(3), и соответствуют коду ошибки, указанному в десятичной системе(DEC) счисления в свойстве id. В корневом теге <LdapErrorMessages> какие-либо свойства игнорируются. Структура XML-файла ldapsi-msg.xml
<?xml version="1.0" encoding="UTF-8" ?>
<LdapErrorMessages>
<message id="0" msg="Error Code: 0x00. The request was successful"/>
<message id="1" msg="Error Code: 0x01. An operations error
occurred"/>
<message id="2" msg="Error Code: 0x02. A protocol violation was detected"/>
<message id="3" msg="Error Code: 0x03. An LDAP time limit was exceeded"/>
<message id="4" msg="Error Code: 0x04. An LDAP size limit was exceeded"/>
<message id="5" msg="Error Code: 0x05. A compare operation returned false"/>
<message id="6" msg="Error Code: 0x06. A compare operation returned true"/>
<message id="7" msg="Error Code: 0x07. The LDAP server does not support strong authentication"/>
<message id="8" msg="Error Code: 0x08. Strong authentication is required for the operation"/>
<message id="9" msg="Error Code: 0x09. Partial results only returned"/>
<message id="10" msg="Error Code: 0x0A. Referral returned"/>
<message id="11" msg="Error Code: 0x0B. Administration limit exceeded"/>
<message id="12" msg="Error Code: 0x0C. Critical extension not supported"/>
<message id="16" msg="Error Code: 0x10. The attribute type specified does not exist in the entry"/>
<message id="17" msg="Error Code: 0x11. The attribute type specified is not valid"/>
<message id="18" msg="Error Code: 0x12. Filter type not supported for the specified attribute"/>
<message id="19" msg="Error Code: 0x13. An attribute value specified violates some constraint (for example, a postal address has too many lines, or a line that is too long)"/>
<message id="20" msg="Error Code: 0x14. An attribute type or attribute value specified already exists in the entry"/>
<message id="21" msg="Error Code: 0x15. An attribute value was specified that is not valid"/>
</LdapErrorMessages>
Разбор XML-документа
Прежде чем начать разбирать, созданный с Вами файл, мы установим пакет libxml2, который содержит бинарные сборки библиотеки XML версии 2(разделяемая и статическая библиотека), заголовочные файлы, документацию, средства отладки и утилиты командной строки. root@engine:~#apt-get install libxml2 libxml2-dbg libxml2-dev libxml2-utils
Теперь можем смело перейти к написанию читателя, который будет вытаскивать сообщения по свойству id из тега <message>.Как мы говорили в ранее, XML-документ имеет свою структуру, поэтому в библиотеке XML2, предусмотрен маркер имеющий тип xmlDoc от структуры _xmlDoc . В ней очень много полей, но мы рассмотрим не всю структуру xmlDoc, а только часть её, которая в дальнейшем нас будет интересовать. Структура маркера XML документа structure xmlDoc
struct _xmlDoc {
· · ·
void *_private; /* приватные данные приложения */
xmlElementType type; /* XML_DOCUMENT_NODE должен быть всегда вторым!*/
char *name /* имя файла XML-документа */
struct _xmlNode *children; /* указатель на дерево документа(корень) */
struct _xmlNode *last; /* Указатель на последний элемент в дереве */
struct _xmlNode *parent; /* Указатель на родительский узел текущего элемента */
struct _xmlNode *next; /* Указатель на следующий элемент в дереве */
struct _xmlNode *prev; /* Указатель на предыдущий элемент в дереве */
· · ·
const xmlChar * version ; /* Версия XML стандарта */
const xmlChar * encoding ;/* Используемая кодировка */
· · ·
};
Для создания маркера XML-документа воспользуемся функцией xmlParseDoc(),строка 30, которая берет единственный аргумент — имя XML-файла. В этом аргументе указывается абсолютный путь к этому файлу(от корня '/' ) или относительный,где корнем './' является текущая директория. Напишем небольшую программку, которая откроет XML-документ и мы выведем этого XML-документа имя XML-файла, версию XML и кодировку, строки 36-39, с помощью соответствующих полей маркера xmlDoc, по указателю doc.Исходный код программы xml_metadoc
Исходный код xml_metadoc
1 /*
2 * xml_metadoc.c
3 *
4 * This program is parsing XML document and
5 * its version, name and encoding puts into the stdout .
6 * If a error will occur, the program puts a error message into the stderr.
7 * In this case, the program will terminate immediate.
8 *
9 * build:
10 * $CC `xml2-config --cflags` xml_metadoc.c -o xml_metadoc `xml2-config --libs`
11 *
12 * (c) 1974&8212;2010 rjaan <malto:rjaan@yandex.ru> <malto:arjavskov@gmail.com>
13 */
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include <libxml/xmlmemory.h>
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20
21 int main( gint argc, gchar **argv )
22 {
23 xmlDoc *doc;
24
25 if( argc != 2 ) {
26 fprintf(stderr, " usage: %s <xml-file>", argv[0]);
27 return 1;
28 }
29
30 doc= xmlParseFile (argv[1]);
31 if ( doc == NULL ) {
32 fprintf(stderr, "This document was not parsed(xml-file=[%s])!\n " , argv[0]);
33 return 1;
34 }
35
36 fprintf(stdout, "XML document was parsed successfully(xml-file=[%s])!\n " , argv[0] );
37 fprintf(stdout, "The name of XML document: %s\n ", doc->name );
38 fprintf(stdout, "The version of XML standard: %s\n ", doc->version );
39 fprintf(stdout, "The external initial encoding: %s\n ", doc->encoding );
40
41 xmlFreeDoc (doc);
42
43 return 0;
44 }
45
46 /*eof */
Хочу напомнить вечно спешащим братьям по цеху, чтобы они не забывали удалять номера строк, которые указаны в начале каждой строки в примере. Иначе, они будут долго удивляться — почему это компилятор на этапе трансляции исходного кода вернул ошибку. Как видим в конце, перед завершением, мы вызываем в строке 41 функцию xmlFreeDoc(), которая освобождает память используемую под маркер типа xmlDoc. Это надо делать всегда, чтобы избежать утечки памяти. Пример использования
my_user@engine:~/Work/xml-apps$./xml_metadoc ldapsi-msg.xml
XML document was parsed successfully(xml-file=[./xml_metadoc])!
The name of XML document: unknown
The version of XML standard: 1.0
The external initial encoding: UTF-8
Теперь перейдем к рекурсивному чтению структуры XML-документа, тем самым усложним наш предыдущий пример. Мы будем выводить только имена элементов и их свойств, если они есть в наличии. Пишем функцию xml_looking() рекурсивного обхода структуры XML документа и выводим на stdout то, что прочитали. Рекурсивный обход применяем потому, что мы не знаем название элементов и их свойств. Программу назовем ldap_errmsg_filetest, её исходный код сохраним в файле ldap_errmsg_filetest.с. Как собирать программу ldap_errmsg_filetest $(CC) `xml2-config --cflags` ldap_errmsg_filetest.c -o ldap_errmsg_filetest `xml2-config --libs`
Переменная $(CC) указывает на компилятор С, которым собирается программа ldap_errmsg_filetest. Код программы ldap_errmsg_filetest, версия 0.1
1/*
2 * ldap_errmsg_filetest.c
3 *
4 * Version: 0.1
5 *
6 * This program is parsing XML document that puts into the stdout. This program
7 * as well verifying that the XML document have the ldap error messages and
8 * this document have a right format. It's return a zero value.
9 * If a error will occur, the program puts a error message into the stderr.
10 * In this case, the program will terminate immediate with non-zero value.
11 *
12 * build:
13 * $(CC) `xml2-config --cflags` ldap_errmsg_filetest.c -o ldap_errmsg_filetest `xml2-config --libs`
14 *
15 * (c) 2009-2010 rjaan aka Rjavskov(Rzhavskov) <malto:rjaan@yandex.ru>,<mailto:arjavskov@gmail.com>
16 *
17 * This program is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License,
20 * or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License along with this program;
27 * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
29 * (c) rjaan <malto:rjaan@yandex.ru>, <mailto:arjavskov@gmail.com>
30 */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <libxml/xmlmemory.h>
37 #include <libxml/parser.h>
38 #include <libxml/tree.h>
39
40 static int total_line;
41 static void xml_document_round ( xmlNodePtr curr_dn, int level, int line );
42
43 int main( int argc,char **argv )
44 {
45 xmlDocPtr doc;
46 xmlNodePtr root_dn;
47 int line=0;
48
49 total_line = 0;
50
51 if( argc != 2 ) {
52 fprintf(stderr, " usage: %s \n", argv[0]);
53 return 1;
54 }
55
56 doc = xmlParseFile ( argv[1] );
57 if ( doc == NULL ) {
58 fprintf(stderr, "This document was not parsed. xml-file=[%s]\n " , argv[0]);
59 return 1;
60 }
61 /*
62 execution the 1st test.
63 */
64 fprintf(stdout, "1st test. XML document looking that is \'%s\' xml-file\n", argv[1] );
65 root_dn = xmlDocGetRootElement ( doc );
66 if ( root_dn == NULL ) {
67 fprintf(stderr, "The error occur! This document is empty!\n" );
68 xmlFreeDoc (doc);
69 return 1;
70 }
71 fprintf(stdout, "line %06d: <%s>\n", line, root_dn->name );
72 xml_document_round( root_dn->children , 1, line );
73 fprintf(stdout, "line %06d: %s>\n", total_line, root_dn->name );
74
75 fprintf ( stdout, "test is passed!\n" );
76 xmlFreeDoc (doc);
77 return 0;
78 }
79
80 static void xml_document_round( xmlNodePtr curr_dn, int level, int line )
81 {
82
83 while( curr_dn ) {
84 if ( curr_dn->type == XML_ELEMENT_NODE ) {
85 int i;
86 fprintf(stdout, "line %06d: ", ++line );
87 for ( i = 0; i < level; i++ ) {
88 fprintf(stdout, "\t" );
89 }
90 if( curr_dn->properties ) {
91 xmlAttrPtr properties;
92
93 fprintf(stdout, "<%s ",curr_dn->name );
94 properties = curr_dn->properties;
95 while ( properties ) {
96 fprintf(stdout, "%s=\"%s\" ", properties->name,
97 xmlGetProp ( curr_dn, properties->name ) );
98
99 properties = properties->next;
100 }
101 fprintf(stdout, ">\n");
102 }else{
103 fprintf(stdout, "<%s/>\n",curr_dn->name );
104 }
105 }
106 if ( curr_dn->children ) {
107 xml_document_round( curr_dn->children, ++level , line );
108 }
109 curr_dn = curr_dn->next;
110 }
111 total_line = line + 1;
112 }
113
114 /*eof */
Как видим по сравнению с программой xml_metadoc, которая выводит метаданные XML документа из маркера xmlDoc, программа ldap_errmsg_filetest значительно усложнилась, что видим из её кода в файле ldap_errmsg_filetest.c . Изменился заголовок, строки 1-30, он приведён в соответствии с указаниями, которые приводятся в конце лицензии GNU General Public License. В строках 36-38 указываются заголовочные файлы прототипов API-функций и типов данных XML библиотеки libxml2, которые мы используем в этой программе. А в строках 32-34, заголовочные файлы прототипов API-функций и типов данных С библиотеки, известной всем как GLIBC . В строке 44, 45 объявляем два указателя на маркеры — документа XML и корневого элемента в этом документе. Маркер документа XML(xmlDoc) рассматривался нами выше, а корневой элемента(xmlNode) будем разберём чуточку ниже. В строке 44, 45 объявляем два указателя на маркеры — документа XML и корневого элемента в этом документе. Маркер документа XML(xmlDoc) рассматривался нами выше, а корневого элемента(xmlNode) разберём чуточку ниже. В строке 46 объявляем целочисленную переменную line, которая будет являться номером строки. Так корневой элемент <LdapErrorMessages> будет всегда в нулевой строке XML-документа. Строка с XML-декларацией игнорируется. В строке 49 инициализируем статическую переменную total_line типа int которая будет содержать итоговое количество прочитанных элементов XML-документа. Эта переменная была объявлена ранее в строке 40. В строке 56 мы производим разбор XML-документа через API функцию xmlParseFile() библиотеки libxml2. Если указатель на маркер у нас не нулевой мы переходим к анализу дерева XML-документа. Иначе, мы немедленно завершимся с ненулевым значением и передачей на stderr сообщения, что этот XML-документ не был разобран с именем файла, указанного в первом и единственном аргументе программы. Любые отклонения от стандарта XML могут привести к такому плачевному результату. В строке 65, мы пытаемся получить указатель на корневой элемент, который описывается типом xmlNode Этот тип мы будем встречать не только как описание элементов XML-документа, но и их свойств. Тип xmlNode является структурой _xmlNode. Из этой структуры нам будут интересны поля связанные с указателем на дочерние и соседние элементы, а так же тип элемента, который нам понадобится при анализе его структуры, но об этом позже. Ниже привожу декларацию самой структуры _xmlNode и интересующих нас полей.
Structure xmlNode
struct _xmlNode {
· · ·
xmlElementType type; /*Тип элемента у нас он будет имет либо
текстовый тип, имеющее значение 2, либо
тип определяющий элемент*/
const xmlChar *name;/* Имя элемента */
struct _xmlNode *children; /* Указатель на дочерние элементы */
· · ·
struct _xmlNode * next; /* Следующий соседний элемент */
struct _xmlNode * prev; /* Предыдущий соседний элемент */
struct _xmlAttr * properties; /* Указатель на свойства элемента */
· · ·
}; В строке 66, если указатель на корневой элемент не был получен, считаем, что документ пустой и завершаемся с ненулевым значением, т.к. не достигли цели. Иначе, мы переходим к рекурсивному обходу с помощью вызова функции xml_document_round(), которая реализует его в строках 80-112. В основном цикле этой функции, строки 83-110, мы просматриваем соседние элементы, до тех пор пока указатель curr_dn — текущим элемент не будет равен нулю. Если null-указатель будет передан в 1-м аргументе самой функции xml_document_round(), то как мы видим по условию выполнения цикла, строка 83, функция будет немедленно завершена. Кстати, на манипуляции с этой переменной и самой функции xml_document_round(), которая вызывается из собственного тела функции, строится рекурсия. В строки 87- 89 мы осуществляем кол-во отступов от первого символа в строке, которые задаются уровнем вложения элемента в иерархической структуре XML-документа. Его значение содержится в 2-ом аргумент level функции xml_document_round(), значение которого приращивается в строке 107 перед рекурсивным вызовом функции xml_document_round(). В строках 90-102 мы выводим содержимое свойств элемента. Как мы уже говорили свойства элемента описываются одной и той же структурой, что и сам элемент. Поэтому имя свойства и элемента всегда находится в поле _xmlNode::name, а вот само значение свойства возвращает API-функция xmlGetProp() библиотеки libxml2, который мы передаем в 1-м аргументе. Пример использования программы ldap_errmsg_filetest
my_user@engine:~/Work/xml-apps$. ./ldap_errmsg_filetest ldapsi-msg.xml
1st test. XML document looking that is 'ldapsi-msg.xml' xml-file
line 000000: <LdapErrorMessages>
line 000001: <message id="0" msg="Error Code: 0x00. The request was successful" >
line 000002: <message id="1" msg="Error Code: 0x01. An operations error occurred" >
line 000003: <message id="2" msg="Error Code: 0x02. A protocol violation was detected" >
line 000004: <message id="3" msg="Error Code: 0x03. An LDAP time limit was exceeded" >
line 000005: <message id="4" msg="Error Code: 0x04. An LDAP size limit was exceeded" >
line 000006: <message id="5" msg="Error Code: 0x05. A compare operation returned false" >
line 000007: <message id="6" msg="Error Code: 0x06. A compare operation returned true" >
line 000008: <message id="7" msg="Error Code: 0x07. The LDAP server does not support strong authentication" >
line 000009: <message id="8" msg="Error Code: 0x08. Strong authentication is required for the operation" >
line 000010: <message id="9" msg="Error Code: 0x09. Partial results only returned" >
line 000011: <message id="10" msg="Error Code: 0x0A. Referral returned" >
line 000012: <message id="11" msg="Error Code: 0x0B. Administration limit exceeded" >
line 000013: <message id="12" msg="Error Code: 0x0C. Critical extension not supported" >
line 000014: <message id="16" msg="Error Code: 0x10. The attribute type specified does not exist in the entry" >
line 000015: <message id="17" msg="Error Code: 0x11. The attribute type specified is not valid" >
line 000016: <message id="18" msg="Error Code: 0x12. Filter type not supported for the specified attribute" >
line 000017: <message id="19" msg="Error Code: 0x13. An attribute value specified violates some constraint (for example, a postal address has too many lines, or a line that is too long)" >
line 000018: <message id="20" msg="Error Code: 0x14. An attribute type or attribute value specified already exists in the entry" >
line 000019: <message id="21" msg="Error Code: 0x15. An attribute value was specified that is not valid" >
line 000020: </LdapErrorMessages>
test is passed!
Теперь, как только мы научились выводить содержимое XML-документа, мы можем приступить к его анализу для выявления нарушений в декларации элемента <message>, который должен содержать код ошибки LDAP(свойство id) и следующее за ним текстовое сообщение(свойство — msg). Такой формат элемента <message>, который мы разбирали выше, является обязательным и отсутствие какого-либо одного из двух свойств этого элемента будем считать грубой ошибкой и завершать немедленно дальнейшую проверку содержимого XML-документа выходом из программы. Поэтому мы добавляем второй тест, который будет на самом деле искать отклонения от формата элемента <message> и выводить их на stderr. Так же мы будем проверять имя элемента <message>, чтобы выявить ошибки в его написании. Тестирование будет производится случайным перебором кодов ошибок LDAP(свойство id) в диапазоне значений от 0 до 25 с количеством итераций равным 30-ти, что непременно приведет к ошибки теста в нахождении элемента <message> по значению свойства id, в связи c отсутствием таких в нашем файле ldapsi-msg.xml, которые содержали бы значения от 22 до 25. Эту ошибку теста, мы будем игнорировать, так как она не относится к нарушению формата представления элемента <message>, а скорее просто указывает, что такой код ошибки в библиотеки LDAP не используется. Код программы ldap_errmsg_filetest, версия 0.2
1 /*
2 * ldap_errmsg_filetest.c
3 *
4 * Version: 0.2
5 *
6 * This program is parsing XML document that puts into the stdout. This program
7 * as well verifying that the XML document have the ldap error messages and
8 * this document have a right format. It's return a zero value.
9 * If a error will occur, the program puts a error message into the stderr.
10 * In this case, the program will terminate immediate with non-zero value.
11 *
12 * build:
13 * $(CC) `xml2-config --cflags` ldap_errmsg_filetest.c -o ldap_errmsg_filetest `xml2-config --libs`
14 *
15 * Copyright (c) 2009-2010 rjaan aka Andrey Rjavskov(Rzhavskov)
16 *
17 *
18 * This program is free software; you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License,
21 * or (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License along with this program;
28 * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
31/
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <libxml/xmlmemory.h>
37 #include <libxml/parser.h>
38 #include <libxml/tree.h>
39
40 static int total_line;
41 static void xml_document_round ( xmlNodePtr curr_dn, int level, int line );
42 static long int my_errno;
43
44 #define NONE_MYERROR 0x00
45 #define DOC_EMPTY_MYERROR 0x10
46 #define DOC_INVALID_FORMAT_MYERRROR 0x11
47 #define DOC_HAVE_NOT_LDAP_MSGS_MYERROR 0x12
48 #define DOC_NOT_FOUND_LDAP_MSG_MYERROR 0x13
49
50 #define PROG_EXIT(res,msg){ \
51 xmlFreeDoc (doc); \
52 free(msg); \
53 exit(res); \
54 }
55
56 #define TEST_ITERS 30
57
58 static char* ldap_message_get_by_id ( xmlDocPtr doc, int veryfied_id, int *line );
59
60 int main( int argc,char **argv )
61 {
62 xmlDocPtr doc;
63 xmlNodePtr root_dn;
64 int line=0, iter;
65
66 total_line = 0;
67
68 if( argc != 2 ) {
69 fprintf(stderr, " usage: %s \n", argv[0]);
70 return 1;
71 }
72
73 doc = xmlParseFile ( argv[1] );
74 if ( doc == NULL ) {
75 fprintf(stderr, "This document was not parsed. xml-file=[%s]\n " , argv[0]);
76 return 1;
77 }
78 /*
79 execution the 1st test.
80 */
81 fprintf(stdout, "1st test. XML document looking that is \'%s\' xml-file\n", argv[1] );
82 root_dn = xmlDocGetRootElement ( doc );
83 if ( root_dn == NULL ) {
84 fprintf(stderr, "The error occur! This document is empty!\n" );
85 xmlFreeDoc (doc);
86 return 1;
87 }
88
89 fprintf(stdout, "line %06d: <%s>\n", line, root_dn->name );
90 xml_document_round( root_dn->children , 1, line );
91 fprintf(stdout, "line %06d: \n", total_line, root_dn->name );
92 fprintf ( stdout, "test is passed!\n" );
93 /*
94 execution the 2st test.
95 */
96 fprintf(stdout, "2nd test. Checking that the XML document have the ldap error messages \
97 and this document have a right format.\n");
98
99 srandom((unsigned int)time(0));
100 for ( iter = 0; iter < TEST_ITERS; iter++ ) {
101 char *msg;
102 int check_id = ( random() % TEST_ITERS );
103 int break_test=0, line;
104
105 fprintf ( stdout, "iter: %d, check id: 0x%02x ", iter, check_id );
106 msg = ldap_message_get_by_id ( doc, check_id, &line );
107 if ( my_errno != NONE_MYERROR ) {
108 fprintf ( stdout, "\n");
109 fprintf(stderr, "The error occure at %d line! ", line );
119 if ( my_errno == DOC_EMPTY_MYERROR ) {
111 fprintf(stderr, "XML document is empty.\n");
112 PROG_EXIT(1,msg);
113 }else
114 if ( my_errno == DOC_INVALID_FORMAT_MYERRROR ) {
115 fprintf(stderr, "Invalid format.\n");
116 PROG_EXIT(1,msg);
117 }else
118 if ( my_errno == DOC_HAVE_NOT_LDAP_MSGS_MYERROR ) {
119 fprintf(stderr, "have not LDAP messages.\n");
120 PROG_EXIT(1,msg);
121 }else
122 if ( my_errno == DOC_NOT_FOUND_LDAP_MSG_MYERROR ) {
123 fprintf(stderr, "not found LDAP message by 0x%02x id.\n", check_id);
124 }else{
125 fprintf(stderr, "It is not known.\n");
126 PROG_EXIT(1,msg);
127 }
128 }else{
129 fprintf ( stdout, " msg: \"%s\" \n", msg);
130 }
131 free(msg);
132 }
133 fprintf ( stdout, "All tests is passed!\n", iter, (random()%TEST_ITERS) );
134 xmlFreeDoc (doc);
135 return 0;
136 }
137
138 static void xml_document_round( xmlNodePtr curr_dn, int level, int line )
139 {
140
141 while( curr_dn ) {
142 if ( curr_dn->type == XML_ELEMENT_NODE ) {
143 int i;
144 fprintf(stdout, "line %06d: ", ++line );
145 for ( i = 0; i < level; i++ ) {
146 fprintf(stdout, "\t" );
147 }
148 if( curr_dn->properties ) {
149 xmlAttrPtr properties;
150
151 fprintf(stdout, "<%s ",curr_dn->name );
152 properties = curr_dn->properties;
153 while ( properties ) {
154 fprintf(stdout, "%s=\"%s\" ", properties->name,
154 xmlGetProp ( curr_dn, properties->name ) );
155
156 properties = properties->next;
157 }
158 fprintf(stdout, "/>\n");
159 }else{
160 fprintf(stdout, "<%s/>\n",curr_dn->name );
161 }
162 }
163 if ( curr_dn->children ) {
164 xml_document_round( curr_dn->children, ++level , line );
165 }
165 curr_dn = curr_dn->next;
166 }
167 total_line = line + 1;
168 }
169
170 static char* ldap_message_get_by_id ( xmlDocPtr doc, int check_id, int *line )
171 {
172 xmlNodePtr curr_dn;
173 xmlNodePtr root_dn;
174
175 int look_line = 1;
176
177 *line = 0;
178 total_line = 1;
179
180 my_errno = NONE_MYERROR;
181
182 root_dn = xmlDocGetRootElement ( doc );
183 if ( root_dn == NULL ) {
184 my_errno = DOC_EMPTY_MYERROR;
185 return (char*)("");
186 }
187 /* The XML document should have a root node that is called "LdapErrorMessages".
188 This node should have a child nodes have to identified as "message" and
189 these nodes should have in turn the id,msg properties. The id property is
190 a decimal integer value and the msg property have from a chars of string
191 that have linked with the id property. */
192 if( xmlStrcmp ( root_dn->name ,"LdapErrorMessages" ) ) {
193 my_errno = DOC_INVALID_FORMAT_MYERRROR;
194 return (char*)strdup("");
195 }
196
197 if ( root_dn->children == NULL ) {
198 my_errno = DOC_HAVE_NOT_LDAP_MSGS_MYERROR;
199 return (char*)strdup("");
200 }
201
202 if ( root_dn->children ) {
203 if( !root_dn->children->next ) {
204 my_errno = DOC_HAVE_NOT_LDAP_MSGS_MYERROR;
205 return (char*)strdup("");
206 }
207 }
208 curr_dn = root_dn->children;
209 while ( curr_dn ) {
210 int id;
211 xmlAttrPtr properties;
212 if ( curr_dn->type != XML_ELEMENT_NODE ) {
213 curr_dn = curr_dn->next;
214 continue;
215 }
216 if( xmlStrcmp ( curr_dn->name ,"message" ) ) {
217 *line = look_line;
218 my_errno = DOC_INVALID_FORMAT_MYERRROR;
219 return (char*)strdup("");
220 }
221 if( !curr_dn->properties ) {
222 *line = look_line;
223 my_errno = DOC_INVALID_FORMAT_MYERRROR;
224 return (char*)strdup("");
225 }
226 properties = curr_dn->properties;
227 if( xmlStrcmp ( properties->name ,"id" ) ) {
228 *line = look_line;
229 my_errno = DOC_INVALID_FORMAT_MYERRROR;
230 return (char*)strdup("");
231 }
232 id = atoi((char*)xmlGetProp ( curr_dn, properties->name ));
233 properties = properties->next;
234 if( !properties || xmlStrcmp ( properties->name ,"msg" ) ) {
235 *line = look_line;
236 my_errno = DOC_INVALID_FORMAT_MYERRROR;
237 return (char*)strdup("");
238 }
239 if( check_id == id ) {
240 return xmlGetProp ( curr_dn, properties->name );
241 }
242 curr_dn = curr_dn->next;
243 look_line++;
244 }
245
246 *line = look_line;
247 total_line = *line + 1;
248 my_errno = DOC_NOT_FOUND_LDAP_MSG_MYERROR;
249
250 return (char*)strdup("");
251 }
252 /*eof */
По сравнению с версией 0.1 текущая версия 0.2 имеет значительные отличия. Добавляется код, начиная с строки 42, код ошибки — переменная my_errno, которая будет содержать целочисленные значения макросов перечисленных в строках 44 -48 , являющиеся ошибками формата элемента <message> . Значения представлены в шестнадцатеричной системе счисления. Значения присваиваются переменной my_errno только в функции ldap_message_get_by_id(), на которой строится весь второй тест.
Макрос NONE_MYERROR с кодом 0x00 обозначает, что ошибки в формате элементов <message> в текущем XML-документе не были найдены
Макрос DOC_EMPTY_MYERROR с кодом 0x10 указывает, что в данном XML-документе корневой элемент не обнаружен, а указатель, который возвратит функция xmlDocGetRootElement() в строке 182 будет равен нулю.
Макрос DOC_INVALID_FORMAT_MYERRROR с кодом 0x11 указывает, что неправильно введено имя элемента или его свойства. Так же, если не была соблюдена последовательность следования свойств id и msg или один из них не будет указан, той же переменной my_errno будет присвоен этот код ошибки. Проверки на этот макрос производится:
В строках 192-195, проверяется имя корневого элемента с помощью функции xmlStrcmp(), которая производит сравнение имени элемента полученного в результате разбора и ожидаемого. Если они не совпадают функция xmlStrcmp() возвращает ненулевое значение. В обратном случае, она возвратит нулевое значение, когда сравниваемые проверяемые символьные строки будут эквивалентны.
В строках 216-220, тем же самым способ проверяем, что текущий элемент имеет имя message. Если же в элементе <message \> отсутсвуют свойства как таковые или проверка в строке 234 показала, что отсутствует свойство msg или неправильно указано его имя, переменной my_errno будет присвоено значение макроса DOC_INVALID_FORMAT_MYERRROR.
Макрос DOC_INVALID_FORMAT_MYERRROR с кодом 0x12 указывает, что элементы <message \> не найдены, т.е. XML-документ не содержит ни одного кода ошибки библиотеки LDAP.
Макрос DOC_NOT_FOUND_LDAP_MSG_MYERROR с кодом 0x13 указывает, что такое сообщение с таким id, указанного в переменной check_id в строке 170, не было найдено.
|