1. Présentation▲
glib contient des « parser » de XML : http://library.gnome.org/devel/glib/unstable/glib-Simple-XML-Subset-Parser.html
Nous allons étudier un ensemble de fonctions qui utilisent ces fonctions afin de lire un fichier XML. À l'origine, ces fonctions ont été données par gege2061 fournies ici https://c.developpez.com/sources/c/?page=X#GTK_dom . J'y ai apporté quelques corrections et je vais aussi rajouter d'autres fonctions que j'ai développées pour mes propres besoins.
Il me semble aussi indispensable aussi d'expliquer la structure pour pouvoir par la suite faire soi-même ses propres fonctions.
2. Les structures utilisées :▲
2-A. types personnels▲
Ils sont à définir dans notre programme.
2-A-1. GmarkupDomContext▲
typedef
struct
_GMarkupDomContext GmarkupDomContext;
struct
_GMarkupDomContext
{
gint level;
GMarkupDomNode *
root;
GMarkupDomNode *
last_root;
GMarkupDomNode *
last_node;
GMarkupDomNode *
current;
}
;
Elle servira de variable interne pour la fonction de parcours de la structure XML : pas besoin de la mettre dans un fichier en-tête.
2-A-2. GmarkupDomNode▲
Cette structure devra être mise dans un fichier en-tête gmarkup-dom.h par exemple car on l'utilise pour mémoriser notre arborescence XML : c'est l'élément de base de la construction de notre arborescence.
typedef
struct
_GMarkupDomNode GMarkupDomNode;
struct
_GMarkupDomNode
{
gchar *
name;
gint item; /* numéro de l'item dans la liste des items du père */
gint level;
contenu *
content;
gint texte; /* nombre d'entrées texte */
xml_attr *
attributs;
gint nb_att;
contenu *
com;
gint nb_com;
struct
_GMarkupDomNode *
parent;
struct
_GMarkupDomNode *
child;
gint fils; /* nombre de nodes fille */
}
;
On utilise dans GmarkupDomNode les deux structures personnelles suivantes qui devront être mises dans un fichier en-tête gmarkup-dom.h par exemple car on l'utilise pour mémoriser notre arborescence :
typedef
struct
_xml_attr xml_attr;
/* structures =============================================================== */
struct
_xml_attr
{
gchar *
name;
gchar *
value;
}
;
typedef
struct
{
gchar *
texte;
gint item;
}
contenu;
3. Le format du stockage des données▲
Les structures définies précédemment nous donnent cette représentation pour l'élément de base stockage des données :
Les flèches partent d'un entier dans une case jaune qui indique le nombre d'éléments du tableau vert pointé par le champ de la case saumon.
Les attributs arrivant tous en premier : dans la balise même. Par contre, pour les trois autres éléments, il faut mémoriser leur ordre d'arrivée afin de pouvoir le restituer. C'est pour ça que j'ai mis un champ item qui retient l'ordre d'arrivée de l'item en question parmi les éléments de la balise dans les deux structures qui gèrent les trois éléments suivants. Ces structures sont : contenu pour le texte et les commentaires et GmarkupDomNode pour les balises.
4. Les fonctions de base▲
4-A. Les fonctions personnelles publiques▲
Elles sont à écrire (copier) nous-mêmes dans notre programme, ce sont les fonctions visibles de l'extérieur, celles qu'on va appeler. Le mieux est de les mettre dans un fichier spécifique comme gmarkup-dom.c et de mettre leur prototype dans un fichier en-tête : gmarkup-dom.h .
4-A-1. GMarkupDomNode *g_markup_dom_new (const gchar *, GError **error); ▲
C'est la fonction d'entrée pour récupérer notre arborescence sous forme de liste chaînée.
GMarkupDomNode *
g_markup_dom_new (
const
gchar *
filename, GError **
error)
{
GMarkupParseContext *
markup_parse_context;
GMarkupDomNode *
pere =
(
GMarkupDomNode *
)malloc
(
sizeof
(
GMarkupDomNode));
/* pere doit être alloué dynamiquement car il est utilisé par la fonction appelante */
GMarkupDomContext context={
0
,0
,pere,pere}
;
/* ce n'est pas le cas de context qui n'est utilisé que les fonctions appelées */
pere->
name=
NULL
; /* initialisation de la racine du fichier XML */
pere->
level=
0
;
pere->
item=
0
;
pere->
content=
NULL
;
pere->
texte=
0
;
pere->
attributs=
NULL
;
pere->
nb_att=
0
;
pere->
com=
NULL
;
pere->
nb_com=
0
;
pere->
parent=
NULL
;
pere->
child=
NULL
;
pere->
fils=
0
;
g_return_val_if_fail (
filename !=
NULL
, context.root);
/* sort de la fonction si filename ne pointe pas sur une chaîne de caractères */
{
/* création du contexte : fonctions à appeler suivant la nature de l'élément traité */
GMarkupParser markup_parser; /* http://library.gnome.org/devel/glib/stable/glib-Simple-XML-Subset-Parser.html#GMarkupParser */
markup_parser.start_element =
xml_start_element; /* cas : ouverture de balise */
markup_parser.end_element =
xml_end_element; /* cas : fermeture de balise */
markup_parser.text =
xml_text; /* cas : texte entre les balises */
markup_parser.passthrough =
xml_com; /* cas : commentaires entre les balises */
markup_parser.error =
NULL
; /* cas : erreur dans le fichier XML */
markup_parse_context =
g_markup_parse_context_new (&
markup_parser, 0
,
&
context, NULL
);
/* http://library.gnome.org/devel/glib/stable/glib-Simple-XML-Subset-Parser.html#g-markup-parse-context-new */
}
/* maintenant, on sait comment parcourir le fichier */
{
gchar *
text =
NULL
;
gsize length =
-
1
;
g_file_get_contents (
filename, &
text, &
length, error);
/* passe tout le fichier filename dans la chaîne de caractères text
dont la longueur sera dans lenght */
if
(
text !=
NULL
) /* si on a récupéré le fichier */
{
g_markup_parse_context_parse (
markup_parse_context, text, length, error); /* on lance le "parsage" du fichier XML */
g_free (
text), text =
NULL
; /* on lance le parsage du fichier XML */
}
g_free (
markup_parse_context), markup_parse_context =
NULL
;
}
return
pere;
}
On l'utilise très simplement :
GMarkupDomNode *
node =
g_markup_dom_new (
"
chemin vers le fichier XML
"
, NULL
);
Le premier paramètre est le chemin vers le fichier XML, le second est un pointeur sur un système d'erreur (voir http://library.gnome.org/devel/glib/stable/glib-Error-Reporting.html ).
4-A-2. void g_markup_dom_free (GMarkupDomNode *);▲
Comme l'arborescence est récupérée sous forme de liste chaînée et que le nombre d'éléments de la chaîne dépend du fichier XML, il est donc obligatoire que l'allocation mémoire pour la liste soit dynamique. Il faut donc libérer la mémoire après utilisation. Cette manipulation INDISPENSABLE sera faite par cette fonction :
void
g_markup_dom_free (
GMarkupDomNode *
node)
{
if
(
node !=
NULL
)
{
gint i;
g_free (
node->
name), node->
name =
NULL
;
for
(
i =
0
; node->
nb_att>
i; i++
)
{
g_free (
node->
attributs[i].name), node->
attributs[i].name =
NULL
;
g_free (
node->
attributs[i].value), node->
attributs[i].value =
NULL
;
}
g_free (
node->
attributs), node->
attributs =
NULL
;
for
(
i =
0
; node->
texte>
i; i++
)
{
g_free (
node->
content[i].texte);
}
g_free
(
node->
content);
for
(
i =
0
; node->
nb_com>
i; i++
)
{
g_free (
node->
com[i].texte);
}
g_free
(
node->
com);
for
(
i =
0
; node->
fils>
i; i++
)
{
g_markup_dom_free (
node->
child+
i);
}
g_free
(
node->
child), node->
child =
NULL
;
if
(
node->
level==
0
) g_free
(
node), node =
NULL
; /* il faut juste libérer la racine, les autres, sont dans des tableaux */
}
}
L'appel est tout simple :
g_markup_dom_free (
node) ;
4-A-3. La fonction d'écriture▲
4-A-3-A. Sortie sur l'écran▲
Cette fonction ne fait que l'affichage à l'écran de votre fichier XML parsé. À vous de l'adapter pour une autre forme de sortie. C'est une fonction récursive, c'est pour cela qu'on doit lui donner deux paramètres lors de son appel :
dom_print (
node,0
);
Le premier paramètre est la racine du système de mémorisation du fichier XML.
Voici la fonction :
void
dom_print (
GMarkupDomNode *
root,gint pos)
{
#define INDENT(niv) \
do
\
{
\
gint i; \
for
(
i =
1
; i <
(
niv) -
1
; i++
) \
g_print (
"
\t
"
); \
}
while
(
0
)
gint texte=
0
;
gint fils=
0
;
gint com=
0
;
if
(
root !=
NULL
)
{
if
(
root->
level!=
0
)
{
gint i =
0
;
INDENT (
root->
level);
g_print (
"
<%s
"
, root->
name);
while
(
root->
nb_att>
i)
{
g_print (
"
%s=
\"
%s
\"
"
, root->
attributs[i].name,
root->
attributs[i].value);
i++
;
}
}
if
(
root->
fils+
root->
texte+
root->
nb_com!=
0
)
{
if
(
root->
level!=
0
) g_print (
"
>
\n
"
);
while
(
texte<
root->
texte ||
fils<
root->
fils ||
com<
root->
nb_com)
{
if
(
root->
nb_com>
com) if
(
root->
com[com].item==(
texte+
fils+
com))
{
INDENT (
root->
level +
1
);
g_print (
"
%s
\n
"
, root->
com[com].texte);
com++
;
}
if
(
root->
texte>
texte) if
(
root->
content[texte].item==(
texte+
fils+
com))
{
INDENT (
root->
level +
1
);
g_print (
"
%s
\n
"
, root->
content[texte].texte);
texte++
;
}
if
(
root->
fils>
fils) if
(
root->
child[fils].item==(
fils+
texte+
com))
{
dom_print (
root->
child+
fils,pos+
1
);
fils++
;
}
}
if
(
root->
level)
{
INDENT (
root->
level);
g_print (
"
</%s>
\n
"
, root->
name);
}
}
else
if
(
root->
level) g_print (
"
/>
\n
"
);
}
#undef INDENT
}
4-A-3-B. Sortie dans un fichier▲
Cette fois la sortie se fait dans un fichier (ou un autre flux), c'est la fonction précédente que j'ai un peu modifiée. La principale différence se fait lors de l'appel : on passe le pointeur sur le flux en second paramètre en plus du premier et du dernier paramètre qui restent les mêmes.
void
dom_print (
GMarkupDomNode *
root,FILE *
sortie,gint pos)
{
#define INDENT(niv,sortie) \
do \
{ \
gint ii; \
for (ii = 1; ii < (niv) - 1; ii++) \
fprintf(sortie,"\t"); \
} while (0)
gint texte=
0
;
gint fils=
0
;
gint com=
0
;
gint i;
if
(
root !=
NULL
)
{
if
(
root->
level!=
0
)
{
i =
0
;
INDENT (
root->
level,sortie);
fprintf
(
sortie,"
<%s
"
, root->
name);
while
(
root->
nb_att>
i)
{
fprintf
(
sortie,"
%s=
\"
%s
\"
"
, root->
attributs[i].name,
root->
attributs[i].value);
i++
;
}
}
if
(
root->
fils+
root->
texte+
root->
nb_com!=
0
)
{
if
(
root->
level!=
0
) fprintf
(
sortie,"
>
\n
"
);
while
(
texte<
root->
texte ||
fils<
root->
fils ||
com<
root->
nb_com)
{
if
(
root->
nb_com>
com) if
(
root->
com[com].item==(
texte+
fils+
com))
{
INDENT (
root->
level +
1
,sortie);
fprintf
(
sortie,"
%s
\n
"
, root->
com[com].texte);
com++
;
}
if
(
root->
texte>
texte) if
(
root->
content[texte].item==(
texte+
fils+
com))
{
INDENT (
root->
level +
1
,sortie);
for
(
i=
0
;root->
content[texte].texte[i];i++
)
{
/* compatibilité avec le xml */
if
(
root->
content[texte].texte[i]==
'
&
'
)
fprintf
(
sortie,"
&
"
);
else
if
(
root->
content[texte].texte[i]==
'
<
'
)
fprintf
(
sortie,"
<
"
);
else
if
(
root->
content[texte].texte[i]==
'
>
'
)
fprintf
(
sortie,"
>
"
);
else
fputc
(
root->
content[texte].texte[i],sortie);
}
fputc
(
'
\n
'
,sortie);
texte++
;
}
if
(
root->
fils>
fils) if
(
root->
child[fils].item==(
fils+
texte+
com))
{
dom_print (
root->
child+
fils,sortie,pos+
1
);
fils++
;
}
}
if
(
root->
level)
{
INDENT (
root->
level,sortie);
fprintf
(
sortie,"
</%s>
\n
"
, root->
name);
}
}
else
if
(
root->
level)
{
fprintf
(
sortie,"
/>
\n
"
);
}
}
#undef INDENT
}
4-B. Les fonctions personnelles privées▲
Elles ne seront pas appelées par le reste du programme, elles servent aux procédures de parcours du fichier XML. Le mieux est de les mettre dans le fichier spécifique avec les fonctions publiques. Comme elles n'ont pas à être appelées par le reste du programme, on mettra simplement leur prototype au début du fichier contenant nos fonctions XML, pas besoin de les mettre dans un fichier à en-tête.
La compréhension de ces fonctions n'est pas utile pour l'utilisation des fonctions publiques décrites précédemment. Mais il est important de savoir ce qu'elles font si on veut travailler le code source.
4-B-1. int str_isspace (const gchar *s) ▲
Vérifie si une chaîne de caractères contient un texte ou que des espaces, saut de lignes, ou autres éléments formant une chaîne considérée vide. Cette fonction fort pratique peut passer dans les fonctions publiques.
int
str_isspace (
const
gchar *
s)
{
if
(!
s) return
1
;
for
(
; *
s &&
(*
s==
'
'
||
*
s==
'
\t
'
||
*
s==
'
\n
'
); s++
);
return
!*
s;
}
4-B-2. void xml_start_element (GMarkupParseContext *, const gchar *, const gchar **, const gchar **, gpointer , GError **) ▲
Cette fonction initialise un nouveau niveau de balise.
void
xml_start_element (
GMarkupParseContext *
context, const
gchar *
element_name,
const
gchar **
attribute_names,
const
gchar **
attribute_values, gpointer user_data,
GError **
error)
{
gint i,j;
GMarkupDomContext *
dom_context =
user_data;
GMarkupDomNode *
node =
NULL
, *
parent;
g_return_if_fail (
dom_context !=
NULL
); /* déclaration et initialisation du nouvel élément */
/* déclaration et initialisation du nouvel élément */
if
(
dom_context->
root) /* si ce n'est pas la première node, la node mère du système */
{
parent=
dom_context->
current;
//for (parent=dom_context->current;dom_context->level<parent->level;parent=parent->parent);
parent->
fils++
; /* un for car il est possible qu'on vienne de descendre de niveaux */
parent->
child=
realloc
(
parent->
child,sizeof
(*
node)*
parent->
fils); /* on rajoute un fils au père */
for
(
i=
0
;i<
parent->
fils-
1
;i++
) /* ne pas oublier que tous les fils ont changé d'adresse */
for
(
j=
0
;j<
parent->
child[i].fils;j++
)
parent->
child[i].child[j].parent=
parent->
child+
i;
node=
parent->
child+
parent->
fils-
1
; /* on récupère le lien vers la nouvelle node crée chez le père */
node->
parent=
parent; /* j'informe sur le parent de cette node */
node->
item=
dom_context->
item; /* j'informe sur l'ordre de cet élément dans l'ensemble des */
}
/* éléments (texte, node et commentaires) du père */
else
/* si c'est le premier élément */
{
node =
g_malloc (
sizeof
(*
node));
dom_context->
root =
node;
node->
item=
0
;
node->
parent=
NULL
;
}
dom_context->
current =
node;
/* variable générale de l'état actuel du système */
/* la construction d'un nouvel élément est toujours demandée par le père donc on */
dom_context->
level++
; /* vient de monter le niveau de 1 . mise dans la */
dom_context->
item=
0
; /* pas encore d'entrée dans cette node */
/* initiaisation de la node */
node->
name =
g_strdup (
element_name);
node->
content =
NULL
;
node->
texte =
0
; /* pas encore de texte */
node->
com =
NULL
;
node->
nb_com =
0
; /* pas encore de commentaires */
node->
child =
NULL
;
node->
fils=
0
; /* pas encore de fils */
node->
level =
dom_context->
level; /* niveau du noeud */
/* Copy attributs */
for
(
i =
0
; attribute_names[i] !=
NULL
; i++
); /* on compte le nombre d'attributs */
if
(
i!=
0
) node->
attributs =
g_malloc (
sizeof
(*
node->
attributs)*
i);
else
node->
attributs =
NULL
;
node->
nb_att=
i;
for
(
i =
0
; attribute_names[i] !=
NULL
; i++
) /* on parcourt les attributs */
{
/* on informe leur nom et valeur */
node->
attributs[i].name =
g_strdup (
attribute_names[i]);
node->
attributs[i].value =
g_strdup (
attribute_values[i]);
}
/* Unused parameters */
(
void
)context;
(
void
)error;
}
4-B-3. static void xml_end_element (GMarkupParseContext *, const gchar *, gpointer, GError **) ▲
Cette fonction ferme un niveau de balise.
void
xml_end_element (
GMarkupParseContext *
context,
const
gchar *
element_name, gpointer user_data,
GError **
error)
{
GMarkupDomContext *
dom_context =
user_data;
g_return_if_fail (
dom_context !=
NULL
);
g_return_if_fail (
dom_context->
current !=
NULL
);
/* je rends la main au père */
dom_context->
item=
dom_context->
current->
item+
1
; /* je passe à l'élément suivant du père */
dom_context->
level--
; /* le père a un niveau de moins */
dom_context->
current =
dom_context->
current->
parent; /* la node courante sera celle du père */
/* Unused parameters */
(
void
)context;
(
void
)element_name;
(
void
)error;
}
4-B-4. void xml_text (GMarkupParseContext *, const gchar *, gsize, gpointer, GError **) ▲
Cette fonction récupère un texte entre deux autres éléments.
void
xml_text (
GMarkupParseContext *
context, const
gchar *
text,gsize text_len, gpointer user_data, GError **
error)
{
GMarkupDomContext *
dom_context =
user_data;
gchar *
ch=
g_strdup (
text);
g_return_if_fail (
dom_context !=
NULL
);
g_return_if_fail (
dom_context->
current !=
NULL
);
if
(!
str_isspace (
text))/* si le texte n'est pas vide (saut de ligne ou espace ou tabulation */
{
/* on rajoute cet élément (voir rajout d'une node : c'est pareil */
dom_context->
current->
texte++
;
dom_context->
current->
content=
realloc
(
dom_context->
current->
content,dom_context->
current->
texte*
sizeof
(
contenu));
dom_context->
current->
content[dom_context->
current->
texte-
1
].item=
dom_context->
item;
dom_context->
item++
;
dom_context->
current->
content[dom_context->
current->
texte-
1
].texte =
g_strdup (
mintexte
(
ch));
}
free
(
ch);
/* Unused parameters */
(
void
)context;
(
void
)text_len;
(
void
)error;
}
4-B-5. void xml_com (GMarkupParseContext *, const gchar *, gsize, gpointer, GError **)▲
Cette fonction récupère un commentaire entre deux autres éléments.
void
xml_com (
GMarkupParseContext *
context, const
gchar *
text,gsize text_len, gpointer user_data, GError **
error)
{
GMarkupDomContext *
dom_context =
user_data;
g_return_if_fail (
dom_context !=
NULL
);
g_return_if_fail (
dom_context->
current !=
NULL
);
if
(!
str_isspace (
text)) /* si le texte n'est pas vide (saut de ligne ou espace ou tabulation */
{
/* on rajoute cet élément (voir rajout d'une node : c'est pareil */
dom_context->
current->
nb_com++
;
dom_context->
current->
com=
realloc
(
dom_context->
current->
com,dom_context->
current->
nb_com*
sizeof
(
contenu));
dom_context->
current->
com[dom_context->
current->
nb_com-
1
].item=
dom_context->
item;
dom_context->
item++
;
dom_context->
current->
com[dom_context->
current->
nb_com-
1
].texte =
g_strdup (
text);
}
/* Unused parameters */
(
void
)context;
(
void
)text_len;
(
void
)error;
}
4-B-6. gchar * mintexte(gchar * s)▲
Note : jusqu'à maintenant, j'ai présenté des fonctions dont le premier auteur est gege2061 même si parfois je les ai corrigées. Maintenant, je présente uniquement des fonctions dont je suis l'auteur.
Cette fonction enlève les blancs en fin et début de chaînes de caractères. Elle reçoit un pointeur sur le début de la chaîne, place un 0 sur le dernier caractère qui est considéré comme un caractère (pas espace, tabulation ou saut de ligne) et renvoi un pointeur sur le premier caractère qui est considéré comme un caractère.
Remarque : si le pointeur renvoyé pointe sur la valeur 0, alors la chaîne est considérée comme vide.
gchar *
mintexte
(
gchar *
s)
{
gchar *
c;
for
(
; *
s &&
(*
s==
'
'
||
*
s==
'
\t
'
||
*
s==
'
\n
'
); s++
);
c=
s;
while
(*
c)
c++
;
c--
;
while
(*
c==
'
'
||
*
c==
'
\t
'
||
*
c==
'
\n
'
)
c--
;
*(
c+
1
)=
0
;
return
s;
}
5. Fonctions évoluées▲
Ces fonctions servent à travailler avec l'arborescence représentant le fichier XML. On peut y chercher une première occurrence, y modifier un nœud (rajouter un élément, écrire un champ...), y déplacer ou copier des nœuds...
Ces fonctions et les suivantes sont surtout là en exemple de ce qu'on peut faire avec cette structure.
5-A. GMarkupDomNode * g_markup_dom_nom (GMarkupDomNode *node,gchar * nom_val,gchar * attr)▲
Cette fonction cherche dans l'ensemble des nodes filles de la node indiquée par le paramètre node, la première node qui a l'attribut indiqué par le paramètre attr à la valeur indiquée par le paramètre nom_val.
GMarkupDomNode *
g_markup_dom_nom (
GMarkupDomNode *
node,gchar *
nom_val,gchar *
attr)
{
GMarkupDomNode *
tmp;
if
(
node !=
NULL
) /* la node existe t-elle vraiment ? */
{
gint i;
for
(
i =
0
; node->
nb_att>
i; i++
) /* parcourt les attributs de cette node */
{
/* à la recherche de l'attribut attr dont la valeur est nom_val */
if
(
strcmp
((
node->
attributs[i].value),nom_val)==
0
&&
strcmp
(
node->
attributs[i].name,attr)==
0
)
return
node; /* si oui renvoie le pointeur sur la node */
}
/* si le couple attribut/valeur n'a pas été trouvé */
for
(
i =
0
; node->
fils>
i; i++
) /* parcourt des nodes filles */
{
/* c'est une procédure récursive */
if
((
tmp=
g_markup_dom_nom (
node->
child+
i,nom_val,attr))) /* si le couple a été trouvé */
return
tmp; /* renvoie de la node qui contient ce couple */
}
}
return
NULL
; /* si rien n'a été trouvé dans la node ou parmi ces enfants, */
}
Cette fonction est très pratique quand on place des attributs « name » qui doivent être uniques pour trouver une node dans tout un fichier XML.
5-B. GMarkupDomNode * g_markup_dom_node (GMarkupDomNode *node,gchar * nom_val)▲
Cette fonction recherche parmi les enfants de la node indiquée par le paramètre node, la première node dont le nom est indiqué par le paramètre nom_val
GMarkupDomNode *
g_markup_dom_node (
GMarkupDomNode *
node,gchar *
nom_val)
{
GMarkupDomNode *
tmp=
NULL
;
if
(
node !=
NULL
) /* marche comme g_markup_dom_nom, mais plus simplement */
{
gint i;
if
(
node->
name &&
strcmp
(
node->
name,nom_val)==
0
)
return
node;
for
(
i =
0
; node->
fils>
i; i++
)
{
if
((
tmp=
g_markup_dom_node (
node->
child+
i,nom_val)))
return
tmp;
}
}
return
NULL
;
}
5-C. void copie_node(GMarkupDomNode * arrive,GMarkupDomNode * modele)▲
Cette fonction copie la node modele sur la node arrive.
void
copie_node
(
GMarkupDomNode *
arrive,GMarkupDomNode *
modele)
{
if
(
modele !=
NULL
)
{
gint i;
*
arrive=*
modele;
arrive->
attributs=
malloc
(
modele->
nb_att*
sizeof
(
xml_attr));
for
(
i =
0
; modele->
nb_att>
i; i++
)
{
arrive->
attributs[i].name=
g_strdup
(
modele->
attributs[i].name);
arrive->
attributs[i].value=
g_strdup
(
modele->
attributs[i].value);
}
arrive->
content=(
contenu *
)malloc
(
modele->
texte*
sizeof
(
contenu));
for
(
i =
0
; modele->
texte>
i; i++
)
{
arrive->
content[i].item=
modele->
content[i].item;
arrive->
content[i].texte=
g_strdup
(
modele->
content[i].texte);
}
modele->
com=(
contenu *
)malloc
(
modele->
texte*
sizeof
(
contenu));
for
(
i =
0
; modele->
nb_com>
i; i++
)
{
arrive->
com[i].item=
modele->
com[i].item;
arrive->
com[i].texte=
g_strdup
(
modele->
com[i].texte);
}
arrive->
child=(
GMarkupDomNode *
)malloc
(
modele->
fils*
sizeof
(
GMarkupDomNode));
for
(
i =
0
; modele->
fils>
i; i++
)
{
copie_node
(
arrive->
child+
i, modele->
child+
i);
arrive->
child[i].parent=
arrive;
}
}
}
5-D. void xml_ajoute_fin(GMarkupDomNode * node,gchar * texte);▲
Cette fonction met une node fille en fin d'une node mère indiquée par le paramètre node, la node créée aura comme nom la chaîne pointée par le paramètre texte.
void
xml_ajoute_fin
(
GMarkupDomNode *
node,gchar *
texte) /* mettre une node fille en fin d'une node, le nom de la node étant indiqué par le texte */
{
gint i,j;
node->
child=(
GMarkupDomNode *
)realloc
(
node->
child,(
1
+
node->
fils)*
sizeof
(
GMarkupDomNode));
node->
child[node->
fils].item=
node->
texte+
node->
nb_com+
node->
fils;
/* place de la nouvelle node dans l'ensemble des enfants de la node mère */
node->
child[node->
fils].name=
g_strdup
(
texte);
/* nom de la nouvelle node */
node->
child[node->
fils].level=
node->
level+
1
;
/* nouveau niveau de la nouvelle node */
node->
child[node->
fils].content=
NULL
;
/* nouveau contenu de la nouvelle node */
node->
child[node->
fils].texte=
0
;
node->
child[node->
fils].attributs=
NULL
;
node->
child[node->
fils].nb_att=
0
;
node->
child[node->
fils].com=
NULL
;
node->
child[node->
fils].nb_com=
0
;
node->
child[node->
fils].parent=
node;
node->
child[node->
fils].child=
NULL
;
node->
child[node->
fils].fils=
0
;
for
(
i=
0
;i<
node->
fils;i++
)
{
/* mettre à jour les fils du fils à cause du réalloc */
for
(
j=
0
;j<
node->
child[i].fils;j++
)
node->
child[i].child[j].parent=
node->
child+
i;
}
node->
fils++
;
/* la node mère a un enfant de plus */
}
5-E. void modif_xml(GMarkupDomNode *item,gchar * ch)▲
Cette fonction modifie le premier texte de la node indiquée par item. S'il a déjà un premier texte, il sera supprimé et remplacé par le texte pointé par ch. S'il n'y a pas encore de texte, on va ajouter un élément texte à la fin :
void
modif_xml
(
GMarkupDomNode *
item,gchar *
ch)
{
if
(
item)
{
if
(
item->
texte==
0
) /* s'il n'y a pas de texte, rajouter un texte à la fin de la node */
{
item->
content=(
contenu *
)malloc
(
sizeof
(
contenu));
item->
texte=
1
;
item->
content[0
].item=
item->
nb_com+
item->
fils;
}
else
/* sinon, enlever le texte déjà présent */
free
(
item->
content[0
].texte);
item->
content[0
].texte=
g_strdup
(
ch); /* mettre le nouveau texte à la place */
}
}
5-F. void xml_ajoute_fin_texte(GMarkupDomNode * node);▲
Cette fonction rajoute un élément texte à la fin des enfants d'une node passée en paramètre.
void
xml_ajoute_fin_texte
(
GMarkupDomNode *
node) /* rajouter un élément texte en fin d'une node */
{
node->
content=(
contenu *
)realloc
(
node->
content,(
1
+
node->
texte)*
sizeof
(
contenu));
node->
content[node->
texte].item=
node->
texte+
node->
nb_com+
node->
fils;
node->
content[node->
texte].texte=
NULL
;
node->
texte++
;
}
5-G. void xml_ecrit_dernier_texte(GMarkupDomNode * node,gchar * ch);▲
Cette fonction remplace le dernier texte d'une node indiquée par le paramètre node. Le pointeur ch indique le texte à mettre.
void xml_ecrit_dernier_texte(GMarkupDomNode * node,char *ch) /* remplace le dernier texte d'une node */
{
if (node->content[node->texte-1].texte!=NULL)
free(node->content[node->texte-1].texte); /* libère un texte pouvant déjà être là */
node->content[node->texte-1].texte=g_strdup(ch);
}
Les deux précédentes fonctions peuvent être combinées comme ceci pour créer une nouvelle node avec un texte :
void
xml_ajoute_fin_attribut
(
GMarkupDomNode *
node) /* rajouter un élément attribut */
{
node->
attributs=(
xml_attr *
)realloc
(
node->
attributs,(
1
+
node->
nb_att)*
sizeof
(
xml_attr));
node->
attributs[node->
nb_att].name=
NULL
;
node->
attributs[node->
nb_att].value=
NULL
;
node->
nb_att++
;
}
5-H. void xml_ajoute_fin_attribut(GMarkupDomNode * node);▲
Cette fonction rajoute un élément attribut à la node pointée par le paramètre node.
void
xml_ajoute_fin_attribut
(
GMarkupDomNode *
node) /* rajouter un élément attribut */
{
node->
attributs=(
xml_attr *
)realloc
(
node->
attributs,(
1
+
node->
nb_att)*
sizeof
(
xml_attr));
node->
attributs[node->
nb_att].name=
NULL
;
node->
attributs[node->
nb_att].value=
NULL
;
node->
nb_att++
;
}
5-I. void xml_ecrit_dernier_attribut(GMarkupDomNode * node,char * nom, char * val);▲
Cette fonction remplace le dernier attribut d'une node indiquée par le pointeur node par un nouvel attribut dont le nom est indiqué par le pointeur nom et sa valeur par le pointeur valeur.
void
xml_ecrit_dernier_attribut
(
GMarkupDomNode *
node,char
*
nomm, char
*
val) /* remplace le dernier attribut */
{
if
(
node->
attributs[node->
nb_att-
1
].name!=
NULL
) free
(
node->
attributs[node->
nb_att-
1
].name); /* libère la place pouvant être occupée par le précédent nom */
if
(
node->
attributs[node->
nb_att-
1
].value!=
NULL
) free
(
node->
attributs[node->
nb_att-
1
].value); /* libère la place pouvant être occupée par la valeur de l'attribut précédent */
node->
attributs[node->
nb_att-
1
].name=
g_strdup
(
nomm);
node->
attributs[node->
nb_att-
1
].value=
g_strdup
(
val);
}
Comme pour le précédent couple de deux fonctions, ces deux fonctions peuvent être combinées comme cela :
xml_ajoute_fin_attribut
(
node) ;
xml_ecrit_dernier_attribut
(
node, "
nom
"
, "
valeur
"
) ;
5-J. void decale_node(GMarkupDomNode *node,unsigned short i);▲
Cette fonction décale les nodes filles de la node pointée par le paramètre node à partir de la place i (indiqué par le paramètre i) pour mettre la dernière node à la place i.
void
decale_node
(
GMarkupDomNode *
node,unsigned
short
i)
{
/* ATTENTION : laissez les textes et les commentaires inchangés */
gint tmp,tmp1;
unsigned
short
j;
GMarkupDomNode tmp_node;
tmp=
node->
child[node->
fils-
1
].item; /* place actuelle dans la hiérarchie de la dernière node */
tmp_node=
node->
child[node->
fils-
1
]; /* la dernière node actuelle. Attention, le tableau commence à 0*/
for
(
j=
node->
fils-
2
;j>=
i;j--
) /* parcourt des nodes de la future avant dernière à la première */
{
/* à être déplacée */
tmp1=
node->
child[j].item; /* On mémorise la place de la node actuelle */
node->
child[j].item=
tmp; /* On attribue à la node actuelle la place de la précédente */
tmp=
tmp1; /* on mémorise l'ancienne place de la node actuelle */
node->
child[j+
1
]=
node->
child[j]; /* On modifie le tableau des nodes filles */
}
node->
child[i]=
tmp_node; /* la dernière node prend la place de la node i */
node->
child[i].item=
tmp; /* numéro de la node dans la liste des enfants des enfants de la node mère */
}
5-K. void supprime_node(GMarkupDomNode *node,gint i);▲
Cette fonction supprime la ième node fille (i étant le paramètre i passé en paramètre) de la node mère indiquée par le paramètre node.
void
supprime_node
(
GMarkupDomNode *
pere,gint i) /* supprime la ième node */
{
/* Attention : non testé avec les textes et les commentaires */
unsigned
short
j;
gint tmp=
pere->
child[i].item; /* tmp est l'indice de la node laissée vide */
g_markup_dom_free
(
pere->
child+
i); /* libère la placé réservée pour la node */
pere->
fils--
; /* la node mère aura un enfant de moins */
for
(
j=
i;j<
pere->
fils;j++
)
{
pere->
child[j]=
pere->
child[j+
1
]; /* rapatrie le nœud suivant */
pere->
child[j].item--
; /* décrémenter sa place */
}
for
(
i=
0
;i>
pere->
texte;i++
) /* parcourt des textes */
if
(
pere->
content[i].item>
tmp) /* si le texte est après la place laissée libre */
pere->
content[i].item--
; /* décrémenter sa place */
for
(
i=
0
;i>
pere->
nb_com;i++
) /* parcourt des commentaires */
if
(
pere->
com[i].item>
tmp) /* si le commentaire est après la place laissée libre */
pere->
com[i].item--
; /* décrémenter sa place */
}
5-L. void xml_sup_node(GMarkupDomNode *node)▲
Ce serait presque un exercice à vous faire faire ! À partir de la fonction précédente, faire une fonction qui supprime une node dont on ne passe en paramètre qu'un pointeur vers cette node.
void
xml_sup_node
(
GMarkupDomNode *
node) /* supprime la node passée en paramètre */
{
if
(
node==
NULL
||
node->
parent) return
;
supprime_node
(
node->
parent,node-
node->
parent->
child);
}
6. Fonctions pour les documents ODT▲
Toutes ces fonctions ont été réalisées pour travailler sur un document odt (de LibreOffice ou OpenOffice.org). Elles ont été testées et sont régulièrement utilisées dans un programme que vous pouvez récupérer ici : tirage de personnage pour ADD1 et ADD2
6-A. faciliter la lecture d'un fichier XML▲
Si on extrait le fichier content.xml du fichier ODT, on récupère un fichier illisible, car il est en une seule ligne. Pour pouvoir le lire, il suffit de l'ouvrir et de l'enregistrer :
GMarkupDomNode *
ooo=
g_markup_dom_new
(
"
content.xml
"
,NULL
);
enregistre_xml
(
"
fichier.xml
"
,ooo);
Le nouveau fichier créé (fichier.xml) sera correctement indenté avec un saut de ligne à chaque nouvel item.
6-B. void xml_ecrit_dernier_texte_f(GMarkupDomNode * node,gchar * ch)▲
Cette fonction rajoute un texte avec saut de ligne dans le document content.xml qui contient le texte du fichier compressé odt.
La node où on rajoute ce texte est marquée par node. Le teste à rajouter est pointé par ch. Ce dernier texte peut contenir des sauts de ligne (caractère '\n'), ces sauts de lignes seront remplacés par une node fille <text:line-break/> pour faire le saut de ligne dans le document XML.
void
xml_ecrit_dernier_texte_f
(
GMarkupDomNode *
node,gchar *
ch) /* Gestion des sauts de ligne dans un texte */
{
char
*
pt=
ch;
if
(!(*
ch)) return
; /* texte vide => ne rien écrire */
while
(*
pt)
{
if
(*
pt==
'
\n
'
)
{
*
pt=
0
;
xml_ajoute_fin
(
node,"
text:line-break
"
);
//if (ch+1!=pt)
{
xml_ajoute_fin_texte
(
node);
xml_ecrit_dernier_texte
(
node,ch);
}
ch=
pt+
1
;
*
pt=
'
\n
'
;
}
pt++
;
}
if
(
ch!=
pt)
{
xml_ajoute_fin
(
node,"
text:line-break
"
);
xml_ajoute_fin_texte
(
node);
xml_ecrit_dernier_texte
(
node,ch);
}
}
7. Remerciements▲
Pour les relectures, je remercie Mahefasoa.17 commentaires