ОС Debian GNU/Linux: Как использовать библиотеку XML


 Вернуться в Доки-Токи

Зачем нужна библиотека 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: \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, не было найдено.

Вернуться в Доки-Токи

Copyright © 2010 rjaan as Andrey Rjavskov(Rzhavskov) <rjaan@yandex.ru> <arjavskov@gmail.com>



Сайт создан в системе uCoz