/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>


#include <dx/dx.h>


#define M_ERROR   1
#define M_WARNING 2
#define M_MESSAGE 3

#define MAXBUFLEN 2048
#define MAXOUT    21

static Error parseit(char *inputstring, char *control, Object *out);
static void copyformat(char **from, char **to);

int
m_Parse(Object *in, Object *out)
{
    char *inputstring = NULL;
    char *control = NULL;
    int i;

    for (i=0; i<MAXOUT; i++)
	out[i] = NULL;

    if (!in[0] || !DXExtractString(in[0], &inputstring)) {
	DXSetError(ERROR_BAD_PARAMETER, "#10200", "input"); 
	return ERROR;
    }
    
    if (in[1]){
      if (!DXExtractString(in[1], &control)) {
	DXSetError(ERROR_BAD_PARAMETER, "#10200", "format"); 
	return ERROR;
      }
    }
    else 
      control= "%s";
    
    if (!parseit(inputstring, control, out))
	return ERROR;

    return OK;
}



static Error parseit(char *inputstring, char *control, Object *out)
{
    char *ccp, *icp, *tcp;
    Pointer tempspace = NULL;
    Pointer tempcontrol = NULL;
    int i, nmatched;
    int longlongflag, longflag, shortflag, skipflag;
    int bcount, knowtype;
    Object o;
    Type type;

    /* set them all to null so we can delete them all on error */
    for (i=0; i<MAXOUT; i++)
	out[i] = NULL;

    /* each individual variable can't be longer than the input str */
    tempspace = DXAllocate(strlen(inputstring) + 1);
    if (!tempspace)
	goto error;

    /* each individual format spec can't be longer than the whole control */
    tempcontrol = DXAllocate(strlen(control) + 1);
    if (!tempcontrol)
	goto error;


    /* allocate enough memory to hold one value of any type.
     * go thru the string looking for formats.
     * use the real sscanf to parse the string.
     * then make an object of that type.
     */

    for (ccp = control, icp = inputstring, nmatched = 0; ccp && *ccp;  ) {

	/* consume the chars until the next %.  they have to match
	 *  the chars in the inputstring exactly (except whitespace
	 *  which matches any other whitespace).
	 */
	tcp = tempcontrol;
	*tcp = '\0';

	/* while not at complete end of format string, and not at a % char,
         * copy chars to a temp format string.  %% is ok - it matches
	 * a real % char, so it isn't the start of a substitution char.
	 */
	while (1) {
	    if (*ccp == '\0')
		break;
	    if (*ccp == '%') {
		if (*(ccp+1) == '%')
		    *tcp++ = *ccp++;
		else
		    break;
	    }
	    *tcp++ = *ccp++;
	}
	*tcp = '\0';
	
	/* if there was stuff before the next format... */
	if (((char *)tempcontrol)[0] != '\0') {
	    /* add a format which returns the number of chars consumed 
	     *  from the input string.
	     */
	    strcat(tcp, "%n");
	    if ((sscanf(icp, tempcontrol, &bcount) < 0) || (bcount <= 0)) {
		DXSetError(ERROR_BAD_PARAMETER,
			   "any character other than %% must match exactly");
		goto error;
	    }
	    icp += bcount;
	}

	/* at the end? */
	if (*ccp == '\0')
	    break;

	/* next char must be % */
	if (*ccp != '%') {
	    DXSetError(ERROR_INVALID_DATA, "next char not %%??");
	    goto error;
	}
	    
	ccp++;

	/* check for badly formed string - can't end with % */
	if (*ccp == '\0') {
	    DXSetError(ERROR_BAD_PARAMETER, 
		       "missing format character after %%");
	    goto error;
	}
	
	/* copy the chars until the next % or whitespace to the temp
	 * format buffer.
	 */
	tcp = tempcontrol;
	*tcp++ = '%';
	*tcp = '\0';
	copyformat(&ccp, &tcp);
	*tcp = '\0';
	
	/* if we did not get a format... */
	if (((char *)tempcontrol)[0] == '\0') {
	    DXSetError(ERROR_BAD_PARAMETER,
		       "missing format character %d", nmatched+1);
	    goto error;
	}
	
	/* add a format which returns how many chars we consumed from
	 *  the string.
	 */
	strcat(tcp, "%n");
	if ((sscanf(icp, tempcontrol, tempspace, &bcount) != 1) || (bcount <= 0)) {
	    DXSetError(ERROR_BAD_PARAMETER,
		       "format %d did not match input", nmatched+1);
	    goto error;
	}
	icp += bcount;
	
	
	/* now i have to figure out what type of conversion it was so i 
	 *  can make an array of the right type.  bother.
	 */
	tcp = tempcontrol;
	longlongflag = longflag = shortflag = skipflag = 0;
	knowtype = 0;
	while (*tcp != '\0') {
	    switch (*tcp) {
	      case 'l':
		longflag++;
		break;
	      case 'L':
		longlongflag++;
		break;
	      case '*':
		skipflag++;
		break;
	      case 'h':
		shortflag++;
		break;
	      case 'd':
	      case 'i':
	      case 'u':
	      case 'o':
	      case 'x':
	      case 'p':
	      case 'n':
	      case 'B':
		type = TYPE_INT;
		if (shortflag)
		    type = TYPE_SHORT;
		knowtype++;
		break;
	      case 'e':
	      case 'f':
	      case 'g':
		type = TYPE_FLOAT;
		if (longflag)
		    type = TYPE_DOUBLE;
		knowtype++;
		break;
	      case 'c':
	      case 's':
	      case 'S':
	      case '[':
		type = TYPE_STRING;
		knowtype++;
		break;

	      default:
		/* don't recognize the format yet.  keep trying */
		break;
	    }
	    if (knowtype)
		break;
	    tcp++;
	}

	/* skip flag */
	if (skipflag)
	    continue;


	/* make an array */
	if (type != TYPE_STRING) {
	    o = (Object)DXNewArray(type, CATEGORY_REAL, 0);
	    if (!o)
		goto error;
	    if (!DXAddArrayData((Array)o, 0, 1, tempspace)) {
		DXDelete(o);
		goto error;
	    }
	} else {
	    o = (Object)DXNewString((char *)tempspace);
	    if (!o)
		goto error;
	}

	/* set the output object */
	out[nmatched] = o;

	/* we successfully matched a format char. */
	nmatched++;

    }

    DXFree(tempspace);
    DXFree(tempcontrol);
    return OK;

  error:
    DXFree(tempspace);
    DXFree(tempcontrol);
    for (i=0; i<nmatched; i++) {
	DXDelete(out[i]);
	out[i] = NULL;
    }

    return ERROR;
}


