//  crm_math_exec.c  - Controllable Regex Mutilator,  version v1.0
//  Copyright 2001-2006  William S. Yerazunis, all rights reserved.
//  
//  This software is licensed to the public under the Free Software
//  Foundation's GNU GPL, version 2.  You may obtain a copy of the
//  GPL by visiting the Free Software Foundations web site at
//  www.fsf.org, and a copy is included in this distribution.  
//
//  Other licenses may be negotiated; contact the 
//  author for details.  
//
//  include some standard files
#include "crm114_sysincludes.h"

//  include any local crm114 configuration file
#include "crm114_config.h"

//  include the crm114 data structures file
#include "crm114_structs.h"

//  and include the routine declarations file
#include "crm114.h"

//    the command line argc, argv
extern int prog_argc;
extern char **prog_argv;

//    the auxilliary input buffer (for WINDOW input)
extern char *newinputbuf;

//    the globals used when we need a big buffer  - allocated once, used 
//    wherever needed.  These are sized to the same size as the data window.
extern char *inbuf;
extern char *outbuf;
extern char *tempbuf;

static int math_formatter ( double value, char *format, char *buf, long buflen);


//
//           strmath - evaluate a string for the mathematical result,
//            returning the length of the valid string.
//
long strmath (char *buf, long inlen, long maxlen, long *retstat)
{
  long status;
  long old_internal_trace;
  old_internal_trace = internal_trace;
  
  if (inlen < 0)
    {
      fatalerror ("Bug in caller to strmath() - it makes no sense to",
		  " have a negative length string!  \n");
      internal_trace = old_internal_trace;
      return (0);
    };

  //   Check for first-character control of Algebraic v. RPN
  if (buf[0] == 'A')
    {
      //      internal_trace = 1;
      memmove (buf, &buf[1], inlen-1);
      buf[inlen-1] = '\0';
      status = stralmath (buf, inlen-1, maxlen, retstat);
      internal_trace = old_internal_trace;
      return (status);
    }
  if (buf[0] == 'R')
    {
      //      Do we want to do selective tracing?
      // internal_trace = 1;
      memmove (buf, &buf[1], inlen-1);
      buf[inlen-1] = '\0';
      status = strpnmath (buf, inlen-1, maxlen, retstat);
      internal_trace = old_internal_trace;
      return (status);
    }

  //   No first-character control, so use q_expansion_mode to control.    
  if (q_expansion_mode == 0 || q_expansion_mode == 2)
    {
      return (stralmath (buf, inlen, maxlen, retstat));
    }
  else
    {
      return (strpnmath (buf, inlen, maxlen, retstat));
    }
}

