diff options
author | Tristan Riehs <tristan.riehs@bordeaux-inp.fr> | 2023-09-28 08:08:38 +0200 |
---|---|---|
committer | Tristan Riehs <tristan.riehs@bordeaux-inp.fr> | 2023-09-28 08:08:38 +0200 |
commit | b975b7392606050c1ed83a8cce016f872589ad38 (patch) | |
tree | 5c9e3de082700560d8b99a2436f06e55ed888993 /rpt.c | |
parent | 434b226d340c284e26569d4dfae4a6cfdf08d483 (diff) |
source moved
Diffstat (limited to 'rpt.c')
-rw-r--r-- | rpt.c | 150 |
1 files changed, 150 insertions, 0 deletions
@@ -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; +} |