Skip to content

Instantly share code, notes, and snippets.

@mahdavipanah
Last active January 5, 2018 21:07
Show Gist options
  • Save mahdavipanah/8e78d87c0d2f22a31f52653e779747db to your computer and use it in GitHub Desktop.
Save mahdavipanah/8e78d87c0d2f22a31f52653e779747db to your computer and use it in GitHub Desktop.
XML in C - Goli

Format XML

Description du format

Le format XML [https://fr.wikipedia.org/wiki/Extensible_Markup_Language] est un format générique
qui permet l'échange de données structurées. De nombreux formats spécialisés, tels que SVG ou
HTML, correspondent à une utilisation particulière de XML. Les langages de programmation de
haut-niveau sont souvent dotés d'une bibliothèque d'import/export de données XML. Nous

proposons ici d'en développer une pour le langage C.

Voici un exemple de code XML :
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author lastname="Gambardella" firstname="Matthew" />
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author lastname="Ralls" firstname="Kim" />
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
...
</catalog>
Comme on peut le constater dans cet exemple, XML permet de définir des données
(quelconques, ici il s'agit d'une liste de livres mais cela aurait pu être n'importe quel autre type
de données) sous une forme arborescente. Ici, on décrit une liste de livres comme étant un

catalog dont chaque entrée est un book. Chaque book est lui-même décrit en précisant son

auteur, son titre, son genre, son prix, etc.
Les données sont décrites à travers des éléments qui s'imbriquent les uns dans les autres pour

constituer un arbre. Dans notre cas, un élément catalog contient deux éléments book, eux-

mêmes contenant des éléments author, title, etc. Chaque élément est structuré

syntaxiquement de la même façon :
une balise ( markup ) ouvrante données entre chevrons '<' et '>' et contenant :
le nom de l'élément ( tag ), suivi
d'une liste possiblement vide d'attributs,
un contenu qui peut être
soit une suite d'éléments XML,
soit une donnée textuelle,
une balise fermante entre chevrons '</' et '>' contenant juste le nom de l'élément

http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?

Chaque attribut possède un nom et une valeur (donnée entre guillemets).
Cela donne :
<nom_element nom_attribut1 = "valeur_attribut1" nom_attribut2 = "valeur_attribut2">
...
</nom_element>

Lorsque l'élément n'a pas de contenu (c'est par exemple le cas de l'élément author dans notre

exemple, les balises ouvrantes et fermantes sont souvent fusionnées en une seule balise. Ainsi
<author lastname="Gambardella" firstname="Matthew" />
est équivalent à
<author lastname="Gambardella" firstname="Matthew"> </author>
Hormis les quelques règles syntaxiques que nous venons d'évoquer, le reste (le nom des balises,
des attributs, le contenu des valeurs) est laissé libre. Pour simplifier, nous ne nous

préoccuperons pas de la première balise spéciale .

Bibliothèque demandée

On propose de définir le type des documents XML de la façon suivante. Un élément XML contient
:
un nom (chaîne de caractères),
une liste d'attributs (voir ci-dessous),
une référence (pointeur) vers son élément père s'il en contient un,
une référence (pointeur) vers son élément frère s'il en contient un, et
un contenu qui peut être :
soit une donnée brute (chaîne de caractères) ou raw en anglais,
soit une liste d'éléments fils dont il est le père.
Un attribut XML contient :
un nom (chaîne de caractères),
une valeur (chaîne de caractères) sans les guillemets, et
une référence vers l'attribut suivant

Types, constructeurs et destructeurs

Définir le type xattribute_t représentant un attribut XML tel que décrit ci-dessus

Définir le type xelement_t représentant un élément XML tel que décrit ci-dessus

Définir le constructeur xelement_t* create_xelement(const char* name)

qui crée un élément XML de nom name sans attribut ni père ni frère ni contenu ; on

prendra garde de copier le nom de l'élément

Définir la fonction xattribute_t* add_xattribute(xelement_t* e, const

http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?

char* name, const char* value) qui ajoute à l'élément e un attribut de nom

name et de valeur value ; on prendra garde à copier le nom et la valeur de l'attribut ;

si l'élément possède déjà un attribut de ce nom, la précédente valeur sera écrasée (et la
mémoire libérée)