//    strpnmath - do a basic math evaluation of very simple expressions.
//
//    This does math, in RPN, on a string, and returns a string value.
//
long strpnmath (char *buf, long inlen, long maxlen, long *retstat)
{
  double stack [DEFAULT_MATHSTK_LIMIT];     // the evaluation stack
  double sd;             //  how many 10^n's we've seen since a decimal
  long od;               //  output decimal flag               
  long ip, op;           //  in string pointer, out string pointer
  long sp;               //  stack pointer - points to next (vacant) space
  long sinc;             //  stack incrment enable - do we start a new number
  long errstat;          //  error status

  char outformat[64];    // output format
  long outstringlen;

  //    start off by initializing things
  ip = 0;    //  in pointer is zero
  op = 0;    // output pointer is zero
  sp = 0;    // still at the top of the stack
  od = 0;    // no decimals seen yet, so no flag to output in decimal
  sinc = 0;  // no autopush.
  outformat[0] = '\0'; 

  //     now our number-inputting hacks
  stack[sp] = 0.0 ; 
  sd = 1.0;

  //      all initialized... let's begin.
  
  if (internal_trace) 
    fprintf (stderr, "Math on '%s' len %ld retstat %lx \n", 
	     buf, inlen, (long) retstat);

  for (ip = 0; ip < inlen; ip++)
    {
      if (internal_trace)
	fprintf (stderr, "ip = %ld, sp = %ld, stack[sp] = %f, ch='%c'\n", 
	       ip, sp, stack[sp], buf[ip]);

      if (sp < 0) 
	{
	  errstat = nonfatalerror ("Stack Underflow in math evaluation",
			 "");
	  return (0);
	};

      if (sp >= DEFAULT_MATHSTK_LIMIT)
	{
	  errstat=nonfatalerror ("Stack Overflow in math evaluation.\n "
				 "CRM114 Barbie says 'This math is too hard'.",
				 buf);
	  return (0);
	};

      switch (buf[ip])
	{
	  //
	  //        a digit,or maybe a number - big change - we now use strtod
	  //
	case '.':
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '-':
	case '+':
	  {
	    char *frejected;
	    //    handle the case of a minus sign that isn't a unary -.
	    if (buf[ip] == '-' && !( isdigit (buf[ip+1])))
	      {
		if (sp > 0)
		  {
		    sp--;
		    stack[sp] = stack[sp] - stack[sp+1];
		    sinc = 1;
		  }
		break;
	      };
	    if (buf[ip] == '+' && !( isdigit (buf[ip+1])))
	      {
		if (sp > 0)
		  {
		    sp--;
		    stack[sp] = stack[sp] + stack[sp+1];
		    sinc = 1;
		  }
		break;
	      };
	    
	    //   Neither unary +/-  so we use strtod to convert 
	    //   the string we're looking at to floating point.
	    sp++;
	    stack[sp] = strtod ( &buf[ip], &frejected);
	    if (user_trace)
	      fprintf (stderr, "got number: %e\n", stack[sp]);
	    //
	    //    Now, move [ip] over to accomodate characters used.
	    //    (the -1 is because there's an auto-increment in the big 
	    //    FOR-loop)
	    ip = ((unsigned long) frejected) - ((unsigned long) buf ) - 1;
	  }
	  break;
	  //
	  //   and now the standard math operators (except for - and + above)
	  //
	case '*':
	  {
	    if (sp > 0)
	      {
		sp--;
		stack[sp] = stack[sp] * stack[sp+1];
		sinc = 1;
	      }
	  };
	  break;
	case '/':
	  {
	    if (sp > 0)
	      {
		sp--;
		// don't worry about divide-by-zero, we get INF in IEEE.
		stack[sp] = stack[sp] / stack[sp+1];
		sinc = 1;
	      }
	  };
	  break;
	case '%':
	  {
	    if (sp > 0)
	      {
		sp--;
		stack[sp] = ((long long) stack[sp]) % ((long long)stack[sp+1]);
		sinc = 1;
	      }
	  };
	  break;
	  
	case '^': // exponentiation - for positive bases, neg base + int exp.
	  if (sp > 0)
	    {
	      sp--;
	      if (stack[sp] < 0.0 
		  && ((long long)(stack[sp+1]))/1 != stack[sp+1])
		{ stack[sp] = stack[sp] / 0.0; }
	      else
		stack[sp] = pow (stack[sp], stack[sp+1]);
	      if (internal_trace)
		fprintf (stderr, "exp out: %lf\n", stack[sp]);
	      sinc = 1;
	    }
	  break;
	  
	case 'v': // logs as  BASE v ARG; (NaN on BASE <= 0)
	  if (sp > 0)
	    {
	      sp--;
	      if (stack[sp] <= 0.0 )
		{ stack[sp] = stack[sp] / 0.0; }
	      else
		stack[sp] = log (stack[sp+1]) / log (stack[sp]);
	      sinc = 1;
	    }
	  break;
	  
	case '=':
	  {
	    if (sp > 0)
	      {
		sp--;
		if (stack[sp] == stack[sp+1])
		  {
		    if (retstat) *retstat = 0;
		    stack[sp] = 1;
		  }
	        else 
		  {
		    if (retstat) *retstat = 1;
		    stack[sp] = 0;
		  };
		sinc = 1;
	      }
	  };
	  break;

	case '!':
	  {
	    if (sp > 0 && buf[ip+1] == '=')
	      {
		ip++; // gobble up the equals sign
		sp--;
		if (stack[sp] != stack[sp+1])
		  {
		    if (retstat) *retstat = 0;
		    stack[sp] = 1;
		  }
	        else 
		  {
		    if (retstat) *retstat = 1;
		    stack[sp] = 0;
		  };
		sinc = 1;
	      }
	  };
	  break;

	case '>':
	  {
	    if (buf[ip+1] == '=')
	      {
		ip++;   // gobble up the equals sign too... 
		if (sp > 0)
		  {
		    sp--;
		    if (stack[sp] >= stack[sp+1])
		      {
			if (retstat) *retstat = 0;
			stack[sp] = 1;
		      }
		    else 
		      {
			if (retstat) *retstat = 1;
			stack[sp] = 0;
		      };
		    sinc = 1;
		  }
	      }
	    else
	      {
		if (sp > 0)
		  {
		    sp--;
		    if (stack[sp] > stack[sp+1])
		      {
			if (retstat) *retstat = 0;
			stack[sp] = 1;
		      }
		    else 
		      {
			if (retstat) *retstat = 1;
			stack[sp] = 0;
		      };
		    sinc = 1;
		  }
	      };
	  }
	  break;

	case '<':
	  {
	    if (buf[ip+1] == '=')
	      {
		ip++; // gobble up the equals sign
		if (sp > 0)
		  {
		    sp--;
		    if (stack[sp] <= stack[sp+1])
		      {
			if (retstat) *retstat = 0;
			stack[sp] = 1;
		      }
		    else 
		      {
			if (retstat) *retstat = 1;
			stack[sp] = 0;
		      };
		    sinc = 1;
		  }
	      }
	    else
	      {
		if (sp > 0)
		  {
		    sp--;
		    if (stack[sp] < stack[sp+1])
		      {
			if (retstat) *retstat = 0;
			stack[sp] = 1;
		      }
		   else 
		      {
			if (retstat) *retstat = 1;
			stack[sp] = 0;
		      };
		    sinc = 1;
		  }
	      };
	  };
	  break;
	case 'e':
	case 'E':
	case 'f':
	case 'F':
	case 'g':
	case 'G':
	case 'x':
	case 'X':
	  //             User-specified formatting; use the user's 
	  //             top-of-stack value as a format.
	  //
	  {
	    if (sp > 0)
	      {
		char tempstring [2048];
		tempstring[0] = '\0';
		sp--;
		//  Special case - if the format is an integer, add a ".0"
		//  to the format string so we get integer output.
		if ( buf[ip] == 'x' || buf[ip] == 'X')
		  {
		    snprintf (outformat, 63, "%%%g.0ll%c",
                             stack[sp+1], (short) buf[ip] );
                  }
                else
                  {
		    if ( ((long)stack[sp+1]) / 1 == stack[sp+1])
		      {
			snprintf(outformat, 63, "%%%g.0%c", stack[sp+1], buf[ip]);
		      }
		    else
		      {
			snprintf(outformat, 63, "%%%g%c", stack[sp+1], buf[ip]);
		      };
		  };
		if (internal_trace)
		  fprintf (stderr, "Format string -->%s<-- \n", outformat);
		stack[sp+1] = 0;
		if (buf[ip] != 'x' && buf[ip] != 'X')
		  {
		    snprintf (tempstring, 2047, outformat, stack[sp]);
		    if (internal_trace)
		      fprintf (stderr, 
			       "Intermediate result string -->%s<-- \n", 
			       tempstring);
		  }
		else
		  {
		    long long intpart ;
		    intpart = ((long long) stack[sp]) / 1;
		    snprintf (tempstring, 2047, outformat, intpart);
		    if (internal_trace)
		      fprintf (stderr, 
			       "Intermediate hex result string -->%s<-- \n", 
			       tempstring);
		  };
		//   And now do the back conversion of the result.
		//   Note that X formatting (hexadecimal) does NOT do the 
		//   back conversion; the only effect is to store the 
		//   format string for later.  
		if (buf[ip] != 'x' &&
		    buf[ip] != 'X')
		  stack[sp] = strtod (tempstring, NULL);
	      }
	  };
	  break;
	case ' ':
	case '\n':
	case '\t':
	  //
	  //        a space is just an end-of-number - push the number we're 
	  //        seeing.  
	  {
	    sinc = 1;
	  };
	  break;
	case '(':
	case ')':
	  //         why are you using parenthesis in RPN code??
	  {
	    nonfatalerror ("It's just silly to use parenthesis in RPN!",
			   " Perhaps you should check your setups?");
	    sinc = 1;
	  };
	  break;

	default:
	  {
	    char bogus[4];
	    bogus[0] = buf[ip];
	    bogus[1] = '\000';
	    nonfatalerror (" Sorry, but I can't do RPN math on the un-mathy "
			   "character found: ", bogus); 
	    sinc = 1;
	  };
	  break;
	};
    };

  if (internal_trace)
    {
      fprintf (stderr, 
	     "Final qexpand state:  ip = %ld, sp = %ld, stack[sp] = %f, ch='%c'\n", 
	       ip, sp, stack[sp], buf[ip]);
      if (retstat) 
	fprintf (stderr, "retstat = %ld\n", *retstat);
    };

  //      now the top of stack contains the result of the calculation.
  //      fprintf it into the output buffer, and we're done.
  outstringlen = math_formatter ( stack[sp], outformat, buf, maxlen) ;
  return (outstringlen);
}
 


