sous

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

sous.c (2819B)


      1 /* sous, exécuter des sous-commandes
      2  * par ldp <ldp@asteride.xyz>
      3  * Ce programme est distribué sous la licence GPLv3
      4  */
      5 
      6 #include <errno.h>
      7 #include <fcntl.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <unistd.h>
     12 
     13 static int fatal(char *msg);
     14 
     15 int
     16 main(int argc, char **argv)
     17 {
     18 	char *ch; /* chemin vers l’exécutable */
     19 	char *base; /* base de ch */
     20 	char *suff; /* chemin relatif à base */
     21 	size_t tll; /* taille de ch */
     22 	int df1, df2; /* deux descripteurs de fichier */
     23 
     24 	/* Trouver le dossier de base dans les variables d’environnement et
     25 	 * régler le suffixe en conséquence. */
     26 	if ((base = getenv("SOUS_CH")) != NULL)
     27 		suff = "";
     28 	else if ((base = getenv("XDG_DATA_HOME")) != NULL)
     29 		suff = "/sous";
     30 	else if ((base = getenv("HOME")) != NULL)
     31 		suff = "/.local/share/sous";
     32 	else
     33 		return fatal("impossible de trouver $HOME");
     34 
     35 	/* Assembler le chemin menant au dossier. La taille de ce chemin est
     36 	 * d’abord calculé. 1 est ajouté pour laisser de la place au caractère
     37 	 * terminal NUL. De l’espace est alors alloué et base et suff y sont
     38 	 * copiés. */
     39 	tll = strlen(base) + strlen(suff) + 1;
     40 	if ((ch = malloc(tll)) == NULL)
     41 		goto err;
     42 	strcpy(ch, base);
     43 	strcat(ch, suff);
     44 
     45 	/* Ouvrir le dossier. */
     46 	if ((df1 = open(ch, O_RDONLY | O_DIRECTORY)) < 0)
     47 		goto err;
     48 
     49 	/* Pour chaque argument dans argv, tenter de l’ouvrir comme un dossier.
     50 	 * Cesser au premier échec. L’argument suivant devrait alors contenir
     51 	 * le nom d’un exécutable situé quelque part dans l’arborescence de ch.
     52 	 * Compter la taille de chaque argument vu et l’ajouter au total, plus1
     53 	 * un pour laisser de la place au séparateur « / ». Enfin, pour éviter
     54 	 * que des arguments ne soient interprétés comme des chemins absolus,
     55 	 * les séparateurs « / » initiaux sont ignorés. */
     56 	for (argc = 0; df1 >= 0; df1 = df2, argc++) {
     57 		if (argv[argc] == NULL)
     58 			return fatal("trop peu d’arguments");
     59 		while (*argv[argc] == '/')
     60 			argv[argc]++;
     61 		tll += strlen(argv[argc]) + 1;
     62 		df2 = openat(df1, argv[argc], O_RDONLY | O_DIRECTORY);
     63 		if (close(df1) < 0)
     64 			goto err;
     65 	}
     66 	/* Si tout va bien, errno == ENOTDIR. */
     67 	if (errno != ENOTDIR)
     68 		goto err;
     69 
     70 	/* Agrandir le tampon vers lequel pointe ch pour qu’il puisse contenir
     71 	 * le chemin absolu complet vers l’exécutable. */
     72 	if ((ch = realloc(ch, tll)) == NULL)
     73 		goto err;
     74 
     75 	/* Assembler le chemin. */
     76 	while (argc--) {
     77 		strcat(ch, "/");
     78 		strcat(ch, *argv++);
     79 	}
     80 
     81 	/* Exécuter le programme */
     82 	execv(ch, argv-1);
     83 
     84 	/* N’atteint cette partie que s’il y a une erreur. */
     85 err:
     86 	return fatal(strerror(errno));
     87 }
     88 
     89 /* Afficher un message d’erreur. Renvoie 1 pour pouvoir faire « return
     90  * fatal(msg) » dans main() */
     91 static int
     92 fatal(char *msg)
     93 {
     94 	fprintf(stderr, "sous: %s\n", msg);
     95 	return 1;
     96 }