#define _GNU_SOURCE /* getopt_long */ #include #include #include #include #include #include #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; } void exec_child(char **argv) { int status; status = execvp(argv[0], argv); if (status < 0) { perror("rpt[child process]"); exit(4); } exit(4); } void wait_child(pid_t pid, void (*handle_error_f)(int status)) { int exit_status = 0; waitpid(pid, &exit_status, 0); handle_error_f(exit_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 status)) { pid_t pid; for(long i = 0; i < count; ++i) { pid = fork(); if (pid < 0) { perror("rpt"); exit(4); } if (pid == 0) exec_child(argv); else wait_child(pid, handle_error_f); } } 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; }