/////////////////////////////////////////////////////////////////
//
//     Here's where we format a floating point number so it's "purty".
//     Note that if "format" is NULL, or a null string, we do smart
//     formatting on the number itself.
//
//   
int math_formatter ( double value, char *format, char *buf, long buflen)
{
  long outlen;
  //  If the user supplied a format, use that.
  //  
  if (format && format[0] != '\0')
    {
      //
      //  special case - if the user supplied an x or X-format, that's
      //  preconversion to integer; use that strlen() does not count
      //  the null termination.
      if (format[strlen(format)-1] == 'x'
	  || format[strlen(format)-1] == 'X')
	{
	  long long equiv ;
	  if (internal_trace)
	    fprintf (stderr, "Final hex format: %s\n", format ); 
	  equiv = value * 1;
	  outlen = snprintf (buf, buflen, format, equiv);
	  return (outlen);
	};
      //  	
      //    Nothing so special; use the user format as it is.
      if (internal_trace)
	fprintf (stderr, "Final format: %s\n", format ); 
      outlen = snprintf (buf, buflen, format, value);
      return (outlen);
    };
     
  //   Nope - we didn't get a preferred formatting, so here's the 
  //   adaptive smart code.
	       
  //
  //      print out 0 as 0
  //
  if (value == 0.0 )
    {
      outlen = snprintf (buf, buflen, "0");
      goto formatdone;
    }
  //
  //       use E notation if bigger than 1 trillion
  //
  if (value > 1000000000000.0 || value < -1000000000000.0 )
    {
      outlen = snprintf (buf, buflen, "%.5E", value);
      goto formatdone;
    }
  // 
  //       use E notation if smaller than .01
  //
  if ( value  < 0.01 && value > -0.01 )
    {
      outlen = snprintf (buf, buflen, "%.5E", value);
      goto formatdone;
    }
  //
  //       if an integer value, print with 0 precision
  //
  if (((long)(value*2.0) / 2) == value)
    {
      outlen = snprintf (buf, buflen, "%.0f", value);
      goto formatdone;
    }
  //
  //       if none of the above, print with five digits precision
  //
  outlen = snprintf (buf, buflen, "%.5f", value);
  //
  //
  //         one way or another, once we're here, we've sprinted it.
 formatdone:
  if (internal_trace)
    fprintf (stderr, "math_formatter outlen = %ld\n", outlen);
  return (outlen);
}