/* copy chars from one string to another as long as you are in a format
 * definition. 
 */
static void copyformat(char **from, char **to)
{
    while (**from) {
	switch (**from) {

	  /* chars which end the format */
	  case '\0':
	  case ' ':
	  case '\t':
	  case '\n':
	  case '\r':
	  case '\f':
	  case '%':
	    return;
	    
	  /* final char in format spec */
	  case 'd':
	  case 'i':
	  case 'u':
	  case 'o':
	  case 'x':
	  case 'p':
	  case 'e':
	  case 'f':
	  case 'g':
	  case 'c':
	  case 's':
	  case 'S':
	  case 'n':
	  case 'B':
	    *(*to)++ = *(*from)++;
	    return;

	  /* until closing ], except if ] is first char, or ^] */
	  case '[':
	    *(*to)++ = *(*from)++;
	    if (**from == '^')
		*(*to)++ = *(*from)++;
	    if (**from == ']')
		*(*to)++ = *(*from)++;
	    while (**from != ']' && **from != '\0')
		*(*to)++ = *(*from)++;

	    *(*to)++ = *(*from)++;
	    return;
	    
	  /* modifiers which don't determine the format yet */
	  case 'l':
	  case 'L':
	  case 'h':
	  case '*':
	  case '.':
	  case '0': case '1': case '2': case '3': case '4': 
	  case '5': case '6': case '7': case '8': case '9':
	  default:
	    break;
	}
	*(*to)++ = *(*from)++;
    }

    return;
}
