#include <string.h>
#include <stdarg.h>

#define addch(s, c) { int l = strlen(s); s[l++] = c; s[l] = '\0'; }

static char *do_one_match(char *s, char *left_text, char *right_text, char *dest)
{char *x;
  if (strncmp(s, left_text, strlen(left_text)) != 0) return(NULL);
  else s += strlen(left_text);
  if (*right_text == '\0') strcpy(dest, s);
  else if ((x = strstr(s, right_text)) == NULL) return(NULL);
  else { strncpy(dest, s, x-s); dest[x-s] = '\0'; s = x; }
  return(s);
}

int resolve(char *s, const char *fmt, ...)
{
va_list arg_ptr;
char left_text[256], right_text[256], DUMMY[256], *dest;
int c;
  *left_text = '\0'; *right_text = '\0';
  va_start(arg_ptr,fmt);
  for (;;) {
    if ((c = *fmt++) == '\0') {
      va_end(arg_ptr); return(do_one_match(s, left_text, right_text, dest) != NULL);
    } else if (c != '%') addch(right_text, c) else {
      if ((c = *fmt++) == 't') {
        char *insert = va_arg(arg_ptr, char *);
        while ((c = *insert++) != '\0') addch(right_text, c)
      } else if ((c == 's') || (c == 'S')) {
        if (((*left_text != '\0') || (*right_text != '\0')) &&
	    (!(s=do_one_match(s, left_text, right_text, dest)))) break;
        *(dest = (c == 'S' ? DUMMY : va_arg(arg_ptr, char *))) = '\0';
        strcpy(left_text, right_text); *right_text = '\0';
      } else if (c == '%') addch(right_text, '%')
    }
  }
  va_end(arg_ptr); return(0);
}

#include <stdio.h>
int main(int argc, char **argv)
{
  char s[256], a[256], b[256], c[256], dyn[256];
  strcpy(s, "text0text1text2text3text4");

  /* Examples of mapping imp string resolution to a single
     C function call.  We trust the compiler to generate
     the correct format string and make no error checks here.

     A long and commented version with full error checks
     is available on request.
  */

  /* %if s-> a.("text1").b.("text3").c %then %start */
  if (resolve(s, "%stext1%stext3%s", a, b, c)) {
    fprintf(stdout, "s -> %s.(text1).%s.(text3).%s\n", a,b,c);
  }

  /* Use %S instead of %s for first/last args if missing */

  /* %if s-> ("text1").b.("text3") %then %start */
  if (resolve(s, "%Stext1%stext3%S", b)) {
    fprintf(stdout, "s -> (text1).%s.(text3)\n", b);
  }

  /* %unless s-> a.("text99").b.("text3").c %then %start */
  if (!resolve(s, "%stext99%stext3%s", a, b, c)) {
    fprintf(stdout, "s -> %s.(text99).%s.(text3).%s fails, as expected\n", a,b,c);
  }


  /* try a dynamic one */

  strcpy(dyn, "text1");


  /* %if s-> a.(dyn).b.("text3").c %then %start */
  if (resolve(s, "%s%t%stext3%s", a, dyn, b, c)) {
    fprintf(stdout, "s -> %s.(%s).%s.(text3).%s\n", a,dyn,b,c);
  }

  /* awkward case - what is the spec here? */

  strcpy(dyn, "");

  /* %if s-> a.(dyn).b.("text3").c %then %start */
  if (resolve(s, "%s%t%stext3%s", a, dyn, b, c)) {
    fprintf(stdout, "s -> %s.(%s).%s.(text3).%s\n", a,dyn,b,c);
  }

  /* ERRONEOUS OUTPUT IS: s -> text0.().text0text1text2.(text3).text4 */

  /* No...  it's correct when you initialise all the target params to ""
     s -> .().text0text1text2.(text3).text4     */

  return(0);
}