////////////////////////////////////////////////////////////////////
//
//   Alternative implementation of the uglyness that is string math.
//    
//   This version uses two stacks (left arg, op) and a single scalar
//   rightarg.  Partial computations are kept on the leftarg and op 
//   stack.  The current stack status is held in validstack, and is 
//   the OR of LEFTVALID, OPVALID, and RIGHTVALID.    
//
#define LEFTVALID 0x1
#define OPVALID 0x2
#define RIGHTVALID 0x4

long stralmath (char *buf, long inlen, long maxlen, long *retstat)
{
  double leftarg [DEFAULT_MATHSTK_LIMIT] ;   // left float arg 
  long opstack [DEFAULT_MATHSTK_LIMIT];       // operand
  double rightarg;                            // right float arg
  long validstack [DEFAULT_MATHSTK_LIMIT];    // validity markers
  long sp;                                    // stack pointer
  long ip, op;                                // input and output pointer
  long errstat;                              //  error status
  char *frejected;                           //  done loc. for a strtod. 
  char outformat[256];                            //  how to format our result 
  long state;                                // Local copy of state, in case
                                             // retstat is NULL (not used)
  //   Start off by initializing things
  ip = 0;
  op = 0;
  sp = 0; 
  outformat[0] = '\0';
  state = 0;

  //     Set up the stacks
  // 
  leftarg [0] = 0.0;
  rightarg = 0.0;
  opstack [0] = '\0';
  validstack [0] = 0;
  
  //  initialization done... begin the work.
  if (internal_trace)
    fprintf (stderr, "Starting Algebraic Math on '%s' (len %ld)\n",
	     buf, inlen);
  
  for (ip = 0; ip < inlen; ip++)
    {
      
      //   Debugging trace
      if (internal_trace)
	fprintf (stderr, 
		 "ip = %ld, sp = %ld, L=%f, Op=%c, R=%f, V=%x next='%c'\n",
		 ip, sp, 
		 leftarg[sp], (short) opstack[sp], 
		 rightarg, (short) validstack[sp],
		 buf[ip]);
      
      //    Top of the loop- we're a state machine driven by the top of
      //    the stack's validity.
      
      if (sp >= DEFAULT_MATHSTK_LIMIT)
        {
          errstat = nonfatalerror ("Stack Overflow in math evaluation. ",
			    "CRM114 Barbie says 'This math is too hard'.");
	  if (retstat) *retstat = 0;
          return (0);
        };

      switch (validstack[sp])
	{
	case (0):
	  //  empty top of stack; can accept either number or monadic operator
	  if (internal_trace)
	    fprintf (stderr, "stacktop empty\n");

	  switch (buf[ip])
	    {
	      //   Monadic operators and numbers
	    case '-':
	    case '+':
	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
	    case '.':
	    case ',':     // for those locales that use , not . as decimal
	      {
		if (internal_trace)
		  fprintf (stderr, "found left numeric\n");

		leftarg[sp] = strtod (&buf[ip], &frejected);
		if (user_trace) 
		  fprintf (stderr, " Got left arg %e\n", leftarg[sp]);
		ip = ((unsigned long) frejected) - ((unsigned long) buf) - 1;
		validstack[sp] = LEFTVALID;
	      };
	      break;
	    case '(':
	      {
		if (internal_trace)
		  fprintf (stderr, 
			   "Open Paren - start new math stack level\n");
		sp++;
		leftarg[sp] = 0.0;
		rightarg = 0.0;
		opstack[sp] = 0;
		validstack[sp] = 0;
	      }
	      break;
	      //      deal with a possible rightarg strtod situation
	    case ' ':
	      break;
	    default:
	      errstat = nonfatalerror ("Math expression makes no sense",
				       " (need to have a number here).");
	      if (retstat) *retstat = 0;
	      return (0);
	      break;	
	    };
	  break;
	  
	  //  if left arg is valid; next thing must be an operator;
	  //   however op then op is also valid and should form composite
	  //    operators like '>=' and '!=' (see below).

	case (LEFTVALID):
	  if (internal_trace)
	    fprintf (stderr, "leftvalid\n");
	  switch (buf[ip])
	    {
	    case '-':
	    case '+':
	    case '*':
	    case '/':
	    case '%':
	    case '>':
	    case '<':
	    case '=':
	    case '!':
	    case '^':
	    case 'v':
	    case 'e':
	    case 'E':
	    case 'f':
	    case 'F':
	    case 'g':
	    case 'G':
	    case 'x':
	    case 'X':
	      {
		if (internal_trace)
		  fprintf (stderr, "found op\n");
		opstack[sp] = ( buf[ip] & 0xFF );	
		validstack[sp] = LEFTVALID | OPVALID;
		//   is the next char also an op?  If so, gobble it up?
		switch (buf[ip+1])
		  {
		  case '=':
		    if (internal_trace)
		      fprintf (stderr, "two-char operator\n");
		    opstack[sp] = ((opstack[sp] << 8) | buf[ip+1]);
		    ip++;
		  };
	      };
	      break;
	    case ')':
	      //   close paren pops the stack, and returns the left arg
	      //   to "whereever", which might be leftarg stack, or rightarg
	      if (internal_trace)
		fprintf (stderr, "close parenthesis, pop stack down\n");
	      sp--;
	      if (validstack[sp] == (LEFTVALID | OPVALID))
		{
		  rightarg = leftarg [sp+1];
		  validstack[sp] = LEFTVALID | OPVALID | RIGHTVALID;
		}
	      else
		{
		  leftarg[sp] = leftarg [sp+1];
		  validstack[sp] = LEFTVALID;
		};
	      break;
	    case ' ':
	      break;
	    default:
	      errstat = nonfatalerror ("Math needs an operator in: ",
				       buf);
	      if (retstat) *retstat = 0;
	      return (0);
	      break;
	    }
	  break;
	  
	case (LEFTVALID | OPVALID):
	  //  left arg and op are both valid; right now we can have
	  //   an enhanced operator (next char is also an op)
	  if (internal_trace)
	    fprintf (stderr, "left + opvalid \n");
	  switch (buf[ip])
	    {
	    case '(':
	      {
		if (internal_trace)
		  fprintf (stderr, 
			   "Open Paren - start new math stack level\n");
		sp++;
		leftarg[sp] = 0.0;
		rightarg = 0.0;
		opstack[sp] = 0;
		validstack[sp] = 0;
	      }
	      break;
	      //      deal with a possible rightarg strtod situation
	    case '-':
	    case '+':
	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
	    case '.':
	    case ',':
	      {
		rightarg = strtod (&buf[ip], &frejected);
		if (internal_trace) 
		  fprintf (stderr, " Got right arg %e\n", rightarg);
		ip = ((unsigned long) frejected) - ((unsigned long) buf) - 1;
		validstack[sp] = validstack[sp] | RIGHTVALID;
	      };
	    case ' ':
	      break;
	    default:
	      errstat = nonfatalerror ("Math is missing a number in: ",
				       buf);
	      if (retstat) *retstat = 0;
	      return (0);
	      break;
	    };
	};
      
      //////////////////////////////////////////////////
      //
      //   Now if we have a left-op-right situation, and can 
      //    execute the operator right here and now.
      //
      while (validstack[sp] == (LEFTVALID | OPVALID | RIGHTVALID) )
	{
	  if (internal_trace)
	    fprintf (stderr, "Executing %c operator\n", (short)opstack[sp]);
	  switch (opstack[sp])
	    {
	      //    Math operators
	    case '+':
	      leftarg[sp] = leftarg[sp] + rightarg;
	      break;
	    case '-':
	      leftarg[sp] = leftarg[sp] - rightarg;
	      break;
	    case '*':
	      leftarg[sp] = leftarg[sp] * rightarg;
	      break;
	    case '/':
	      leftarg[sp] = leftarg[sp] / rightarg;
	      break;
	    case '%':
	      leftarg[sp] = (long long) leftarg[sp] % (long long) rightarg;
	      break;
	    case '^':
	      //    since we don't do complex numbers (yet) handle as NaN
	      if (leftarg[sp] < 0.0 \
		  && ((long long) (rightarg))/1 != rightarg) 
		{ leftarg[sp] = leftarg[sp] / 0.0;}
	      else
		leftarg[sp] = pow (leftarg[sp], rightarg);
	      if (internal_trace)
	        fprintf (stderr, "exp out: %lf\n", leftarg[sp]);
	      break;
	    case 'v': //   Logarithm  BASE v ARG
	      //      Negative bases on logarithms?  Not for us!  force NaN
	      if (leftarg[sp] <= 0)
		{ leftarg[sp] = leftarg[sp] / 0.0;}
	      else
		leftarg[sp] = log (rightarg) / log (leftarg[sp]);
	      break;
	      //      Relational operators
	    case '<':
	      if (leftarg[sp] < rightarg)
		{ leftarg[sp] = 1;
		  state = 0;}
	      else
		{ leftarg[sp] = 0;
		  state = 1;};
	      break;
	    case '>':
	      if (leftarg[sp] > rightarg)
		{ leftarg[sp] = 1;
		  state = 0;}
	      else
		{ leftarg[sp] = 0;
		  state = 1;};
	      break;
	    case '=':
	      if (leftarg[sp] == rightarg)
		{ leftarg[sp] = 1;
 		  state = 0; }
	      else
		{ leftarg[sp] = 0;
		  state = 1;};
	      break;
	    case (('<' << 8) + '='):
	      if (leftarg[sp] <= rightarg)
		{ leftarg[sp] = 1;
		  state = 0;}
	      else 
		{ leftarg[sp] = 0;
		  state = 1;};
	      break;
	    case (('>' << 8) + '='):
	      if (leftarg[sp] >= rightarg)
		{ leftarg[sp] = 1;
		  state = 0;}
	      else
		{ leftarg[sp] = 0;
		  state = 1;};
	      break;
	    case ( ('!' << 8) + '='):
	      if (leftarg[sp] != rightarg)
		{ leftarg[sp] = 1;
		  state = 0;}
	      else
		{ leftarg[sp] = 0;
		  state = 1;};
	      break;
	      //           Formatting operators
	    case 'e':
	    case 'E':
	    case 'f':
	    case 'F':
	    case 'g':
	    case 'G':
	    case 'x':
	    case 'X':
	      {
		char tempstring [2048];
		if (internal_trace)
		  fprintf (stderr, "Formatting operator %c \n", 
			   (short)opstack[sp]);
		//     Do we have a float or an int format?
		if (opstack[sp] == 'x' || opstack[sp] == 'X')
		  {
		    snprintf (outformat, 255, "%%%g.0ll%c",
			     rightarg, (short) opstack[sp]);
		  }
		else 
		  {
		    if (((long) rightarg) / 1 == rightarg)
		      {
			snprintf (outformat, 255, "%%%g.0%c",
				 rightarg, (short) opstack[sp]);
		      }
		    else
		      {
			snprintf (outformat, 255, "%%%g%c", 
				 rightarg, (short)opstack[sp]);
		      };
		  };
		if (internal_trace)
		  fprintf (stderr, "Format string -->%s<-- \n", outformat);
		
		//      A little more funny business needed for
		//      hexadecimal print out, because X format
		//      can't take IEEE floating point as inputs.
		
		if (opstack[sp] != 'x' &&
		    opstack[sp] != 'X')
		  {
		    if (internal_trace)
		      fprintf (stderr, "Normal convert ");
		    snprintf (tempstring, 2047, outformat, leftarg[sp] );
		    leftarg[sp] = strtod (tempstring, NULL);
		    validstack[sp] = LEFTVALID;
		  }
		else
		  {
		    //    Note that we actually don't use the
		    //    results of octal conversion; the only
		    //    effect is to set the final format
		    //    string.
		    long long equiv;
		    if (internal_trace)
		      fprintf (stderr, "Oct/Hex Convert ");
		    equiv = leftarg[sp] + 0.0;
		    if (internal_trace)
		      fprintf (stderr, "equiv -->%10lld<-- \n", equiv);
		    snprintf (tempstring, 2047, outformat, equiv);
		  };
	      };
	      break;
	    default:	      	      
              errstat = nonfatalerror ("Math operator makes no sense in: ",
                                       buf);
	      if (retstat) *retstat = 0;
	      return (0);
	      break;
	    };
	  validstack[sp] = LEFTVALID;
	};
      
      //   Check to see that the stack is still valid.
      if (sp < 0)
        {
          errstat = nonfatalerror ("Too many close parenthesis in this math: ",
				   buf);
	  if (retstat) *retstat = 0;
          return (0);
        };
      
      
    };
  //      We made it all the way through.  Now return the math formatter result
  if (internal_trace)
    fprintf (stderr, "Returning at sp= %ld and value %lf\n", sp, leftarg[sp]);
  if (retstat) *retstat = state;

  //      Check that we made it all the way down the stack  
  if (sp != 0)
    {
      errstat = nonfatalerror ("Not enough close parenthesis in this math: ",
			       buf);
      if (retstat) *retstat = 0;
      return (0);
    }

  //    All's good, return with a value.
  {
    long return_length;
    return_length = (math_formatter (leftarg[sp], outformat, buf, maxlen ));
    outformat [return_length] = '\000';
    return (return_length);
  };
}      



syntax highlighted by Code2HTML, v. 0.9.1