/****************************************************************************/
/* mapec is a simple program designed to be used in Bernstein chains,       */
/* usualy as part of a supervise run script for a qmail-style service. It   */
/* is invoked as follows -                                                  */
/*                                                                          */
/*   mapec <map> <command> <arg> <arg> ...                                  */
/*                                                                          */
/* mapec forks, invokes the command with the specified arguments and waits  */
/* for the child process to exit. Once it does, mapec exits as specified by */
/* the map entry for the exit code of the child. The map consists of colon  */
/* separated terms, each of which is a list of codes (or nothing to set the */
/* default exit code), an equals sign, then the exit code to use if this    */
/* term matches. List of codes are comma separated, and may use two numbers */
/* with a hyphen between them to specify ranges. For example -              */
/*                                                                          */
/*   3-7,12=43:15=44:=45                                                    */
/*                                                                          */
/* would cause mapec to exit 43 if the child exits 12 or from 3 to 7, 44 if */
/* the child exists 15 and 45 otherwise.                                    */
/*                                                                          */
/* mapec was written by Matt S Trout in 2004, and the source provided here  */
/* is under the terms of the BSD License (revised).                         */
/****************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

char* tok(char* s, char delim) {
  while (*s != '\0') {
    if (*s == delim) {
      *s = '\0';
      return ++s;
    }
    s++;
  }
  return NULL;
}

int map_ec(char* ptr, int ec) {
  char* next;
  char* rhs;
  char* nextmatch;
  char* upper;
  int dflt;
  dflt = ec;
  while (ptr != NULL) {
    next = tok(ptr, ':'); /* grab address of next expression */
    if ((rhs = tok(ptr, '=')) == NULL)
      return 255;
    if (*ptr == '\0') /* found an =<val>, set default */
      dflt = atoi(rhs);
    while (ptr != NULL) {
      nextmatch = tok(ptr, ',');
      if ((upper = tok(ptr,'-')) != NULL) { /* range */
        if (atoi(ptr) <= ec && ec <= atoi(upper))
          return atoi(rhs);
      } else { /* single value */
        if (atoi(ptr) == ec)
          return atoi(rhs);
      }
      ptr = nextmatch;
    }
    ptr = next;
  }
  return dflt;
}

int main (int argc, char* argv[]) {

  if (argc < 2) {
    puts("Usage: mapec <map> <command>");
    return 255;
  }

  pid_t pid;
  if ((pid = fork()) < 0) {
    printf("fork error: %s", strerror(errno));
  }

  if (pid == 0) { /* We're the child */
    execvp(argv[2], argv + 2);
    printf("exec error: %s starting %s", strerror(errno),argv[1]);
    return 255;
  } /* If we get here, we're the parent */

  int status;
  wait(&status);

  if (WIFEXITED(status) == 0) {
    return 255;
  }

  return map_ec(argv[1], WEXITSTATUS(status));
}
