
/*** SEE ALSO resolve-small.c ***/

#define DBMAIN 1

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>

#ifndef FALSE
#define TRUE (0==0)
#define FALSE (0!=0)
#endif

static void addch(char *s, int c)
{
  int l = strlen(s);
  s[l++] = c; s[l] = '\0';
}

static char *do_one_match(/*  in */ char *s, char *left_text, char *right_text,
                          /* out */ char *dest)
{
  char *x, *debugs = s;
  if (strncmp(s, left_text, strlen(left_text)) == 0) {
    s += strlen(left_text);
  } else {
    /*fprintf(stdout, "match(%s -> (%s).*.(%s) -> * = NULL, s = %s\n", debugs,left_text,right_text, s);*/
    return(NULL);
  }
  if (*right_text == '\0') { /* final case */
    strcpy(dest, s);
  } else if ((x = strstr(s, right_text)) != NULL) {
    strncpy(dest, s, x-s); dest[x-s] = '\0';
    s = x;
  } else {
    /*fprintf(stdout, "match(%s -> (%s).*.(%s) -> * = NULL, s = %s\n", debugs,left_text,right_text, s);*/
    return(NULL);
  }
  /*fprintf(stdout, "match(%s -> (%s).*.(%s) -> * = %s, s = %s\n", debugs,left_text,right_text,dest, s);*/
  return(s);
}

int resolve(char *s, const char *fmt, ...)
{
va_list arg_ptr;
int c, last;
char left_text[256], right_text[256], DUMMY[256], prepend[256];
char *dest;

  /* generic form is   s -> a.("1").b.("2").c
              resolve(s, "%s1%s2%s", a, b, c)
     exceptional form is   s -> ("1").b.("2")
              resolve(s, "%S1%s2%S", b)
     ie a missing first or last argument is
     replaced by %S.
     This way we implement the generic procedure only with
     a small tweak using a dummy arg for the missing arg
     when %S is given.

     There may be partial assignments.  Assignments are
     carried out on the fly, one by one.

     May later add %255s or %*s to allow passing in a maxlength.
     Should accept them in fmt string for now, for future
     compatibility  (%*s means get size from an int param
     following the string address)
   */
  last = strlen(fmt)-1;
  assert((fmt[0] == '%') && (tolower(fmt[1]) == 's'));
  assert((fmt[last-1] == '%') && (tolower(fmt[last]) == 's'));
  *left_text = '\0'; *right_text = '\0';

  va_start(arg_ptr,fmt);

  for (;;) {
    c = *fmt++;
    if (c == '\0') {
      va_end(arg_ptr);
      return(do_one_match(s, left_text, right_text, dest) != NULL);
      break;
    } else if (c == '%') {
      c = *fmt++;
      if (c == 't') {
        /* prepend next string arg to current position of 's' */
        char *insert = va_arg(arg_ptr, char *);
        while ((c = *insert++) != '\0') addch(right_text, c);
      } else if (tolower(c) == 's') {

        /* First we handle the PREVIOUS %s, then we
           shift right_string into left_string and wait for
           the following one before we handle this one.

           Initial case is  "" %s "right text"
           General case is  "left text" %s "right text"
           Final case is    "left text" %s ""

           All that we do for the %s itself is note its
           address as a destination.  Or note the DUMMY
           address if it was %S
         */

        if ((*left_text == '\0') && (*right_text == '\0')) {
          /* Leading %s - ignore for now */
	} else {
          if ((s=do_one_match(s, left_text, right_text, dest)) == NULL) break;
	}
        if (c == 'S') {
          /* dummy argument - matches text but doesn't assign
             to an arg.  Can only come first or last in a fmt string */
          dest = DUMMY;
	} else {
          dest = va_arg(arg_ptr, char *);
          assert(dest != NULL);
	}

        strcpy(left_text, right_text);
        *right_text = '\0';

        /* Everything between previous string and next
           string should be written to s.
           If no text follows, then to end of input string,
           ie    string -> a.("text 1).b
           would assign everything after text1 to b.

           Similarly if there is no text before the %s,
           everything up to the next literal is assigned to s, ie
           ie    string -> a.("text 1).b
           would assign everything before "text1" to a.

           The case of   string -> ("text1").a.("text2")
           is treated as string -> X1.("text1").a.("text2").X2
           with a dummy X1 and X2.
         */
      } else if (c == '%') {
        addch(right_text, '%');
      } else {
        assert(c == 's'); /* only %s allowed */
      }
    } else {
      addch(right_text, c);
    }
  }
  va_end(arg_ptr);
  return(FALSE);
}

#ifdef DBMAIN
int main(int argc, char **argv)
{
  char s[256], a[256], b[256], c[256], var[256];
  strcpy(s, "text0text1text2text3text4");

  printf("\n\n");
  /* %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);
  }

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

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

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

  exit(0);
}
#endif
