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 }