Définir la fonction void add_sub_xelement(xelement_t* e, xelement_t*

s) qui ajoute l'élément s comme fils à l'élément e ; on vérifiera que s n'a pas de père et

que e est soit vide, soit contient déjà des sous-éléments ; dans le cas contraire, le

programme échouera

Définir la fonction void add_raw(xelement_t* e, const char* r) qui

ajoute une donnée brute r à l'élément e ; on vérifiera que e est soit vide, soit contient

déjà une donnée brute (auquel cas, celle-ci sera remplacée et la mémoire libérée) ; dans
le cas contraire, le programme échouera

Définir la fonction void delete_xelement(xelement_t* e) qui libère la

mémoire occupée par e ; on vérifiera que e n'a pas de père

Entrées/sorties

Export

Définir la fonction void save_xelement(FILE* fd, xelement_t* e) qui

sauvegarde l'élément e dans fd en utilisant la syntaxe XML

Définir la fonction void save_xml(const char* fname, xelement_t* e)

qui sauvegarde l'élément e dans le fichier nommé fname (qui sera écrasé le cas

échéant)

Ajouter la fonction void print_xelement(xelement_t* e) définie par :

void print_xelement(xelement_t* e) {
save_xelement(stdout, e);
}

Import

L'import d'un fichier XML est une fonctionnalité autrement plus difficile à programmer que
l'export. Elle demande en effet à parser un fichier texte. Voici une partie du code permettant cet
import à la condition de programmer correctement les fonctions spécifiées ci-dessous :
xelement_t* load_xelement(FILE* fd, const char* end_tag);
void load_xelement_raw(FILE* fd, xelement_t* e) {
char* w = next_raw(fd);
check_next_char(fd, '<');
check_next_char(fd, '/');
check_next_word(fd, e->name);
check_next_char(fd, '>');
add_raw(e,w);
free(w);
}

http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?

void load_xelement_sub(FILE* fd, xelement_t* e) {
xelement_t* f = load_xelement(fd, e->name);
if (f != NULL) {
add_sub_xelement(e,f);
load_xelement_sub(fd, e);
}
}
void load_xelement_content(FILE* fd, xelement_t* e) {
if (is_next_char(fd,'<',false))
load_xelement_sub(fd, e);
else
load_xelement_raw(fd, e);
}
xelement_t* load_xelement(FILE* fd, const char* end_tag) {
xelement_t* e = NULL;
char c;
check_next_char(fd,'<');
if ((end_tag) && (is_next_char(fd,'/',true))) {
check_next_word(fd,end_tag);
check_next_char(fd,'>');
return NULL;
}
char* name = next_word(fd);
if (name == NULL) {
fprintf(stderr, "load_xelement: tag name expected\n");
exit(EXIT_FAILURE);
}
e = create_xelement(name);
free(name);
while((name = next_word(fd)) != NULL) {
check_next_char(fd,'=');
char* value = next_string(fd);
add_xattribute(e,name,value);
}
c = next_char(fd);
if (c == '/') {
check_next_char(fd,'>');
return e;
}
if (c == '>') {
load_xelement_content(fd, e);
return e;
}
fprintf(stderr, "load_xelement: end of markup expected ('>' or '/>'), but got %c\n", c);
exit(EXIT_FAILURE);
}
xelement_t* load_xml(const char* fname) {
FILE* fd = fopen(fname, "r");
xelement_t* e = load_xelement(fd,NULL);
fclose(fd);
return e;
}
Pour que ce code fonctionne, il est nécessaire de programmer les fonctions suivantes :

char next_char(FILE* fd) qui retourne le prochain caractère dans fd qui n'est

pas un caractère d'espacement (c'est-à-dire ni ' ', ni '\n', ni '\r', ni '\t') : le programme
échouera si la fin de fichier est rencontrée

http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?

void check_next_char(FILE* fd, char c) qui fait échouer le programme si

le prochain caractère (next_char) n'est pas c

