star-style

Writing conventions for co-authors
Log | Files | Refs | README | LICENSE

commit 6ead1ec36508dca5d65cd50207021519b937fc28
parent cf07cc9eb56cda8bb4a12ec0245908082ff8ea49
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 14 Oct 2026 17:22:58 +0200

Rédige la section "fichiers"

Ajoute un fichier vimrc documenté dans la page de manuel. Le chemin vers
ce fichier dépend de son répertoire d'installation. Par conséquent, le
man, qui y fait référence, devient générique à ce répertoire et dois
donc être régénéré à chaque invocation du make.

Diffstat:
M.gitignore | 1+
MMakefile | 25+++++++++++++++++++++----
Mconfig.mk | 1+
Ddoc/fr/star-c.7 | 1792-------------------------------------------------------------------------------
Adoc/fr/star-c.7.in | 1804+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avimrc | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 1918 insertions(+), 1796 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,2 +1,3 @@ *~ *.swp +star-c.7 diff --git a/Makefile b/Makefile @@ -19,15 +19,32 @@ include config.mk -default: +MAN = doc/fr/star-c.7 -lint: - mandoc -Tlint -Werror doc/fr/star-c.7 +default: $(MAN) -install: +man: # Force rebuild + +doc/fr/star-c.7: man + sed 's#@APPPREFIX@#$(APPPREFIX)#g' $@.in > $@ + +clean: + rm -f $(MAN) + +lint: $(MAN) + @for i in $(MAN); do \ + printf 'lint %s\n' "$${i}"; \ + mandoc -Tlint -Werror "$${i}"; \ + done + +install: $(MAN) mkdir -p "$(DESTDIR)$(MANPREFIX)/fr/man7" cp doc/fr/star-c.7 "$(DESTDIR)$(MANPREFIX)/fr/man7" chmod 644 "$(DESTDIR)$(MANPREFIX)/fr/man7/star-c.7" + mkdir -p "$(DESTDIR)$(APPPREFIX)/star-style" + cp vimrc "$(DESTDIR)$(APPPREFIX)/star-style" + chmod 644 "$(DESTDIR)$(APPPREFIX)/star-style/vimrc" uninstall: rm -rf "$(DESTDIR)$(MANPREFIX)/fr/man7/star-c.7" + rm -rf "$(DESTDIR)$(APPPREFIX)/star-style/vimrc" diff --git a/config.mk b/config.mk @@ -1,3 +1,4 @@ # Default install paths PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man +APPPREFIX = $(PREFIX)/share diff --git a/doc/fr/star-c.7 b/doc/fr/star-c.7 @@ -1,1792 +0,0 @@ -.\" Copyright (C) 2026 |Méso|Star> (contact@meso-star.com) -.\" -.\" Ce fichier fait partie de Star-Style. -.\" -.\" Star-Style est un logiciel libre ; vous pouvez le redistribuer ou le -.\" modifier suivant les termes de la GNU General Public License telle -.\" que publiée par la Free Software Foundation ; soit la version 3 de -.\" la licence, soit (à votre gré) toute version ultérieure. -.\" -.\" Star-Style est distribué dans l'espoir qu'il sera utile, mais SANS -.\" AUCUNE GARANTIE ; sans même la garantie tacite de QUALITÉ MARCHANDE -.\" ou d'ADÉQUATION à UN BUT PARTICULIER. Consultez la GNU General -.\" Public License pour plus de détails. -.\" -.\" Vous devez avoir reçu une copie de la GNU General Public License en -.\" même temps que Star-Style ; si ce n'est pas le cas, consultez -.\" <http://www.gnu.org/licenses>. -.Dd October 14, 2026 -.Dt STAR-C 7 -.Os -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh NOM -.Nm star-c -.Nd guide d'écriture de code en C -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh DESCRIPTION -Ce document décrit les conventions d'écriture des programmes C -distribués par |Méso|Star>. -Ces recommandations sont données à titre indicatif, et reste -subordonnées aux pratiques effectivement retenues dans chaque -projet ; le plus important étant d'en conserver la cohérence. -Si bien que s'il appartient aux co-auteurs d'un projet de prendre -certaines libertés quant à ce guide de style, toute participation à son -développement devra alors s'efforcer de respecter le style d'écriture du -projet, avant les préférences listées ici. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LE CONTENU D'UN PROJET -En suivant le philosophie UNIX, assurer un jeu de -fonctionnalités par programme aussi ramassé que possible de -sorte à ce que sa mise en oeuvre et son interface restent -simples et ciselées. -L'objet étant d'assurer sa robustesse, son efficacité et sa -modularité qui dépendent d'abord de sa -.Em relation -à un écosystème logiciel qui le dépasse, bien avant son catalogue de -fonctionnalités ou ses prouesses de mise en oeuvre. -.Pp -Le périmètre étroit de chaque programme se retrouve dès lors dans la -structure du projet auquel il appartient, dont le contenu est alors -simple et peu hiérarchisé car comptant en définitive peu de fichiers. -.Pp -La structure type du répertoire d'un projet est : -.Bd -literal -offset Ds -README.md -COPYING -config.mk -Makefile -src/foo.h -src/foo.c -src/foo_bar.c -doc/foo_bar.1 -doc/foo.3 -.Ed -.Pp -Avec : -.Bl -dash -compact -.It -.Pa README.md -le fichier qui donne le premier niveau d'informations sur le projet ; -.It -.Pa COPYING -la license du projet qui liste ses conditions légales d'utilisation ; -.It -.Pa config.mk -et -.Pa Makefile -les fichiers du système de génération automatique du projet ; -.It -.Pa src/ -le répertoire qui contient les codes source du projet ; -.It -.Pa doc/ -le répertoire qui stocke sa documentation. -.El -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LE LANGAGE C -Utiliser le langage -.Em C89 -.Pq ANSI X3.159-1989 -aussi appelé C90 -.Pq ISO/IEC 9899:1990 , -les deux étant équivalent. -.Pp -Cette norme est plus épurée que celle qui lui succède à savoir le C99 -.Pq ISO SO/IEC 9899:1999 , -ce qui en fait un dialecte C à la fois plus simple, plus portable et -plus consistant. -Par exemple en ne proposant qu'une seule façon d'écrire les commentaires, -et en interdisant de mélanger du code avec la définition de variables. -.\"""""""""""""""""""""""""""""""""" -.Ss Le standard POSIX -Ajouter le support du standard POSIX pour les seuls fichiers qui en ont -besoin, pour notamment pouvoir utiliser des fonctions de la bibliothèque -C standard sinon indisponibles via la seule norme du langage retenue. -.Pp -Pour ce faire, définir la macro -.Sy _POSIX_C_SOURCE -tout en haut du fichier C concerné, avant la moindre directive -d'inclusion. -Par exemple, pour utiliser le standard POSIX.1-2001 : -.Bd -literal -offset Ds -#define _POSIX_C_SOURCE 200112L -.Ed -.Pp -Sous GNU/Linux, se référer à -.Xr feature_test_macros 7 -pour une description exhaustive des macros utilisées pour activer le jeu -de fonctionnalités d'un standard donné. -.Pp -L'utilisation d'un C enrichi du standard POSIX n'est ainsi utilisé que -sur les seuls fichiers qui en explicite le besoin ; le C89 restant le -langage utilisé partout ailleurs. -Dans un même souci de portabilité, retenir la première version du -standard POSIX à partir de laquelle la fonctionnalité recherchée est -apparue. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LA STRUCTURE D'UN FICHIER SOURCE -Suivre une seule et même structure pour tous les fichiers -sources, qu'ils soient des fichiers d'en-tête -.Pq fichiers Ql *.h -ou des unités de compilation -.Pq fichiers Ql *.c . -Un seul schéma de lecture participant à la clarté du code source. -.Pp -Ci-après sont listées les différentes parties d'un fichier source : -.Bl -enum -.\"""""""""""""""""""""""""""""""""" -.It -Un commentaire avec l'avis de copyright et les avis de licence du -programme -.Pq section Sx L'AVIS DE COPYRIGHT ET LES AVIS DE LICENCE Ns - ; -.\"""""""""""""""""""""""""""""""""" -.It -La définition des macros qui ajoute au langage C du fichier le support -d'un standard POSIX -.Pq section Sx LE LANGAGE C Ns - ; -.\"""""""""""""""""""""""""""""""""" -.It -Pour un fichier d'en-tête, l'ouverture d'un garde-fou évitant sa double -inclusion. -Il est fermé en toute fin du fichier -.Pq partie 10 Ns - : -.Bd -literal -offset Ds -#ifndef FOO_H -#define FOO_H -.Ed -.Pp -Le nom de la macro testée puis définie est celui du fichier d'en-tête, -en majuscule, suffixé par -.Ql _H -en référence à l'extension -.Ql .h -du fichier. -Dans l'exemple qui précède, le garde-fou concerne donc le fichier -.In foo.h . -La convention de nommage est sinon celle utilisée pour n'importe quelle -macro -.Pq section Sx LE NOMMAGE . -.\"""""""""""""""""""""""""""""""""" -.It -L'inclusion des fichiers d'en-tête requis par le fichier source ; -.Pq section Sx LES FICHIERS D'EN-TÊTE Ns - ; -.\"""""""""""""""""""""""""""""""""" -.It -La définition des macros -.Pq section Sx LES MACROS Ns - ; -.\"""""""""""""""""""""""""""""""""" -.It -La déclaration anticipée des types structurés : -.Bd -literal -offset Ds -/* Type structurés externes au programme */ -struct plugh; -struct quux; -struct xyzzy; - -/* Types structurés définis ailleurs dans le programme */ -struct bar; -struct foo; -.Ed -.\"""""""""""""""""""""""""""""""""" -.It -La définition des constantes symboliques de type -.Vt enum -.Pq section Sx LES CONSTANTES SYMBOLIQUES Ns - ; -.\"""""""""""""""""""""""""""""""""" -.It -La définition des types structurés et de leur(s) constante(s) -.Pq section Sx LES STRUCTURES Ns - ; -.\"""""""""""""""""""""""""""""""""" -.It -La déclaration et définition des fonctions -.Pq section Sx LES FONCTIONS . -Dans l'ordre qui suit : -.Pp -.Bl -tag -compact -width a. -.It a. -la déclaration des fonctions ; -.It b. -la définition des fonctions statiques ; -.It c. -pour les unités de compilation, la définition des fonctions. -.El -.\"""""""""""""""""""""""""""""""""" -.It -Pour les fichiers d'en-tête, la fin du garde-fou ouvert en 3 pour éviter -la double inclusion du contenu du fichier. -.El -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LA LONGUEUR DES LIGNES -La longueur maximale recommandée pour une ligne est de -.Em 80 -caractères. -.Pp -Ce nombre, standardisé par les cartes perforées et les terminaux des -années 1970, vise aussi à faciliter la lecture du code source en -s'insipirant des conventions d'édition. -Pour un texte imprimé avec une taille de police entre 9 à 12 points et -un inter-ligne d'un caractère, un confort de lecture est assuré dès -lors que chaque ligne compte entre 60 et 75 caractères. -Une proximité avec les 80 caractères retenus, que l'indentation -des sources vient encore renforcer. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh L'AVIS DE COPYRIGHT ET LES AVIS DE LICENCE -Utiliser la licence libre GPLv3+ qui, en tant que licence copyleft, -défend la copie, l'étude et la modification libres du programme ainsi -licencié, et de ses évolutions. -.Pp -Ajouter une copie de la licence à la racine du projet, dans un fichier -texte nommé -.Pa COPYING -.Pq voir Lk https://www.gnu.org/licenses/gpl-3.0.txt . -.Pp -Lister en commentaire l'avis de copyright et la déclaration -d'autorisation de copie en en-tête de -.Em chaque -fichier source. -.Pp -Chaque copyright débute par le mot -.Ql Copyright , -en anglais, suivi des 3 caractères -.Ql (C) , -traduction ASCII du caractère © qui, quant à lui, peut ne pas être -supporté par la police de caractères utilisée. -Lister ensuite les années pour lesquelles une version du programme a été -publiée, avant le nom de l'auteur(e) ayant participé(e) à -sa réalisation. -Conclure chaque avis par l'adresse de contact de l'auteur(e), donnée -entre parenthèses. -.Pp -Sauter une ligne après l'avis de copyright et ajouter la déclaration -autorisant la copie telle que donnée par la Fondation pour le logiciel -libre. -Utiliser l'avis de copyright en anglais qui, au contraire de sa -traduction française, revêt une signification juridique. -.Pp -L'en-tête type d'un fichier source du programme -.Ql Foo -est : -.Bd -literal -offset Ds -/* Copyright (C) 2016-2018, 2020, 2022, 2026 - * Jeanne Lambda (jeanne.lambda@courriel.fr) - * Copyright (C) 2017, 2019 Jean Untel (juntel@courriel.fr) - * Copyright (C) 2024 |Méso|Star> (contact@meso-star.com) - * - * This file is part of Foo. - * - * Foo is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later - * version. - * - * Foo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with Foo. If not, see - * <https://www.gnu.org/licenses/>. */ -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh L'INDENTATION -Indenter le texte par 2 espaces. -La mise en page du texte, taille de ligne comprise, est ce faisant -indépendante de la taille d'une tabulation. -.Pp -Les auteures et auteurs sont encouragés à configurer leur éditeur de -texture pour qu'il développe chaque caractère tabulation en 2 espaces -.Pq section Sx FICHIERS . -Et ainsi continuer à utiliser la touche tabulation pour l'indentation. -.Pp -Limiter l'indentation à 2 caractères, contre 8 pour le standard de facto -des tabulations, laisse plus d'espace aux différents niveaux -d'indentation, dès lors moins contraints par la limite du nombre de -caractères par ligne -.Pq section Sx LA LONGUEUR DES LIGNES . -Néanmoins, un niveau d'indentation supérieur à 3 est aussi le signe d'un -déficit de structure dans l'écriture du programme. -Les 2 espaces retenus pour indenter le code n'est donc pas une -incitation à aller au delà de 3 niveaux d'indentation sous prétexte de -disposer de plus d'espace par niveau. -.Pp -Indenter le contenu de chaque bloc -.Pq section Sx LES BLOCS . -Pour la directive -.Ql switch , -indenter chaque -.Ql case -ainsi que leur contenu : -.Bd -literal -offset Ds -switch (opt) { - case 'e': - errno = 0; - epsilon = strtod(optarg, NULL); - if (errno != 0) err = 1; - break; - case 'h': - printf("usage: foo [-hov]\en"); - break; - case 'o': - output = optarg; - break; - case 'v': - verbose += (verbose < 3); - break; - default: - err = 1; - break; -} -.Ed -.Pp -Motiver l'utilisation de plusieurs instructions par ligne par -l'expressivité du code en résultant, qu'une écriture resserrée viendrait -renforcer : -.Bd -literal -offset Ds -if (x == NULL || y == NULL) { err = 1; goto error; } -x[0] = 1.0; x[1] = 0.0; -y[0] = 0.0; y[1] = 1.0; -.Ed -.Pp -Ne sauter au plus qu'une ligne. -Ne pas laisser d'espace en fin de ligne et supprimer les lignes vides -en fin de fichier. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES COMMENTAIRES -Utiliser des commentaires dès lors que la seule expressivité serrée du -code ne permet pas d'exprimer l'entièreté du discours que les sources -doivent rendre compte, ou la logique qu'il met effectivement en oeuvre. -.Pp -Ajouter un espace après l'ouverture du commentaire -.Ql /* -et avant sa fermeture -.Ql */ . -.Pp -Si un commentaire occupe plusieurs lignes, ajouter un caractère -.Ql * -en début de ligne, aligné avec le caractère -.Ql * -de la ligne qui précède. -Ajouter un espace entre le caractère -.Ql * , -qui marque la continuation du commentaire, et la suite du commentaire : -.Bd -literal -offset Ds -/* Valeurs de hachage initiales, à savoir les 32 premiers bits - * de la partie fractionnaire des racines carrées des 4 premiers - * nombres premiers (2, 3, 5 et 7) */ -state[0] = 0x6a09e667; -state[1] = 0xbb67ae85; -state[2] = 0x3c6ef372; -state[3] = 0xa54ff53a; -.Ed -.Pp -Les commentaires servent aussi à structurer la lecture du code source. -Que ce soit à l'échelle des instructions, par un commentaire -.Dq chapeau -qui résume la séquence de code qui suit : -.Bd -literal -offset Ds -/* Enregistrer le résultat */ -((uint32_t*)hash)[0] = big_endian_32(state[0]); -((uint32_t*)hash)[1] = big_endian_32(state[1]); -((uint32_t*)hash)[2] = big_endian_32(state[2]); -((uint32_t*)hash)[3] = big_endian_32(state[3]); -.Ed -.Pp -ou à l'échelle du fichier, où les commentaires servent alors de -séparateur entre ses différentes sections -.Pq voir Sx LA STRUCTURE D'UN FICHIER SOURCE . -Dans ce cas, encadrer le commentaire par deux ligne de caractères -.Ql * -qui débute ou se termine par le caractère -.Ql / -si, respectivement, la ligne précède ou suit l'intitulé de la section : -.Bd -literal -offset Ds -/*********************************************************** - * Définition des fonctions utilitaires - **********************************************************/ -static void -foo(uint32_t bar[4], const char baz[64]) -{ - ... -} - -static int -qux(const int xyzzy) -{ - ... -} -.Ed -.Pp -À noter que dans l'exemple qui précède, la taille des lignes -d'encadrement est limitée par des contraintes d'édition de la présente -page de manuel. -Dans un fichier source, étendre ces lignes pour qu'elles occupent la -longueur maximale recommandée pour une ligne -.Pq section Sx LA LONGUEUR DES LIGNES . -.Pp -Pour expliciter le contexte général d'un fichier, en terme d'utilisation -ou d'architecture logicielle, insérer un commentaire en en-tête du -fichier en laissant les caractères d'ouverture ou de fermeture de -commentaires sur une ligne séparée : -.Bd -literal -offset Ds -/* - * Interface de programmation des tableaux extensibles. - * Cette structure de données peut être utilisée avec des - * types de données qui ne nécessitent pas de processus - * d'initialisation ou de libération et qui peuvent être - * copiés bit à bit - */ -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES FICHIERS D'EN-TÊTE -Inclure les fichiers d'en-tête dans l'ordre qui suit : -.Bl -enum -compact -.It -les en-têtes locaux au programme ; -.It -les en-têtes des dépendances du programme ; -.It -les en-têtes systèmes et ceux de la bibliothèque C standard. -.El -.Pp -Les fichiers d'en-tête sont ainsi inclus dans l'ordre décroissant de -leur niveau d'abstraction. -Cet ordre participe à garantir que chaque fichier d'en-tête inclus les -en-têtes dont il a lui même besoin, indépendamment des directives -d'inclusion qui précèdent sa propre inclusion. -Si ce n'est pas le cas, la compilation pourra échouer, symptôme qu'un -des fichiers d'en-tête n'est pas auto-consistant. -.Pp -Dans chaque groupe, trier les directives d'inclusion par ordre -alphabétique des fichiers d'en-tête. -Si besoin, ajouter un commentaire court, sur la même ligne que la -directive d'inclusion, qui explicite la raison pour laquelle la fichier -est inclus. -.Bd -literal -offset Ds -#include "bar.h" -#include "foo.h" -#include "qux.h" - -#include <baz.h> - -#include <float.h> /* FLT_MAX */ -#include <stdio.h> -.Ed -.Pp -S'efforcer de n'inclure que les seuls fichiers d'en-tête -réellement nécessaires au fichier ; -par exemple par une déclaration anticipée des types structurés à -la place d'inclure des en-têtes dans le seul but de déclarer lesdits -types. -Un enjeu a considérer avec d'autant plus d'attention que le fichier -concerné par les inclusions est lui même un fichier d'en-tête, par -conséquent amené à être lui même inclus. -L'objet étant de limiter autant que possible le nombre de fichiers -inclus par unité de compilation, pour limiter les accès disque et ainsi -réduire les temps de compilation. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LA VISIBILITÉ DES SYMBOLES -Par défaut, n'exposer aucun symbole -.Po -option -.Fl fvisibility=hidden -du compilateur -.Xr gcc 1 -.Pc , -à l'exception de ceux de l'interface de programmation d'une -bibliothèque. -.\"""""""""""""""""""""""""""""""""" -.Ss Les symboles d'interface -Privilégier l'écriture d'un seul fichier d'en-tête pour exposer -l'interface de programmation d'une bibliothèque. -Y définir une macro qui exporte les symboles qu'elle déclare dès lors -que ce fichier d'en-tête est inclus par une unité de compilation de la -bibliothèque. -Et qui se contente d'importer ces mêmes symboles si ce même fichier est -inclus par un programme tiers : -.Bd -literal -offset Ds -#include <rsys/rsys.h> - -#if defined(FOO_SHARED_BUILD) - #define FOO_API extern EXPORT_SYM -else - #define FOO_API extern IMPORT_SYM -#endif -.Ed -.Pp -Avec : -.Bl -dash -compact -.It -.Sy FOO_SHARED_BUILD -une macro définie uniquement à la compilation de la bibliothèque -.Po -option -.Fl DFOO_SHARED_BUILD -du compilateur C -.Pc Ns - ; -.It -.Sy EXPORT_SYM -et -.Sy IMPORT_SYM -des directives définies dans la bibliothèque -.Ql RSys . -Elles enrichissent le langage C d'une gestion explicite de la visibilité -des symboles. -.El -.Pp -Utiliser cette macro à la déclaration des variables et constantes -d'interfaces : -.Bd -literal -offset Ds -/* Variables globales de l'interface de programmation */ -FOO_API const struct foo foo_plugh; -FOO_API const struct foo foo_xyzzy; -.Ed -.Pp -Déclarer le prototype des fonctions d'interface entre les directives -.Sy BEGIN_DECLS -et -.Sy END_DECLS , -elles aussi définies dans la bibliothèque -.Ql RSys -.Pq en-tête In rsys/rsys.h . -Ainsi, le fichier d'en-tête peut être inclus par un programe C++ : -.Bd -literal -offset Ds -BEGIN_DECLS - -FOO_API void -foo_bar - (int i, - int* j); - -FOO_API int -foo_qux - (double d, - int i); - -END_DECLS -.Ed -.\"""""""""""""""""""""""""""""""""" -.Ss Les symboles internes partagés -Pour les fichiers d'en-tête internes au programme, utiliser la directive -.Sy LOCAL_SYM , -définie dans le fichier -.In rsys/rsys.h -de la bibliothèque -.Ql RSys , -pour déclarer les variables globales et prototypes de fonctions. -Ainsi leur symbole n'est pas exposé à l'extérieur du programme. -.Bd -literal -offset Ds -#include <rsys/rsys.h> - -extern LOCAL_SYM char bar[128]; - -extern LOCAL_SYM void -quux - (char* tab, - size_t length); -.Ed -.Pp -Cette directive est redondante si le compilateur est configuré pour -masquer par défaut tous les symboles -.Po -option -.Fl fvisibility=hidden -de -.Xr gcc 1 -.Pc . -Utiliser -.Sy LOCAL_SYM -permet néanmoins de s'exonérer de cet a priori, tout en uniformisant -les déclarations des fonctions et variables en explicitant pour chaque -déclaration la visibilité du symbole associé. -.\"""""""""""""""""""""""""""""""""" -.Ss Les symboles internes à une unité de compilation -Utiliser le mot clé -.Ql static -pour déclarer des variables, constantes, et fonctions visibles -uniquement au sein d'une unité de compilation. -.Pp -C'est notamment le cas des constantes symboliques structurées : -.Bd -literal -offset Ds -struct foo { - int bar; - int qux; -}; -static const struct foo FOO_DEFAULT = {1, 0}; -.Ed -.Pp -Mais aussi des fonctions utilitaires, qu'elles soient -propres à un fichier C, ou définies dans un fichier d'en-tête. -.Bd -literal -offset Ds -static void -hello(void) -{ - printf("Hello, world!\en"); -} -.Ed -.Pp -Ces fonctions peuvent en plus être déclarées avec la directive -.Sy INLINE , -définie dans l'en-tête -.In rsys/rsys.h -de la bibliothèque -.Ql RSys , -pour suggérer au compilateur de substituer l'appel de la fonction par le -corps de celle-ci, de sorte à éviter le surcoût de l'appel. -Cette directive est équivalente au mot clé -.Ql inline -du C99, indisponible dans le dialecte C retenu -.Pq section Sx LE LANGAGE C . -.Bd -literal -offset Ds -static INLINE void -foo(void) -{ - printf("bar\en"); -} -.Ed -.Pp -Limiter la directive -.Sy INLINE -aux fonctions élémentaires, destinées à être appelées fréquemment et -dont le coût de l'appel pourrait alors s'avérer significatif. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES BLOCS -Ouvrir chaque bloc sur la même ligne que la directive qui en est à -l'origine -.Po -.Ql do , -.Ql enum -.Ql for , -.Ql if , -.Ql struct , -.Ql switch , -.Ql union , -.Ql while -.Pc , -en séparant par un espace la fin de la directive et le caractère -.Ql { -qui marque l'ouverture du bloc : -.Bd -literal -offset Ds -if (foo) { - bar(); - qux(); -} -.Ed -.Pp -Exception faite des fonctions, ou le bloc associé est ouvert sur la -ligne qui suit : -.Bd -literal -offset Ds -static void -foo(void) -{ - printf("bar\en"); -} -.Ed -.Pp -Fermer un bloc sur une ligne à part sauf s'il est suivi d'une nouvelle -structure de contrôle associée à la précédente -.Po -.Ql if else , -.Ql do while -.Pc . -Dans ce cas, ajouter la nouvelle instruction sur la même ligne que celle -utilisée pour fermer le bloc, en la séparant du caractère -.Ql } -par un espace. -.Pp -Aligner la fermeture du bloc à l'indentation de sa directive, ou, dans -le cas de directives qui se suivent, à l'indentation de la première -directive à l'origine des blocs successifs : -.Bd -literal -offset Ds -if (foo) { - bar(); -} else { - qux(); -} -.Ed -.Pp -Écrire sur une seule ligne une directive et les opérations qu'elle -contrôle que si la clarté du code n'en est pas impactée. -Dans ce cas, l'ouverture et la fermeture du bloc associé se fait sur une -seule et même ligne : -.Bd -literal -offset Ds -if (foo) { bar(); return 0; } -.Ed -.Pp -Ne pas utiliser d'accolades si la structure de contrôle n'est suivie -d'aucune ou d'une seule directive écrite sur la même ligne : -.Bd -literal -offset Ds -while (foo()); - -if (bar) return 0; -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES MOTS CLÉS -Ajouter un espace après chaque structure de contrôle -.Ql if , -.Ql switch , -.Ql for -et -.Ql while -pour les différencier des appels de fonctions. -Ne pas ajouter d'espace après l'ouverture et avant la fermeture des -parenthèses qui détourent leur(s) expression(s) : -.Bd -literal -offset Ds -if (i < 10) { - foo(i); -} -.Ed -.Pp -Ajouter un espace après chaque point virgule qui sépare les expressions -d'une boucle -.Ql for -sauf si l'expression qui suit est vide : -.Bd -literal -offset Ds -for (i=0; i<10; foo(i++)); - -for (;;) { /* Boucle infinie */ - poll(); - if(bar) break; -} -.Ed -.Pp -Assimiler l'instruction -.Ql sizeof -à une fonction ; ne pas insérer d'espace entre le mot clé et son -expression entourée de parenthèses : -.Bd -literal -offset Ds -sz = sizeof(int); -.Ed -.\"""""""""""""""""""""""""""""""""" -.Ss L'instruction Ql switch -Limiter à quelques lignes le contenu de chaque -.Ql case -d'une instruction -.Ql switch , -celle-ci devant donner à lire la seule répartition des traitements, -fonction de la valeur que peut prendre l'expression du -.Ql switch . -Et non les traitements eux même, sauf s'ils sont triviaux. -Un -.Ql case -au contenu trop fourni est alors le signe d'un manque de structure dans -l'écriture du programme. -.Pp -Toujours ajouter une instruction -.Ql default -même si l'ensemble des valeurs que pourraient prendre l'expression du -.Ql switch -est censé être couvert par les différents -.Ql case . -C'est notamment le cas quand l'expression est une variable -d'énumération. -Utiliser alors la directive -.Sy FATAL , -définie par la bibliothèque -.Ql RSys -.Pq en-tête In rsys/rsys.h , -pour signifier un comportement inattendu. -Et ainsi pouvoir diagnostiquer une erreur dans la valeur de l'expression -du -.Ql switch , -ou un -.Ql case -manquant : -.Bd -literal -offset Ds -switch (i) { - case FOO: foo(); break; - case BAR: bar(); break; - case QUX: qux = 1; break; - default: FATAL("Unreachable code\en"); break; -} -.Ed -.Pp -Ajouter la directive -.Sy FALLTHROUGH , -définie dans le fichier d'en-tête -.In rsys/rsys.h -de la bibliothèque -.Ql RSys , -en fin des instructions -.Ql case -qui s'enchaînent. -L'ajout de cette directive permet d'expliciter que c'est bien le -comportement attendu et non l'oublie d'une instruction -.Ql break , -en plus d'éviter un possible message d'avertissement à la compilation -.Pq option Fl Wimplicit-fallthrough No de Xr gcc 1 Ns - : -.Bd -literal -offset Ds -switch (c) { - case 'a': - foo = 1; - FALLTHROUGH; - case 'b': - bar = 1; - break; - default: - usage(); - break; -} -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LE NOMMAGE -.\"""""""""""""""""""""""""""""""""" -.Ss Les variables -Nommer les variables en minuscules. -Utiliser le tiret bas -.Ql _ -au titre de séparateur entre les termes utilisés dans le nom des -variables : -.Bd -literal -offset Ds -foo_bar -.Ed -.Pp -Expliciter l'objet d'une variable dans son nom avec d'autant plus de -précision que sa portée est grande. -Une variable locale à un bloc de quelques lignes pourra se contenter -d'un nom abrégé tel que -.Va tmp -pour un résultat temporaire, voire n'être qu'un seul caractère -pour un indice -.Va i -ou un nombre d'éléments -.Va n Ns - ; leur contexte d'utilisation venant préciser ce que leur nom résume. -Ce nom abrégé vient non seulement alléger l'écriture mais aussi en -renforcer l'expressivité. -Par exemple -.Va tableau Ns Bq Va i -reste plus clair que -.Va tableau Ns Bq Va indice . -.Pp -Les variables globales sont au contraire à nommer de sorte à décrire ce -qu'elles représentent, indépendamment de tout contexte d'utilisation, -par construction distant de leur déclaration. -Un compteur d'allocations global à un programme aura donc pour nom -.Va compteur_allocations -plutôt que -.Va cpt_allocs . -.Pp -Pour une variable globale déclarée au niveau de l'interface d'une -bibliothèque, préfixer ladite variable avec l'acronyme de la -bibliothèque. -Un compteur d'allocation global à la bibliothèque -.Ql Foo , -et déclaré en tant que variable globale de son interface, sera alors -nommé -.Va foo_compteur_allocations . -.\"""""""""""""""""""""""""""""""""" -.Ss Les fonctions -Nommer les fonctions avec des caractères alphanumérique en minuscules, -et séparer les termes qui composent leur nom par un tiret bas -.Pq caractère Ql _ . -.Pp -Donner à une fonction un nom d'autant plus explicite que sa portée est -importante. -Une fonction utilitaire pourra se contenter d'un nom abrégé, tel que -.Fn cmp -pour une fonction de comparaison utilisée comme argument d'un appel à -.Xr qsort 3 -au sein d'un fichier C. -Là où une fonction partagée entre plusieurs unités de compilation aura -un nom plus expressif, tel que -.Fn compare_bar , -pour notamment expliciter le type -.Vt struct bar -des variables comparées. -Jusqu'à préfixer le nom de la fonction par l'acronyme de la bibliothèque -quand elle est une fonction d'interface de ladite bibliothèque. -Pour la bibliothèque -.Ql Foo , -une fonction d'interface sera alors nommée -.Fn foo_compare_bar . -.\"""""""""""""""""""""""""""""""""" -.Ss Les déclarations de types -Utiliser des caractères alphanumériques en minuscules pour nommer les -structures, unions et énumérations. -Utiliser le tiret bas -.Ql _ -pour séparer les différents termes qui composent leur nom. -.Bd -literal -offset Ds -struct foo { - int bar; - int baz -}; - -union foo_bar { - double qux; - int xyzzy; -}; -.Ed -.Pp -Utiliser la même convention pour nommer les déclarations typedef. -À l'exception du suffixe -.Ql _T -ajouté au nom de l'identificateur du type, en écho au suffixe -.Ql _t -souvent utilisé pour ce type de déclaration, mais réservé par le -standard POSIX. -.Bd -literal -offset Ds -typedef int foo_T; -typedef char foo_bar_T[256]; -.Ed -.Pp -Ne pas utiliser de déclaration typedef sur les structures, unions et -énumérations de sorte à permettre leur déclaration anticipée. -.Pp -Préfixer le nom d'un type par l'acronyme de la bibliothèque dès lors -qu'il est un type déclaré en tant que type de son interface. -Par exemple, un type structuré de l'interface de la bibliothèque -.Ql Foo -sera nommé -.Vt struct foo_mon_type . -.\"""""""""""""""""""""""""""""""""" -.Ss Constantes et macros -Utiliser des majuscules pour nommer les constantes symboliques, qu'elles -soient des macros, des constantes énumérées, ou des variables déclarées -comme constantes. -Séparer par un tiret bas -.Ql _ -les termes qui composent leur nom : -.Bd -literal -offset Ds -#define FOO_BAR 42 - -enum foo { - BAR_BAZ, - QUX -}; - -static const enum foo FOO_XYZZY = BAR_BAZ; -.Ed -.Pp -Nommer une constante ou une macro de manière d'autant plus explicite -que sa portée est importante. -Une constante définie localement à une fonction pourra se contenter -d'un nom abrégé, jusqu'à n'être qu'un seul caractère, par exemple pour -un nombre d'éléments constant -.Sy N . -Là où le nom d'une macro ou d'une constante définie dans un fichier -d'en-tête se devra d'être plus explicite, tel que -.Sy NOMBRE_ELEMENTS_MAX . -Et être préfixée par l'acronyme de la bibliothèque si elle est déclarée -comme macro ou constante de son interface. -Par exemple, pour la bibliothèque -.Ql Foo , -.Sy FOO_NOMBRE_ELEMENTS_MAX . -.Pp -Utiliser la convention typographique dite -.Dq camel case -pour nommer les arguments des macros ; -les termes qui composent leur nom sont séparés par une variation de la -casse typographique. -Un terme débute par une majuscule, suivi de caractères -alphanumériques en minuscules. -.Bd -literal -offset Ds -#define FOO_BAR(FooBar, Qux) ((FooBar) + (Qux)) -.Ed -.Pp -Ainsi, les arguments de macros sont différenciés des constantes -symboliques et des variables. -.Pp -À noter que la macro elle même est nommée selon la même convention que -celle utilisée pour les constante symboliques -.Pq en majuscule et un tiret bas pour séparer ses différents termes . -La parenthèse ouvrante, collée au nom de la macro, permettant de -différencier les 2 cas. -.\"""""""""""""""""""""""""""""""""" -.Ss Les variables et macros internes -Suffixer par deux tirets bas -.Ql __ -les variables membres d'une structure définie publiquement, qui n'ont -cependant une signification qu'en interne des fonctions d'interface de -la structure. -L'enjeu étant de souligner que ces variables ne sont accessibles que par -effet de bord, et ne s'addressent -.Em pas -aux utilisatrices et utilisateurs, qui ne devraient donc pas y accéder -directement : -.Bd -literal -offset Ds -struct foo { - double bar; - int baz; - int* qux__; /* Variable interne */ -}; -.Ed -.Pp -Utiliser le même suffixe en double tirets bas -.Ql __ -pour nommer les variables internes à une macro, afin d'éviter de masquer -les variables définies dans le contexte où la macro est développée : -.Bd -literal -offset Ds -#define FOO(Bar, N) { \e - int i__; \e - for (i__ = 0; i__ < N; Bar(i__), ++i__); \e -} (void)0 -.Ed -.Pp -Suffixer les macros d'un fichier d'en-tête par deux tirets bas -.Ql __ -dès lors qu'elles sont propres au fichier d'en-tête, et donc -vraisemblablement inacessibles au delà : -.Bd -literal -offset Ds -#define FOO__(Type, Dim) \e - struct Type { \e - int i[Dim]; \e - float f[Dim]; \e - } -FOO__(bar, 2); -FOO__(baz, 3); -FOO__(qux, 4); -#undef FOO__ -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES FONCTIONS -S'attacher à ce que chaque fonction reste simple et concise, en ne -s'appliquant à ne lui faire faire qu'une seule chose. -Ce faisant, le corps d'une fonction devrait être lisible sur un ou deux -écran, avec comme référence la taille des terminaux telle que -démocratisée à la fin des années 1970, à savoir 24 lignes. -.Pp -Un indice quant à la taille qu'une fonction devrait s'efforcer à avoir -est donné par ses niveaux d'indentation. -Plus elle compte de niveaux et plus elle devrait être ramassée. -De même, un nombre de variables locales supérieur à dix peut être le -signe d'une fonction trop dense. -.Pp -Écrire les directives qui contrôlent la portée d'une fonction et son -type de retour sur une ligne séparée de son nom. -L'expression régulière -.Ql ^nom_de_fonction -peut ainsi être utilisée pour la recherche d'une fonction dans les -différents fichiers sources. -.Pp -Pour une déclaration, revenir à la ligne avant d'ouvrir la parenthèse de -la fonction, précédée d'une indentation par rapport au nom de la -fonction sur la ligne qui précède. -Puis, lister les arguments de la fonction, en revenant à la ligne après -chacun d'eux et en les alignant les uns par rapport aux autres. -Ajouter la parenthèse fermante -.Ql \&) -et le point virgule -.Ql \&; -sur la ligne du dernier argument, sans espace supplémentaire : -.Bd -literal -offset Ds -extern LOCAL_SYM void -foo_bar - (struct foo* foo, - const int qux, - const float xyzzy); -.Ed -.Pp -Les différentes parties qui composent le profil de la fonction sont -ainsi identifiables par la seule mise en page de sa déclaration. -.Pp -Lors de sa définition, lister les arguments de la fonction sur la même -ligne que le nom de la fonction, sans ajouter d'espace entre le nom de -la fonction et sa parenthèse ouvrante : -.Bd -literal -offset Ds -void -foo_bar(struct foo* foo, const int qux, const float xyzzy) -{ - ... -} -.Ed -.Pp -Si la liste des arguments dépasse la longueur maximale d'une ligne -.Pq section Sx LA LONGUEUR DES LIGNES -les lister comme pour une déclaration. -.Pp -Ordonner les arguments d'une fonction comme suit : -.Bl -enum -compact -.It -pour une fonction d'interface, la variable sur laquelle la fonction -opère ; -.It -les données d'entrées ; -.It -les données en sortie. -.El -.Pp -Ajouter l'instruction -.Ql const -aux variables qui n'ont pas vocation à être modifiées par la fonction. -Et ce quand bien même leur modification n'aurait aucune conséquence, -comme pour les variables de données simples, copiées à l'appel de la -fonction. -L'objet étant de souligner qu'elles sont des variables en entrée : -.Bd -literal -offset Ds -static void -foo - (struct foo* foo, - constr struct bar* bar, - const int longueur, - const int* liste, - int* resultat); - -static INLINE double -madd(const double a, const double b, const double c) -{ - return a*b + c; -} -.Ed -.Pp -Ne passer en copie que les seuls paramètres en entrée de la fonction de -type primitif -.Pq Vt char , int , double , No énumération, ... . -Utiliser un pointeur constant dès lors que le paramètre d'entrée est -de type structuré, afin d'éviter le surcoût de sa copie à chaque appel de -fonction ; son occupation mémoire étant a priori plus important -qu'une donnée simple. -.Pp -Les paramètres d'une fonction peuvent ne pas être utilisés à l'intérieur -de celle-ci. -C'est notamment le cas si les paramètres ne sont utiles que pour -répondre à un profil de fonction spécifique ou dans un contexte de -compilation particulier, par exemple pour le débogage. -Lister ces paramètres en en-tête de la fonction, après -la définition des variables locales, en les préfixant d'une conversion -explicite vers un type vide : -.Bd -literal -offset Ds -static void -foo(int x, int y, int z) -{ - int i = 0; - (void)y, (void)z; /* Paramètres inutilisés */ - - i = bar(x); - if (i < 42) { - printf("Foobar\en"); - } -} -.Ed -.Pp -Cette conversion explicite quels paramètres sont ignorés, en plus de -désactiver les avertissements de compilation quant à la définition de -paramètres non utilisés -.Pq option Fl Wunused-parameter No de Xr gcc 1 . -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES VARIABLES -Limiter le nombre de variables par bloc entre 5 et 10. -Un nombre de variables trop important peut être le signe d'un manque de -structure auquel un découpage en sous-fonction(s) pourrait remédier -.Pq section Sx LES FONCTIONS . -.Pp -Initialiser les variables dès leur définition avec sinon une valeur -valide, au moins une valeur par défaut. -L'objet étant d'éviter l'utilisation de variables non initialisées. -D'apparence peu critique pour les variables de type primitif, cette -initialisation l'est bien plus pour les variables structurées, dont la -liste des membres peut changer. -Si elle existe, utiliser la constante proposée avec la définition du -type structuré pour initialiser ses membres -.Pq voir section Sx LES STRUCTURES . -En son absence, n'initialiser que le premier membre de la variable ; le -language C assure alors que les autres membres seront initialisés à -zéro. -De même pour un tableau alloué sur la pile, initialiser son premier -élément suffit à garantir que le reste du tableau sera initialisé à -zero : -.Bd -literal -offset Ds -struct foo foo = FOO_DEFAULT; -struct bar bar = {0}; -int qux[10] = {0}; -int i = 0; -.Ed -.Pp -Au sein d'une même fonction, définir les variables au plus proche de -leur utilisation de sorte à ce que le contexte dans lequel elles sont -utilisées participe à les caractériser. -Par exemple, une variable -.Va i -utilisée dans un bloc comme variable temporaire, et comme indice de -boucle dans un autre, gagnera en expressivité et en robustesse à être -définie localement à chaque bloc ; -les deux variables étant alors, par construction, non seulement séparées -mais aussi sans effet de bord de l'une sur l'autre : -.Bd -literal -offset Ds -if(foo) { - const int i = bar(); - if (i > max_i) max_val = i; - if (i < min_i) min_val = i; -} else { - int i = 0; - for(i = 0; i < N; ++i) qux(i); -} -.Ed -.Pp -Regrouper les définitions des variables dès lors qu'elles sont liées -sémantiquements. -Les trier ensuite par taille mémoire décroissante, et enfin par ordre -alphabétique : -.Bd -literal -offset Ds -/* Bibliothèque Foo */ -struct foo_args foo_args = FOO_ARGS_DEFAULT; -struct foo* foo = NULL; - -/* Tableau à traiter */ -double* liste = NULL; -int capacite = 0; -int longueur = 0; -.Ed -.Pp -Trier la définition des variables par taille mémoire tend à limiter le -nombre d'octets de remplissage que le compilateur C ajoute pour garantir -l'alignement mémoire de chaque variable eu égard à leur type. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES CONSTANTES -Utiliser une énumération si les constantes à définir sont liées -sémantiquement, et une macro sinon : -.Bd -literal -offset Ds -#define ID_INVALIDE ((unsigned)-1) - -enum { X, Y, Z }; - -enum attribut { - POSITION, - NORMALE, - TEXCOORD -}; -.Ed -.Pp -Pour une énumération qui utilise des valeurs par défaut, -ajouter si besoin une dernière constante qui définit le nombre de -constantes valides ; -sa valeur sera ainsi automatiquement mise à jour à chaque changement de -l'énumération. -Une telle constante peut alors servir à définir la cardinalité d'un -tableau, comme valeur du dernier indice marquant la fin d'une itération, -ou encore comme valeur vis à vis de laquelle la validité d'une variable -du type énuméré peut être vérifiée : -.Bd -literal -offset Ds -enum molecule { - CH4, - CO, - CO2, - H2O, - N2O, - O3, - - NOMBRE_DE_MOLECULES -}; - -/* Vérifier qu'une constante définie une molecule valide */ -#define MOLECULE_EST_VALIDE(Mol) \e - ((unsigned)(Mol) < NOMBRE_DE_MOLECULES) - -static const char* NOM_DES_MOLECULES[NOMBRE_DE_MOLECULES] = { - "CH4", "CO", "CO2", "H2O", "N2O", "O3" -}; -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES STRUCTURES -Définir les structures en en-tête de fichier -.Pq section Sx LA STRUCTURE D'UN FICHIER SOURCE , -à l'exeption des structures locales à une fonction. -.Pp -Lister les variables membres d'une structure suivant la même convention -que pour la définition des variables d'un bloc -.Pq section Sx LES VARIABLES Ns - : -les regrouper d'abord par sémantique, puis les trier par occupation -mémoire décroissante, et enfin par ordre alphabétique. -.Pp -Ne définir qu'une variable membre par ligne. -.Pp -Pour une structure dont aucune fonction ne permet d'en initialiser les -membres, définir une constante qui fixe leur valeur par défaut. -Suffixer cette constante par -.Ql DEFAULT -ou -.Ql NULL -fonction de si une variable structurée ainsi initialisée est une donnée -valide ou non. -Déclarer cette constante en tant que variable statique et l'initialiser par -une macro de même nom, différenciée de la variable constante par un -double tiret bas final -.Ql __ Ns - : -.Bd -literal -offset Ds -struct arg { - char* fichier; /* NULL <=> entrée standard */ - int verbosite; -}; -#define ARG_DEFAULT__ {NULL, 0} -static const struct arg ARG_DEFAULT = ARG_DEFAULT__; - -struct chaine { - char* mem; - int longueur; - int capacite; -}; -#define CHAINE_NULL__ {NULL,0,0} -static const struct chaine CHAINE_NULL = CHAINE_NULL__; -.Ed -.Pp -N'utiliser la macro que lorsqu'il est impossible d'utiliser la variable -constante, en l'occurence pour initialiser, dès sa définition, les -membres d'une autre variable structurée : -.Bd -literal -offset Ds -struct qux { - struct chaine foo; - int bar; -}; -#define QUX_DEFAULT__ {CHAINE_NULL__, 0} -static const struct qux QUX_DEFAULT = QUX_DEFAULT__; -.Ed -.Pp -Éviter d'utiliser une déclaration typedef des structures afin -d'autoriser leur déclaration anticipée. -Et l'utilisation de pointeur vers une donnée structurée sans avoir sa -définition. -Si un déclaration typedef est néanmoins souhaitée, nommer le type -structuré en suivant la convention de nommage des déclarations typedef -.Pq section Sx Les déclarations de types . -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES MACROS -Pour définir une séquence d'instructions, -préférer l'utilisation de fonctions aux macros. -Déclarer la fonction avec la directive -.Sy INLINE -si son coût d'appel est un enjeu -.Pq section Sx Les symboles internes à une unité de compilation . -.Pp -Regrouper la séquence d'instructions d'une macro dans un bloc terminé -par l'instruction -.Ql (void)0 . -Elle peut ainsi être utilisée comme unique expression d'une structure de -contrôle, et force l'ajout d'un point virgule -.Ql \&; -.Pq ou d'une virgule Ql \&, -après son utilisation, telle n'importe quelle autre instruction C. -.Pp -En C, il est plus courant d'encapsuler les instructions d'une macro -dans une structure de contrôle -.Ql do { ... } while (0) -plutôt que dans un bloc terminé par la converstion de l'entier zéro vers -un type vide -.Ql (void)0 . -Si les deux écritures répondent aux mêmes objectifs, cette dernière -convention évite les avertissements émis par certains compilateurs quant -à l'utilisation d'une expression conditionnelle constante dans -.Ql while (0) . -.Pp -Ouvrir le bloc sur la même ligne que le nom de la macro, en ajoutant un -espace avant l'acolade -.Ql { . -Indenter le contenu du bloc par rapport à la directive de définition de -la macro. -Justifer à droite les caractères anti-slash -.Ql \e -en fin de chaque ligne de sorte à faciliter la lecture de la séquence -d'instructions développée par la macro : -.Bd -literal -offset Ds -#define FOO(X, Y) { \e - if ((X) == (Y)) printf("Bar \en"); \e - (Y) += 2; \e -} (void)0 -.Ed -.Pp -À noter que dans l'exemple qui précède, les caractères anti-slash -.Ql \e -sont alignés en suivant des contraintes d'édition propres à ce manuel. -Dans un fichier source, positioner l'anti-slash en tant que dernier -caractère de lignes qui occupent la longueur maximale autorisée -.Pq section Sx LA LONGUEUR DES LIGNES . -.Pp -Pour une macro dont la portée est l'unité de compilation, ne pas changer -le déroulé des instructions de son contexte d'appel, par exemple en -intégrant une directive -.Ql return . -Son utilisation contredirait l'exécution séquentielle du code et ce -faisant nuirait à sa lisibilité. -Il n'est donc -.Em pas -recommandé de définir une macro comme suit : -.Bd -literal -offset Ds -#define FOO(X) { - if (bar(X)) - return -1; -} (void)0 -.Ed -.Pp -Ne pas présupposer l'existance de variables externes à la macro, -exeption faite des variables globales. -L'objet étant de ne pas lier son bon fonctionnement au contexte local -dans lequel elle est développée. -L'écriture qui suit est donc -.Em découragée Ns - : -.Bd -literal -offset Ds -#define BAR(X,Y) { - z = (X) + (Y); - if (xyzzy(z)) - z += 1; -} -.Ed -.Pp -Contrairement aux macros définies à l'échelle d'une unité de -compilation, une macro locale peut non seulement changer le fil -d'exécution du contexte d'appel, mais aussi utiliser des variables -externes. -Et ce précisément en raison de son caractère local, qui lie étroitement -la macro à son seul contexte d'utilisation. -.Bd -literal -offset Ds -static int -foo(const int x) -{ - char s[10] = {0}; - int line = 0; - int err = 0; - - #define CALL(Func) { \e - if((err=(Func)) != 0) { \e - line = __LINE__; \e - goto error; \e - } \e - } (void)0 - - CALL(bar(x, s)); - CALL(quux(s)); - - #undef CALL - -exit: - return err; -error: - fprinf(stderr, "erreur %d ligne %d\en", err, line); - goto exit; -} -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES ALLOCATIONS DYNAMIQUES -Préférer l'interface d'allocation proposée par la -bibliothèque -.Ql RSys -via son fichier d'en-tête -.In rsys/mem_allocator.h . -Elle enrichit la gestion de la mémoire dynamique proposée par la -bibliothèque C standard, notamment en enregistrant la quantité de -mémoire allouée. -.Pp -Utiliser dès lors les fonctions -.Fn mem_alloc , -.Fn mem_calloc -et -.Fn mem_realloc -pour allouer dynamiquement de la mémoire. -Leur profil est celui des fonctions équivalentes proposées par la -bibliothèque C, à savoir ces mêmes fonctions mais sans le prefixe -.Ql mem_ -.Po -voir -.Xr alloc 3 , -.Xr calloc 3 , -et -.Xr realloc 3 -.Pc . -.Pp -Privilégier la fonction -.Fn mem_calloc -à -.Fn mem_alloc -de sorte à initialiser la mémoire allouée à zéro, et d'éviter ainsi -d'utiliser des données non initialisées. -.Pp -Ne pas convertir le pointeur retourné par les fonctions d'allocation. -La conversion d'un pointeur vide vers n'importe quel autre type de -pointeur est déjà assuré par le langage C. -.Pp -Définir la taille du bloc mémoire à allouer via le type pointé par la -variable destination : -.Bd -literal -offset Ds -p = mem_calloc(42, sizeof(*p)); -.Ed -.Pp -L'alternative qui consiste à épeller le type pointé en argument de -.Ql sizeof -non seulement nuit à la lisibilité des sources, mais laisse en plus -l'opportunité d'introduire un bogue dès lors que le type de pointeur -est mis à jour mais pas le nom du type renseigné à -.Ql sizeof . -.Pp -Utiliser la fonction -.Fn mem_alloc_aligned -pour allouer un bloc mémoire dont l'adresse doit être alignée sur un -nombre d'octets spécifique. -Utiliser la fonction -.Xr memset 3 , -de la bibliothèque C standard, pour forcer la mise à zéro du bloc ainsi -alloué sinon rempli d'octets -aléatoires : -.Bd -literal -offset Ds -foo = mem_alloc_aligned(sizeof(*foo), 128/* Alignement */); -memset(foo, 0, sizeof(*foo)); -.Ed -.Pp -Vérifier chaque allocation en testant que l'adresse retournée n'est pas -.Ql NULL . -Traiter ce cas comme une erreur et non un bogue -.Pq section Sx CENTRALISER LA SORTIE D'UNE FONCTION Ns - : -.Bd -literal -offset Ds - foo = mem_calloc(1, sizeof(*foo); - if (!foo) { - res = RES_MEM_ERR; - goto error; - } -.Ed -.Pp -Libérer la mémoire allouée via -.Ql RSys -avec la fonction -.Fn mem_rm -dont le profil est le même que celui de la fonction -.Xr free 3 Ns - : -.Bd -literal -offset Ds -mem_rm(foo); -.Ed -.Pp -Détecter la présence de fuites mémoires via la fonction -.Fn mem_allocated_size -qui retourne la quantité de mémoire qui reste allouée par la -bibliothèque : -.Bd -literal -offset Ds -int -main(void) -{ - size_t sz = 0; - int err = 0; - - ... - - if ((sz = mem_alloc_aligned()) != 0) { - fprintf(stderr, "Fuites mémoires : %lu octets\en", sz); - if (err == 0) err = 1; - } - return err; -} -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Ss Les allocateurs mémoire -Dans son fichier d'en-tête -.In rsys/mem_allocator.h , -la bibliothèque -.Ql RSys -définit en plus une interface d'allocateur mémoire. -Au contraire de son interface d'allocation qui enregistre la mémoire -alloué globalement par la bibliothèque, -chaque allocateur enregistre ses seules allocations. -.Pp -Plusieurs types d'allocateurs sont proposés par la bibliothèque -.Ql RSys , -chacun mettant en oeuvre une politique d'allocation qui lui est propre. -Si bien qu'en fonction du contexte, un type d'allocateur particulier -peut s'avérer plus approprié, par exemple pour réduire les coûts -d'allocations/désallocations. -Décrire les différents types d'allocateurs définis dans la bibliothèque -.Ql RSys -sort du cadre de cette documentation. -Le lecteur est invité à se référer à son fichier d'en-tête -.In rsys/mem_allocator.h -pour plus d'informations. -.Pp -Les convention listées précédemment quant aux allocation dynamiques -s'appliquent à l'identique à l'utilisation des allocateurs. -.Pp -Utiliser un allocateur consiste à appeler des macros, dont le nom est -une version en majuscule des fonctions de l'interface d'allocation. -Avec en plus en premier argument l'addresse de l'allocateur concerné : -.Bd -literal -offset Ds -foo = MEM_CALLOC(&mem_default_allocator, 1, sizeof(*foo)); - -\&... - -MEM_RM(&mem_default_allocator, foo); - -if (MEM_ALLOCATED_SIZE(&mem_default_allocator)) { - fprintf(stderr, "Fuites mémoires\en"); -} -.Ed -.Pp -avec -.Va mem_default_allocator -l'allocateur par défaut définit par la bibliothèque -.Ql RSys . -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh CENTRALISER LA SORTIE D'UNE FONCTION -Utiliser la directive -.Ql goto -pour centraliser les traitements à effectuer en sortie de fonction, tels -les affectations de variables de sorties, les libérations de variables -locales temporaires, ou le renvoie d'une valeur en retour de la fonction. -Regrouper ces traitements en fin de fonction sous le label -.Ql exit -dont la dernière instruction est la valeur retournée par la fonction. -.Pp -De même, centraliser la gestion des erreurs détectées pendant -l'exécutation de la fonction sous un label -.Ql error , -qui vise à revenir à l'état du programme avant l'appel de la fonction, -et à préparer son retour compte tenu de l'erreur. -Ses traitements recouvrent notamment la libération de -l'espace mémorie alloué à destination de l'appelant, la restauration des -données en mise à jour modifiées par la fonction avant la détection de -l'erreur, ou la définition de valeurs à destination des variables en -sortie en conséquence de l'erreur détectée. -.Pp -Placer le label -.Ql error -après le label -.Ql exit . -Terminer la gestion des erreurs par la directive -.Ql goto exit , -de sorte à effectuer les traitements en sortie, -.Em indépendants -de la présence ou non d'une erreur d'exécution, et donc à appliquer en -toute circonstance. -En structurant les labels de la sorte, les traitements en sortie -.Pq label Ql exit -sont ainsi exécutés soit automatiquement au fil du bon déroulé de la -fonction, sans que l'auteur(e) n'est nécessairement à le préciser. -Soit après la détection d'une erreur dont la gestion explicite via la -directive -.Ql goto error -précède la sortie de la fonction et ses traitements associés ; auxquels -renvoit finalement la gestion des erreurs centralisée sous le label -.Ql error . -.Bd -literal -offset Ds -static res_T -foo(const int bar, int** out_list) -{ - int* list = NULL; - res_T res = RES_OK; - - if (out == NULL) { - res = RES_BAD_ARG; - goto error; - } - - if ((list = mem_calloc(42, sizeof(*list)) == NULL) { - res = RES_MEM_ERR; - goto error; - } - - if ((res = quux(bar, list)) != RES_OK) goto error; - -exit: - if (out_list != NULL) *out_list = list; - return res; -error: - if (list) { mem_rm(list); list = NULL; } - goto exit; -} -.Ed -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh LES PROGRAMMES EN LIGNE DE COMMANDE -Privilégier une interface en ligne de commande pour les programmes -directement exécutables par l'utilisateur. -À la fois simple et légère, elle s'intègre parfaitement aux shells, que -ce soit en session interactive ou via des scripts. -Avant d'être une interface utilisateur, elle est l'interface avec le -système UNIX avec lequel le programme peut alors dialoguer, par exemple -en terme de gestion des entrées/sorties ou de communication -inter-processus. -.Pp -Analyser les arguments du programme en utilisant la fonction -.Xr getopt 3 -définie par le standard POSIX.1-2001 dans l'en-tête -.In unistd.h -de la bibliohtèque C. -Cette fonction assure la consistante de l'analyse des arguments et -participe au respect des conventions énoncées par le standard POSIX pour -les utilitaires en ligne de commandes. -.Pp -Proposer l'option -.Fl h -qui affiche le seul synopsis de la commande en guise de résumé de ses -attendus et options. -Réserver la description du programme et de ses options à sa page de -manuel. -Afficher ce même synopsis en cas d'erreur lors de l'analyse des -arguments de sorte à renvoyer l'utilisateur vers la syntaxe de la -commande. -.Bd -literal -offset Ds -static void -usage(FILE* stream) -{ - fprintf(stream, "usage: foo [-hv] [-b bar]\en"); -} - -int -main(int argc, char** argv) -{ - FILE* bar = NULL; - int err = 0; - int opt = 0; - int verbosity = 0; - - while ((opt = getopt(argc, argv, "b:hv")) != -1) { - switch (opt) { - case 'a': - if((bar = fopen(optarg, "r")) == NULL) err = 1; - break; - case 'h': usage(stdout); goto exit; - case 'v': verbosity += (verbosity < 3); break; - default: err = 1; break; - } - if (err) { usage(stderr); goto error; } - } - if (bar == NULL) bar = stdin; - - if ((err = foo(bar, verbosity)) != 0) goto error; - -exit: - if(bar && bar != stdin) fclose(bar); - return err; -error: - goto exit; -} -.Ed -.Pp -Dès que possible, donner la possibilité de lire les données d'entrée du -programme directement sur l'entrée standard. -Il pourra ainsi être chaîné avec un autre processus en charge, par -exemple, de pré-traiter ses données d'entrée. -De même, écrire les données en sortie du programme sur la sortie -standard pour qu'un autre utilitaire puisse en post-traiter le -résultat. -Si plusieurs fichiers d'entrée/de sortie sont lus/écrits par le -programme, définir les données les plus à même d'être pré/post-traitées -pour choisir lesquelles seront lues/écriture sur l'entrée/la sortie -standard. -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh FICHIERS -.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -.Sh VOIR AUSSI -.Xr gcc 1 , -.Xr getopt 3 , -.Xr feature_test_macros 7 -.Pp -.Rs -.%A IEEE -.%A The Open Group -.%R Base Definitions, POSIX.1-2001 -.%T Section 12, Utility Conventions -.Re -.Pp -.Rs -.%A La Fondation pour le logiciel libre -.%T Comment utiliser les licences GNU pour vos logiciels -.%U https://www.gnu.org/licenses/gpl-howto.fr.html -.Re diff --git a/doc/fr/star-c.7.in b/doc/fr/star-c.7.in @@ -0,0 +1,1804 @@ +.\" Copyright (C) 2026 |Méso|Star> (contact@meso-star.com) +.\" +.\" Ce fichier fait partie de Star-Style. +.\" +.\" Star-Style est un logiciel libre ; vous pouvez le redistribuer ou le +.\" modifier suivant les termes de la GNU General Public License telle +.\" que publiée par la Free Software Foundation ; soit la version 3 de +.\" la licence, soit (à votre gré) toute version ultérieure. +.\" +.\" Star-Style est distribué dans l'espoir qu'il sera utile, mais SANS +.\" AUCUNE GARANTIE ; sans même la garantie tacite de QUALITÉ MARCHANDE +.\" ou d'ADÉQUATION à UN BUT PARTICULIER. Consultez la GNU General +.\" Public License pour plus de détails. +.\" +.\" Vous devez avoir reçu une copie de la GNU General Public License en +.\" même temps que Star-Style ; si ce n'est pas le cas, consultez +.\" <http://www.gnu.org/licenses>. +.Dd October 14, 2026 +.Dt STAR-C 7 +.Os +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh NOM +.Nm star-c +.Nd guide d'écriture de code en C +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh DESCRIPTION +Ce document décrit les conventions d'écriture des programmes C +distribués par |Méso|Star>. +Ces recommandations sont données à titre indicatif, et reste +subordonnées aux pratiques effectivement retenues dans chaque +projet ; le plus important étant d'en conserver la cohérence. +Si bien que s'il appartient aux co-auteurs d'un projet de prendre +certaines libertés quant à ce guide de style, toute participation à son +développement devra alors s'efforcer de respecter le style d'écriture du +projet, avant les préférences listées ici. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LE CONTENU D'UN PROJET +En suivant le philosophie UNIX, assurer un jeu de +fonctionnalités par programme aussi ramassé que possible de +sorte à ce que sa mise en oeuvre et son interface restent +simples et ciselées. +L'objet étant d'assurer sa robustesse, son efficacité et sa +modularité qui dépendent d'abord de sa +.Em relation +à un écosystème logiciel qui le dépasse, bien avant son catalogue de +fonctionnalités ou ses prouesses de mise en oeuvre. +.Pp +Le périmètre étroit de chaque programme se retrouve dès lors dans la +structure du projet auquel il appartient, dont le contenu est alors +simple et peu hiérarchisé car comptant en définitive peu de fichiers. +.Pp +La structure type du répertoire d'un projet est : +.Bd -literal -offset Ds +README.md +COPYING +config.mk +Makefile +src/foo.h +src/foo.c +src/foo_bar.c +doc/foo_bar.1 +doc/foo.3 +.Ed +.Pp +Avec : +.Bl -dash -compact +.It +.Pa README.md +le fichier qui donne le premier niveau d'informations sur le projet ; +.It +.Pa COPYING +la license du projet qui liste ses conditions légales d'utilisation ; +.It +.Pa config.mk +et +.Pa Makefile +les fichiers du système de génération automatique du projet ; +.It +.Pa src/ +le répertoire qui contient les codes source du projet ; +.It +.Pa doc/ +le répertoire qui stocke sa documentation. +.El +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LE LANGAGE C +Utiliser le langage +.Em C89 +.Pq ANSI X3.159-1989 +aussi appelé C90 +.Pq ISO/IEC 9899:1990 , +les deux étant équivalent. +.Pp +Cette norme est plus épurée que celle qui lui succède à savoir le C99 +.Pq ISO SO/IEC 9899:1999 , +ce qui en fait un dialecte C à la fois plus simple, plus portable et +plus consistant. +Par exemple en ne proposant qu'une seule façon d'écrire les commentaires, +et en interdisant de mélanger du code avec la définition de variables. +.\"""""""""""""""""""""""""""""""""" +.Ss Le standard POSIX +Ajouter le support du standard POSIX pour les seuls fichiers qui en ont +besoin, pour notamment pouvoir utiliser des fonctions de la bibliothèque +C standard sinon indisponibles via la seule norme du langage retenue. +.Pp +Pour ce faire, définir la macro +.Sy _POSIX_C_SOURCE +tout en haut du fichier C concerné, avant la moindre directive +d'inclusion. +Par exemple, pour utiliser le standard POSIX.1-2001 : +.Bd -literal -offset Ds +#define _POSIX_C_SOURCE 200112L +.Ed +.Pp +Sous GNU/Linux, se référer à +.Xr feature_test_macros 7 +pour une description exhaustive des macros utilisées pour activer le jeu +de fonctionnalités d'un standard donné. +.Pp +L'utilisation d'un C enrichi du standard POSIX n'est ainsi utilisé que +sur les seuls fichiers qui en explicite le besoin ; le C89 restant le +langage utilisé partout ailleurs. +Dans un même souci de portabilité, retenir la première version du +standard POSIX à partir de laquelle la fonctionnalité recherchée est +apparue. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LA STRUCTURE D'UN FICHIER SOURCE +Suivre une seule et même structure pour tous les fichiers +sources, qu'ils soient des fichiers d'en-tête +.Pq fichiers Ql *.h +ou des unités de compilation +.Pq fichiers Ql *.c . +Un seul schéma de lecture participant à la clarté du code source. +.Pp +Ci-après sont listées les différentes parties d'un fichier source : +.Bl -enum +.\"""""""""""""""""""""""""""""""""" +.It +Un commentaire avec l'avis de copyright et les avis de licence du +programme +.Pq section Sx L'AVIS DE COPYRIGHT ET LES AVIS DE LICENCE Ns + ; +.\"""""""""""""""""""""""""""""""""" +.It +La définition des macros qui ajoute au langage C du fichier le support +d'un standard POSIX +.Pq section Sx LE LANGAGE C Ns + ; +.\"""""""""""""""""""""""""""""""""" +.It +Pour un fichier d'en-tête, l'ouverture d'un garde-fou évitant sa double +inclusion. +Il est fermé en toute fin du fichier +.Pq partie 10 Ns + : +.Bd -literal -offset Ds +#ifndef FOO_H +#define FOO_H +.Ed +.Pp +Le nom de la macro testée puis définie est celui du fichier d'en-tête, +en majuscule, suffixé par +.Ql _H +en référence à l'extension +.Ql .h +du fichier. +Dans l'exemple qui précède, le garde-fou concerne donc le fichier +.In foo.h . +La convention de nommage est sinon celle utilisée pour n'importe quelle +macro +.Pq section Sx LE NOMMAGE . +.\"""""""""""""""""""""""""""""""""" +.It +L'inclusion des fichiers d'en-tête requis par le fichier source ; +.Pq section Sx LES FICHIERS D'EN-TÊTE Ns + ; +.\"""""""""""""""""""""""""""""""""" +.It +La définition des macros +.Pq section Sx LES MACROS Ns + ; +.\"""""""""""""""""""""""""""""""""" +.It +La déclaration anticipée des types structurés : +.Bd -literal -offset Ds +/* Type structurés externes au programme */ +struct plugh; +struct quux; +struct xyzzy; + +/* Types structurés définis ailleurs dans le programme */ +struct bar; +struct foo; +.Ed +.\"""""""""""""""""""""""""""""""""" +.It +La définition des constantes symboliques de type +.Vt enum +.Pq section Sx LES CONSTANTES SYMBOLIQUES Ns + ; +.\"""""""""""""""""""""""""""""""""" +.It +La définition des types structurés et de leur(s) constante(s) +.Pq section Sx LES STRUCTURES Ns + ; +.\"""""""""""""""""""""""""""""""""" +.It +La déclaration et définition des fonctions +.Pq section Sx LES FONCTIONS . +Dans l'ordre qui suit : +.Pp +.Bl -tag -compact -width a. +.It a. +la déclaration des fonctions ; +.It b. +la définition des fonctions statiques ; +.It c. +pour les unités de compilation, la définition des fonctions. +.El +.\"""""""""""""""""""""""""""""""""" +.It +Pour les fichiers d'en-tête, la fin du garde-fou ouvert en 3 pour éviter +la double inclusion du contenu du fichier. +.El +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LA LONGUEUR DES LIGNES +La longueur maximale recommandée pour une ligne est de +.Em 80 +caractères. +.Pp +Ce nombre, standardisé par les cartes perforées et les terminaux des +années 1970, vise aussi à faciliter la lecture du code source en +s'insipirant des conventions d'édition. +Pour un texte imprimé avec une taille de police entre 9 à 12 points et +un inter-ligne d'un caractère, un confort de lecture est assuré dès +lors que chaque ligne compte entre 60 et 75 caractères. +Une proximité avec les 80 caractères retenus, que l'indentation +des sources vient encore renforcer. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh L'AVIS DE COPYRIGHT ET LES AVIS DE LICENCE +Utiliser la licence libre GPLv3+ qui, en tant que licence copyleft, +défend la copie, l'étude et la modification libres du programme ainsi +licencié, et de ses évolutions. +.Pp +Ajouter une copie de la licence à la racine du projet, dans un fichier +texte nommé +.Pa COPYING +.Pq voir Lk https://www.gnu.org/licenses/gpl-3.0.txt . +.Pp +Lister en commentaire l'avis de copyright et la déclaration +d'autorisation de copie en en-tête de +.Em chaque +fichier source. +.Pp +Chaque copyright débute par le mot +.Ql Copyright , +en anglais, suivi des 3 caractères +.Ql (C) , +traduction ASCII du caractère © qui, quant à lui, peut ne pas être +supporté par la police de caractères utilisée. +Lister ensuite les années pour lesquelles une version du programme a été +publiée, avant le nom de l'auteur(e) ayant participé(e) à +sa réalisation. +Conclure chaque avis par l'adresse de contact de l'auteur(e), donnée +entre parenthèses. +.Pp +Sauter une ligne après l'avis de copyright et ajouter la déclaration +autorisant la copie telle que donnée par la Fondation pour le logiciel +libre. +Utiliser l'avis de copyright en anglais qui, au contraire de sa +traduction française, revêt une signification juridique. +.Pp +L'en-tête type d'un fichier source du programme +.Ql Foo +est : +.Bd -literal -offset Ds +/* Copyright (C) 2016-2018, 2020, 2022, 2026 + * Jeanne Lambda (jeanne.lambda@courriel.fr) + * Copyright (C) 2017, 2019 Jean Untel (juntel@courriel.fr) + * Copyright (C) 2024 |Méso|Star> (contact@meso-star.com) + * + * This file is part of Foo. + * + * Foo is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later + * version. + * + * Foo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with Foo. If not, see + * <https://www.gnu.org/licenses/>. */ +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh L'INDENTATION +Indenter le texte par 2 espaces. +La mise en page du texte, taille de ligne comprise, est ce faisant +indépendante de la taille d'une tabulation. +.Pp +Les auteures et auteurs sont encouragés à configurer leur éditeur de +texture pour qu'il développe chaque caractère tabulation en 2 espaces +.Pq section Sx FICHIERS . +Et ainsi continuer à utiliser la touche tabulation pour l'indentation. +.Pp +Limiter l'indentation à 2 caractères, contre 8 pour le standard de facto +des tabulations, laisse plus d'espace aux différents niveaux +d'indentation, dès lors moins contraints par la limite du nombre de +caractères par ligne +.Pq section Sx LA LONGUEUR DES LIGNES . +Néanmoins, un niveau d'indentation supérieur à 3 est aussi le signe d'un +déficit de structure dans l'écriture du programme. +Les 2 espaces retenus pour indenter le code n'est donc pas une +incitation à aller au delà de 3 niveaux d'indentation sous prétexte de +disposer de plus d'espace par niveau. +.Pp +Indenter le contenu de chaque bloc +.Pq section Sx LES BLOCS . +Pour la directive +.Ql switch , +indenter chaque +.Ql case +ainsi que leur contenu : +.Bd -literal -offset Ds +switch (opt) { + case 'e': + errno = 0; + epsilon = strtod(optarg, NULL); + if (errno != 0) err = 1; + break; + case 'h': + printf("usage: foo [-hov]\en"); + break; + case 'o': + output = optarg; + break; + case 'v': + verbose += (verbose < 3); + break; + default: + err = 1; + break; +} +.Ed +.Pp +Motiver l'utilisation de plusieurs instructions par ligne par +l'expressivité du code en résultant, qu'une écriture resserrée viendrait +renforcer : +.Bd -literal -offset Ds +if (x == NULL || y == NULL) { err = 1; goto error; } +x[0] = 1.0; x[1] = 0.0; +y[0] = 0.0; y[1] = 1.0; +.Ed +.Pp +Ne sauter au plus qu'une ligne. +Ne pas laisser d'espace en fin de ligne et supprimer les lignes vides +en fin de fichier. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES COMMENTAIRES +Utiliser des commentaires dès lors que la seule expressivité serrée du +code ne permet pas d'exprimer l'entièreté du discours que les sources +doivent rendre compte, ou la logique qu'il met effectivement en oeuvre. +.Pp +Ajouter un espace après l'ouverture du commentaire +.Ql /* +et avant sa fermeture +.Ql */ . +.Pp +Si un commentaire occupe plusieurs lignes, ajouter un caractère +.Ql * +en début de ligne, aligné avec le caractère +.Ql * +de la ligne qui précède. +Ajouter un espace entre le caractère +.Ql * , +qui marque la continuation du commentaire, et la suite du commentaire : +.Bd -literal -offset Ds +/* Valeurs de hachage initiales, à savoir les 32 premiers bits + * de la partie fractionnaire des racines carrées des 4 premiers + * nombres premiers (2, 3, 5 et 7) */ +state[0] = 0x6a09e667; +state[1] = 0xbb67ae85; +state[2] = 0x3c6ef372; +state[3] = 0xa54ff53a; +.Ed +.Pp +Les commentaires servent aussi à structurer la lecture du code source. +Que ce soit à l'échelle des instructions, par un commentaire +.Dq chapeau +qui résume la séquence de code qui suit : +.Bd -literal -offset Ds +/* Enregistrer le résultat */ +((uint32_t*)hash)[0] = big_endian_32(state[0]); +((uint32_t*)hash)[1] = big_endian_32(state[1]); +((uint32_t*)hash)[2] = big_endian_32(state[2]); +((uint32_t*)hash)[3] = big_endian_32(state[3]); +.Ed +.Pp +ou à l'échelle du fichier, où les commentaires servent alors de +séparateur entre ses différentes sections +.Pq voir Sx LA STRUCTURE D'UN FICHIER SOURCE . +Dans ce cas, encadrer le commentaire par deux ligne de caractères +.Ql * +qui débute ou se termine par le caractère +.Ql / +si, respectivement, la ligne précède ou suit l'intitulé de la section : +.Bd -literal -offset Ds +/*********************************************************** + * Définition des fonctions utilitaires + **********************************************************/ +static void +foo(uint32_t bar[4], const char baz[64]) +{ + ... +} + +static int +qux(const int xyzzy) +{ + ... +} +.Ed +.Pp +À noter que dans l'exemple qui précède, la taille des lignes +d'encadrement est limitée par des contraintes d'édition de la présente +page de manuel. +Dans un fichier source, étendre ces lignes pour qu'elles occupent la +longueur maximale recommandée pour une ligne +.Pq section Sx LA LONGUEUR DES LIGNES . +.Pp +Pour expliciter le contexte général d'un fichier, en terme d'utilisation +ou d'architecture logicielle, insérer un commentaire en en-tête du +fichier en laissant les caractères d'ouverture ou de fermeture de +commentaires sur une ligne séparée : +.Bd -literal -offset Ds +/* + * Interface de programmation des tableaux extensibles. + * Cette structure de données peut être utilisée avec des + * types de données qui ne nécessitent pas de processus + * d'initialisation ou de libération et qui peuvent être + * copiés bit à bit + */ +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES FICHIERS D'EN-TÊTE +Inclure les fichiers d'en-tête dans l'ordre qui suit : +.Bl -enum -compact +.It +les en-têtes locaux au programme ; +.It +les en-têtes des dépendances du programme ; +.It +les en-têtes systèmes et ceux de la bibliothèque C standard. +.El +.Pp +Les fichiers d'en-tête sont ainsi inclus dans l'ordre décroissant de +leur niveau d'abstraction. +Cet ordre participe à garantir que chaque fichier d'en-tête inclus les +en-têtes dont il a lui même besoin, indépendamment des directives +d'inclusion qui précèdent sa propre inclusion. +Si ce n'est pas le cas, la compilation pourra échouer, symptôme qu'un +des fichiers d'en-tête n'est pas auto-consistant. +.Pp +Dans chaque groupe, trier les directives d'inclusion par ordre +alphabétique des fichiers d'en-tête. +Si besoin, ajouter un commentaire court, sur la même ligne que la +directive d'inclusion, qui explicite la raison pour laquelle la fichier +est inclus. +.Bd -literal -offset Ds +#include "bar.h" +#include "foo.h" +#include "qux.h" + +#include <baz.h> + +#include <float.h> /* FLT_MAX */ +#include <stdio.h> +.Ed +.Pp +S'efforcer de n'inclure que les seuls fichiers d'en-tête +réellement nécessaires au fichier ; +par exemple par une déclaration anticipée des types structurés à +la place d'inclure des en-têtes dans le seul but de déclarer lesdits +types. +Un enjeu a considérer avec d'autant plus d'attention que le fichier +concerné par les inclusions est lui même un fichier d'en-tête, par +conséquent amené à être lui même inclus. +L'objet étant de limiter autant que possible le nombre de fichiers +inclus par unité de compilation, pour limiter les accès disque et ainsi +réduire les temps de compilation. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LA VISIBILITÉ DES SYMBOLES +Par défaut, n'exposer aucun symbole +.Po +option +.Fl fvisibility=hidden +du compilateur +.Xr gcc 1 +.Pc , +à l'exception de ceux de l'interface de programmation d'une +bibliothèque. +.\"""""""""""""""""""""""""""""""""" +.Ss Les symboles d'interface +Privilégier l'écriture d'un seul fichier d'en-tête pour exposer +l'interface de programmation d'une bibliothèque. +Y définir une macro qui exporte les symboles qu'elle déclare dès lors +que ce fichier d'en-tête est inclus par une unité de compilation de la +bibliothèque. +Et qui se contente d'importer ces mêmes symboles si ce même fichier est +inclus par un programme tiers : +.Bd -literal -offset Ds +#include <rsys/rsys.h> + +#if defined(FOO_SHARED_BUILD) + #define FOO_API extern EXPORT_SYM +else + #define FOO_API extern IMPORT_SYM +#endif +.Ed +.Pp +Avec : +.Bl -dash -compact +.It +.Sy FOO_SHARED_BUILD +une macro définie uniquement à la compilation de la bibliothèque +.Po +option +.Fl DFOO_SHARED_BUILD +du compilateur C +.Pc Ns + ; +.It +.Sy EXPORT_SYM +et +.Sy IMPORT_SYM +des directives définies dans la bibliothèque +.Ql RSys . +Elles enrichissent le langage C d'une gestion explicite de la visibilité +des symboles. +.El +.Pp +Utiliser cette macro à la déclaration des variables et constantes +d'interfaces : +.Bd -literal -offset Ds +/* Variables globales de l'interface de programmation */ +FOO_API const struct foo foo_plugh; +FOO_API const struct foo foo_xyzzy; +.Ed +.Pp +Déclarer le prototype des fonctions d'interface entre les directives +.Sy BEGIN_DECLS +et +.Sy END_DECLS , +elles aussi définies dans la bibliothèque +.Ql RSys +.Pq en-tête In rsys/rsys.h . +Ainsi, le fichier d'en-tête peut être inclus par un programe C++ : +.Bd -literal -offset Ds +BEGIN_DECLS + +FOO_API void +foo_bar + (int i, + int* j); + +FOO_API int +foo_qux + (double d, + int i); + +END_DECLS +.Ed +.\"""""""""""""""""""""""""""""""""" +.Ss Les symboles internes partagés +Pour les fichiers d'en-tête internes au programme, utiliser la directive +.Sy LOCAL_SYM , +définie dans le fichier +.In rsys/rsys.h +de la bibliothèque +.Ql RSys , +pour déclarer les variables globales et prototypes de fonctions. +Ainsi leur symbole n'est pas exposé à l'extérieur du programme. +.Bd -literal -offset Ds +#include <rsys/rsys.h> + +extern LOCAL_SYM char bar[128]; + +extern LOCAL_SYM void +quux + (char* tab, + size_t length); +.Ed +.Pp +Cette directive est redondante si le compilateur est configuré pour +masquer par défaut tous les symboles +.Po +option +.Fl fvisibility=hidden +de +.Xr gcc 1 +.Pc . +Utiliser +.Sy LOCAL_SYM +permet néanmoins de s'exonérer de cet a priori, tout en uniformisant +les déclarations des fonctions et variables en explicitant pour chaque +déclaration la visibilité du symbole associé. +.\"""""""""""""""""""""""""""""""""" +.Ss Les symboles internes à une unité de compilation +Utiliser le mot clé +.Ql static +pour déclarer des variables, constantes, et fonctions visibles +uniquement au sein d'une unité de compilation. +.Pp +C'est notamment le cas des constantes symboliques structurées : +.Bd -literal -offset Ds +struct foo { + int bar; + int qux; +}; +static const struct foo FOO_DEFAULT = {1, 0}; +.Ed +.Pp +Mais aussi des fonctions utilitaires, qu'elles soient +propres à un fichier C, ou définies dans un fichier d'en-tête. +.Bd -literal -offset Ds +static void +hello(void) +{ + printf("Hello, world!\en"); +} +.Ed +.Pp +Ces fonctions peuvent en plus être déclarées avec la directive +.Sy INLINE , +définie dans l'en-tête +.In rsys/rsys.h +de la bibliothèque +.Ql RSys , +pour suggérer au compilateur de substituer l'appel de la fonction par le +corps de celle-ci, de sorte à éviter le surcoût de l'appel. +Cette directive est équivalente au mot clé +.Ql inline +du C99, indisponible dans le dialecte C retenu +.Pq section Sx LE LANGAGE C . +.Bd -literal -offset Ds +static INLINE void +foo(void) +{ + printf("bar\en"); +} +.Ed +.Pp +Limiter la directive +.Sy INLINE +aux fonctions élémentaires, destinées à être appelées fréquemment et +dont le coût de l'appel pourrait alors s'avérer significatif. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES BLOCS +Ouvrir chaque bloc sur la même ligne que la directive qui en est à +l'origine +.Po +.Ql do , +.Ql enum +.Ql for , +.Ql if , +.Ql struct , +.Ql switch , +.Ql union , +.Ql while +.Pc , +en séparant par un espace la fin de la directive et le caractère +.Ql { +qui marque l'ouverture du bloc : +.Bd -literal -offset Ds +if (foo) { + bar(); + qux(); +} +.Ed +.Pp +Exception faite des fonctions, ou le bloc associé est ouvert sur la +ligne qui suit : +.Bd -literal -offset Ds +static void +foo(void) +{ + printf("bar\en"); +} +.Ed +.Pp +Fermer un bloc sur une ligne à part sauf s'il est suivi d'une nouvelle +structure de contrôle associée à la précédente +.Po +.Ql if else , +.Ql do while +.Pc . +Dans ce cas, ajouter la nouvelle instruction sur la même ligne que celle +utilisée pour fermer le bloc, en la séparant du caractère +.Ql } +par un espace. +.Pp +Aligner la fermeture du bloc à l'indentation de sa directive, ou, dans +le cas de directives qui se suivent, à l'indentation de la première +directive à l'origine des blocs successifs : +.Bd -literal -offset Ds +if (foo) { + bar(); +} else { + qux(); +} +.Ed +.Pp +Écrire sur une seule ligne une directive et les opérations qu'elle +contrôle que si la clarté du code n'en est pas impactée. +Dans ce cas, l'ouverture et la fermeture du bloc associé se fait sur une +seule et même ligne : +.Bd -literal -offset Ds +if (foo) { bar(); return 0; } +.Ed +.Pp +Ne pas utiliser d'accolades si la structure de contrôle n'est suivie +d'aucune ou d'une seule directive écrite sur la même ligne : +.Bd -literal -offset Ds +while (foo()); + +if (bar) return 0; +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES MOTS CLÉS +Ajouter un espace après chaque structure de contrôle +.Ql if , +.Ql switch , +.Ql for +et +.Ql while +pour les différencier des appels de fonctions. +Ne pas ajouter d'espace après l'ouverture et avant la fermeture des +parenthèses qui détourent leur(s) expression(s) : +.Bd -literal -offset Ds +if (i < 10) { + foo(i); +} +.Ed +.Pp +Ajouter un espace après chaque point virgule qui sépare les expressions +d'une boucle +.Ql for +sauf si l'expression qui suit est vide : +.Bd -literal -offset Ds +for (i=0; i<10; foo(i++)); + +for (;;) { /* Boucle infinie */ + poll(); + if(bar) break; +} +.Ed +.Pp +Assimiler l'instruction +.Ql sizeof +à une fonction ; ne pas insérer d'espace entre le mot clé et son +expression entourée de parenthèses : +.Bd -literal -offset Ds +sz = sizeof(int); +.Ed +.\"""""""""""""""""""""""""""""""""" +.Ss L'instruction Ql switch +Limiter à quelques lignes le contenu de chaque +.Ql case +d'une instruction +.Ql switch , +celle-ci devant donner à lire la seule répartition des traitements, +fonction de la valeur que peut prendre l'expression du +.Ql switch . +Et non les traitements eux même, sauf s'ils sont triviaux. +Un +.Ql case +au contenu trop fourni est alors le signe d'un manque de structure dans +l'écriture du programme. +.Pp +Toujours ajouter une instruction +.Ql default +même si l'ensemble des valeurs que pourraient prendre l'expression du +.Ql switch +est censé être couvert par les différents +.Ql case . +C'est notamment le cas quand l'expression est une variable +d'énumération. +Utiliser alors la directive +.Sy FATAL , +définie par la bibliothèque +.Ql RSys +.Pq en-tête In rsys/rsys.h , +pour signifier un comportement inattendu. +Et ainsi pouvoir diagnostiquer une erreur dans la valeur de l'expression +du +.Ql switch , +ou un +.Ql case +manquant : +.Bd -literal -offset Ds +switch (i) { + case FOO: foo(); break; + case BAR: bar(); break; + case QUX: qux = 1; break; + default: FATAL("Unreachable code\en"); break; +} +.Ed +.Pp +Ajouter la directive +.Sy FALLTHROUGH , +définie dans le fichier d'en-tête +.In rsys/rsys.h +de la bibliothèque +.Ql RSys , +en fin des instructions +.Ql case +qui s'enchaînent. +L'ajout de cette directive permet d'expliciter que c'est bien le +comportement attendu et non l'oublie d'une instruction +.Ql break , +en plus d'éviter un possible message d'avertissement à la compilation +.Pq option Fl Wimplicit-fallthrough No de Xr gcc 1 Ns + : +.Bd -literal -offset Ds +switch (c) { + case 'a': + foo = 1; + FALLTHROUGH; + case 'b': + bar = 1; + break; + default: + usage(); + break; +} +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LE NOMMAGE +.\"""""""""""""""""""""""""""""""""" +.Ss Les variables +Nommer les variables en minuscules. +Utiliser le tiret bas +.Ql _ +au titre de séparateur entre les termes utilisés dans le nom des +variables : +.Bd -literal -offset Ds +foo_bar +.Ed +.Pp +Expliciter l'objet d'une variable dans son nom avec d'autant plus de +précision que sa portée est grande. +Une variable locale à un bloc de quelques lignes pourra se contenter +d'un nom abrégé tel que +.Va tmp +pour un résultat temporaire, voire n'être qu'un seul caractère +pour un indice +.Va i +ou un nombre d'éléments +.Va n Ns + ; leur contexte d'utilisation venant préciser ce que leur nom résume. +Ce nom abrégé vient non seulement alléger l'écriture mais aussi en +renforcer l'expressivité. +Par exemple +.Va tableau Ns Bq Va i +reste plus clair que +.Va tableau Ns Bq Va indice . +.Pp +Les variables globales sont au contraire à nommer de sorte à décrire ce +qu'elles représentent, indépendamment de tout contexte d'utilisation, +par construction distant de leur déclaration. +Un compteur d'allocations global à un programme aura donc pour nom +.Va compteur_allocations +plutôt que +.Va cpt_allocs . +.Pp +Pour une variable globale déclarée au niveau de l'interface d'une +bibliothèque, préfixer ladite variable avec l'acronyme de la +bibliothèque. +Un compteur d'allocation global à la bibliothèque +.Ql Foo , +et déclaré en tant que variable globale de son interface, sera alors +nommé +.Va foo_compteur_allocations . +.\"""""""""""""""""""""""""""""""""" +.Ss Les fonctions +Nommer les fonctions avec des caractères alphanumérique en minuscules, +et séparer les termes qui composent leur nom par un tiret bas +.Pq caractère Ql _ . +.Pp +Donner à une fonction un nom d'autant plus explicite que sa portée est +importante. +Une fonction utilitaire pourra se contenter d'un nom abrégé, tel que +.Fn cmp +pour une fonction de comparaison utilisée comme argument d'un appel à +.Xr qsort 3 +au sein d'un fichier C. +Là où une fonction partagée entre plusieurs unités de compilation aura +un nom plus expressif, tel que +.Fn compare_bar , +pour notamment expliciter le type +.Vt struct bar +des variables comparées. +Jusqu'à préfixer le nom de la fonction par l'acronyme de la bibliothèque +quand elle est une fonction d'interface de ladite bibliothèque. +Pour la bibliothèque +.Ql Foo , +une fonction d'interface sera alors nommée +.Fn foo_compare_bar . +.\"""""""""""""""""""""""""""""""""" +.Ss Les déclarations de types +Utiliser des caractères alphanumériques en minuscules pour nommer les +structures, unions et énumérations. +Utiliser le tiret bas +.Ql _ +pour séparer les différents termes qui composent leur nom. +.Bd -literal -offset Ds +struct foo { + int bar; + int baz +}; + +union foo_bar { + double qux; + int xyzzy; +}; +.Ed +.Pp +Utiliser la même convention pour nommer les déclarations typedef. +À l'exception du suffixe +.Ql _T +ajouté au nom de l'identificateur du type, en écho au suffixe +.Ql _t +souvent utilisé pour ce type de déclaration, mais réservé par le +standard POSIX. +.Bd -literal -offset Ds +typedef int foo_T; +typedef char foo_bar_T[256]; +.Ed +.Pp +Ne pas utiliser de déclaration typedef sur les structures, unions et +énumérations de sorte à permettre leur déclaration anticipée. +.Pp +Préfixer le nom d'un type par l'acronyme de la bibliothèque dès lors +qu'il est un type déclaré en tant que type de son interface. +Par exemple, un type structuré de l'interface de la bibliothèque +.Ql Foo +sera nommé +.Vt struct foo_mon_type . +.\"""""""""""""""""""""""""""""""""" +.Ss Constantes et macros +Utiliser des majuscules pour nommer les constantes symboliques, qu'elles +soient des macros, des constantes énumérées, ou des variables déclarées +comme constantes. +Séparer par un tiret bas +.Ql _ +les termes qui composent leur nom : +.Bd -literal -offset Ds +#define FOO_BAR 42 + +enum foo { + BAR_BAZ, + QUX +}; + +static const enum foo FOO_XYZZY = BAR_BAZ; +.Ed +.Pp +Nommer une constante ou une macro de manière d'autant plus explicite +que sa portée est importante. +Une constante définie localement à une fonction pourra se contenter +d'un nom abrégé, jusqu'à n'être qu'un seul caractère, par exemple pour +un nombre d'éléments constant +.Sy N . +Là où le nom d'une macro ou d'une constante définie dans un fichier +d'en-tête se devra d'être plus explicite, tel que +.Sy NOMBRE_ELEMENTS_MAX . +Et être préfixée par l'acronyme de la bibliothèque si elle est déclarée +comme macro ou constante de son interface. +Par exemple, pour la bibliothèque +.Ql Foo , +.Sy FOO_NOMBRE_ELEMENTS_MAX . +.Pp +Utiliser la convention typographique dite +.Dq camel case +pour nommer les arguments des macros ; +les termes qui composent leur nom sont séparés par une variation de la +casse typographique. +Un terme débute par une majuscule, suivi de caractères +alphanumériques en minuscules. +.Bd -literal -offset Ds +#define FOO_BAR(FooBar, Qux) ((FooBar) + (Qux)) +.Ed +.Pp +Ainsi, les arguments de macros sont différenciés des constantes +symboliques et des variables. +.Pp +À noter que la macro elle même est nommée selon la même convention que +celle utilisée pour les constante symboliques +.Pq en majuscule et un tiret bas pour séparer ses différents termes . +La parenthèse ouvrante, collée au nom de la macro, permettant de +différencier les 2 cas. +.\"""""""""""""""""""""""""""""""""" +.Ss Les variables et macros internes +Suffixer par deux tirets bas +.Ql __ +les variables membres d'une structure définie publiquement, qui n'ont +cependant une signification qu'en interne des fonctions d'interface de +la structure. +L'enjeu étant de souligner que ces variables ne sont accessibles que par +effet de bord, et ne s'addressent +.Em pas +aux utilisatrices et utilisateurs, qui ne devraient donc pas y accéder +directement : +.Bd -literal -offset Ds +struct foo { + double bar; + int baz; + int* qux__; /* Variable interne */ +}; +.Ed +.Pp +Utiliser le même suffixe en double tirets bas +.Ql __ +pour nommer les variables internes à une macro, afin d'éviter de masquer +les variables définies dans le contexte où la macro est développée : +.Bd -literal -offset Ds +#define FOO(Bar, N) { \e + int i__; \e + for (i__ = 0; i__ < N; Bar(i__), ++i__); \e +} (void)0 +.Ed +.Pp +Suffixer les macros d'un fichier d'en-tête par deux tirets bas +.Ql __ +dès lors qu'elles sont propres au fichier d'en-tête, et donc +vraisemblablement inacessibles au delà : +.Bd -literal -offset Ds +#define FOO__(Type, Dim) \e + struct Type { \e + int i[Dim]; \e + float f[Dim]; \e + } +FOO__(bar, 2); +FOO__(baz, 3); +FOO__(qux, 4); +#undef FOO__ +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES FONCTIONS +S'attacher à ce que chaque fonction reste simple et concise, en ne +s'appliquant à ne lui faire faire qu'une seule chose. +Ce faisant, le corps d'une fonction devrait être lisible sur un ou deux +écran, avec comme référence la taille des terminaux telle que +démocratisée à la fin des années 1970, à savoir 24 lignes. +.Pp +Un indice quant à la taille qu'une fonction devrait s'efforcer à avoir +est donné par ses niveaux d'indentation. +Plus elle compte de niveaux et plus elle devrait être ramassée. +De même, un nombre de variables locales supérieur à dix peut être le +signe d'une fonction trop dense. +.Pp +Écrire les directives qui contrôlent la portée d'une fonction et son +type de retour sur une ligne séparée de son nom. +L'expression régulière +.Ql ^nom_de_fonction +peut ainsi être utilisée pour la recherche d'une fonction dans les +différents fichiers sources. +.Pp +Pour une déclaration, revenir à la ligne avant d'ouvrir la parenthèse de +la fonction, précédée d'une indentation par rapport au nom de la +fonction sur la ligne qui précède. +Puis, lister les arguments de la fonction, en revenant à la ligne après +chacun d'eux et en les alignant les uns par rapport aux autres. +Ajouter la parenthèse fermante +.Ql \&) +et le point virgule +.Ql \&; +sur la ligne du dernier argument, sans espace supplémentaire : +.Bd -literal -offset Ds +extern LOCAL_SYM void +foo_bar + (struct foo* foo, + const int qux, + const float xyzzy); +.Ed +.Pp +Les différentes parties qui composent le profil de la fonction sont +ainsi identifiables par la seule mise en page de sa déclaration. +.Pp +Lors de sa définition, lister les arguments de la fonction sur la même +ligne que le nom de la fonction, sans ajouter d'espace entre le nom de +la fonction et sa parenthèse ouvrante : +.Bd -literal -offset Ds +void +foo_bar(struct foo* foo, const int qux, const float xyzzy) +{ + ... +} +.Ed +.Pp +Si la liste des arguments dépasse la longueur maximale d'une ligne +.Pq section Sx LA LONGUEUR DES LIGNES +les lister comme pour une déclaration. +.Pp +Ordonner les arguments d'une fonction comme suit : +.Bl -enum -compact +.It +pour une fonction d'interface, la variable sur laquelle la fonction +opère ; +.It +les données d'entrées ; +.It +les données en sortie. +.El +.Pp +Ajouter l'instruction +.Ql const +aux variables qui n'ont pas vocation à être modifiées par la fonction. +Et ce quand bien même leur modification n'aurait aucune conséquence, +comme pour les variables de données simples, copiées à l'appel de la +fonction. +L'objet étant de souligner qu'elles sont des variables en entrée : +.Bd -literal -offset Ds +static void +foo + (struct foo* foo, + constr struct bar* bar, + const int longueur, + const int* liste, + int* resultat); + +static INLINE double +madd(const double a, const double b, const double c) +{ + return a*b + c; +} +.Ed +.Pp +Ne passer en copie que les seuls paramètres en entrée de la fonction de +type primitif +.Pq Vt char , int , double , No énumération, ... . +Utiliser un pointeur constant dès lors que le paramètre d'entrée est +de type structuré, afin d'éviter le surcoût de sa copie à chaque appel de +fonction ; son occupation mémoire étant a priori plus important +qu'une donnée simple. +.Pp +Les paramètres d'une fonction peuvent ne pas être utilisés à l'intérieur +de celle-ci. +C'est notamment le cas si les paramètres ne sont utiles que pour +répondre à un profil de fonction spécifique ou dans un contexte de +compilation particulier, par exemple pour le débogage. +Lister ces paramètres en en-tête de la fonction, après +la définition des variables locales, en les préfixant d'une conversion +explicite vers un type vide : +.Bd -literal -offset Ds +static void +foo(int x, int y, int z) +{ + int i = 0; + (void)y, (void)z; /* Paramètres inutilisés */ + + i = bar(x); + if (i < 42) { + printf("Foobar\en"); + } +} +.Ed +.Pp +Cette conversion explicite quels paramètres sont ignorés, en plus de +désactiver les avertissements de compilation quant à la définition de +paramètres non utilisés +.Pq option Fl Wunused-parameter No de Xr gcc 1 . +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES VARIABLES +Limiter le nombre de variables par bloc entre 5 et 10. +Un nombre de variables trop important peut être le signe d'un manque de +structure auquel un découpage en sous-fonction(s) pourrait remédier +.Pq section Sx LES FONCTIONS . +.Pp +Initialiser les variables dès leur définition avec sinon une valeur +valide, au moins une valeur par défaut. +L'objet étant d'éviter l'utilisation de variables non initialisées. +D'apparence peu critique pour les variables de type primitif, cette +initialisation l'est bien plus pour les variables structurées, dont la +liste des membres peut changer. +Si elle existe, utiliser la constante proposée avec la définition du +type structuré pour initialiser ses membres +.Pq voir section Sx LES STRUCTURES . +En son absence, n'initialiser que le premier membre de la variable ; le +language C assure alors que les autres membres seront initialisés à +zéro. +De même pour un tableau alloué sur la pile, initialiser son premier +élément suffit à garantir que le reste du tableau sera initialisé à +zero : +.Bd -literal -offset Ds +struct foo foo = FOO_DEFAULT; +struct bar bar = {0}; +int qux[10] = {0}; +int i = 0; +.Ed +.Pp +Au sein d'une même fonction, définir les variables au plus proche de +leur utilisation de sorte à ce que le contexte dans lequel elles sont +utilisées participe à les caractériser. +Par exemple, une variable +.Va i +utilisée dans un bloc comme variable temporaire, et comme indice de +boucle dans un autre, gagnera en expressivité et en robustesse à être +définie localement à chaque bloc ; +les deux variables étant alors, par construction, non seulement séparées +mais aussi sans effet de bord de l'une sur l'autre : +.Bd -literal -offset Ds +if(foo) { + const int i = bar(); + if (i > max_i) max_val = i; + if (i < min_i) min_val = i; +} else { + int i = 0; + for(i = 0; i < N; ++i) qux(i); +} +.Ed +.Pp +Regrouper les définitions des variables dès lors qu'elles sont liées +sémantiquements. +Les trier ensuite par taille mémoire décroissante, et enfin par ordre +alphabétique : +.Bd -literal -offset Ds +/* Bibliothèque Foo */ +struct foo_args foo_args = FOO_ARGS_DEFAULT; +struct foo* foo = NULL; + +/* Tableau à traiter */ +double* liste = NULL; +int capacite = 0; +int longueur = 0; +.Ed +.Pp +Trier la définition des variables par taille mémoire tend à limiter le +nombre d'octets de remplissage que le compilateur C ajoute pour garantir +l'alignement mémoire de chaque variable eu égard à leur type. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES CONSTANTES +Utiliser une énumération si les constantes à définir sont liées +sémantiquement, et une macro sinon : +.Bd -literal -offset Ds +#define ID_INVALIDE ((unsigned)-1) + +enum { X, Y, Z }; + +enum attribut { + POSITION, + NORMALE, + TEXCOORD +}; +.Ed +.Pp +Pour une énumération qui utilise des valeurs par défaut, +ajouter si besoin une dernière constante qui définit le nombre de +constantes valides ; +sa valeur sera ainsi automatiquement mise à jour à chaque changement de +l'énumération. +Une telle constante peut alors servir à définir la cardinalité d'un +tableau, comme valeur du dernier indice marquant la fin d'une itération, +ou encore comme valeur vis à vis de laquelle la validité d'une variable +du type énuméré peut être vérifiée : +.Bd -literal -offset Ds +enum molecule { + CH4, + CO, + CO2, + H2O, + N2O, + O3, + + NOMBRE_DE_MOLECULES +}; + +/* Vérifier qu'une constante définie une molecule valide */ +#define MOLECULE_EST_VALIDE(Mol) \e + ((unsigned)(Mol) < NOMBRE_DE_MOLECULES) + +static const char* NOM_DES_MOLECULES[NOMBRE_DE_MOLECULES] = { + "CH4", "CO", "CO2", "H2O", "N2O", "O3" +}; +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES STRUCTURES +Définir les structures en en-tête de fichier +.Pq section Sx LA STRUCTURE D'UN FICHIER SOURCE , +à l'exeption des structures locales à une fonction. +.Pp +Lister les variables membres d'une structure suivant la même convention +que pour la définition des variables d'un bloc +.Pq section Sx LES VARIABLES Ns + : +les regrouper d'abord par sémantique, puis les trier par occupation +mémoire décroissante, et enfin par ordre alphabétique. +.Pp +Ne définir qu'une variable membre par ligne. +.Pp +Pour une structure dont aucune fonction ne permet d'en initialiser les +membres, définir une constante qui fixe leur valeur par défaut. +Suffixer cette constante par +.Ql DEFAULT +ou +.Ql NULL +fonction de si une variable structurée ainsi initialisée est une donnée +valide ou non. +Déclarer cette constante en tant que variable statique et l'initialiser par +une macro de même nom, différenciée de la variable constante par un +double tiret bas final +.Ql __ Ns + : +.Bd -literal -offset Ds +struct arg { + char* fichier; /* NULL <=> entrée standard */ + int verbosite; +}; +#define ARG_DEFAULT__ {NULL, 0} +static const struct arg ARG_DEFAULT = ARG_DEFAULT__; + +struct chaine { + char* mem; + int longueur; + int capacite; +}; +#define CHAINE_NULL__ {NULL,0,0} +static const struct chaine CHAINE_NULL = CHAINE_NULL__; +.Ed +.Pp +N'utiliser la macro que lorsqu'il est impossible d'utiliser la variable +constante, en l'occurence pour initialiser, dès sa définition, les +membres d'une autre variable structurée : +.Bd -literal -offset Ds +struct qux { + struct chaine foo; + int bar; +}; +#define QUX_DEFAULT__ {CHAINE_NULL__, 0} +static const struct qux QUX_DEFAULT = QUX_DEFAULT__; +.Ed +.Pp +Éviter d'utiliser une déclaration typedef des structures afin +d'autoriser leur déclaration anticipée. +Et l'utilisation de pointeur vers une donnée structurée sans avoir sa +définition. +Si un déclaration typedef est néanmoins souhaitée, nommer le type +structuré en suivant la convention de nommage des déclarations typedef +.Pq section Sx Les déclarations de types . +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES MACROS +Pour définir une séquence d'instructions, +préférer l'utilisation de fonctions aux macros. +Déclarer la fonction avec la directive +.Sy INLINE +si son coût d'appel est un enjeu +.Pq section Sx Les symboles internes à une unité de compilation . +.Pp +Regrouper la séquence d'instructions d'une macro dans un bloc terminé +par l'instruction +.Ql (void)0 . +Elle peut ainsi être utilisée comme unique expression d'une structure de +contrôle, et force l'ajout d'un point virgule +.Ql \&; +.Pq ou d'une virgule Ql \&, +après son utilisation, telle n'importe quelle autre instruction C. +.Pp +En C, il est plus courant d'encapsuler les instructions d'une macro +dans une structure de contrôle +.Ql do { ... } while (0) +plutôt que dans un bloc terminé par la converstion de l'entier zéro vers +un type vide +.Ql (void)0 . +Si les deux écritures répondent aux mêmes objectifs, cette dernière +convention évite les avertissements émis par certains compilateurs quant +à l'utilisation d'une expression conditionnelle constante dans +.Ql while (0) . +.Pp +Ouvrir le bloc sur la même ligne que le nom de la macro, en ajoutant un +espace avant l'acolade +.Ql { . +Indenter le contenu du bloc par rapport à la directive de définition de +la macro. +Justifer à droite les caractères anti-slash +.Ql \e +en fin de chaque ligne de sorte à faciliter la lecture de la séquence +d'instructions développée par la macro : +.Bd -literal -offset Ds +#define FOO(X, Y) { \e + if ((X) == (Y)) printf("Bar \en"); \e + (Y) += 2; \e +} (void)0 +.Ed +.Pp +À noter que dans l'exemple qui précède, les caractères anti-slash +.Ql \e +sont alignés en suivant des contraintes d'édition propres à ce manuel. +Dans un fichier source, positioner l'anti-slash en tant que dernier +caractère de lignes qui occupent la longueur maximale autorisée +.Pq section Sx LA LONGUEUR DES LIGNES . +.Pp +Pour une macro dont la portée est l'unité de compilation, ne pas changer +le déroulé des instructions de son contexte d'appel, par exemple en +intégrant une directive +.Ql return . +Son utilisation contredirait l'exécution séquentielle du code et ce +faisant nuirait à sa lisibilité. +Il n'est donc +.Em pas +recommandé de définir une macro comme suit : +.Bd -literal -offset Ds +#define FOO(X) { + if (bar(X)) + return -1; +} (void)0 +.Ed +.Pp +Ne pas présupposer l'existance de variables externes à la macro, +exeption faite des variables globales. +L'objet étant de ne pas lier son bon fonctionnement au contexte local +dans lequel elle est développée. +L'écriture qui suit est donc +.Em découragée Ns + : +.Bd -literal -offset Ds +#define BAR(X,Y) { + z = (X) + (Y); + if (xyzzy(z)) + z += 1; +} +.Ed +.Pp +Contrairement aux macros définies à l'échelle d'une unité de +compilation, une macro locale peut non seulement changer le fil +d'exécution du contexte d'appel, mais aussi utiliser des variables +externes. +Et ce précisément en raison de son caractère local, qui lie étroitement +la macro à son seul contexte d'utilisation. +.Bd -literal -offset Ds +static int +foo(const int x) +{ + char s[10] = {0}; + int line = 0; + int err = 0; + + #define CALL(Func) { \e + if((err=(Func)) != 0) { \e + line = __LINE__; \e + goto error; \e + } \e + } (void)0 + + CALL(bar(x, s)); + CALL(quux(s)); + + #undef CALL + +exit: + return err; +error: + fprinf(stderr, "erreur %d ligne %d\en", err, line); + goto exit; +} +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES ALLOCATIONS DYNAMIQUES +Préférer l'interface d'allocation proposée par la +bibliothèque +.Ql RSys +via son fichier d'en-tête +.In rsys/mem_allocator.h . +Elle enrichit la gestion de la mémoire dynamique proposée par la +bibliothèque C standard, notamment en enregistrant la quantité de +mémoire allouée. +.Pp +Utiliser dès lors les fonctions +.Fn mem_alloc , +.Fn mem_calloc +et +.Fn mem_realloc +pour allouer dynamiquement de la mémoire. +Leur profil est celui des fonctions équivalentes proposées par la +bibliothèque C, à savoir ces mêmes fonctions mais sans le prefixe +.Ql mem_ +.Po +voir +.Xr alloc 3 , +.Xr calloc 3 , +et +.Xr realloc 3 +.Pc . +.Pp +Privilégier la fonction +.Fn mem_calloc +à +.Fn mem_alloc +de sorte à initialiser la mémoire allouée à zéro, et d'éviter ainsi +d'utiliser des données non initialisées. +.Pp +Ne pas convertir le pointeur retourné par les fonctions d'allocation. +La conversion d'un pointeur vide vers n'importe quel autre type de +pointeur est déjà assuré par le langage C. +.Pp +Définir la taille du bloc mémoire à allouer via le type pointé par la +variable destination : +.Bd -literal -offset Ds +p = mem_calloc(42, sizeof(*p)); +.Ed +.Pp +L'alternative qui consiste à épeller le type pointé en argument de +.Ql sizeof +non seulement nuit à la lisibilité des sources, mais laisse en plus +l'opportunité d'introduire un bogue dès lors que le type de pointeur +est mis à jour mais pas le nom du type renseigné à +.Ql sizeof . +.Pp +Utiliser la fonction +.Fn mem_alloc_aligned +pour allouer un bloc mémoire dont l'adresse doit être alignée sur un +nombre d'octets spécifique. +Utiliser la fonction +.Xr memset 3 , +de la bibliothèque C standard, pour forcer la mise à zéro du bloc ainsi +alloué sinon rempli d'octets +aléatoires : +.Bd -literal -offset Ds +foo = mem_alloc_aligned(sizeof(*foo), 128/* Alignement */); +memset(foo, 0, sizeof(*foo)); +.Ed +.Pp +Vérifier chaque allocation en testant que l'adresse retournée n'est pas +.Ql NULL . +Traiter ce cas comme une erreur et non un bogue +.Pq section Sx CENTRALISER LA SORTIE D'UNE FONCTION Ns + : +.Bd -literal -offset Ds + foo = mem_calloc(1, sizeof(*foo); + if (!foo) { + res = RES_MEM_ERR; + goto error; + } +.Ed +.Pp +Libérer la mémoire allouée via +.Ql RSys +avec la fonction +.Fn mem_rm +dont le profil est le même que celui de la fonction +.Xr free 3 Ns + : +.Bd -literal -offset Ds +mem_rm(foo); +.Ed +.Pp +Détecter la présence de fuites mémoires via la fonction +.Fn mem_allocated_size +qui retourne la quantité de mémoire qui reste allouée par la +bibliothèque : +.Bd -literal -offset Ds +int +main(void) +{ + size_t sz = 0; + int err = 0; + + ... + + if ((sz = mem_alloc_aligned()) != 0) { + fprintf(stderr, "Fuites mémoires : %lu octets\en", sz); + if (err == 0) err = 1; + } + return err; +} +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Ss Les allocateurs mémoire +Dans son fichier d'en-tête +.In rsys/mem_allocator.h , +la bibliothèque +.Ql RSys +définit en plus une interface d'allocateur mémoire. +Au contraire de son interface d'allocation qui enregistre la mémoire +alloué globalement par la bibliothèque, +chaque allocateur enregistre ses seules allocations. +.Pp +Plusieurs types d'allocateurs sont proposés par la bibliothèque +.Ql RSys , +chacun mettant en oeuvre une politique d'allocation qui lui est propre. +Si bien qu'en fonction du contexte, un type d'allocateur particulier +peut s'avérer plus approprié, par exemple pour réduire les coûts +d'allocations/désallocations. +Décrire les différents types d'allocateurs définis dans la bibliothèque +.Ql RSys +sort du cadre de cette documentation. +Le lecteur est invité à se référer à son fichier d'en-tête +.In rsys/mem_allocator.h +pour plus d'informations. +.Pp +Les convention listées précédemment quant aux allocation dynamiques +s'appliquent à l'identique à l'utilisation des allocateurs. +.Pp +Utiliser un allocateur consiste à appeler des macros, dont le nom est +une version en majuscule des fonctions de l'interface d'allocation. +Avec en plus en premier argument l'addresse de l'allocateur concerné : +.Bd -literal -offset Ds +foo = MEM_CALLOC(&mem_default_allocator, 1, sizeof(*foo)); + +\&... + +MEM_RM(&mem_default_allocator, foo); + +if (MEM_ALLOCATED_SIZE(&mem_default_allocator)) { + fprintf(stderr, "Fuites mémoires\en"); +} +.Ed +.Pp +avec +.Va mem_default_allocator +l'allocateur par défaut définit par la bibliothèque +.Ql RSys . +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh CENTRALISER LA SORTIE D'UNE FONCTION +Utiliser la directive +.Ql goto +pour centraliser les traitements à effectuer en sortie de fonction, tels +les affectations de variables de sorties, les libérations de variables +locales temporaires, ou le renvoie d'une valeur en retour de la fonction. +Regrouper ces traitements en fin de fonction sous le label +.Ql exit +dont la dernière instruction est la valeur retournée par la fonction. +.Pp +De même, centraliser la gestion des erreurs détectées pendant +l'exécutation de la fonction sous un label +.Ql error , +qui vise à revenir à l'état du programme avant l'appel de la fonction, +et à préparer son retour compte tenu de l'erreur. +Ses traitements recouvrent notamment la libération de +l'espace mémorie alloué à destination de l'appelant, la restauration des +données en mise à jour modifiées par la fonction avant la détection de +l'erreur, ou la définition de valeurs à destination des variables en +sortie en conséquence de l'erreur détectée. +.Pp +Placer le label +.Ql error +après le label +.Ql exit . +Terminer la gestion des erreurs par la directive +.Ql goto exit , +de sorte à effectuer les traitements en sortie, +.Em indépendants +de la présence ou non d'une erreur d'exécution, et donc à appliquer en +toute circonstance. +En structurant les labels de la sorte, les traitements en sortie +.Pq label Ql exit +sont ainsi exécutés soit automatiquement au fil du bon déroulé de la +fonction, sans que l'auteur(e) n'est nécessairement à le préciser. +Soit après la détection d'une erreur dont la gestion explicite via la +directive +.Ql goto error +précède la sortie de la fonction et ses traitements associés ; auxquels +renvoit finalement la gestion des erreurs centralisée sous le label +.Ql error . +.Bd -literal -offset Ds +static res_T +foo(const int bar, int** out_list) +{ + int* list = NULL; + res_T res = RES_OK; + + if (out == NULL) { + res = RES_BAD_ARG; + goto error; + } + + if ((list = mem_calloc(42, sizeof(*list)) == NULL) { + res = RES_MEM_ERR; + goto error; + } + + if ((res = quux(bar, list)) != RES_OK) goto error; + +exit: + if (out_list != NULL) *out_list = list; + return res; +error: + if (list) { mem_rm(list); list = NULL; } + goto exit; +} +.Ed +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh LES PROGRAMMES EN LIGNE DE COMMANDE +Privilégier une interface en ligne de commande pour les programmes +directement exécutables par l'utilisateur. +À la fois simple et légère, elle s'intègre parfaitement aux shells, que +ce soit en session interactive ou via des scripts. +Avant d'être une interface utilisateur, elle est l'interface avec le +système UNIX avec lequel le programme peut alors dialoguer, par exemple +en terme de gestion des entrées/sorties ou de communication +inter-processus. +.Pp +Analyser les arguments du programme en utilisant la fonction +.Xr getopt 3 +définie par le standard POSIX.1-2001 dans l'en-tête +.In unistd.h +de la bibliohtèque C. +Cette fonction assure la consistante de l'analyse des arguments et +participe au respect des conventions énoncées par le standard POSIX pour +les utilitaires en ligne de commandes. +.Pp +Proposer l'option +.Fl h +qui affiche le seul synopsis de la commande en guise de résumé de ses +attendus et options. +Réserver la description du programme et de ses options à sa page de +manuel. +Afficher ce même synopsis en cas d'erreur lors de l'analyse des +arguments de sorte à renvoyer l'utilisateur vers la syntaxe de la +commande. +.Bd -literal -offset Ds +static void +usage(FILE* stream) +{ + fprintf(stream, "usage: foo [-hv] [-b bar]\en"); +} + +int +main(int argc, char** argv) +{ + FILE* bar = NULL; + int err = 0; + int opt = 0; + int verbosity = 0; + + while ((opt = getopt(argc, argv, "b:hv")) != -1) { + switch (opt) { + case 'a': + if((bar = fopen(optarg, "r")) == NULL) err = 1; + break; + case 'h': usage(stdout); goto exit; + case 'v': verbosity += (verbosity < 3); break; + default: err = 1; break; + } + if (err) { usage(stderr); goto error; } + } + if (bar == NULL) bar = stdin; + + if ((err = foo(bar, verbosity)) != 0) goto error; + +exit: + if(bar && bar != stdin) fclose(bar); + return err; +error: + goto exit; +} +.Ed +.Pp +Dès que possible, donner la possibilité de lire les données d'entrée du +programme directement sur l'entrée standard. +Il pourra ainsi être chaîné avec un autre processus en charge, par +exemple, de pré-traiter ses données d'entrée. +De même, écrire les données en sortie du programme sur la sortie +standard pour qu'un autre utilitaire puisse en post-traiter le +résultat. +Si plusieurs fichiers d'entrée/de sortie sont lus/écrits par le +programme, définir les données les plus à même d'être pré/post-traitées +pour choisir lesquelles seront lues/écriture sur l'entrée/la sortie +standard. +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh FICHIERS +.Bl -tag -width Ds +.It Pa @APPPREFIX@/star-style/vimrc +Fichier de configuration pour l'éditeur +.Xr vim 1 +qui automatise la mise en page du code C suivant les conventions +d'écriture définies dans ce manuel. +Pour l'utiliser, lancer l'éditeur comme suit : +.Bd -literal -offset Ds +vim -u @APPPREFIX@/star-style/vimrc +.Ed +.El +.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.Sh VOIR AUSSI +.Xr gcc 1 , +.Xr vim 1 , +.Xr getopt 3 , +.Xr feature_test_macros 7 +.Pp +.Rs +.%A IEEE +.%A The Open Group +.%R Base Definitions, POSIX.1-2001 +.%T Section 12, Utility Conventions +.Re +.Pp +.Rs +.%A La Fondation pour le logiciel libre +.%T Comment utiliser les licences GNU pour vos logiciels +.%U https://www.gnu.org/licenses/gpl-howto.fr.html +.Re diff --git a/vimrc b/vimrc @@ -0,0 +1,91 @@ +" Copyright (C) 2026 |Méso|Star> (contact@meso-star.com) +" Copyright (C) 2023-2025 Vincent Forest (vaplv@posteo.net) +" +" Copying and distribution of this file, with or without modification, +" are permitted in any medium without royalty provided the copyright +" notice and this notice are preserved. This file is offered as-is, +" without any warranty. + +syntax on " Enable syntax highlighting + +set nocompatible " Do not use Vim just like Vi but allow Vim features + +set autoindent " Copy indent from current line when starting a new line +set backup " Keep a copy of the origin file while writing to an existing file +set mouse= " Disable the use of the mouse in all modes +set nohls " Do not highlight all the matches of a search pattern +set number " Print the line number in front of each line +set nofoldenable " Disable folding +set textwidth=72 " Maximum width of text that is being inserted + +" Characters to display in list mode (set [no]list), +" i.e. display hidden characters +set listchars=eol:$,nbsp:_ + +" How Backspace, Del, CTRL-W and CTRL-U work in Insert mode +set backspace=indent,eol,start + +" Set 'formatoptions' to break comment lines (c) but not other lines +" (t). Insert the comment leader (r) when hitting <CR> or using "o" (o). +" Allow formatting of comments with "gq" (q). Do not automatically +" format long lones (l), i.e., the ones that exceed 'textwidth'. +set fo-=t fo+=croql + +filetype plugin on " Load the plugin files for specific file types + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Formatting and indentation of C sources +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Setup C indentation rules: +" +2: indent a continuation line inside a function 2 additional characters +" t0: Indent of a function return type declaration 0 characters +" from the margin +" (0: in unclosed parentheses, indent 0 characters from the line with the +" unclosed parenthesis +" u0: same as (0 but for one nesting level deeper. +set cino=+2,t0,(0,u0 + +" Define as C the filetype of *.h and *.c files. +" Otherwise, *.h files were defined as C++ files. +au BufNewFile,BufRead *.h,*.c set filetype=c + +au FileType c setlocal tabstop=8 " Number of spaces that a <Tab> in the file counts for +au FileType c setlocal softtabstop=2 " Number of spaces that a <Tab> counts for while editing +au FileType c setlocal shiftwidth=2 " Number of spaces to use for each step of (auto)indent +au FileType c setlocal expandtab " In insert mode, replace <Tab>s by spaces + +au FileType c setlocal cindent " Set the cindent option for C files +au FileType c setlocal textwidth=80 " Set text width for C files + +" Setup C comments format rules: +" "sO:*\ -" +" Start (s) a comment that is not treated as a normal comment by +" the o (O) command that insert a new line. +" It actually starts the comment of a list item (see below). +" "mO:*\ \ " +" Add two spaces (\ \ ) after the C comment character (*) when +" adding a new line (O) in the middle (m) of a list item comment +" (see above). +" "exO:*/" +" End (e) a three piece C comment by typing the last end-comment +" character (/), as the first action on a new line commenting a +" list item (x) (see above), i.e. automatically ignore the 2 +" spaces following the '*' character. +" "s1:/*" +" Start (s) a three piece C comment (/*). +" Automatically adds a space offset (1) to the middle or end of +" the comment leader. +" "mb:*" +" Middle (m) of three piece C comment (*). +" Add a blank (b) after the comment string (*). +" This avoid to recognize a pointer indirection as a comment. +" "ex:*/" +" End (e) of three piece C comment (*/). +" Allow to close the comment by typing the last char of +" end-comment (i.e. '/') as the first action on a new +" middle-comment line (x), i.e. automatically discard the blank +" following the '*' char. +" ":///" +" "://" +" Continue comments in C99 format on the following lines. +au Filetype c setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://