/* * Copyright (c) 1995 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Configuration routines. */ #include "calc.h" #include "token.h" #include "zrand.h" /* * Table of configuration types that can be set or read. */ NAMETYPE configs[] = { {"all", CONFIG_ALL}, {"mode", CONFIG_MODE}, {"display", CONFIG_DISPLAY}, {"epsilon", CONFIG_EPSILON}, /*epsilonprec -- tied to epsilon not a configuration type*/ {"trace", CONFIG_TRACE}, {"maxprint", CONFIG_MAXPRINT}, {"mul2", CONFIG_MUL2}, {"sq2", CONFIG_SQ2}, {"pow2", CONFIG_POW2}, {"redc2", CONFIG_REDC2}, {"tilde", CONFIG_TILDE}, {"tab", CONFIG_TAB}, {"quomod", CONFIG_QUOMOD}, {"quo", CONFIG_QUO}, {"mod", CONFIG_MOD}, {"sqrt", CONFIG_SQRT}, {"appr", CONFIG_APPR}, {"cfappr", CONFIG_CFAPPR}, {"cfsim", CONFIG_CFSIM}, {"outround", CONFIG_OUTROUND}, {"round", CONFIG_ROUND}, {"leadzero", CONFIG_LEADZERO}, {"fullzero", CONFIG_FULLZERO}, {"maxerr", CONFIG_MAXERR}, {"prompt", CONFIG_PROMPT}, {"more", CONFIG_MORE}, {"random", CONFIG_RANDOM}, {NULL, 0} }; /* * configurations */ CONFIG oldstd = { /* backward compatible standard configuration */ MODE_INITIAL, /* current output mode */ 20, /* current output digits for float or exp */ NULL, /* loaded in at startup - default error for real functions */ EPSILONPREC_DEFAULT, /* binary precision of epsilon */ FALSE, /* tracing flags */ MAXPRINT_DEFAULT, /* number of elements to print */ MUL_ALG2, /* size of number to use multiply alg 2 */ SQ_ALG2, /* size of number to use square alg 2 */ POW_ALG2, /* size of modulus to use REDC for powers */ REDC_ALG2, /* size of modulus to use REDC algorithm 2 */ TRUE, /* ok to print a tilde on aproximations */ TRUE, /* ok to print tab before numeric values */ 0, /* quomod() default rounding mode */ 2, /* quotent // default rounding mode */ 0, /* mod % default rounding mode */ 24, /* sqrt() default rounding mode */ 24, /* appr() default rounding mode */ 0, /* cfappr() default rounding mode */ 8, /* cfsim() default rounding mode */ 2, /* output default rounding mode */ 24, /* round()/bround() default rounding mode */ FALSE, /* ok to print leading 0 before decimal pt */ 0, /* ok to print trailing 0's */ MAXERRORCOUNT, /* max errors before abort */ PROMPT1, /* normal prompt */ PROMPT2, /* prompt when inside multi-line input */ 3 /* require 1 mod 4 and to pass ptest(newn,1) */ }; CONFIG newstd = { /* new non-backward compatible configuration */ MODE_INITIAL, /* current output mode */ 10, /* current output digits for float or exp */ NULL, /* loaded in at startup - default error for real functions */ NEW_EPSILONPREC_DEFAULT, /* binary precision of epsilon */ FALSE, /* tracing flags */ MAXPRINT_DEFAULT, /* number of elements to print */ MUL_ALG2, /* size of number to use multiply alg 2 */ SQ_ALG2, /* size of number to use square alg 2 */ POW_ALG2, /* size of modulus to use REDC for powers */ REDC_ALG2, /* size of modulus to use REDC algorithm 2 */ TRUE, /* ok to print a tilde on aproximations */ TRUE, /* ok to print tab before numeric values */ 0, /* quomod() default rounding mode */ 0, /* quotent // default rounding mode */ 0, /* mod % default rounding mode */ 24, /* sqrt() default rounding mode */ 24, /* appr() default rounding mode */ 0, /* cfappr() default rounding mode */ 8, /* cfsim() default rounding mode */ 24, /* output default rounding mode */ 24, /* round()/bround() default rounding mode */ TRUE, /* ok to print leading 0 before decimal pt */ 1, /* ok to print trailing 0's */ MAXERRORCOUNT, /* max errors before abort */ "; ", /* normal prompt */ ";; ", /* prompt when inside multi-line input */ 3 /* require 1 mod 4 and to pass ptest(newn,1) */ }; CONFIG *conf = NULL; /* loaded in at startup - current configuration */ /* * Possible output modes. */ static NAMETYPE modes[] = { {"frac", MODE_FRAC}, {"decimal", MODE_FRAC}, {"dec", MODE_FRAC}, {"int", MODE_INT}, {"real", MODE_REAL}, {"exp", MODE_EXP}, {"hexadecimal", MODE_HEX}, {"hex", MODE_HEX}, {"octal", MODE_OCTAL}, {"oct", MODE_OCTAL}, {"binary", MODE_BINARY}, {"bin", MODE_BINARY}, {NULL, 0} }; /* * Possible binary config state values */ static NAMETYPE truth[] = { {"y", TRUE}, {"n", FALSE}, {"yes", TRUE}, {"no", FALSE}, {"set", TRUE}, {"unset", FALSE}, {"on", TRUE}, {"off", FALSE}, {"true", TRUE}, {"false", FALSE}, {"t", TRUE}, {"f", FALSE}, {"1", TRUE}, {"0", FALSE}, {NULL, 0} }; /* * declate static functions */ static int modetype(char *name); static int truthtype(char *name); static char *modename(int type); /* * Given a string value which represents a configuration name, return * the configuration type for that string. Returns negative type if * the string is unknown. * * given: * name configuration name */ int configtype(char *name) { NAMETYPE *cp; /* current config pointer */ for (cp = configs; cp->name; cp++) { if (strcmp(cp->name, name) == 0) return cp->type; } return -1; } /* * Given the name of a mode, convert it to the internal format. * Returns -1 if the string is unknown. * * given: * name mode name */ static int modetype(char *name) { NAMETYPE *cp; /* current config pointer */ for (cp = modes; cp->name; cp++) { if (strcmp(cp->name, name) == 0) return cp->type; } return -1; } /* * Given the name of a truth value, convert it to a BOOL or -1. * Returns -1 if the string is unknown. * * given: * name mode name */ static int truthtype(char *name) { NAMETYPE *cp; /* current config pointer */ for (cp = truth; cp->name; cp++) { if (strcmp(cp->name, name) == 0) return cp->type; } return -1; } /* * Given the mode type, convert it to a string representing that mode. * Where there are multiple strings representing the same mode, the first * one in the table is used. Returns NULL if the node type is unknown. * The returned string cannot be modified. */ static char * modename(int type) { NAMETYPE *cp; /* current config pointer */ for (cp = modes; cp->name; cp++) { if (type == cp->type) return cp->name; } return NULL; } /* * Set the specified configuration type to the specified value. * An error is generated if the type number or value is illegal. */ void setconfig(int type, VALUE *vp) { NUMBER *q; CONFIG *newconf; /* new configuration to set */ long temp; char *p; switch (type) { case CONFIG_ALL: newconf = NULL; /* firewall */ if (vp->v_type == V_STR) { if (strcmp(vp->v_str, "oldstd") == 0) { newconf = &oldstd; } else if (strcmp(vp->v_str, "newstd") == 0) { newconf = &newstd; } else { math_error("CONFIG alias not oldstd or newstd"); /*NOTREACHED*/ } } else if (vp->v_type != V_CONFIG) { math_error("non-CONFIG for all"); /*NOTREACHED*/ } else { newconf = vp->v_config; } /* free the current configuration */ config_free(conf); /* set the new configuration */ conf = config_copy(newconf); break; case CONFIG_TRACE: if (vp->v_type != V_NUM) { math_error("Non-numeric for trace"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || !zistiny(q->num) || ((unsigned long) temp > TRACE_MAX)) { math_error("Bad trace value"); /*NOTREACHED*/ } conf->traceflags = (FLAG)temp; break; case CONFIG_DISPLAY: if (vp->v_type != V_NUM) { math_error("Non-numeric for display"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) temp = -1; math_setdigits(temp); break; case CONFIG_MODE: if (vp->v_type != V_STR) { math_error("Non-string for mode"); /*NOTREACHED*/ } temp = modetype(vp->v_str); if (temp < 0) { math_error("Unknown mode \"%s\"", vp->v_str); /*NOTREACHED*/ } math_setmode((int) temp); break; case CONFIG_EPSILON: if (vp->v_type != V_NUM) { math_error("Non-numeric for epsilon"); /*NOTREACHED*/ } setepsilon(vp->v_num); break; case CONFIG_MAXPRINT: if (vp->v_type != V_NUM) { math_error("Non-numeric for maxprint"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) temp = -1; if (temp < 0) { math_error("Maxprint value is out of range"); /*NOTREACHED*/ } conf->maxprint = temp; break; case CONFIG_MUL2: if (vp->v_type != V_NUM) { math_error("Non-numeric for mul2"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q)) temp = -1; if (temp == 0) temp = MUL_ALG2; if (temp < 2) { math_error("Illegal mul2 value"); /*NOTREACHED*/ } conf->mul2 = (int)temp; break; case CONFIG_SQ2: if (vp->v_type != V_NUM) { math_error("Non-numeric for sq2"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q)) temp = -1; if (temp == 0) temp = SQ_ALG2; if (temp < 2) { math_error("Illegal sq2 value"); /*NOTREACHED*/ } conf->sq2 = (int)temp; break; case CONFIG_POW2: if (vp->v_type != V_NUM) { math_error("Non-numeric for pow2"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q)) temp = -1; if (temp == 0) temp = POW_ALG2; if (temp < 1) { math_error("Illegal pow2 value"); /*NOTREACHED*/ } conf->pow2 = (int)temp; break; case CONFIG_REDC2: if (vp->v_type != V_NUM) { math_error("Non-numeric for redc2"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q)) temp = -1; if (temp == 0) temp = REDC_ALG2; if (temp < 1) { math_error("Illegal redc2 value"); /*NOTREACHED*/ } conf->redc2 = (int)temp; break; case CONFIG_TILDE: if (vp->v_type == V_NUM) { q = vp->v_num; conf->tilde_ok = !qiszero(q); } else if (vp->v_type == V_STR) { temp = truthtype(vp->v_str); if (temp < 0) { math_error("Illegal truth value"); /*NOTREACHED*/ } conf->tilde_ok = (int)temp; } break; case CONFIG_TAB: if (vp->v_type == V_NUM) { q = vp->v_num; conf->tab_ok = !qiszero(q); } else if (vp->v_type == V_STR) { temp = truthtype(vp->v_str); if (temp < 0) { math_error("Illegal truth value"); /*NOTREACHED*/ } conf->tab_ok = (int)temp; } break; case CONFIG_QUOMOD: if (vp->v_type != V_NUM) { math_error("Non numeric for quomod"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal quomod parameter value"); /*NOTREACHED*/ } conf->quomod = temp; break; case CONFIG_QUO: if (vp->v_type != V_NUM) { math_error("Non numeric for quo"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal quo parameter value"); /*NOTREACHED*/ } conf->quo = temp; break; case CONFIG_MOD: if (vp->v_type != V_NUM) { math_error("Non numeric for mod"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal mod parameter value"); /*NOTREACHED*/ } conf->mod = temp; break; case CONFIG_SQRT: if (vp->v_type != V_NUM) { math_error("Non numeric for sqrt"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal sqrt parameter value"); /*NOTREACHED*/ } conf->sqrt = temp; break; case CONFIG_APPR: if (vp->v_type != V_NUM) { math_error("Non numeric for appr"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal appr parameter value"); /*NOTREACHED*/ } conf->appr = temp; break; case CONFIG_CFAPPR: if (vp->v_type != V_NUM) { math_error("Non numeric for cfappr"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal cfappr parameter value"); /*NOTREACHED*/ } conf->cfappr = temp; break; case CONFIG_CFSIM: if (vp->v_type != V_NUM) { math_error("Non numeric for cfsim"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal cfsim parameter value"); /*NOTREACHED*/ } conf->cfsim = temp; break; case CONFIG_OUTROUND: if (vp->v_type != V_NUM) { math_error("Non numeric for outround"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal output parameter value"); /*NOTREACHED*/ } conf->outround = temp; break; case CONFIG_ROUND: if (vp->v_type != V_NUM) { math_error("Non numeric for round"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) { math_error("Illegal output parameter value"); /*NOTREACHED*/ } conf->round = temp; break; case CONFIG_LEADZERO: if (vp->v_type == V_NUM) { q = vp->v_num; conf->leadzero = !qiszero(q); } else if (vp->v_type == V_STR) { temp = truthtype(vp->v_str); if (temp < 0) { { math_error("Illegal truth value"); /*NOTREACHED*/ } } conf->leadzero = (int)temp; } break; case CONFIG_FULLZERO: if (vp->v_type == V_NUM) { q = vp->v_num; conf->fullzero = !qiszero(q); } else if (vp->v_type == V_STR) { temp = truthtype(vp->v_str); if (temp < 0) { { math_error("Illegal truth value"); /*NOTREACHED*/ } } conf->fullzero = (int)temp; } break; case CONFIG_MAXERR: if (vp->v_type != V_NUM) { math_error("Non-numeric for maxerr"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) temp = -1; if (temp < 0) { math_error("Maxerr value is out of range"); /*NOTREACHED*/ } conf->maxerrorcount = temp; break; case CONFIG_PROMPT: if (vp->v_type != V_STR) { math_error("Non-string for prompt"); /*NOTREACHED*/ } p = (char *)malloc(strlen(vp->v_str) + 1); if (p == NULL) { math_error("Cannot duplicate new prompt"); /*NOTREACHED*/ } strcpy(p, vp->v_str); free(conf->prompt1); conf->prompt1 = p; break; case CONFIG_MORE: if (vp->v_type != V_STR) { math_error("Non-string for more prompt"); /*NOTREACHED*/ } p = (char *)malloc(strlen(vp->v_str) + 1); if (p == NULL) { math_error("Cannot duplicate new more prompt"); /*NOTREACHED*/ } strcpy(p, vp->v_str); free(conf->prompt2); conf->prompt2 = p; break; case CONFIG_RANDOM: if (vp->v_type != V_NUM) { math_error("Non-numeric for random config value"); /*NOTREACHED*/ } q = vp->v_num; temp = qtoi(q); if (qisfrac(q) || qisneg(q) || !zistiny(q->num)) temp = -1; if (temp < BLUM_CFG_MIN || temp > BLUM_CFG_MAX) { math_error("Random config value is out of range"); /*NOTREACHED*/ } conf->random = temp; break; default: math_error("Setting illegal config parameter"); /*NOTREACHED*/ } } /* * config_copy - copy the configuration from one value to another * * given: * src copy this configuration * * returns: * prointer to the configuration copy */ CONFIG * config_copy(CONFIG *src) { CONFIG *dest; /* the new CONFIG to return */ /* * firewall */ if (src == NULL || src->epsilon == NULL || src->prompt1 == NULL || src->prompt2 == NULL) { math_error("bad CONFIG value"); /*NOTREACHED*/ } /* * malloc the storage */ dest = (CONFIG *)malloc(sizeof(CONFIG)); if (dest == NULL) { math_error("malloc of CONFIG failed"); /*NOTREACHED*/ } /* * copy over the values */ *dest = *src; /* * clone the pointer values */ dest->epsilon = qlink(src->epsilon); dest->prompt1 = (char *)malloc(strlen(src->prompt1)+1); if (dest->prompt1 == NULL) { math_error("cannot dup prompt1 for new CONFIG value"); /*NOTREACHED*/ } strcpy(dest->prompt1, src->prompt1); dest->prompt2 = (char *)malloc(strlen(src->prompt2)+1); if (dest->prompt2 == NULL) { math_error("cannot dup prompt2 for new CONFIG value"); /*NOTREACHED*/ } strcpy(dest->prompt2, src->prompt2); /* * return the new value */ return dest; } /* * config_free - free a CONFIG value * * given: * cfg the CONFIG value to free */ void config_free(CONFIG *cfg) { /* * firewall */ if (cfg == NULL) { return; } /* * free prointer values */ if (cfg->epsilon != NULL) { qfree(cfg->epsilon); } if (cfg->prompt1 != NULL) { free(cfg->prompt1); } if (cfg->prompt2 != NULL) { free(cfg->prompt2); } /* * free the CONFIG value itself */ free(cfg); return; } /* * config_value - return a CONFIG element as a value * * given: * cfg CONFIG from which an element will be returned * type the type of CONFIG element to print * ret where to return the value * * returns: * ret points to the VALUE returned */ void config_value(CONFIG *cfg, int type, VALUE *vp) { long i=0; /* * firewall */ if (cfg == NULL || cfg->epsilon == NULL || cfg->prompt1 == NULL || cfg->prompt2 == NULL) { math_error("bad CONFIG value"); /*NOTREACHED*/ } /* * convert element to value */ vp->v_type = V_NUM; switch (type) { case CONFIG_ALL: vp->v_type = V_CONFIG; vp->v_config = config_copy(conf); return; case CONFIG_TRACE: i = cfg->traceflags; break; case CONFIG_DISPLAY: i = cfg->outdigits; break; case CONFIG_MODE: vp->v_type = V_STR; vp->v_subtype = V_STRLITERAL; vp->v_str = modename(cfg->outmode); return; case CONFIG_EPSILON: vp->v_num = qlink(cfg->epsilon); return; case CONFIG_MAXPRINT: i = cfg->maxprint; break; case CONFIG_MUL2: i = cfg->mul2; break; case CONFIG_SQ2: i = cfg->sq2; break; case CONFIG_POW2: i = cfg->pow2; break; case CONFIG_REDC2: i = cfg->redc2; break; case CONFIG_TILDE: i = cfg->tilde_ok; break; case CONFIG_TAB: i = cfg->tab_ok; break; case CONFIG_QUOMOD: i = cfg->quomod; break; case CONFIG_QUO: i = cfg->quo; break; case CONFIG_MOD: i = cfg->mod; break; case CONFIG_SQRT: i = cfg->sqrt; break; case CONFIG_APPR: i = cfg->appr; break; case CONFIG_CFAPPR: i = cfg->cfappr; break; case CONFIG_CFSIM: i = cfg->cfsim; break; case CONFIG_OUTROUND: i = cfg->outround; break; case CONFIG_ROUND: i = cfg->round; break; case CONFIG_LEADZERO: i = cfg->leadzero; break; case CONFIG_FULLZERO: i = cfg->fullzero; break; case CONFIG_MAXERR: i = cfg->maxerrorcount; break; case CONFIG_PROMPT: vp->v_type = V_STR; vp->v_subtype = V_STRLITERAL; vp->v_str = cfg->prompt1; return; case CONFIG_MORE: vp->v_type = V_STR; vp->v_subtype = V_STRLITERAL; vp->v_str = cfg->prompt2; return; case CONFIG_RANDOM: i = cfg->random; break; default: math_error("Getting illegal CONFIG element"); /*NOTREACHED*/ } /* * if we got this far, we have a V_NUM in i */ vp->v_num = itoq(i); return; } /* * config_cmp - compare two CONFIG states * * given: * cfg1 - 1st CONFIG to compare * cfg2 - 2nd CONFIG to compare * * return: * TRUE if configurations differ */ BOOL config_cmp(CONFIG *cfg1, CONFIG *cfg2) { /* * firewall */ if (cfg1 == NULL || cfg1->epsilon == NULL || cfg1->prompt1 == NULL || cfg1->prompt2 == NULL) { math_error("CONFIG #1 value is invaid"); /*NOTREACHED*/ } if (cfg2 == NULL || cfg2->epsilon == NULL || cfg2->prompt1 == NULL || cfg2->prompt2 == NULL) { math_error("CONFIG #2 value is invaid"); /*NOTREACHED*/ } /* * compare */ return cfg1->traceflags != cfg2->traceflags || cfg1->outdigits != cfg2->outdigits || cfg1->outmode != cfg2->outmode || qcmp(cfg1->epsilon, cfg2->epsilon) || cfg1->epsilonprec != cfg2->epsilonprec || cfg1->maxprint != cfg2->maxprint || cfg1->mul2 != cfg2->mul2 || cfg1->sq2 != cfg2->sq2 || cfg1->pow2 != cfg2->pow2 || cfg1->redc2 != cfg2->redc2 || cfg1->tilde_ok != cfg2->tilde_ok || cfg1->tab_ok != cfg2->tab_ok || cfg1->quomod != cfg2->quomod || cfg1->quo != cfg2->quo || cfg1->mod != cfg2->mod || cfg1->sqrt != cfg2->sqrt || cfg1->appr != cfg2->appr || cfg1->cfappr != cfg2->cfappr || cfg1->cfsim != cfg2->cfsim || cfg1->outround != cfg2->outround || cfg1->round != cfg2->round || cfg1->leadzero != cfg2->leadzero || cfg1->fullzero != cfg2->fullzero || cfg1->maxerrorcount != cfg2->maxerrorcount || strcmp(cfg1->prompt1, cfg2->prompt1) != 0 || strcmp(cfg1->prompt2, cfg2->prompt2) != 0 || cfg1->random != cfg2->random; }