aboutsummaryrefslogtreecommitdiff
path: root/rpt.c
diff options
context:
space:
mode:
authorTristan Riehs <tristan.riehs@bordeaux-inp.fr>2023-09-28 08:08:38 +0200
committerTristan Riehs <tristan.riehs@bordeaux-inp.fr>2023-09-28 08:08:38 +0200
commitb975b7392606050c1ed83a8cce016f872589ad38 (patch)
tree5c9e3de082700560d8b99a2436f06e55ed888993 /rpt.c
parent434b226d340c284e26569d4dfae4a6cfdf08d483 (diff)
source moved
Diffstat (limited to 'rpt.c')
-rw-r--r--rpt.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/rpt.c b/rpt.c
new file mode 100644
index 0000000..1a9a256
--- /dev/null
+++ b/rpt.c
@@ -0,0 +1,150 @@
+#define _GNU_SOURCE /* getopt_long */
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define VERSION "1.0.0"
+
+void
+print_usage(FILE *output)
+{
+ fprintf(output, "Usage:\n");
+ fprintf(output, "rpt [-n | --count COUNT] [-f | --force] COMMAND\n");
+ fprintf(output, "rpt [-h | --help]\n");
+ fprintf(output, "rpt [-V | --version]\n");
+}
+
+void
+print_help()
+{
+ puts("Repeat : repeat a shell command");
+ puts("");
+ print_usage(stdout);
+}
+
+void
+print_version()
+{
+ puts(VERSION);
+}
+
+/* Exit whenever a subprocess fails. */
+void
+exit_on_error(int status)
+{
+ if (WIFEXITED(status))
+ return;
+
+ fprintf(stderr, "Process exited with status %d, aborting.\n", status);
+ exit(1);
+}
+
+/* Ignore subprocesses' errors. */
+void
+continue_on_error(int status)
+{
+ (void) status;
+}
+
+/* Invoke the executable *ARGV COUNT times, passing it the rest of ARG as
+ arguments. Call HANDLE_ERROR_F between each process. */
+void
+repeat_cmd(char **argv, long count, void (*handle_error_f)(int))
+{
+ pid_t pid;
+ int status;
+
+ for(long i = 0; i < count; ++i)
+ {
+ pid = fork();
+
+ if (pid == 0)
+ {
+ /* child */
+ status = execvp(argv[0], argv);
+
+ if (status < 0)
+ exit(1);
+
+ exit(2);
+ }
+ else
+ {
+ /* parent */
+ int exit_status = 0;
+
+ waitpid(pid, &exit_status, 0);
+
+ handle_error_f(exit_status);
+ }
+ }
+}
+
+int
+main(int argc, char* argv[])
+{
+ struct option const opts[] = {
+ {"count", required_argument, NULL, 'n'},
+ {"force", no_argument, NULL, 'f'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ opterr = 0; /* dismiss getopt error message */
+ int opt;
+
+ char *strcount = NULL;
+ long count = 1;
+
+ void (*handle_error_f)(int status) = exit_on_error;
+
+ while ((opt = getopt_long(argc, argv, "+:n:fVh", opts, NULL)) >= 0)
+ {
+ switch(opt)
+ {
+ case 'n':
+ strcount = optarg;
+ break;
+ case 'f':
+ handle_error_f = continue_on_error;
+ break;
+ case 'h':
+ print_help();
+ return 0;
+ case 'V':
+ print_version();
+ return 0;
+ case ':':
+ fprintf(stderr, "COUNT value missing.\n");
+ print_usage(stderr);
+ return 1; /* invalid option */
+ default:
+ print_usage(stderr);
+ return 1; /* invalid option */
+ }
+ }
+
+ if (strcount)
+ {
+ char *err = NULL;
+ count = strtol(strcount, &err, 10);
+
+ if (err && (*err != '\0'))
+ {
+ fprintf(stderr, "Failed to read COUNT : '%s'.\n",
+ strcount);
+ return 2; /* error while reading COUNT */
+ }
+ }
+
+ if (optind >= argc)
+ return 3; /* COMMAND not provided */
+
+ repeat_cmd(argv + optind, count, handle_error_f);
+
+ return 0;
+}