la magie des SLIST

Tags: ,
No Comments »

Il y a 5 ans de cela, je vous entretenais sur la beauté des SLIST, un concept issu du monde BSD permettant de facilement manipuler des listes chainées dans votre code C sans avoir à réinventer la roue à chaque code.

Comme je n’avais plus manipulé ce type d’objet depuis un certain temps, je me suis replongé dans ce petit tutoriel, mais je lui ai trouvé un manque que je m’apprète à combler ici même.

La manipulation des SLIST est très aisée lorsque l’on a la possibilité de déclarer sa liste de manière globale, il suffit alors, dans chaque fonction, d’appeler la “tête de liste”, souvent appelée head, via les multiples macros présentes dans queue.h.
J’ai ecrit, toujours dans la documentation sus-citée, un minuscule passage sur la façon d’utiliser les SLIST avec les macros SLIST_FIRST et SLIST_NEXT, mais je m’aperçois qu’on ne se rend pas compte de leur utilité.

Prenons le cas suivant, je veux créer une liste chainée de type SLIST dans une fonction. Je souhaite pouvoir ballader cette liste de fonctions en fonctions, et evidemment, la libérer à la fin de mes travaux. Nous utiliserons notre liste de la sorte :

/* headers et diverses déclarations */
#include <sys/queue.h>

typedef struct Mastruct {
	int id;
	char *element;
	SLIST_ENTRY(, Mastruct) next;
} Mastruct;

/* plein de trucs */

Mastruct *
initialisation(int id, const char *str)
{
	Mastruct *mesdatas;

	/* on déclare la tête de liste */
	SLIST_HEAD(, Mastruct) datahead;
	/* on l'initialise à NULL */
	SLIST_INIT(&datahead);

	if ((mesdatas = malloc(sizeof(Mastruct)) == NULL)
		err("malloc error");

	mesdatas->id = id;
	if (str != NULL) {
		mesdatas->element = strdup(str);
	} else {
		mesdatas->element = NULL;
	}

	/* on accroche la structure fraichement allouée à la tête de liste */
	SLIST_INSERT_HEAD(&datahead, mesdatas, next);

	/* et on renvoie le premier élément de datahead, ici, mesdatas*/
	return SLIST_FIRST(&datahead);
}

Dans ce cas de figure, nous ne renvoyons pas la tête de la liste mais simplement le premier élément de la liste chainée. Pour parcourir simplement cette liste, nous utiliserons la macro SLIST_NEXT :

	Mastruct *mesdatas, *p;

	/* des trucs */

	mesdatas = initialisation(id, unechaine);

	for (p = mesdatas; p != NULL; p = SLIST_NEXT(p, next))
		printf("%d -> %s", p->id, p->element);

De la même façon, on pourra simplement libérer cette liste simplement chainée grace à une boucle de ce type :

void
free_datas(Mastruct *mesdatas)
{
	struct Mastruct *p;

	/* on boucle tant que le nouveau chainon n'est pas nul */
	while (mesdatas != NULL) {
		/* on sauvegarde le prochain chainon */
		p = SLIST_NEXT(mesdatas, next);
		/* on libère le contenu */
		free(mesdatas->element);
		/* puis le chainon courant */
		free(mesdatas);
		/* puis on fait pointer la variable principale vers la sauvegarde */
		mesdatas = p;
	}
}

Désormais, avec un effort minimum, nous voici en mesure de très simplement créer des listes de données rapides et dynamiques.

char *slurp(const char *url)

Tags: ,
No Comments »

Pour mon projet top secret que j’ai, j’avais besoin de télécharger un fichier, en C, via FTP ou HTTP. En cherchant ce qui est fait dans pkg_install, et plus particulièrement dans admin/audit.c, j’ai trouvé un bon exemple d’utilisation de la libfetch, dont le man est à mon avis complètement indigeste.

Petit résumé.

Afin de visualiser un code complet et fonctionnel sur l’utilisation de la libfetch, le fichier audit.c précedemment cité fera reference.

Considérons une fonction, fetch_url qui prendra en paramètre une URL. Afin d’utiliser la libfetch, nous auront besoin du header fetch.h. Voici donc le début de notre fichier contenant la fonction fetch_url :

#include <fetch.h>

void
fetch_url(const char *url)

La première fonction que nous allons utiliser se nomme fetchXGetURL(), elle permettra de valider que l’url passée en paramètre est bien de la forme proto://hostname/peut-etre-quelque-chose, elle renverra une sorte de descripteur, de type fetchIO et placera la taille du fichier visé dans la variable st, de type struct url_stat, le 3eme parametre représente les flags eventuels, nous n’en passons pas :

	struct url_stat st;
	fetchIO *f;

	if ((f = fetchXGetURL(url, &st, "")) == NULL) {
		/* gérez l'erreur */
	}

Une fois le descripteur ouvert et les informations sur la cible récupérées dans la variable st, nous pouvons préparer le conteneur, un pointeur de type char * dont nous pouvons d’ores et déjà reserver la taille :

    buf_len = st.size;
    if ((buf = malloc(buf_len + 1)) == NULL)
        err(EXIT_FAILURE, "malloc failed");

On aura bien entendu préalablement déclaré les variables buf_len et buf :

	char *buf;
	size_t buf_len;

On n’oubliera evidemment pas de libérer buf lorsque l’on en aura plus besoin grace à un free(buf).

Arrive la boucle principale, qui bouclera tant que la taille de ce qui est lu + ce qui a déjà été lu est inferieure à la taille présente dans st.size, soit buf_len. La fonction issue de la libfetch en charge de la lecture se nomme fetchIO_read() :

    while (buf_fetched < buf_len) {
        cur_fetched = fetchIO_read(f, buf + buf_fetched,
            buf_len - buf_fetched);
        if (cur_fetched == 0)
            errx(EXIT_FAILURE, "truncated file");
        else if (cur_fetched == -1)
            errx(EXIT_FAILURE, "Failure during fetch: %s",
                fetchLastErrString);
        buf_fetched += cur_fetched;
    }

Et voila ! la variable buf contient maintenant le resultat de la lecture de l'url const char *url, à vous d'inventer le reste :)

WP Theme & Icons based on GlossyBlue by N.Design Studio
Banner from www.trynthlas.com
Entries RSS Comments RSS Log in