// crm_math_exec.c - Controllable Regex Mutilator, version v1.0 // Copyright 2001-2004 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; // // strmath - evaluate a string for the mathematical result // long strmath (char *buf, long inlen, long maxlen, long *retstat) { 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 // 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. // 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", "This math is too complex"); return (0); }; switch (buf[ip]) { // // a digit - for now, that's a 10x "look" + the digit value // case '0': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 0; } else { stack[sp] = stack[sp] + 0 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '1': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 1; } else { stack[sp] = stack[sp] + 1 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '2': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 2; } else { stack[sp] = stack[sp] + 2 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '3': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 3; } else { stack[sp] = stack[sp] + 3 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '4': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 4; } else { stack[sp] = stack[sp] + 4 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '5': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 5; } else { stack[sp] = stack[sp] + 5 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '6': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 6; } else { stack[sp] = stack[sp] + 6 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '7': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 7; } else { stack[sp] = stack[sp] + 7 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '8': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 8; } else { stack[sp] = stack[sp] + 8 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '9': { if (sinc) { sp++; stack[sp] = 0.0; }; if (sd == 1.0) { stack[sp] = stack[sp] * 10 + 9; } else { stack[sp] = stack[sp] + 9 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '.': { if (sinc) { sp++; stack[sp] = 0.0; }; sd = sd / 10.0; sinc = 0; }; break; // // and some basic math ops... // case '+': { if (sp > 0) { sp--; stack[sp] = stack[sp] + stack[sp+1]; sinc = 1; } }; break; case '-': { if (sp > 0) { sp--; stack[sp] = stack[sp] - stack[sp+1]; sinc = 1; } }; break; case '*': { if (sp > 0) { sp--; stack[sp] = stack[sp] * stack[sp+1]; sinc = 1; } }; break; case '/': { if (sp > 0) { sp--; if(stack[sp+1] != 0.0) { stack[sp] = stack[sp] / stack[sp+1]; sinc = 1; } else { if (retstat) *retstat = -1; nonfatalerror ("Attempt to divide by zero in this:", inbuf); } } }; break; case '%': { if (sp > 0) { sp--; stack[sp] = ((long) stack[sp]) % ((long)stack[sp+1]); 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) { 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) { 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 ' ': default: // // a space is just an end-of-number - push the number we're // seeing. { 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. // // print out 0 as 0 // if (stack[sp] == 0.0 ) { sprintf (buf, "0"); goto formatdone; } // // use E notation if bigger than 1 trillion // if (stack[sp] > 1000000000000.0 || stack[sp] < -1000000000000.0 ) { sprintf (buf, "%.5E", stack[sp]); goto formatdone; } // // use E notation if smaller than .0000000000001 // if (stack[sp] < 0.0000000000001 && stack[sp] > -0.0000000000001 ) { sprintf (buf, "%.5E", stack[sp]); goto formatdone; } // // if an integer value, print with 0 precision // if (((long)(stack[sp]*2.0) / 2) == stack[sp]) { sprintf (buf, "%.0F", stack[sp]); goto formatdone; } // // if none of the above, print with five digits precision // sprintf (buf, "%.5F", stack[sp]); // // // one way or another, once we're here, we've sprinted it. formatdone: return (strlen (buf)); } // // stralnmath - evaluate a mathematical expression in algebraic // (that is, infix parenthesized) notation. // // The algorithm is this: // see an open parenthesis - push an empty level // see a close parethesis - try to "reduce", then pop over the empty // see an operator - push it onto opstack, sp++ // see a number - push it, then try to "reduce" if there's a valid op. // // reduce: // while sp > 0 // if opstack[sp-1] valid // sp-- // execute opstack[sp] on [sp], [sp+1] // replace sp with result // // Note that the empty levels (opstack[sp] == '\000') that are // produced by open and close parens are how we prevent runaway // reduce operations on the stack. // long stralmath (char *buf, long inlen, long maxlen, long *retstat) { double valstack [DEFAULT_MATHSTK_LIMIT]; // the evaluation value stack char opstack [DEFAULT_MATHSTK_LIMIT]; // the evaluation operator 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 incrmenter - do we push on next digit in? long errstat; // error status // 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. // now our number-inputting hacks valstack[sp] = 0.0 ; opstack[sp] = '\000'; 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, valstack[sp] = %f," "opstack = '%c', h='%c'\n", ip, sp, valstack[sp], opstack[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", "This math is too complex"); return (0); }; switch (buf[ip]) { // // a digit - for now, that's a 10x "look" + the digit value // case '0': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000'; }; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 0; } else { valstack[sp] = valstack[sp] + 0 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '1': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 1; } else { valstack[sp] = valstack[sp] + 1 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '2': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 2; } else { valstack[sp] = valstack[sp] + 2 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '3': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 3; } else { valstack[sp] = valstack[sp] + 3 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '4': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 4; } else { valstack[sp] = valstack[sp] + 4 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '5': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 5; } else { valstack[sp] = valstack[sp] + 5 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '6': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 6; } else { valstack[sp] = valstack[sp] + 6 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '7': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 7; } else { valstack[sp] = valstack[sp] + 7 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '8': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 8; } else { valstack[sp] = valstack[sp] + 8 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '9': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; if (sd == 1.0) { valstack[sp] = valstack[sp] * 10 + 9; } else { valstack[sp] = valstack[sp] + 9 * sd; }; if (sd < 1.0) sd = sd / 10.0; sinc = 0; }; break; case '.': { if (sinc) { sp++; valstack[sp] = 0.0; opstack[sp] = '\000';}; sd = sd / 10.0; sinc = 0; }; break; // // and now the parenthesis ops // // open paren- we start a new stack level case '(': { sd = 1.0; sp++; valstack[sp] = 0.0; opstack[sp] = '\000'; }; break; // // close paren- we finish evaluation down to the open, // then supply the result to the next lower level. case ')': { long state; if (sp > 0) sp--; state = stralmath_reduce (valstack, opstack, &sp); if (retstat) *retstat = state; if (sp > 0) sp--; // and get rid of that extra level we inserted before. valstack[sp] = valstack[sp+1]; }; break; // // The operators just put themselves on the opstack. // case '+': case '-': case '*': case '/': case '%': case '=': case '>': case '<': { if (sp > 0 && opstack[sp-1] != '\000') { long state; if (sp > 0) sp--; state = stralmath_reduce (valstack, opstack, &sp); if (retstat) *retstat = state; }; sd = 1.0; sinc = 1; opstack[sp] = buf[ip]; if (opstack[sp] == ' ') opstack[sp] = '\000'; }; break; default: break; }; }; // Now do final executes.... if (sp > 0) { long state; sp--; state = stralmath_reduce (valstack, opstack, &sp); if (retstat) *retstat = state; }; if (internal_trace) { fprintf (stderr, "Final qexpand state: ip = %ld, sp = %ld, valstack[sp] = %f, ch='%c'\n", ip, sp, valstack[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. // // print out 0 as 0 // if (valstack[sp] == 0.0 ) { sprintf (buf, "0"); goto formatdone; } // // use E notation if bigger than 1 trillion // if (valstack[sp] > 1000000000000.0 || valstack[sp] < -1000000000000.0 ) { sprintf (buf, "%.5E", valstack[sp]); goto formatdone; } // // use E notation if smaller than .0000000000001 // if (valstack[sp] < 0.0000000000001 && valstack[sp] > -0.0000000000001 ) { sprintf (buf, "%.5E", valstack[sp]); goto formatdone; } // // if an integer value, print with 0 precision // if (((long)(valstack[sp]*2.0) / 2) == valstack[sp]) { sprintf (buf, "%.0F", valstack[sp]); goto formatdone; } // // if none of the above, print with three digits precision // sprintf (buf, "%.5F", valstack[sp]); // // one way or another, once we're here, we've sprinted it. formatdone: return (strlen (buf)); } // // stralmath_reduce - actually do the math for algebraic arithmetic // long stralmath_reduce (double *valstack, char *opstack, long *sp) { long retval; retval = 0; if (internal_trace) fprintf (stderr, " start: *sp = %3ld, " "vs[*sp] = %6.3f, vs[*sp+1] = %6.3f, " "op[*sp] = '%c'\n", *sp, valstack[*sp], valstack[*sp+1], opstack[*sp]); while (*sp >= 0 && opstack[*sp] != '\000') { if (internal_trace) fprintf (stderr, "running: *sp = %3ld, " "vs[*sp] = %6.3f, vs[*sp+1] = %6.3f, " "op[*sp] = '%c'\n", *sp, valstack[*sp], valstack[*sp+1], opstack[*sp]); switch (opstack[*sp]) { case '+': valstack[*sp] = valstack[*sp] + valstack[*sp+1]; opstack[*sp] = '\000'; break; case '-': valstack[*sp] = valstack[*sp] - valstack[*sp+1]; opstack[*sp] = '\000'; break; case '*': valstack[*sp] = valstack[*sp] * valstack[*sp+1]; opstack[*sp] = '\000'; break; case '/': if (valstack[*sp+1] == 0.0) { opstack[*sp+1] = '\000'; nonfatalerror ("Attempt to divide by zero.",""); return (-1); }; valstack[*sp] = valstack[*sp] / valstack[*sp+1]; opstack[*sp] = '\000'; break; case '%': valstack[*sp] = ((long)valstack[*sp]) % ((long) valstack[*sp+1]); opstack[*sp] = '\000'; break; case '=': if (valstack[*sp] == valstack[*sp+1]) { valstack[*sp] = 1 ; } else { valstack[*sp] = 0; } opstack[*sp] = '\000'; retval = 1 - valstack[*sp]; break; case '>': if (valstack[*sp] > valstack[*sp+1]) { valstack[*sp] = 1 ; } else { valstack[*sp] = 0; } opstack[*sp] = '\000'; retval = 1 - valstack[*sp]; break; case '<': if (valstack[*sp] < valstack[*sp+1]) { valstack[*sp] = 1 ; } else { valstack[*sp] = 0; } opstack[*sp] = '\000'; retval = 1 - valstack[*sp]; break; default: break; }; if (*sp > 0 && opstack[(*sp)-1] != '\000') *sp = *sp - 1; }; if (internal_trace) fprintf (stderr, " finish: *sp = %3ld, " "vs[*sp] = %6.3f, vs[*sp+1] = %6.3f, " "op[*sp] = '%c'\n", *sp, valstack[*sp], valstack[*sp+1], opstack[*sp]); return (retval); }