sous

Exécuter de sous-commandes
git clone git://git.asteride.xyz/~ldp/sous.git
Journaux | Fichiers | Références | LICENCE

commit fc438badb464f4f81b12fda7f0f884a6ef47b41b
parent c389a0aa54ec09d3daa125a2c4fbe923bf9422f1
Auteur: ldp <ldp@asteride.xyz>
Date:   Mon, 21 Apr 2025 17:59:20 -0400

refonte pour simplifier

Diffstat:
MMakefile | 2+-
Msous.c | 130++++++++++++++++++++++++++++++++-----------------------------------------------
2 files changed, 54 insertions(+), 78 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,4 +1,4 @@ -COPT = -Wall -O2 -Wextra -Wpedantic --std=c89 #-DNDEBUG +COPT = -Wall -O2 -Wextra -Wpedantic --std=c89 CC = cc PREFIX = "/usr/local/bin" diff --git a/sous.c b/sous.c @@ -3,118 +3,94 @@ * Ce programme est distribué sous la licence GPLv3 */ -#include <assert.h> -#include <dirent.h> #include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#define DOS_DEF "/sous" /* dossier relatif à $XDG_DATA_HOME */ -#define CH_DEF "/.local/share/sous" /* dossier relatif à $HOME */ -#define CH_TLL 256 /* taille initiale du tampon du chemin */ - -struct tampon { - char *tp; /* pointeur vers le tampon */ - size_t tll; /* taille du tampon */ - size_t noc; /* nombre d’octets occupés, sans le caractère terminal */ -}; - -static struct tampon *tcreer(size_t tll); -static void tajouter(struct tampon *t, char *cc); -static void fatal(char *msg); +static int fatal(char *msg); int main(int argc, char **argv) { - struct tampon *ch; /* chemin vers l’exécutable */ + char *ch; /* chemin vers l’exécutable */ char *base; /* base de ch */ char *suff; /* chemin relatif à base */ - DIR *dos; + size_t tll; /* taille de ch */ + int df1, df2; /* deux descripteurs de fichier */ /* Trouver le dossier de base dans les variables d’environnement et * régler le suffixe en conséquence. */ if ((base = getenv("SOUS_CH")) != NULL) suff = ""; else if ((base = getenv("XDG_DATA_HOME")) != NULL) - suff = DOS_DEF; + suff = "/sous"; else if ((base = getenv("HOME")) != NULL) - suff = CH_DEF; + suff = "/.local/share/sous"; else - fatal("$HOME est introuvable"); + return fatal("impossible de trouver $HOME"); + + /* Assembler le chemin menant au dossier. La taille de ce chemin est + * d’abord calculé. 1 est ajouté pour laisser de la place au caractère + * terminal NUL. De l’espace est alors alloué et base et suff y sont + * copiés. */ + tll = strlen(base) + strlen(suff) + 1; + if ((ch = malloc(tll)) == NULL) + goto err; + strcpy(ch, base); + strcat(ch, suff); - ch = tcreer(CH_TLL); - tajouter(ch, base); - tajouter(ch, suff); + /* Ouvrir le dossier. */ + if ((df1 = open(ch, O_RDONLY | O_DIRECTORY)) < 0) + goto err; - for (argc = 0; (dos = opendir(ch->tp)) != NULL; argc++) { - closedir(dos); + /* Pour chaque argument dans argv, tenter de l’ouvrir comme un dossier. + * Cesser au premier échec. L’argument suivant devrait alors contenir + * le nom d’un exécutable situé quelque part dans l’arborescence de ch. + * Compter la taille de chaque argument vu et l’ajouter au total, plus1 + * un pour laisser de la place au séparateur « / ». Enfin, pour éviter + * que des arguments ne soient interprétés comme des chemins absolus, + * les séparateurs « / » initiaux sont ignorés. */ + for (argc = 0; df1 >= 0; df1 = df2, argc++) { if (argv[argc] == NULL) - fatal("trop peu d’arguments"); - tajouter(ch, "/"); - tajouter(ch, argv[argc]); + return fatal("trop peu d’arguments"); + while (*argv[argc] == '/') + argv[argc]++; + tll += strlen(argv[argc]) + 1; + df2 = openat(df1, argv[argc], O_RDONLY | O_DIRECTORY); + if (close(df1) < 0) + goto err; } /* Si tout va bien, errno == ENOTDIR. */ if (errno != ENOTDIR) goto err; - /* Exécuter le programme. */ - if (execv(ch->tp, argv+argc-1) < 0) + /* Agrandir le tampon vers lequel pointe ch pour qu’il puisse contenir + * le chemin absolu complet vers l’exécutable. */ + if ((ch = realloc(ch, tll)) == NULL) goto err; - return 0; - -err: - fatal(strerror(errno)); - return 1; -} - -/* Créer un tampon de taille tll. En cas d’erreur, le programme quitte et en - * affiche la cause. */ -static struct tampon * -tcreer(size_t tll) -{ - struct tampon *t; - - assert(tll >= 1); + /* Assembler le chemin. */ + while (argc--) { + strcat(ch, "/"); + strcat(ch, *argv++); + } - if ((t = malloc(sizeof(struct tampon))) == NULL || - (t->tp = malloc(t->tll = tll)) == NULL) - fatal(strerror(errno)); - *t->tp = '\0'; - t->noc = 0; + /* Exécuter le programme */ + execv(ch, argv-1); - return t; -} - -/* Ajouter la chaîne de caractères cc au tampon t. Si le tampon est trop petit - * pour accueillir cc, il est doublé jusqu’à être assez grand. En cas d’erreur, - * le programme quitte et en affiche la cause. */ -static void -tajouter(struct tampon *t, char *cc) -{ - size_t lgr; /* longeur minimale de la chaîne de caratères */ - - lgr = t->noc + strlen(cc); - if (lgr >= t->tll) { - int puissance = 1; - int lgr2 = lgr + 1; - while (lgr2 >>= 1) - puissance++; - t->tll = 1 << puissance; - assert(t->tll > lgr + 1); - if ((t->tp = realloc(t->tp, t->tll)) == NULL) - fatal(strerror(errno)); - } - strcpy(t->tp + t->noc, cc); - t->noc = lgr; + /* N’atteint cette partie que s’il y a une erreur. */ +err: + return fatal(strerror(errno)); } -/* Afficher un message d’erreur et quitter. */ -static void +/* Afficher un message d’erreur. Renvoie 1 pour pouvoir faire « return + * fatal(msg) » dans main() */ +static int fatal(char *msg) { fprintf(stderr, "sous: %s\n", msg); - exit(1); + return 1; }