Catégorie : développement

Tout ce qui concerne le développement en général, que ce soit des choses générales, ou des choses bien précises. Cela va de la gestion de projet à la recherche du fonctionnement de pointeurs en C.

Librairie Apr : tutoriel : getopt-sample.c

Note

Regardez le tutoriel au complet, en Anglais, ici.
Vous trouverez tout ce tutoriel séparé en plusieurs pages ici.
Ce fichier est l’exemple le plus simple pour mettre en oeuvre les fonctions apr_xx
Il vient d’ici.

/**
 * Exemple du tutoriel apr
 * http://dev.ariel-networks.com/apr/
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <apr_general.h>
#include <apr_getopt.h>

/**
 * command line options sample code
 * @remark Error checks omitted
 */
int main(int argc, const char *argv[])
{
    apr_status_t rv;
    apr_pool_t *mp;
    /* API is data structure driven */
    static const apr_getopt_option_t opt_option[] = {
        /* -i nomfichier or --in nomfichier : */
        { "in", 'i', TRUE, "fichier entrant" },
        /* -o nomfichier or --out nomfichier : */
        { "out", 'o', TRUE, "fichier sortant" },
        /* -h or --help : */
        { "help", 'h', FALSE, "voir l'aide" },
        /* sentinelle de fin : */
        { NULL, 0, 0, NULL },
    };
    apr_getopt_t *opt;
    int optch;
    const char *optarg;

    apr_initialize();
    apr_pool_create(&mp, NULL);

    /* initialize apr_getopt_t */
    apr_getopt_init(&opt, mp, argc, argv);

    /* parcourir toutes les options via opt_option[] */
    while ((rv = apr_getopt_long(opt,opt_option,
                                 &optch,
                                 &optarg)) == APR_SUCCESS) {
        switch (optch) {
        case 'i':
            printf("opt=i, %s\n", optarg);
            break;
        case 'o':
            printf("opt=o, %s\n", optarg);
            break;
        case 'h':
            printf("afficher l'aide\n");  /* no arg */
            break;
        }
    }
    if (rv != APR_EOF) {
        printf("mauvaises options\n");
    }

    apr_terminate();
    return 0;
}

Hint et tips jQuery

Voici quelques astuces toutes bêtes mais qui vont vous faire gagner beaucoup de temps si vous démarrez avec jQuery :

  1. Dans d’autres librairies JavaScript (prototype pour ne pas la nommer), le symbole $(« Xx ») va chercher l’objet de votre document (DOM) en fonction de son Id. Donc, ici $(« Xx« ) ira chercher l’objet déclaré avec la propriété Id= »Xx ».
    Avec jQuery, ce n’est pas la même chose : si on veut chercher par Id, il faut ajouter le dièse (#). Donc le code équivalent précédent, mais pour jQuery, serait : $(« #Xx« ).
    J’espère que cette astuce vous fera déjà gagner quelques minutes / heures.
  2. Il y a deux possibilités de mettre du code au chargement de la page :
    1. Lorsque votre document a fini d’être chargé, mais pas forcément le reste : c’est le code suivant :
      $(document).ready(function() {
        // code lorsque le DOM a tout juste fini
        // (la page n'est pas encore visible !)
      });
    2. Lorsque la page au complet, avec toutes les images et tout ce que vous avez déclaré dans la page, a fini d’être chargée : c’est le code suivant :

      $(window).bind('load', function() {
        // code lorsque toute la page a fini
        // d'être chargée
      });

    L’intérêt de ça me direz-vous ? Voilà ce que vous pouvez faire :

    • Créer une page XHTML entièrement valide, mais qui ne cache rien de manière à ce que n’importe quel navigateur, y compris un navigateur destiné à lire en mode texte (lynx, par exemple), afin que des personnes handicapées puissent tout lire sans problème ;
    • Créer un évènement qui, dès le chargement de la page, alors qu’elle n’est pas affichée, cache la plupart des éléments afin d’afficher un message pour patienter ;
    • Créer un évènement qui, une fois que toutes les images et la totale sont chargées, affiche le menu et ce qu’il y a derrière.

    Vous pouvez éventuellement penser que c’est un retour en arrière, à savoir qu’il y a quelques années, on n’affichait la page que lorsque tout était chargé, et puis on s’est dit : « le monde va vite, très vite, il faut afficher des choses dès que l’on peut ». Donc maintenant la plupart des navigateurs, dès qu’ils ont le minimum de choses à afficher, le mettent visuellement devant l’Internaute. Du point de vue élégance, je trouve ça laid et si vous voulez faire quelque chose de moderne, affichez uniquement un texte en fondu qui affiche « patientez, chargement », et vous verrez que le fait d’afficher la page en entier uniquement lorsqu’elle est téléchargée est vraiment sympa et démarque des autres sites. Vous pouvez aussi ne cacher que certains menus qui ne sont pas vraiment présentables tant que les images qui vont avec ne sont pas affichées, et ne pas cacher le reste. Bon bref c’est quelque chose de vraiment utile, de pouvoir gérer ces deux événements qui sont déclenchés à des moments cruciaux.

Librairie Apr : tutoriels 8 et 9

8. gestion des chaines de caractères

Je suppose que vous connaissez les bases de manipulation de chaine en C, notamment les fonctions strlen(3) et strcpy(3). La librairie libapr propose des fonctions extrêmement utiles de gestion des chaines. Elles sont pratiquement les mêmes que leur équivalent en ANSI C. Pourquoi libapr fournit encore une autre possibilité de gestion de chaines ? L’intérêt est directement lié avec le principe de gestion par pool mémoire. Dans une gestion habituelle de chaine en C, il faut écrire du code de gestion de mémoire : allocation, libération, etc. Le code qui suit en est l’exemple.

/* exemple de manipulation de chaine ANSI C */
/* (code simpliste) */
/* on concatène 3 chaines : s1, s2, s3 */
int len1 = strlen(s1);
int len2 = strlen(s2);
int len3 = strlen(s3);
int total_len = len1 + len2 + len3;
char *cat_str = malloc(total_len + 1);
strcpy(cat_str, s1);
strcat(cat_str, s2);
strcat(cat_str, s3);
/* plus tard, il nous faut libérer la mémoire allouée */
free(cat_str);

La même chose avec la librairie libapr s’écrira ainsi :

/* pseudo code sur la gestion des chaines de libapr */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
/* apr_pstrcat() gère à la fois l'allocation
 * mémoire et la concaténation des chaines
 * Si la chaine concaténée est en lecture seule,
 * il nous faut utiliser le type 'const char*'.
 */
const char *cat_str = apr_pstrcat(mp, s1, s2, s3, NULL);
/* plus tard, la seule chose à faire
 * est de détuire le pool mémoire
 * pour tout libérer
 */
apr_pool_destroy(mp);

De la même façon que apr_pstrcat(), apr_psprintf() vous aide à écrire un code beaucoup plus simple. Vous trouvez d’autres fonctions sur les chaines dans apr_strings.h.

9. Gestion de de l’heure

Les fonctions mises à disposition par libapr sont basées sur le schéma POSIX. Une valeur de type apr_time_t représente le temps écoulé depuis une époque UNIX c’est à dire depuis le 1/1/1970. Seulement il y a deux différences de taille :

  1. apr_time_t est géré sur 64 bits (long long)
  2. apr_time_t réprésente des microsecondes

La fonction la plus utile est apr_time_now(). Comme vous l’imaginez, elle renvoie la date et l’heure courante. Vous trouverez sa déclaration dans apr_time.h.

/* extrait de apr_time.h */
APR_DECLARE(apr_time_t) apr_time_now(void);

Très souvent nous avons à convertir une valeur apr_time_t dans d’autres formats. Il y a principalement deux formats qui vous seront utiles :

  1. Une structure temps : apr_time_exp_t (time structure)
  2. Les formats exprimant les dates sous forme de chaines (p.ex. la rfc822)

Pour convertir apr_time_t en une structure apr_time_exp_t, il faut utiliser les fonctions de la libapr suivantes :

/* extrait de apr_time.h */
APR_DECLARE(apr_status_t) apr_time_exp_gmt(
    apr_time_exp_t *result, apr_time_t input);
APR_DECLARE(apr_status_t) apr_time_exp_lt(
    apr_time_exp_t *result, apr_time_t input);

La fonction apr_time_exp_gmt() renvoie le résultat qui est dans une zone GMT, et apr_time_exp_lt() renvoie le résultat en se basant sur la zone de temps locale. Le premier argument des deux fonctions est un argument résultat.
Il est possible de faire la conversion dans l’autre sens : convertir une structure apr_time_exp_t en une valeur apr_time_t.

/* extrait de apr_time.h */
APR_DECLARE(apr_status_t) apr_time_exp_get(
    apr_time_t *result, apr_time_exp_t *input);

Il y a plusieurs fonctions pour convertir une valeur apr_time_t en différents formats de chaines représentant une date / heure :

/* extrait de apr_time.h */
APR_DECLARE(apr_status_t) apr_rfc822_date(
    char *date_str, apr_time_t t);
APR_DECLARE(apr_status_t) apr_ctime(
    char *date_str, apr_time_t t);
APR_DECLARE(apr_status_t) apr_strftime(
    char *s, apr_size_t *retsize, apr_size_t max,
    const char *format, apr_time_exp_t *tm);

A l’inverse, si on a des chaines dans ces formats, et qu’on veut les convertir en une valeur apr_time_t, il faut appeler des fonctions utilitaires de apr-util (un sous ensemble de la libapr), définis dans apr_date.h.
Regardez l’exemple de time-sample.c pour avoir un exemple concret de l’utilisation de ces fonctions.
REMARQUE : Comme expliqué juste avant, apr_time_t est un type long long (64 bits). Notez bien que le code suivant déclenchera un débordement.

/* Exemple BOGUE. Génération de débordement */
const apr_time_t UNE_HEURE = 1000 * 1000 * 60 * 60;

On le résoud en faisant une conversion de type explicite, mais je vous recommande d’utiliser simplement le type que fournit la librairie libapr. Le code suivant le montre.

/* deux exemples pour contourner
 * le débordement expliqué juste avant
 */
const apr_time_t UNE_HEURE = APR_TIME_C(1000) * 1000 * 60 * 60;

ou bien :

const apr_time_t UNE_HEURE = APR_USEC_PER_SEC * 60 * 60;

REMARQUE : Parfois, souvent en déboguant, on veut afficher des valeurs de temps en clair. Malheureusement, Unix et Windows ont des manière différentes de spécifier le type 64 bits quand on se sert de printf(3). Sur Unix c’est « %lld » est sur Windows c’est « %I64d ». Pour de tels problèmes de portabilité, la librairie libapr fournit des spécificateurs de format, p.ex. APR_INT64_T_FMT. Il y a aussi APR_TIME_T_FMT dans apr_time.h. On peut écrire du code parfaitement portable en utilisant ces spécificateurs.

/* Sur Unix, APR_INT64_T_FMT est défini dans apr.h */
#define APR_INT64_T_FMT "lld"
/* Sur Windows, APR_INT64_T_FMT est défini dans apr.h */
#define APR_INT64_T_FMT "I64d"
/* extrait de apr_time.h */
#define APR_TIME_T_FMT APR_INT64_T_FMT
/* On peut utiliser APR_TIME_T_FMT de cette façon : */
printf("L'heure courante est : %" APR_TIME_T_FMT "[us]\n",
    apr_time_now());

Librairie Apr : tutoriel : fichier time-sample.c

/**
 * Exemple du tutoriel apr
 * http://dev.ariel-networks.com/apr/
 */
#ifdef HAVE_CONFIG_H
#include 
#endif

#include 
#include 
#include 

#include 
#include 

static void format_to_struct(apr_time_t t)
{
    apr_time_exp_t tm;
    apr_time_t t2;

    apr_time_exp_gmt(&tm, t);
    printf("GMT: tm.hour = %d, tm.min = %d, tm.sec = %d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
    apr_time_exp_lt(&tm, t);
    printf("local: tm.hour = %d, tm.min = %d, tm.sec = %d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);

    /* conversion inverse */
    apr_time_exp_gmt_get(&t2, &tm);/* from tm to t2 */
    assert(t == t2);
}

static void format_to_string(apr_time_t t)
{
    char rbuf[APR_RFC822_DATE_LEN + 1];
    char cbuf[APR_CTIME_LEN + 1];

    apr_rfc822_date(rbuf, t);
    printf("apr_rfc822_date: %s\n", rbuf);
    apr_ctime(cbuf, t);
    printf("apr_ctime: %s\n", cbuf);
}

/**
 * Exemple des routines de gestion de l'heure
 * @remark Vérif. des erreurs omise
 */
int main(int argc, const char *argv[])
{
    apr_pool_t *mp;
    apr_time_t t;

    apr_initialize();
    apr_pool_create(&mp, NULL);

    /* récupérer l'heure courante
     * (temps écoulé depuis le 1/1/1973, en micro-secondes)
     */
    t = apr_time_now();
    printf("Heure courante : %" APR_TIME_T_FMT "[us]\n", t);
    printf("Heure courante : %" APR_TIME_T_FMT "[ms]\n", apr_time_as_msec(t));
    printf("Heure courante : %" APR_TIME_T_FMT "[s]\n", apr_time_sec(t));

    /* formattage de l'heure */
    format_to_struct(t);
    format_to_string(t);

    apr_terminate();
    return 0;
}

Librairie Apr : tutoriel : dir-sample.c

Note

Regardez le tutoriel au complet, en Anglais, ici.
Vous trouverez tout ce tutoriel séparé en plusieurs pages ici.
Ce fichier est l’exemple le plus simple pour mettre en oeuvre les fonctions apr_xx
Il vient d’ici.

/**
 * Exemple de code tutoriel
 * http://dev.ariel-networks.com/apr/
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <apr_general.h>
#include <apr_file_info.h>
#include <apr_strings.h>

/**
 * Parcours d'un répertoire de manière récursive
 * @return TRUE si tout est Ok, sinon FALSE.
 */
static int traverse_dir(const char *dirpath, apr_pool_t *mp)
{
    apr_status_t rv;
    apr_finfo_t dirent;
    apr_dir_t *dir;

    printf("Directory: %s\n", dirpath);
    if ((rv = apr_dir_open(&dir, dirpath, mp)) != APR_SUCCESS) {
        return FALSE;
    }

    while ((apr_dir_read(&dirent,
                         APR_FINFO_DIRENT | APR_FINFO_TYPE | APR_FINFO_NAME,
                         dir)) == APR_SUCCESS) {
        if (dirent.filetype == APR_DIR) {
            char *path;
            if (strcmp(dirent.name, ".") == 0 || strcmp(dirent.name, "..") == 0) {
                continue;
            }

            if ((rv = apr_filepath_merge(&path,
                                         dirpath,
                                         dirent.name,
                                         0,
                                         mp)) != APR_SUCCESS) {
                goto error;
            }
            /* beaucoup plus simple :
             * path = apr_pstrcat(mp, dirpath, "/", dirent.name, NULL);
             */
             traverse_dir(path, mp);
        } else if (dirent.filetype == APR_REG) {
            printf("Nom de fichier normal/classique : %s\n",
                apr_pstrcat(mp, dirpath, "/", dirent.name, NULL));
        } else {
            printf("Nom de fichier anormal/pas classique : %s\n",
                apr_pstrcat(mp, dirpath, "/", dirent.name, NULL));
        }
    }

    if ((rv = apr_dir_close(dir)) != APR_SUCCESS) {
        return FALSE;
    }
    return TRUE;

 error:
    apr_dir_close(dir);
    return FALSE;
}

/**
 * Exemple de listing d'un répertoire
 * @remark Vérif. des erreurs omises
 */
int main(int argc, char **argv)
{
    apr_status_t rv;
    apr_pool_t *mp;
    const char *dirname;
    apr_finfo_t finfo;

    if (argc == 2) {
        dirname = argv[1];
    } else {
        dirname = ".";
    }

    apr_initialize();
    apr_pool_create(&mp, NULL);

    if ((rv = apr_stat(&finfo,
                       dirname,
                       APR_FINFO_TYPE, mp)) != APR_SUCCESS) {
        goto error;
    }
    if (finfo.filetype != APR_DIR) {
        printf("precisez un nom de repertoire\n");
        goto error;
    }
    traverse_dir(dirname, mp);

    apr_terminate();
    return 0;

 error:
    apr_terminate();
    return -1;
}

Librairie Apr : tutoriel : fichier flock-sample.c

Note

Regardez le tutoriel au complet, en Anglais, ici.
Vous trouverez tout ce tutoriel séparé en plusieurs pages ici.
Ce fichier exemple vient d’ici.

/**
 * Exemple pour le tutoriel apr
 * http://dev.ariel-networks.com/apr/
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <apr_general.h>
#include <apr_file_io.h>

static apr_status_t do_writelock(const char *fname, apr_pool_t *mp)
{
    apr_status_t rv;
    apr_file_t *fp;

    if ((rv = apr_file_open(&fp, fname, APR_WRITE, APR_OS_DEFAULT, mp)) != APR_SUCCESS) {
        return rv;
    }
    rv = apr_file_lock(fp, APR_FLOCK_EXCLUSIVE | APR_FLOCK_NONBLOCK);
    /* si vous mettez le drapeau APR_FLOCK_NONBLOCK, apr_file_lock() ne fonctionnera pas.
     * Il faut toujours vérifier la valeur de retour */
    if (rv != APR_SUCCESS) {
        puts("writable-lock failed");
        goto done;
    }
    printf("%s est verrouillé en écriture.\n", fname);
    printf("Appuyez sur une touche pour le déverrouiller.\n");
    getchar();

    apr_file_unlock(fp);
 done:
    apr_file_close(fp);
    return rv;
}

static apr_status_t do_readlock(const char *fname, apr_pool_t *mp)
{
    apr_status_t rv;
    apr_file_t *fp;

    if ((rv = apr_file_open(&fp, fname, APR_READ,  APR_OS_DEFAULT, mp)) != APR_SUCCESS) {
        return rv;
    }
    rv = apr_file_lock(fp, APR_FLOCK_SHARED | APR_FLOCK_NONBLOCK);
    if (rv != APR_SUCCESS) {
        puts("Impossible de mettre le verrou en lecture.");
        goto done;
    }

    apr_file_unlock(fp);
 done:
    apr_file_close(fp);
    return rv;
}

/**
 * Exemple de verrou fichier
 * @remark Vérif. des erreur supprimée pour plus de clarté
 */
int main(int argc, const char *argv[])
{
    apr_status_t rv;
    apr_pool_t *mp;
    int c;
    const char *fname;

    if (argc < 2) {
        printf("Utilisation : %s fichier-de-sortie\n", argv[0]);
        return 0;
    }
    fname = argv[1];

    apr_initialize();
    apr_pool_create(&mp, NULL);

    puts("verrou-écriture / verrou-lecture [w/r]?");
    c = getchar();
    if (c == 'w') {
        rv = do_writelock(fname, mp);
    } else if (c == 'r') {
        rv = do_readlock(fname, mp);
    }

    apr_pool_destroy(mp);
    apr_terminate();
    return 0;
}

Librairie Apr : tutoriels : 6 et 7

6. Les verrous sur les fichiers

Lorsqu’on veut verrouiller des fichiers entre processus, il faut utiliser apr_file_lock(). Historiquement, des confusions se sont installées concernant le verouillage sur Unix. C’est pourquoi le fait de n’avoir que deux fonctions simples grâce à la librairie libapr est très appréciable.

/* extrait de apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_lock(apr_file_t *thefile, int type);
APR_DECLARE(apr_status_t) apr_file_unlock(apr_file_t *thefile);

apr_file_lock() demande deux arguments. Le premier est un objet apr_file_t. Le second est un drapeau (« flag »), qui sert à spécifier les blocages qu’on veut imposer (« lock type »). Il peut être soit APR_FLOCK_SHARED, soit APR_FLOCK_EXCLUSIVE. We can use the former as a readable lock, and the latter as a writable lock. Pour déverrouiller le fichier, il suffit d’appeler apr_file_unlock(). Ou bien le fait d’appeler apr_file_close() le déverrouille implicitement. Regardez flock-sample.c pour savoir comment faire.
De plus, il est possible d’utiliser un paramètre composé de drapeaux (« bitwised-or flag ») en précisant APR_FLOCK_NONBLOCK. Sans le flag APR_FLOCK_NONBLOCK, apr_file_lock() est bloquant. Avec ce flag, APR_FLOCK_NONBLOCK, si apr_file_lock() ne peut pas verouiller un fichier, il ne bloque pas et renvoie immédiatement un code erreur : APR_EAGAIN.
Il faut toujours s’assurer que la valeur de retour de apr_file_lock() est APR_SUCCESS. Si c’est le cas, le fichier a été verouillé avec succès. Sinon, il n’a pas pu être verouillé.

7. Gestion des répertoires dans le système de fichiers

Quand on veut faire quelque chose avec des répertoires, il faut toujours appeler d’abord apr_dir_open(). Après cet appel, on a un objet apr_dir_t. La seule chose que l’on puisse faire avec un objet apr_dir_t est de parcourir le répertoire. La fonction à utiliser est apr_dir_read(). A la fin on appelle apr_dir_close() pour fermer le répertoire. Ci-suivent les déclarations de la librairie :

/* extrait de apr_file_info.h */
APR_DECLARE(apr_status_t) apr_dir_open(apr_dir_t **new_dir,
    const char *dirname, apr_pool_t *pool);
APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo,
    apr_int32_t wanted, apr_dir_t *thedir);
APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *thedir);

