#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>

#include <getopt.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/wait.h>

int repetitions = 0;
bool stop_on_error = false;
char * this_cmd = NULL;
bool print_iterations = false;

void parse_args(int argc, char * argv[]);
void setup();
int run();

int main(int argc, char * argv[]) {
	parse_args(argc, argv);
	setup();
	for(int i = 0; i < repetitions; i++) {
		if(print_iterations) {
			printf("\r%d / %d", i, repetitions);
		}
		int retcode = run();
		if( !WIFEXITED(retcode) ) {
			printf("FAILURE: %d @ %d\n", retcode, i + 1);
		}
		if( !WIFEXITED(retcode) && stop_on_error ) {
			return retcode;
		}
	}

	if(print_iterations) {
		printf("\r%d / %d\n", repetitions, repetitions);
	}

	return 0;
}

void usage( FILE * out, int code ) {
	fprintf(out, "%s [OPTION] [--] N CMD\n", this_cmd);
	fprintf(out, "Repeat CMD N times\n\n");
	fprintf(out, "\t-h,--help\tprint this usage message\n");
	fprintf(out, "\t-s\tstop on error\n");
	fprintf(out, "\t-i\toutput iterations instead of CMD stdout\n");
	fprintf(out, "\t-x\tprint CMD before running it\n");
	exit(code);
}

char ** cmd_to_run = NULL;
bool print_cmd = false;
pid_t child_pid = 0;

void error() {
	fprintf(stderr,"\n");
	usage(stderr, 1);
}

void parse_args(int argc, char * argv[]) {
	this_cmd = argv[0];

	enum { Help, };
	static struct option long_opts[] = {
		{ "help", no_argument, 0, Help },
		{ 0, 0, 0, 0 }
	}; // long_opts
	int long_index;

	int c;
	while ( (c = getopt_long( argc, argv, "hsxi", long_opts, &long_index)) != -1 ) {
		switch ( c ) {
			case Help:
			case 'h':
				usage(stdout, 0);
				break;
		 	case 's':
				stop_on_error = true;
				break;
		 	case 'x':
				print_cmd = true;
				break;
			case 'i':
				print_iterations = true;
				break;
			default:
				error("");
				break;
		} // switch
	} // while

	if( argc < optind + 2 ) {
		fprintf(stderr, "Too few arguments\n");
		error();
	}

	char * pEnd;
	char * repeats_c = argv[optind];
	repetitions = strtol(repeats_c, &pEnd, 10);

	bool is_number = (size_t)(pEnd - repeats_c) == strlen(repeats_c);
	bool in_range  = repetitions > 0;
	if( !is_number || !in_range ) {
		fprintf(
			stderr,
			"repetitions option : %s not an number greater than 0\n",
			repeats_c
		);
		error();
	}

	cmd_to_run = argv + optind + 1;
}

void setup() {

}

int run() {
	/* Duplicate this process. */
	child_pid = fork();
	if (child_pid != 0) {

		/* This is the parent process. */
		int status;
		waitpid(child_pid, &status, 0);
		child_pid = 0;
		return status;
	}
	else {
		/* Now execute PROGRAM, searching for it in the path.  */
		if( print_cmd ) {
			char ** cmd = cmd_to_run;
			while (*cmd) {
				printf("%s ", *cmd);
				cmd++;
			}
			printf("\n");
		}
		if(print_iterations) {
			__attribute__((unused)) FILE * ignore =
				freopen("/dev/null", "w", stdout);
		}
		execvp ( *cmd_to_run, cmd_to_run);
		/* The execvp  function returns only if an error occurs.  */
		fprintf(stderr, "an error occurred in execvp\n");
		abort ();
	}

	return 0;
}