# Pourquoi le langage C?
       
       Je n'ai jamais eu de formation en programmation/développement/code. Enfin presque, j'ai eu droit à un module d'algorithmique en première année de fac de sciences. Mais j'ai toujours aimé programmer. Pour moi, c'est comme une partie d'Age of empire/Rimworld/échecs/ Smallworld/go/... : il faut anticiper, s'adapter et chercher le meilleur moyen d'arriver à ses fins. Quand je dis "meilleur moyen", ça signifie à la fois rapide et élégant.
       
       J'ai appris énormément grâce à l'esprit du logiciel libre, en lisant ce que d'autres ont bien voulu partager sur le web. Aussi j'ai pu découvrir des scripts shell (si on peut parler de programmation), puis le python, le PHP, le javascript et le go. En passant, j'ai appris le C via le site du zéro (pour ceux qui s'en souviennent). Cependant je n'ai pas approfondi : grave erreur.
       
       Pendant la période de confinement de 2020, j'en ai profité pour m'y remettre et réécrire certains projets en C. J'ai donc de nouveau lu du code, notamment celui de suckless.org mais aussi celui d'OpenBSD (en partie). Cette expérience a été une aventure exaltante qui me fait dire aujourd'hui : j'adore le C.
       
 (HTM) suckless.org
       
 (HTM) OpenBSD
       
       Certes, je ne suis pas un expert, loin de là. Cependant, je suis convaincu qu'il s'agit d'un des meilleurs langages existants. Voilà pourquoi...
       
       ## Pas de compromis
       
       La syntaxe en C demande de la rigueur. Certains la désapprouve. Je trouve au contraire qu'elle permet d'écrire du code plus lisible et moins souvent source d'erreur.
       
       Une erreur, et le compilateur ne voudra pas vous créer un binaire. Pas de pitié. Oui, mais en même temps, ça évite de mauvaises surprises. Au passage, clang sous OpenBSD donne de bons conseils :)
       
       ## C'est léger
       
       La bibliothèque standard est à la fois complète et restreinte. Autrement dit, il y a tout ce qu'il faut dedans, mais il faut parfois assembler quelques pièces entre elles pour parvenir à ses fins.
       
       ## Ça force à mieux programmer
       
       Les exigences du langage poussent à se poser davantage de questions, à bien réfléchir à son projet et comment y parvenir. Ça force à mieux programmer.
       
       ## C'est sur-documenté
       
       C'est un langage qui date. Il y a énormément de ressources et de conseils sur le web pour apprendre. (merci StackOverflow).
       
       ## C'est rapide
       
       Comparé aux autres langage que j'ai pu appréhender, la différence est flagrante.
       
       ## C'est beau
       
       Argument complètement subjectif : je trouve le code C beau.
       
       # Tout ce que j'aurais aimé qu'on me dise la première fois que j'ai appris le C
       
       Je code comme certains colorient un carreau sur 2, gribouillent des petits dessins rigolos ou font des arabesques pour s'occuper pendant les réunions. Je ne pense pas être bon à ça, mais j'aime beaucoup m'occuper l'esprit en écrivant du C. C'est un peu comme jouer à résoudre une énigme en s'amusant avec les règles du jeu.
       
       J'ai appris le C il y a longtemps, en lisant le tutoriel du "site du zero" (j'ai dit, c'était il y a longtemps). Je m'y suis remis récemment, et avec du recul, sans être un expert, je m'aperçois que j'aurais bien aimé savoir certaines choses à l'époque. Voici donc tout ce que j'aurais aimé lire la première fois que j'ai appris le C.
       
       ## Macros utiles
       
       Quand on déclare une chaîne de caractères, on ne sait jamais trop quelle doit être sa longueur. Il existe en fait des limites déjà bien définies pour nous :
       
       PATH_MAX : La taille maximale d'un chemin vers un fichier (#include limits.h)
       BUFSIZ : Taille d'un buffer, souvent utiliser pour traiter du texte. (#include stdio.h)
       
       ## Lecture
       
       Aller regarder dans les fichiers /usr/include peut parfois aider à mieux comprendre. Mais par dessus tout, il faut lire les pages man. J'ignore si c'est spécifique à OpenBSD, mais les sections (3) regorgent d'explication, d'exemples, et de suggestions de fonctions associées dont on ignore peut-être l'existence. Par exemple, man strncat propose de lire des infos sur strlcpy pour la compléter/remplacer. Cette dernière nous montre carrément des exemples.
       
 (HTM) http://man.openbsd.org/strncat
       
 (HTM) http://man.openbsd.org/strlcpy
       
       ## ternaire
       
       Bon, c'est pas original, mais je n'avais jamais vraiment compris avant:
       
       ```
       (condition) ? expression_si_vrai : expression_si_faux ;
       ```
       
       ## Les chaînes de caractères
       
       En C, il y a le type "char". Les chaînes de caractères ne sont pas un type. En C, il n'y a que des tableaux de "char", terminés par un NUL : '\0'. C'est idiot, mais en fait très important à garder en tête pour travailler sur du texte. La confusion est facile car on mélange les expressions avec des " ou des '. En bref :
       
       'a' : c'est le char a
       "a" : c'est la "chaîne" constituée de 'a' puis '\0'.
       
       On ne peut pas "additionner" ou modifier un tableau de char aussi "facilement" que dans d'autres langages. Pourtant, dans string.h, on peut trouver tout ce qu'il faut pour gérer les chaînes de caractères.
       
       Il vaut mieux se garder une bibliothèque personnelle de fonctions gérant les chaînes, et les réemployer au besoin.
       
       Si une manipulation sur des chaînes de caractères nécessite d'allouer de la mémoire avec malloc, alors il existe certainement une solution plus simple. Ce n'est pas toujours vrai ceci dit ^^.
       
       Utiliser la fonction strdup() permet de copier une chaîne de caractères. Il faut penser à appeler "free()" ensuite avec cette fonction.
       
       La fonction strsep() modifie la chaîne qui lui est donnée. Il faut donc parfois utiliser strdup() avant, car il faut donner une chaîne modifiable et non constante. Autrement dit:
       
       ```
       char *str = "coucou"; /* n'est pas modifiable */
       char *str = strdup("coucou"); /* modifiable */
       ```
       
       strchr() et strstr() sont très utiles pour récupérer l'emplacement d'un caractère ou d'une chaîne de caractères. On peut directement intervenir à partir de ce point ensuite.
       
       Par exemple
       
       ```
       char *pos = NULL;
       char *str = "Respirer de la compote fait tousser";
       
       pos = strstr(str, "compote");
       puts(str); /* affiche "compote fait tousser /*
       ```
       
       Toujours initialiser une chaîne avec des 0 : ainsi, on est sûr qu'elle est toujours terminée correctement (par un '\0').
       
       ```
       char str[BUFSIZ] = {'\0'};
       char *str2       = calloc(BUFSIZ, sizeof(char));
       ```
       
       Pour "vider" (remettre à zéro) une chaine de caractère (reset) :
       
       ```
       bzero(s, sizeof(s));
       ```
       
       Pour modifier une chaîne de caratères à l'aide d'une fonction, cette dernière doit prendre en argument la taille de la chaîne. En effet, elle ne reçoit en réalité qu'un pointeur vers un tableau de char, et un sizeof() ne retournerait que la taille du pointeur. Exemple :
       
       ```
       void
       modif_str(char *str, ssize_t strsiz)
       {
           strlcpy(str, "coucou", strsiz);
       }
       
       char    test[256] = {'\0'};
       
       modif_str(test, sizeof(test));
       ```
       
       ## Fichiers
       
       Il vaut mieux utiliser les fonctions fread()/fwrite() plutôt que read()/write() car elles sont plus efficaces (utilisation d'un cache).
       
       ```
       size_t nread = 0;
       FILE *fd = NULL;
       char           *buffer[BUFSIZ];
       
       if ((fd = fopen(fp, "r")) == NULL) { goto err; }
       
       while ((nread = fread(buffer, 1, sizeof(buffer), fd)) != 0)
           fwrite(buffer, 1, nread, stdout);
       fclose(fd);
       if (ferror(fd)) {
           err(1,"closefile");
       }
       ```
       
       Pour lister le contenu répertoire: scandir()
       
       ```
       #include <sys/types.h>
       #include <dirent.h>
       int n = 0;
       struct dirent **namelist;
       
       if ((n = scandir(path, &namelist, NULL, alphasort)) < 0) {
           err(1, "Can't scan %s", path);
       } else {
           for(int j = 0; j < n; j++) {
               if (!strcmp(namelist[j]->d_name, ".")) {
                   continue;
               }
               if (namelist[j]->d_type == DT_DIR) {
                   printf("%s\n", namelist[j]->d_name);
               }
               free(namelist[j]);
       }
       free(namelist);
       }
       ```
       
       ## Gestion des erreurs
       
       ```
       #include <err.h>
       #include <errno.h>
       ```
       
       Pour afficher une erreur et quitter : err(1, "erreur :%s", blabla);
       Pour afficher un avertissement et quitter : warn("attention :%s", blabla);
       
       ## Compiler
       
       Dans la plupart des cours en C, on n'apprend pas à compiler. Pour un simple fichier "main.c", il suffit d'un:
       
       ```
       make main
       ```
       
       Qui est en fait un raccourci pour:
       
       ```
       cc -o main main.c
       ```
       
       Pour compiler en utilisant une bibliothèque, il faut utiliser les options "-l" "-L", et "-I". Le "-I" permet de dire où se trouvent les entêtes (.h), "-L" indique où se trouvent les bibliothèques et "-l" indique quelle bibilothèque utiliser.
       
       C'est à partir de ce moment qu'un fichier Makefile devient intéressant. Voici le modèle que j'utilise. Il est assez strict au niveau de la gestion des erreurs, ça me force à écrire du code moins sensible:
       
       ```
       NAME = thename
       VERSION = 0.1
       
       PREFIX?=/usr/local/
       INCS     = -I/usr/local/include
       LIBS     = -L/usr/local/lib -lCHANGEME -lOTHERLIB
       LDFLAGS  = ${LIBS}
       CPPFLAGS = -DVERSION=\"${VERSION}\"
       
       CFLAGS  += -pedantic -Wall -Wextra -Wmissing-prototypes \
                  -Werror -Wshadow -Wstrict-overflow -fno-strict-aliasing \
                  -Wstrict-prototypes -Wwrite-strings \
                  ${CPPFLAGS} \
                   ${INCS} \
                  -Os 
       
       .SUFFIXES: .c .o
       
       SRC != find . -type f -name \*.c
       H != find . -type f -name \*.h
       OBJ = ${SRC:.c=.o}
       
       .c.o:
           ${CC} ${CFLAGS} -c $<
       
       all: ${NAME}
       
       ${OBJ}: ${H}
       
       clean:
           rm -f ${NAME} *.core *.o
       
       ${NAME}: ${OBJ}
           ${CC} -o $@ ${OBJ} ${CFLAGS} ${LDFLAGS}
       ```
       
       Bien sûr, il faudra changer "NAME", les "CHANGEME" après les "-l" et pourquoi pas lire "man make" :)
       
       Si on veut compiler un binaire pour un chroot, il faut ajouter l'option "-static".
       
       ## Listes chaînées
       
       Les listes chaînées sont une méthode pour enregistrer des données. Un peu comme un dictionnaire en python. Et si c'est trop lent, vous pouvez regarder du côté des hashtables. Mais je trouve les premières largement suffisantes (on parle du C hein, c'est hyper rapide! :)).
       
       ## Modestie
       
       Parfois, on lit du code très difficile à comprendre, avec des opérations sur pointeurs pour gérer des chaînes de caractère par exemple.
       
       Il faut rester parfois modeste, et arrêter de vouloir un code surpuissant meilleur que les autres. Il n'y a pas de code parfait.
       
       Par exemple, c'est parfois tout aussi efficace de lire un fichier caractère après caractères plutôt que de chercher à le découper en morceaux à la recherche d'un motif particulier. Le C est très rapide :)
       
       ## Lire un fichier ligne après lignes
       
       getline(), et hop :) Le man présente même un exemple.
       
       Ceci dit, c'est souvent aussi rapide d'utiliser fgets() ou fgetc().
       
       ## Initialiser une structure
       
       ```
       memset(&structure, 0, sizeof(struc structure_type));
       ```
       
       ## Utiliser fork() et éviter les zombies
       
       En appelant un signal qui matche tous les processus enfants. Attention, cela ne fait pas bon ménage avec kqueue().
       
       ```
       #include <signal.h>
       
       static void sigchld(int unused);
       
       void
       sigchld(int unused)
       {
           (void)unused; // avoid unused variable warning
           while (waitpid(WAIT_ANY, NULL, WNOHANG) > 0);
       }
       
       int
       main(int argc, char *argv[])
           /* ... */
           if (signal(SIGCHLD, sigchld) == SIG_ERR)
               err(1, "can't install SIGCHLD handler:");
           /* ... */
       ```
       
       Ou avec un wait/waitpid bien placé :
       
       ```
       const char *cmd[] = { "/usr/bin/top", "-s", "0.5", NULL };
       pid_t pid = fork();
       if (pid == -1) {
           err(1, "fork");
       } else if (pid == 0) {
           /* child */
           execv(cmd[0], (char **)cmd);
           err(1, "execv");
       } else {
           /* parent */
           wait(NULL);  // blocking
           /* or : 
           waitpid(pid, NULL, WNOHANG); // non-blocking
           */
       }
       ```
       
       ## Ressources
       
 (HTM) Excellent site pour apprendre les bases du C
       
 (HTM) Learn C the Hard Way de Zed A. Shaw
       
 (HTM) Des remarques intéressantes pour écrire du C récent
       
 (HTM) Notions pour présenter du code propre
       
 (HTM) Idem, à la OpenBSD
       
 (HTM) Des exemples de code très instructifs
       
 (HTM) https://c-for-dummies.com/
       
 (HTM) https://beej.us/guide/bgnet/html/
       
       ## Snippets C
       
       Cette partie sera alimentée de fonctions et extraits de code en C en guise d'aide mémoire. Ne prenez rien pour parfait, ces extraits peuvent certainement être améliorés.
       
       ### Modifier une chaîne de caratères dans une fonction
       
       Il est nécessaire de connaître la taille de la chaîne pour que tout fonctionne correctement :
       
       ```
       void
       modif_str(char *str, ssize_t strsiz)
       {
           strlcpy(str, "coucou", strsiz);
       }
       
       char    test[256] = {'\0'};
       
       modif_str(test, sizeof(test));
       ```
       
       ### ecalloc, erealloc : allocation avec gestion d'erreur
       
       ```
       /* same thing that calloc and reallocarray but check for
        * error and exit if necessary
        */
       
       void *
       ecalloc(size_t nmemb, size_t size)
       {
           void *p;
       
           if ((p = calloc(nmemb, size)) == NULL)
               err(1, "calloc");
           return p;
       }
       
       void *
       ereallocarray(void *ptr, size_t nmemb, size_t size)
       {
           if ((ptr = reallocarray(ptr, nmemb, size)) == NULL)
               err(1, "reallocarray");
       
           return ptr;
       }
       ```
       
       ### get_stream_txt: Récupérer le contenu d'un fichier ouvert
       
       ```
       /* return text in fp
        * the fp must be closed properly after
        * the returned string is mallocated and must be freed.
        */
       char *
       get_stream_txt(FILE *fp)
       {
           char *buf = NULL;
           size_t s;
           size_t len = 0;
           size_t bsize = 2 * BUFSIZ;
       
           buf = ecalloc(bsize, sizeof(char));
           while((s = fread(buf + len, 1, BUFSIZ, fp))) {
               len += s;
               if(BUFSIZ + len + 1 > bsize) {
                   bsize += BUFSIZ;
                   buf = ereallocarray(buf, bsize, sizeof(char));
               }
           }
           buf[len] = '\0';
       
           return(buf);
       }
       ```
       
       ### lire un fichier
       
       ```
       FILE *fd = NULL;
       char buf[BUFSIZ] = {0};
       size_t nread = 0;
       
       if ((fd = fopen(path, "rb")) == NULL) {
               err(1, "%s: %s", "Fail to open", path);
       }
       
       while ((nread = fread(buf, 1, BUFSIZ, fd)) != 0 ) {
               fwrite(buf, 1, nread, stdout);
       }
       fclose(fd);
       if (ferror(fd)) {
               err(1, "%s: %s", "close", path);
       }
       ```
       
       ## get_cmd_output: Récupérer la sortie d'une commande
       
       ```
       /* return the output of cmd in an allocated
        * string created by get_stream_txt().
        * It is the caller responsibility to free it later
        */
       char *
       get_cmd_output(const char *cmd)
       {
           FILE *fp = NULL;
           char *res = NULL;
       
           if ((fp = popen(cmd, "r")) == NULL)
               err(1, "%s: %s", "Error opening pipe for cmd", cmd);
       
           res = get_stream_txt(fp);
           if (pclose(fp) != 0)
               err(1, "%s %s", cmd, "not found or exited with error status");
       
           return res;
       }
       ```
       
       la fonction popen peut poser des soucis dans le cas où ce n'est pas le développeur qui précise la commande à entrer : les variables d'environnement sont conservées. Voici donc une autre façon de faire :
       
       Créer un "pipe" pour lequel "0" est la sortie et "1" l'entrée.
       Indiquer que "stdout" correspond à la sortie du pipe avec la fonction dup2(). Ceci permet de relier la sortie de la commande qu'on veut lancer au pipe pour pouvoir le lire ensuite comme un fichier normal.
       Lancer la commande avec execlp() aprèq avoir fork()
       Lecture du retour de la commande dans le processus parent. On utilise fdopen() puisque tout ce qu'on connaît, c'est le "file descriptor", pas un chemin vers un fichier.
       
       ```{=html}
       <!-- -->
       ```
       
       ```
       /* use pipe and fork to avoid shell with popen */
       int fildes[2] = {0};
       int status = 0;
       pid_t pid = 0;
       char *str = "blabla";
       char *cmd = "hello";
       
       if (pipe(fildes) != 0) {
           err(1, "pipe failed");
       }
       
       pid = fork();
       
       if (pid < 0) {
           close(fildes[0]);
           close(fildes[1]);
           err(1, "fork failed");
       }
       
       if (pid > 0) { /* parent */
           char buf[3];
           size_t nread = 0;
           FILE *output = NULL;
       
           close(fildes[1]); /* make sure entry is closed so fread() gets EOF */
       
           /* use fread/fwrite because are buffered */
           output = fdopen(fildes[0], "r");
           if (output == NULL) {
               err(1, "fdopen failed");
           }
       
           /* read pipe output */
           while ((nread = fread(buf, 1, sizeof(buf), output)) != 0) {
               fwrite(buf, 1, nread, stdout);
           }
           close(fildes[0]);
           fclose(output);
       
           /* wait for child to terminate */
           waitpid(pid, &status, 0);
       
           exit(0);
       
       } else if (pid == 0) { /* child */
           dup2(fildes[1], STDOUT_FILENO); /* set pipe output equal to stdout */
           dup2(fildes[1], STDERR_FILENO);
           close(fildes[1]); /* no need this file descriptor : it is now stdout of the command below */
           execlp(cmd, cmd, NULL);
           /* if execlp is ok, this will never be reached */
           err(1, "execlp failed to run: %s", "ls");
       }
       ```
       
       ### startswith
       
       ```
       int
       startswith(const char *str, const char *start)
       {
               size_t str_len = strlen(str);
               size_t start_len = strlen(start);
       
       
               int ret = 0;
               if ((str_len >= start_len) &&
                       (strncmp(str, start, start_len) == 0)) {
                       ret = 1;
               }
       
               return ret;
       }
       ```
       
       ### endswith
       
       ```
       int
       endswith(const char *str, const char *end)
       {
           size_t str_len = strlen(str);
           size_t end_len = strlen(end);
       
           int ret = 0;
           if ((str_len >= end_len) &&
               (strcmp(str + (str_len -end_len), end) == 0)) {
               ret = 1;
           }
       
           return ret;
       }
       ```
       
       ### (v)estrlcat
       
       ```
       /* strlcat with error handling */
       size_t
       estrlcat(char *dst, const char *src, size_t dstsize)
       {
           size_t size;
           if ((size = strlcat(dst, src, dstsize)) >= dstsize)
               err(1, "strlcat");
       
           return size;
       }
       
       /* variadic strlcat
        * a call must end with NULL
        * to append "bar", "zoo", yo" to foo, call:
        * vestrlcat(foo, sizeof(foo), "bar", "zoo", "yo", NULL);
        */
       size_t
       vestrlcat(char *dst, size_t dstsize, ...)
       {
           size_t size = 0;
           char *s;
           va_list ap;
       
           va_start(ap, dstsize);
       
           while ((s = va_arg(ap, char *)) != NULL) {
               size += estrlcat(dst, s, dstsize);
           }
           va_end(ap);
       
           return size;
       }
       ```
       
       ### stralloc
       
       Et ma favorite qui permet de fabriquer n'importe quelle chaîne de caractères. On y passe un pointeur (qui peut être NULL au départ. Tous les arguments sont assemblés pour former une longue chaîne, cette dernière étant allouée dynamiquement au besoin.
       
       ```
       /* 
        * Make any string 
        * add in str every argument and realloc str if necessary
        * if str is NULL, it is allocated
        * argument list **must** end with NULL
        * return str
        * str should be dynamically allocated and freed later
        * example: stralloc(&foo, "hello", " there.", NULL);
        */
       
       char *
       stralloc(char **str, ...)
       {
           size_t len = 0;    /* length of str at last*/
           char *s = NULL;
           va_list ap;
           va_list apcopy;
       
           va_copy(apcopy, ap);
       
           /* count required length */
           va_start(ap, str);
           while ((s = va_arg(ap, char *)) != NULL) {
               len += strlen(s);
           }
           va_end(ap);
           len++; /* \0 */
       
           if (*str != NULL) {
               len += strlen(*str);
               *str = ereallocarray(*str, len, sizeof(char));
           } else {
               *str = ecalloc(len, sizeof(char));
           }
       
           /* copy */
           va_start(apcopy, str);
       
           while ((s = va_arg(apcopy, char *)) != NULL) {
               estrlcat(*str, s, len);
           }
           va_end(apcopy);
           return(*str);
       }
       ```
       
       Je lui préfère une version qui ne nécessite aucune allocation, et en profite pour vous présenter "estrlcpy" et "estrlcat" qui font comme strlcpy et strlcat mais en gérant les erreurs :
       
       ```
       size_t
       estrlcpy(char *dst, const char *src, size_t dstsize)
       {
           size_t n = 0;
       
           n = strlcpy(dst, src, dstsize);
           if (n >= dstsize) {
               err(1, "strlcpy failed for %s = %s", dst, src);
           }
       
           return n;
       }
       
       size_t
       estrlcat(char *dst, const char *src, size_t dstsize)
       {
           size_t size;
           if ((size = strlcat(dst, src, dstsize)) >= dstsize)
               err(1, "strlcat on %s + %s", dst, src);
       
           return size;
       }
       
       /* usage:
        * mkstr(str, sizeof(str), optarg, "/INBOX/", NULL);
        */
       size_t
       mkstr(char *str, size_t dstsize, ...)
       {
           char *s = NULL;
           size_t size = 0;
           va_list ap;
       
           va_start(ap, dstsize);
       
           while ((s = va_arg(ap, char *)) != NULL) {
               if (size == 0) {
                   size += estrlcpy(str, s, dstsize);
               } else {
                   size += estrlcat(str, s, dstsize);
               }
           }
           va_end(ap);
           return(size);
       }
       ```
       
       ### trim
       
       Comme son nom l'indique, elle vire les espaces et sauts de ligne en début et fin de chaîne. Il faut passer un pointeur vers la chaîne à modifier (et donc allouée dynamiquement).
       
       ```
       /* return a trimmed copy of str */
       /* trim(&str); */
       void
       trim(char **str)
       {
           size_t begin = 0;
           size_t end = strlen(*str);
       
       
           while (isspace((*str)[begin])) {
               begin++;
           }
       
           while (isspace((*str)[end-1])) {
               end--;
           }
           for (size_t i = begin, j=0; i < end; i++, j++) {
               (*str)[j] = (*str)[i];
           }
           (*str)[end - begin] = '\0';
       }
       ```
       
       ### (v)esnprintf
       
       Cette fonction fait comme snprintf avec gestion d'erreur. Il faut avoir un tableau de char déjà prêt, par exemple char s[256]. Ce code est en partie tiré de celui utilisé souvent par suckless.org. Elle est très pratique car permet de définir le format des variables qu'une chaîne doit contenir. Par exemple :
       
       ```
       esnprintf(s, sizeof(s), "%s%d%c", "blabla", 42, 'a');
       
       static int
       evsnprintf(char *str, size_t size, const char *format, va_list ap)
       {
           int ret = vsnprintf(str, size, format, ap);
       
           if (ret < 0 || (size_t)ret >= size) {
               warn("%s", "vsnprintf: Output truncated");
           }
       
           return ret;
       }
       
       int
       esnprintf(char *str, size_t size, const char *format, ...)
       {
           va_list ap;
           int ret = 0;
       
           va_start(ap, format);
           ret = evsnprintf(str, size, format, ap);
           va_end(ap);
       
           return ret;
       }
       ```
       
       ### strrstr : rechercher le dernier morceau
       
       strrstr() fait comme strstr, mais pointe vers la dernière occurence, pas la première :
       
       ```
       char *   strrstr(const char *, const char *);
       
       char *
       strrstr(const char *big, const char *little)
       {
           char *p = NULL;
           char *ret = NULL;
       
           if ((p = strstr(big, little)) == NULL) {
               return NULL;
           }
       
           /* loop until last occurence */
           while ((p = strstr(p, little)) != NULL) {
               ret = p;
               p++;
           }
       
           return ret;
       }
       ```
       
       ### efopen, efclose, evfprintf : gestion des fichiers
       
       On ouvre et on ferme les fichiers en gérant les erreurs
       
       ```
       FILE *
       efopen(const char *path, const char *mode)
       {
           FILE *f = NULL;
           if ((f = fopen(path, mode)) == NULL)
               err(1, "%s: %s", "Fail to open", path);
       
           return f;
       }
       
       int
       efclose(FILE *stream)
       {
           int ret = 0;
           ret = fclose(stream);
           if (ferror(stream)) {
               err(1,"closefile");
           }
           return ret;
       }
       ```
       
       On écrit dans un fichier autant de choses que l'on veut en une fois. C'est comme fprint mais avec une liste d'arguments variable.
       
       ```
       int
       evfprintf(FILE *stream, const char *format, va_list ap)
       {
           int ret;
       
           ret = vfprintf(stream, format, ap);
           if (ret < 0)
               err(1,"evfprintf:");
       
           return ret;
       }
       
       int
       efprintf(FILE *stream, const char *format, ...)
       {
           va_list ap;
           int ret;
       
           va_start(ap, format);
           ret = evfprintf(stream, format, ap);
           va_end(ap);
       
           return ret;
       }
       ```
       
       ### OpenBSD unveil et pledge
       
       Deux macros pratiques et un exemple pour éviter des erreurs de compilation sur un autre système:
       
       ```
       #define PLEDGEORDIE(prom) if (pledge(prom, NULL) == -1) { err(1, "pledge"); }
       #define UNVEILORDIE(path,perms) if (unveil(path, perms) == -1) { err(1, "unveil"); }
       ```
       
       Exemple:
       
       ```
       #ifdef __OpenBSD__
           UNVEILORDIE(argv[1], "rwc");
           PLEDGEORDIE("stdio rpath cpath wpath exec proc");
       #endif
       ```
       
       ### Retirer le "\n" de fin de ligne
       
       ```
       line[strcspn(line, "\r\n")] = '\0'; /* remove newline */
       ```
       
       ### de la bonne façon d'utiliser strsep
       
       strsep modifie la chaîne, et donc fait perdre la référence du pointeur correspondant. Il faut donc en garder une trace, ce qui donnerait (merci stacko) :
       
       ```
       char *r = strdup(str);
       
       char *tofree = r;
       char *tok;
       while ((tok = strsep(&r, " ")) != NULL) {
           puts(tok);
       }
       
       free(tofree);
       ```
       
       ### récupérer les substrings d'une regex (regex.h)
       
       ```
       #include <regex.h>
       #include <sys/types.h>
       
       #define LEN(X)               (sizeof X / sizeof X[0])
       
       #define SE_MAX 4 /* max subexpression, in nmatch. This is the number of () + 1 */
       
       int 
       main(int argc, char *argv[])
       {
           regex_t greg;   /* compiled gemini regex */
           size_t nmatch = SE_MAX;
           regmatch_t match[SE_MAX];
           int ret = 0;
       
           char buf[BUFSIZ] = {'\0'};
       
           char *gemini_regex = "^gemini://([^/]*)/?([^\?]*)[\?]?(.*)?$";
           char *lines[] = {
               "gemini://si3t.ch",
               "gemini://si3t.ch/subdir/index.gmi?query?weird",
               "gemini://si3t.ch/subdir?query",
               "gemini://si3t.ch?query",
               "gemini://si3t.ch//?query",
           };
       
           if ((ret = regcomp(&greg, gemini_regex, REG_EXTENDED)) != 0) {
               regerror(ret, &greg, buf, sizeof(buf));
               regfree(&greg);
               err(1, "%s", buf);
           }
       
           for (size_t l = 0; l < LEN(lines); l++) {
               printf("[%s]\n", lines[l]);
       
               if ((ret = regexec(&greg, lines[l], nmatch, match, 0)) != 0) {
                   regerror(ret, &greg, buf, sizeof(buf));
                   if (ret == REG_NOMATCH)
                       warnx("No match :(");
                   else {
                       regfree(&greg);
                       errx(1, "%s", buf);
                   }
               }
       
               for (int i = 1; i <= greg.re_nsub; i++) {
                   if (match[i].rm_so != match[i].rm_eo)
                       size_t len = match[i].rm_eo - match[i].rm_so;
                       memcpy(buf, lines[l] + match[i].rm_so, len);
                       puts(buf);
                   }
               }
           }
       
           regfree(&greg);
           return 0;
       }
       ```
       
       ### Dictionnaire (liste chaînée)
       
       ```
       #include <err.h>
       #include <errno.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <string.h>
       
       typedef struct Dict {
           char *key;
           char *val;
           struct Dict *next;
       } Dict;
       
       
       void * emalloc(size_t);
       Dict * dict_new(void);
       void dict_free(Dict *);
       char * dict_get_key(Dict *, const char *);
       void dict_print(Dict *);
       void dict_append(Dict *, const char *, const char *);
       void dict_push(Dict **, const char *, const char *);
       
       
       
       void *
       emalloc(size_t size)
       {
           void *p;
       
           p = malloc(size);
           if (p == NULL)
               err(1, "malloc");
       
           return p;
       }
       
       void
       dict_free(Dict *d)
       {
           Dict *nextitm;
       
       
           while (d != NULL) {
               nextitm = d->next;
               free(d->key);
               free(d->val);
               free(d);
               d = nextitm;
           }
           free(d);
       }
       
       void
       dict_print(Dict *d)
       {
           Dict *cur = d;
       
           while (cur->next != NULL) {
               printf("%s: %s\n", cur->key, cur->val);
               cur = cur->next;
           }
       }
       
       char *
       dict_get_key(Dict *d, const char *key)
       {
           Dict *cur = d;
       
           while (cur->next != NULL) {
               if ((strcmp(cur->key, key)) == 0) {
                   return cur->val;
               }
               cur = cur->next;
           }
       
       
           return NULL;
       }
       
       void
       dict_append(Dict *d, const char *key, const char *val)
       {
       
           Dict *cur = d;
       
           while (cur->next != NULL) {
               cur = cur->next;
           }
       
           cur->next = dict_new();
           cur->key = strdup(key);
           cur->val = strdup(val);
       }
       
       void
       dict_push(Dict **d, const char *key, const char *val)
       {
       
           Dict *new = dict_new();
       
           new->key = strdup(key);
           new->val = strdup(val);
           new->next = *d;
       
           *d = new;
       }
       
       Dict *
       dict_new(void)
       {
           Dict *d = emalloc(sizeof(Dict));
           d->key = NULL;
           d->val = NULL;
           d->next = NULL;
           return d;
       }
       
       int 
       main(void)
       {
           Dict *d = dict_new();
           dict_append(d, "batman", "un super héros");
           dict_append(d, "chat", "envahisseur d'internet");
           dict_push(&d, "abricot", "fruit placé au début");
           dict_print(d);
           dict_free(d);
           return 0;
       }
       ```
       
       ### Listes et queues (queue.h)
       
       Beaucoup plus simple que de réimplémenter des listes chaînées.
       
       Voici un exemple. Le reste, c'est dans ''man queue''. Il n'y a pas de code de vérification d'erreur :!:.
       
       J'utilise les conventions suivantes :
       
       Les structures qui seront dans les listes ont un nom commençant pas une majuscule
       Les entête de liste portent le nom de la structure suivi de "_head".
       Le nom des entrées porte le nom de la structure en minuscule.
       
       ```{=html}
       <!-- -->
       ```
       
       ```
       #include <limits.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <string.h>
       #include <sys/queue.h>
       
       struct Item {
           int id;
           char path[PATH_MAX];
           char title[256];    /* 256 is more than enough */
           SIMPLEQ_ENTRY(Item) item;
       };
       SIMPLEQ_HEAD(Item_head, Item);
       
       int 
       main(int argc, char *argv[])
       {
       
           struct Item_head itemh;
           struct Item *i1, *ip;
       
           SIMPLEQ_INIT(&itemh);
       
           i1 = malloc(sizeof(struct Item));
       
           strlcpy(i1->title, "coucou", sizeof(i1->title));
       
           SIMPLEQ_INSERT_TAIL(&itemh, i1, item);
           SIMPLEQ_FOREACH(ip, &itemh, item) {
               puts(ip->title);
           }
       
           free(i1);
       
       
           return 0;
       }
       ```
       
       ---
 (DIR) /