Le premier argument de apr_dir_open() est un argument résultat. C’est grâce à lui qu’on récupère l’objet apr_dir_t qui est crée. Le second argument est le nom du répertoire. Le troisième est le nom du pool mémoire à utiliser.
Le premier arguement de  apr_dir_read() est un argument résultat. Comme mentionné précédemment, apr_finfo_t est un type complet. Donc il faut l’allouer explicitement. apr_dir_read() renvoie une entrée du répertoire  apr_finfo_t. L’entrée est soit un fichier soit un répertoire. Le second argument est une aggrégation de drapeaux (« bit-wised flag »). Ces drapeaux sont définis dans apr_file_info.h. Ils ont tous le préfixe APR_FINFO_, par exemple APR_FINFO_SIZE, APR_FINFO_TYPE et APR_FINFO_NAME. Le troisième argument est l’objet apr_dir_t à parcourir.

Voici un code d’exemple :

/* pseudo code expliquant apr_dir_read() */
/* no error checks */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
/* répertoire à scanner */
const char *dirpath = "/home";
apr_dir_t *dir;
/* créer l'objet apr_dir_t */
apr_dir_open(&dir, dirpath, mp);
apr_finfo_t dirent;
/* remplir l'objet apr_finfo_t */
apr_dir_read(&dirent, APR_FINFO_DIRENT, dir);
/* dirent est la première entrée du répertoire.
 * Cette entrée est soit un fichier soit un répertoire. */
apr_dir_close(dir);

Dans l’exemple au-dessus, on appelle apr_dir_read() une seule fois, mais habituellement on le fait en boucle pour énumérer tous les fichiers d’un répertoire. Pour ce faire, il suffit juste d’appeler apr_dir_read() tant qu’il renvoie APR_SUCCESS. Regardez l’exemple qui est plus parlant : dir-sample.c.

/* pseudo code sur la boucle apr_dir_read(). Vérif. des erreurs omise */
/* Boucle typique de apr_dir_read() */
apr_dir_open(&dir, dirpath, mp);
while ((apr_dir_read(&dirent, APR_FINFO_NAME, dir)) == APR_SUCCESS) {
    printf("Le nom est %s\n", dirent.name);
}
apr_dir_close(dir);

Comme vous pouvez l’imaginer, la position courante du répertoire est stockée dans l’objet apr_dir_t. Le fait d’appeler apr_dir_read() fait avancer la position d’un cran. On peut reculer cette potision (qui est interne) en appelant apr_dir_rewind(). Les seules opération que l’on peut faire avec la position sont ces deux là : aller en avant et aller en arrière.

Comme vous pouvez le voir dans dir-sample.c, si vous scannez un répertoire de manière récursive, vous devez appeler apr_dir_open() de manière récursive, ici aussi.

REMARQUE : Sur Unix, apr_dir_read() renvoie un objet apr_finfo_t dont la propriété apr_file_t::fname est NULL.

Librairie Apr : tutoriel : finfo-sample.c

Note

Regardez le tutoriel au complet, en Anglais, ici.
Vous trouverez tout ce tutoriel séparé en plusieurs pages ici.
Ce fichier est l’exemple le plus simple pour mettre en oeuvre les fonctions apr_xx
Il vient d’ici.

/**
 * Tutoriel apr, exemple de code
 * http://dev.ariel-networks.com/apr/
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <apr_general.h>
#include <apr_file_io.h>

/**
 * exemple de récupération d'informations sur un fichier
 * @remark Vérif. des erreurs omise
 */
int main(int argc, const char *argv[])
{
    apr_status_t rv;
    apr_pool_t *mp;
    apr_file_t *fp;
    const char *fname;
    apr_finfo_t finfo;

    if (argc < 2) {
        printf("Utilisation : %s fichier-de-sortie\n", argv[0]);
        return 0;
    }
    fname = argv[1];

    apr_initialize();
    apr_pool_create(&mp, NULL);

    /* @remark depuis apr-1.1.0, utiliser APR_FOPEN_READ au lieu de APR_READ */
    if ((rv = apr_file_open(&fp, fname, APR_READ, APR_OS_DEFAULT, mp)) != APR_SUCCESS) {
        return -1;
    }

    /* Ci-suivent deux façons différentes pour récupérer
     * l'information : elles donnent le même résultat.
     *
     * Si vous avez déjà ouvert le fichier,
     * le premier est le meilleur. Sinon, le
     * second est le meilleur.
     */

    /* Récupération des informations. Via apr_file_t */
    rv = apr_file_info_get(&finfo, APR_FINFO_NORM, fp);
    /* Récupération des informations. Via le nom de fichier */
    rv = apr_stat(&finfo, fname, APR_FINFO_NORM, mp);

    apr_file_close(fp);

    apr_terminate();
    return 0;
}

Librairie Apr : tutoriel 5

5. Gestion des fichiers

Quand on veut des opérations dans un fichier, il faut en premier lieu appeler apr_file_open(). Voilà la déclaration :

/* extrait de apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **newf,
    const char *fname, apr_int32_t flag,
    apr_fileperms_t perm, apr_pool_t *pool);

Le type du premier argument est apr_file_t**, qui est un argument résultat. Plus précisément, il stocke l’objet apr_file_nouvellement crée lors de l’appel à apr_file_open(). Le second argument est le répertoire + nom du fichier. Le troisième est une composition de flags, définis dans apr_file_io.h. Le quatrième argument est le type de permission qu’on accorde sur ce fichier, mais cela ne fonctionne que lors de la création de fichier. Les flags sont définis dans apr_file_info.h. Par exemple, si vous voulez créer un fichier dont les permissions d’accès sont 0600, c-à-d. lecture-écriture uniquement autorisée par le possesseur du fichier, il vous faudra spécifier APR_UREAD|APR_UWRITE. Vous utiliserez le plus couramment APR_OS_DEFAULT comme permission. Le cinquième et dernier argument est le pool mémoire à utiliser. Précisons pour le lecteur occasionnel que le pool mémoire aura été crée auparavant via apr_pool_create().
Après qu’on ait ouvert le fichier, on peut l’utiliser pour d’autres fonctions de la librairie apr. On peut trouver ces fonctions dans apr_file_io.h. Les fonctions les plus basiques sont apr_file_read() et apr_file_write(). Comme vous l’imaginez, apr_file_read() donne la possibilité de lire quelque chose du fichier et apr_file_write() d’écrire quelque chose dedans.Voilà les déclarations :

/* extrait de apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile,
    void *buf, apr_size_t *nbytes);
APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile,
    const void *buf, apr_size_t *nbytes);

Le troisième argument des deux fonctions est un argument en entrée mais aussi en sortie (= résultat). En entrée, il sert à spécifier la longueur de la valeur, et on a en sortie le retour du nombre d’octets résultant de l’opération. Pour simplifier, apr_file_read() renvoie le nombre d’octets lus, et apr_file_write() renvoie le nombre d’octets écrits. Voici un code d’exemple.

/* pseudo-code expliquant apr_file_write() */
strcpy(outbuf, "123456789");
apr_size_t outlen = strlen(outbuf);
rv = apr_file_write(fp, outbuf, &outlen);
printf("apr_file_write() rv = %d, nb octets = %d\n", rv, outlen);

Dans ce cas, avant d’appeler apr_file_write(), la variable ‘outlen’ vaut 9. En passant &outlen à apr_file_write(), on dit à la librairie qu’il faut écrire 9 octets. Au retour de apr_file_write(), la valeur de ‘outlen’ est remplie de ce qui a été effectivement écrit. Habituellement c’est 9, surtout si c’est un fichier local. En théorie, la valeur pourrait être plus petite (si le disque est plein par exemple).
Il faut toujours appeler apr_file_close() pour fermer le fichier. Il est possible aussi de le fermer automatiquement en détruisant le pool mémoire par lequel il a été crée, mais je préfère fermer mes fichiers de manière explicite. Cela n’engage que moi.

REMARQUE : Il y a quelques incompatibilités entre les différentes versions de la librairie libapr. Le troisième argument de apr_file_open() a comme préfixe APR_FOPEN_ depuis libapr-1.1.0, alors que ce n’était pas le cas avant. Il faut donc utiliser APR_FOPEN_CREATE au lieu de APR_CREATE. Consultez apr_file_io.h pour voir ce qu’il vous faut réellement utiliser. De la même façon, le quatrième argument de apr_file_open() a le préfixe APR_FPROT_ depuis la libapr-1.1.0.

REMARQUE : Il y a un problème de portabilité sur le séparateur des répertoires/noms de fichiers. Unix(POSIX) utilise le slash (‘/’), et les systèmes Microsoft utilisent le backslash (‘\’) en tant que séparateur. Si vous voulez écrire une application qui tourne sur les deux système, je vous conseille de transformer toujours les séparateur en slash (‘/’) car les systèmes Microsoft l’acceptent quand même.

REMARQUE : Faites attention lors du l’utilisation de apr_file_gets(). Faire un appel à cette fonction sans le paramètre APR_BUFFERED dégrade sérieusement les performances de l’opération. Cela s’explique par le fait que apr_file_gets() appelle apr_file_read(), et, sans ce paramètre, il y aura un appel pour chaque octet à lire. Souvenez vous bien qu’il vous faut ouvrir le fichier avec le flag APR_BUFFERED si vous voulez utiliser apr_file_gets().

Je vous recommande d’utiliser toujours APR_BUFFERED sauf dans ces cas :

  • Quand vous « mmap »-ez le fichier (cela génère une erreur du fichier à « mmapp-er ») ;
  • Aucune lecture/écriture (p.ex. un fichier destiné à être uniquement un verrou) ;
  • Vous êtes sûr que vous lisez ou écrivez avec un buffer suffisamment gros.

REMARQUE : Si vous ouvrez un fichier avec le flag APR_BUFFERED et que vous appelez par la suite apr_file_trunc(), n’oubliez pas d’appeler apr_file_flush() avant apr_file_trunc(). Sinon vous allez perdre des informations.

REMARQUE: Si vous ouvrez un fichier avec le flag APR_BUFFERED, et que le fichier est partagé par plusieurs threads, il vous faut obligatoirement préciser APR_XTHREAD. Malheureusement, ce flag a quelques revers de médailles sur les systèmes d’exploitation Windows. Par expérience, je vous conseillerai d’éviter d’utiliser APR_XTHREAD sur les systèmes d’exploitation Windows.

Il est possible d’avoir des informations sur le fichier telles que la date de création, de modification, le possesseur du fichier, les permissions, etc. Ces informations sont dans la structure apr_finfo_t, que vous trouverez décrite dans apr_file_info.h. Il y a deux fonctions que fournit la librairie libapr :

/* extrait de apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
    apr_int32_t wanted, apr_file_t *thefile);
/* extrait de apr_file_info.h */
APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo,
    const char *fname, apr_int32_t wanted, apr_pool_t *pool);

Il faut un objet apr_file_t pour la fonction apr_file_info_get(), et un nom de fichier pour apr_stat(). Si on a déjà ouvert ce fichier et qu’on a donc un objet apr_file_t crée, c’est plus pratique de se servir de apr_file_info_get(). Sinon, il faut appeler apr_stat(). A l’inverse de beaucoup d’autres types, apr_finfo_t est un type complet. Plutôt que d’appeler une fonction de la librairie pour créer l’objet, il faut allouer une structure apr_finfo_t explicitement. Typiquement, c’est alloué sur la pile locale, parce qu’on n’a souvent besoin besoin que d’un seul attribut, comme la taille du fichier, ou sa date de création, et rien de plus. Notez que quelquefois des informations telles que apr_finfo_t::fname, sont allouées dans le pool mémoire que vous passez en paramètre. Faites attention, c’est parfois la cause de problèmes de fuites mémoire. Regardez l’exemple à finfo-sample.c pour bien comprendre le fonctionnement.

Il y a quelques fonctions de gestion des fichiers qui fonctionnent en se basant sur un nom de fichiers. Par exemple, apr_file_remove() et apr_file_copy(). Vous lez trouverez dans apr_file_io.h et apr_file_info.h.

REMARQUE : Quelques fonctions de la librairie aprlib prennent en paramètre des argument initialisés par des flags que l’on peut combiner (« bit-wised flags ») pour récupérer les attributs des fichiers. Ces fonctions sont apr_dir_read(), apr_stat(), apr_lstat(), et apr_file_info_get(). Notez que selon la valeur que vous donnez à l’argument, ce dernier peut ne pas exister sur le système d’exploitation, et dans ce cas, renvoyer la valeur d’erreur APR_INCOMPLETE.