bool is_next_char(FILE* fd, char c, bool cons) qui vérifie si le

prochain caractère (next_char) est c ; dans le cas contraire ou si cons est à faux, le

prochain caractère ne sera pas consommé (utiliser ungetc)

char* next_word(FILE* fd) qui retourne le prochain identifiant (nom d'élément

ou d'attribut) dans fd ; les caractères d'espacement précédant le mot seront consommés

et la lecture du mot s'arrêtera dès qu'on rencontrera un caractère parmi '<', '>', '/', '=' et

les caractères d'échappement, qui ne sera pas consommé (ungetc) ; le programme

échouera si la fin de fichier est rencontrée pendant la lecture ; la chaîne retournée sera
allouée par la fonction (attention, il n'y a pas de limite de taille sur les identifiants)

void check_next_word(FILE* fd, const char* w) qui fait échouer le

programme si le prochain mot (next_word) n'est pas w (utiliser strcmp pour

comparer les mots)

char* next_string(FILE* fd) qui consomme les caractères d'espacement

jusqu'au prochain guillemet ” (échoue dans le cas contraire) et retourne l'ensemble des

caractères rencontrés jusqu'au guillemet suivant (qui sera consommé) ; la chaîne
retournée sera allouée par la fonction (attention, il n'y a pas de limite de taille sur les
valeurs des attributs)

char* next_raw(FILE* fd) qui retourne la suite des caractères jusqu'au prochain

< (qui ne sera pas consommé ; utiliser ungetc) ; la chaîne retournée sera allouée par

la fonction (attention, il n'y a pas de limite de taille sur les données brutes)
l2info/progc/xml.txt · Dernière modification: 2017/11/23 16:34 par aspicher
Sauf mention contraire, le contenu de ce wiki est placé sous la licence suivante:CC Attribution-
Noncommercial-Share Alike 3.0 Unported [http://creativecommons.org/licenses/by-nc-sa/3.0/]

http://www.lacl.fr/~aspicher/Wiki/l2info/progc/xml 31/12/2017, 10?

#include "xml.h"
/**
* Creates a new element.
*/
xelement_t* create_xelement (const char* name) {
xelement_t* e;
e = (xelement_t*) malloc(sizeof(xelement_t));
e->nom = strdup(name);
e->listatt = NULL;
e->father = NULL;
e->brother = NULL;
e->content = NULL;
return e;
}
/**
* Adds a new attribute to an element.
*
* @return {xattribute_t*} Newly added attribute or NULL if something went wrong.
*/
xattribute_t* add_xattribute (xelement_t* e, const char* name, const char* value){
if (e == NULL)
return NULL;
// either element already has some attributes or not
// in both situations we have to create a new attribute to add
// we call it "atr" and first we initate it
xattribute_t* atr = (xattribute_t*)malloc(sizeof(xattribute_t));
atr->name = strdup(name);
atr->value = strdup(value);
atr->next = NULL;
// Now we check if element has no attribute
if (e->listatt == NULL) {
e->listatt=atr;
}
// but if element already has some attributes, we add the new attribute
// at the end of it's attribute list
else {
xattribute_t* temp = e->listatt;
// go until reaching the last attribute
while (temp->next != NULL)
temp = temp->next;
temp->next = atr;
}
// then we return the newly added attribute
return atr;
}
/**
* Adds an element as a child to another element.
*
* @param e Father element
* @param s Child element
*
* @return {short} It's 1 in case of successfull invokation and 0 otherwise.
*/
short add_sub_xelement (xelement_t* e, xelement_t* s){
// make sure "e" is an element
if(e == NULL)
return 0;
// make sure "s" is an element
if (s == NULL)
return 0;
// make sure "s" does not already have a father
if (s->father == NULL) {
// check if "e" has no content
if(e->content == NULL){
e->contype = element;
e->content = (content_t*) malloc(sizeof(content_t));
e->content->elem = s;
} else if (e->contype == element) { // check if "e"'s content type is element
// now add "s" at the end of "e"'s children list
xelement_t* tmpHead = e->content->elem;
while (tmpHead->brother != NULL)
tmpHead = tmpHead->brother;
tmpHead->brother = s;
} else
// "e" has a raw content so no element can be added as it's child
return 0;
return 1;
}
return 0;
}
/**
* Adds a raw content to an element.
*
* @return {short} It's 1 in case of successfull invokation and 0 otherwise.
*/
short add_raw (xelement_t* e, const char* r){
if (e == NULL)
return 0;
if(e->content == NULL) {
e->contype = raw;
e->content = (content_t*) malloc(sizeof(content_t));
e->content->raw_data = strdup(r);
return 1;
} else if(e->contype == raw){
free(e->content->raw_data);
e->content->raw_data = strdup(r);
return 1;
} else
return 0;
}
/**
* Deletes an element and frees it's memory.
*/
void delete_xelement (xelement_t* e) {
if (e == NULL)
return 0;
if (e->nom != NULL)
free(e->nom);
// if element has any attributes
if (e->listatt != NULL) {
xattribute_t tmpHead = e->listatt,
tmp;
do {
if (tmpHead->name != NULL)
free(tmpHead->name);
if (tmpHead->value != NULL)
free(tmpHead->value);
tmp = tmpHead;
tmpHead = tmpHead->next;
free(tmp);
} while (tmpHead != NULL);
}
// if element is an child of another element
if (e->father != NULL) {
/**
* remove the element from children list of it's father
*/
xelement_t tmpHead = e->father->content_t;
if (tmpHead == e)
e->father->content = e->brother;
else {
while (tmpHead != e) {
xelement_t tmpBeforeHead = tmpHead;
tmpHead = tmpHEAD->brother;
}
tmpBeforeHead->brother = tmpHead->brother;
}
}
// if element has any content
if (e->content !== NULL) {
if (e->contype == raw)
free(e->content->raw_data);
else if (e->contype == element)
// delete it's children one by one
while (e->content !== NULL)
delete_xelement(e->content);
free(c->content)
}
free(e);
}
void save_xelement (FILE* fd, xelement_t* e) {
fprintf(fd, "<%s", e->nom);
if (e->listatt != NULL) {
xattribute_t tmpHead = e->listatt;
while (tmpHead != NULL) {
fprintf(fd, " %s=\"%s\"", tmpHead->name, tmpHead->value);
tmpHead = tmpHead->next;
}
}
if (e->content != NULL) {
fputs(">", fd);
if (e->contype == raw)
fputs(e->content, fd);
else {
xelement_t tmpHead = e->content;
while (tmpHead != NULL) {
save_xelement(fd, tmpHead);
tmpHead = tmpHead->brother;
}
}
fprintf(fd, "</%s>", e->nom);
} else
fputs(" />", fd);
}
/**
* Writes an XML element to a file.
*
* @param name File's name
* @param e XML element
*
* @return {short} It's 1 in case of successfull invokation and 0 otherwise.
*/
short save_xml (const char* name, xelement_t* e) {
FILE *fptr = fopen(name, "w");
if (fptr == NULL)
return 0;
save_xelement(fptr, e);
fclose(fptr);
return 1;
}
void print_xelement (xelement_t* e) {
save_xelement(stdout, e);
}
#ifndef XML_H
#define XML_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
typedef enum type {
raw = 1,
element
} type_t;
typedef struct xattribute_s {
char* name;
char* value;
struct xattribute_s* next;
} xattribute_t;
typedef union content_u {
char* raw_data;
struct xelement_s* elem;
} content_t;
typedef struct xelement_s {
char* nom;
struct xattribute_s* listatt;
struct xelement_s* father;
struct xelement_s* brother;
type_t contype;
content_t* content;
} xelement_t;
xelement_t* create_xelement (const char* name);
xattribute_t* add_xattribute (xelement_t* e, const char* name, const char* value);
short add_sub_xelement (xelement_t* e, xelement_t* s);
short add_raw (xelement_t* e, const char* r);
void delete_xelement (xelement_t* e);
void save_xelement (FILE* fd, xelement_t* e);
void save_xml (const char* name, xelement_t* e);
void print_xelement (xelement_t* e);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment