/* #includes */ /*{{{C}}}*//*{{{*/ #ifndef NO_POSIX_SOURCE #undef _POSIX_SOURCE #define _POSIX_SOURCE 1 #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 2 #endif #define _XOPEN_SOURCE /* glibc2 needs this */ #ifdef DMALLOC #include "dmalloc.h" #endif #include #include #include #include #include extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */ #include #include #include #include "default.h" #include "eval.h" #include "func.h" #include "main.h" #include "misc.h" #include "parser.h" #include "scanner.h" #include "sheet.h" /*}}}*/ /* #defines */ /*{{{*/ /* There is a BSD extensions, but they are possibly more exact. */ #ifdef M_E #define CONST_E M_E #else #define CONST_E ((double)2.7182818284590452354) #endif #ifdef M_PI #define CONST_PI M_PI #else #define CONST_PI ((double)3.14159265358979323846) #endif /*}}}*/ #ifdef WIN32 // This strptime implementation Copyright 2009 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Implement strptime under windows static const char* kWeekFull[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; static const char* kWeekAbbr[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char* kMonthFull[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static const char* kMonthAbbr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char* _parse_num(const char* s, int low, int high, int* value) { const char* p = s; for (*value = 0; *p && isdigit(*p); ++p) { *value = (*value) * 10 + *p - '0'; } if (p == s || *value < low || *value > high) return NULL; return p; } static char* _strptime(const char *s, const char *format, struct tm *tm) { int i, len; while (*format && *s) { if (*format != '%') { if (*s != *format) return NULL; ++format; ++s; continue; } ++format; len = 0; switch (*format) { // weekday name. case 'a': case 'A': tm->tm_wday = -1; for (i = 0; i < 7; ++i) { len = strlen(kWeekAbbr[i]); if (strnicmp(kWeekAbbr[i], s, len) == 0) { tm->tm_wday = i; break; } len = strlen(kWeekFull[i]); if (strnicmp(kWeekFull[i], s, len) == 0) { tm->tm_wday = i; break; } } if (tm->tm_wday == -1) return NULL; s += len; break; // month name. case 'b': case 'B': case 'h': tm->tm_mon = -1; for (i = 0; i < 12; ++i) { len = strlen(kMonthAbbr[i]); if (strnicmp(kMonthAbbr[i], s, len) == 0) { tm->tm_mon = i; break; } len = strlen(kMonthFull[i]); if (strnicmp(kMonthFull[i], s, len) == 0) { tm->tm_mon = i; break; } } if (tm->tm_mon == -1) return NULL; s += len; break; // month [1, 12]. case 'm': s = _parse_num(s, 1, 12, &tm->tm_mon); if (s == NULL) return NULL; --tm->tm_mon; break; // day [1, 31]. case 'd': case 'e': s = _parse_num(s, 1, 31, &tm->tm_mday); if (s == NULL) return NULL; break; // hour [0, 23]. case 'H': s = _parse_num(s, 0, 23, &tm->tm_hour); if (s == NULL) return NULL; break; // minute [0, 59] case 'M': s = _parse_num(s, 0, 59, &tm->tm_min); if (s == NULL) return NULL; break; // seconds [0, 60]. 60 is for leap year. case 'S': s = _parse_num(s, 0, 60, &tm->tm_sec); if (s == NULL) return NULL; break; // year [1900, 9999]. case 'Y': s = _parse_num(s, 1900, 9999, &tm->tm_year); if (s == NULL) return NULL; tm->tm_year -= 1900; break; // year [0, 99]. case 'y': s = _parse_num(s, 0, 99, &tm->tm_year); if (s == NULL) return NULL; if (tm->tm_year <= 68) { tm->tm_year += 100; } break; // arbitray whitespace. case 't': case 'n': while (isspace(*s)) ++s; break; // '%'. case '%': if (*s != '%') return NULL; ++s; break; // All the other format are not supported. default: return NULL; } ++format; } if (*format) { return NULL; } else { return (char *)s; } } char* strptime(const char *buf, const char *fmt, struct tm *tm) { return _strptime(buf, fmt, tm); } #endif /* sci_func -- map a double to a double */ /*{{{*/ static Token sci_func(int argc, const Token argv[], double (*func)(double), const char *func_name) { Token result; if (argc==1 && argv[0].type==FLOAT) { result.type=FLOAT; errno=0; result.u.flt=(func)(argv[0].u.flt); if (errno) { result.type=EEK; result.u.err=malloc(strlen(func_name)+2+strlen(strerror(errno))+1); sprintf(result.u.err,"%s: %s",func_name,strerror(errno)); } } else { if (argc==1 && argv[0].type==INT) { result.type=FLOAT; errno=0; result.u.flt=(func)((double)argv[0].u.integer); if (errno) { result.type=EEK; result.u.err=malloc(strlen(func_name)+2+strlen(strerror(errno))+1); sprintf(result.u.err,"%s: %s",func_name,strerror(errno)); } } else { result.type=EEK; /* This is actually too much, but always enough for %s formats. */ result.u.err=malloc(strlen(_("Usage: %s(float)"))+strlen(func_name)+1); sprintf(result.u.err,_("Usage: %s(float)"),func_name); } } return result; } /*}}}*/ /* arsinh */ /*{{{*/ static double arsinh(double x) { return log(x+sqrt(x*x+1.0)); } /*}}}*/ /* arcosh */ /*{{{*/ static double arcosh(double x) { if (x>=1.0) return log(x+sqrt(x*x-1.0)); else { errno=EDOM; return 0.0; } } /*}}}*/ /* artanh */ /*{{{*/ static double artanh(double x) { if (x>-1.0 && x<1.0) return 0.5*log((1.0+x)/(1.0-x)); else { errno=EDOM; return 0.0; } } /*}}}*/ /* rad2deg */ /*{{{*/ static double rad2deg(double x) { return (360.0/(2.0*CONST_PI))*x; } /*}}}*/ /* deg2rad */ /*{{{*/ static double deg2rad(double x) { return (2.0*CONST_PI/360.0)*x; } /*}}}*/ typedef enum {ABSOLUTE, RELATIVE} LocConvention; /* & */ /*{{{*/ static Token adr_func(int argc, const Token argv[], LocConvention lcon) { /* variables */ /*{{{*/ Token result; Dimensions dim; size_t i; /*}}}*/ /* asserts */ /*{{{*/ assert(argv != (Token*)0); /*}}}*/ LOCATION_GETS(result.u.location, upd_l); if (argc == 1 && argv[0].type == LOCATION) if (lcon == ABSOLUTE) return argv[0]; else { LOCATION_ADD(result.u.location, argv[0].u.location); return result; } for (i = 0; i < argc && i < HYPER; ++i) { if (argv[i].type == INT) if (lcon == ABSOLUTE) result.u.location[i] = argv[i].u.integer; else result.u.location[i] += argv[i].u.integer; else if (argv[i].type != EMPTY) break; } if (i < argc) { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: &([integer x][,[integer y][,[integer z]]])"))+1),_("Usage: &([integer x][,[integer y][,[integer z]]])")); } else result.type = LOCATION; return result; } /*}}}*/ /* @ */ /*{{{*/ static Token at_func(int argc, const Token argv[], LocConvention lcon) { Token location; location = adr_func(argc, argv, lcon); if (location.type == EEK) { Token result; result.type = EEK; char *pref = _("Inside @: "); result.u.err = malloc(strlen(location.u.err) + strlen(pref) + 1); strcpy(result.u.err, pref); strcat(result.u.err, location.u.err); tfree(&location); return result; } return getvalue(upd_sheet, location.u.location); } static Token abs_adr_func(int argc, const Token argv[]) { return adr_func(argc, argv, ABSOLUTE); } static Token abs_at_func(int argc, const Token argv[]) { return at_func(argc, argv, ABSOLUTE); } static Token rel_adr_func(int argc, const Token argv[]) { return adr_func(argc, argv, RELATIVE); } static Token rel_at_func(int argc, const Token argv[]) { return at_func(argc, argv, RELATIVE); } /* x */ /*{{{*/ static Token x_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==0) /* result is currently updated x position */ /*{{{*/ { result.type = INT; result.u.integer = upd_l[X]; } /*}}}*/ else if (argc==1 && argv[0].type==LOCATION) /* return x component of location */ /*{{{*/ { result.type = INT; result.u.integer = argv[0].u.location[X]; } /*}}}*/ else /* x type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: x([location])"))+1),_("Usage: x([location])")); } /*}}}*/ return result; } /*}}}*/ /* y */ /*{{{*/ static Token y_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==0) /* result is currently updated y position */ /*{{{*/ { result.type = INT; result.u.integer = upd_l[Y]; } /*}}}*/ else if (argc==1 && argv[0].type==LOCATION) /* return y component of location */ /*{{{*/ { result.type = INT; result.u.integer = argv[0].u.location[Y]; } /*}}}*/ else /* y type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: y([location])"))+1),_("Usage: y([location])")); } /*}}}*/ return result; } /*}}}*/ /* z */ /*{{{*/ static Token z_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==0) /* result is currently updated z position */ /*{{{*/ { result.type = INT; result.u.integer = upd_l[Z]; } /*}}}*/ else if (argc == 1 && argv[0].type == LOCATION) /* return z component of location */ /*{{{*/ { result.type = INT; result.u.integer = argv[0].u.location[Z]; } /*}}}*/ else /* result is z type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: z([location])")); } /*}}}*/ return result; } /*}}}*/ typedef enum { LOG_AND, LOG_OR } LogicalFunction; #define INTPATIBLE(t) (t.type == INT || t.type == EMPTY) static Token bitwise_func(int argc, const Token argv[], LogicalFunction lop) { Token result; int accum; if (lop == LOG_AND) accum = -1; else accum = 0; for (size_t i = 0; i < argc; ++i) { if (!INTPATIBLE(argv[i])) { result.type = EEK; result.u.err = mystrmalloc(_("Bitwise functions operate only on integers.")); return result; } int val = 0; if (argv[i].type == INT) val = argv[i].u.integer; if (lop == LOG_AND) accum = accum & val; else accum = accum | val; } result.type = INT; result.u.integer = accum; return result; } static Token bitand_func(int argc, const Token argv[]) { return bitwise_func(argc, argv, LOG_AND); } static Token bitor_func(int argc, const Token argv[]) { return bitwise_func(argc, argv, LOG_OR); } /* e */ /*{{{*/ static Token e_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==0) /* result is constant e */ /*{{{*/ { result.type=FLOAT; result.u.flt=CONST_E; } /*}}}*/ else /* result is e type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: e()")); } /*}}}*/ return result; } /*}}}*/ /* eval */ /*{{{*/ static Token eval_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ --max_eval; if (max_eval<0) /* nesting error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("nested eval()")); } /*}}}*/ else if (argc==1 && argv[0].type==LOCATION) /* evaluate expression in cell at given position */ /*{{{*/ { Token **contents; contents = EMPTY_TVEC; if (LOC_WITHIN(upd_sheet, argv[0].u.location)) contents = getcont(CELL_AT(upd_sheet,argv[0].u.location), CONTINGENT); if (contents == EMPTY_TVEC) result.type = EMPTY; else result=eval(contents); } /*}}}*/ else /* eval type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: eval(location)"))+1),_("Usage: eval(location)")); } /*}}}*/ ++max_eval; return result; } /*}}}*/ /* error */ /*{{{*/ static Token error_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ /* asserts */ /*{{{*/ assert(argv!=(Token*)0); /*}}}*/ result.type=EEK; if (argc!=1 || argv[0].type!=STRING) /* result is type error */ /*{{{*/ result.u.err=strcpy(malloc(strlen(_("Usage: error(string message)"))+1),_("Usage: error(string message)")); /*}}}*/ else /* result is user defined error */ /*{{{*/ result.u.err=strcpy(malloc(strlen(argv[0].u.string)+1),argv[0].u.string); /*}}}*/ return result; } /*}}}*/ /* string */ /*{{{*/ static Token string_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; Cell *cell; char *buf; char staticbuf[4096]; size_t size; /*}}}*/ if (argc==1 && argv[0].type==LOCATION) /* cell to string */ /*{{{*/ { result.type=STRING; if (LOC_WITHIN(upd_sheet, argv[0].u.location)) { cell = CELL_AT(upd_sheet, argv[0].u.location); printvalue(staticbuf, sizeof(staticbuf), 0, 0, getscientific(cell), getprecision(cell), upd_sheet, argv[0].u.location); result.u.string = malloc(strlen(staticbuf) + 1); strcpy(result.u.string, staticbuf); } else { result.u.string = malloc(1); result.u.string[0] = '\0'; } } /*}}}*/ else if (argc==1 && argv[0].type==FLOAT) /* float to string */ /*{{{*/ { result.u.string=malloc(def_precision+10); sprintf(result.u.string,DEF_SCIENTIFIC ? "%.*e" : "%.*f", def_precision,argv[0].u.flt); result.type=STRING; } /*}}}*/ else if (argc==1 && argv[0].type==INT) /* int to string */ /*{{{*/ { int length=2; int n=argv[0].u.integer; while (n!=0) n/=10; result.u.string=malloc(length); sprintf(result.u.string,"%ld",argv[0].u.integer); result.type=STRING; } /*}}}*/ else if (argc==2 && argv[0].type==FLOAT && argv[1].type==INT) /* float to string */ /*{{{*/ { result.u.string=malloc(argv[1].u.integer==-1 ? def_precision+10 : argv[1].u.integer+10); sprintf(result.u.string,DEF_SCIENTIFIC ? "%.*e" : "%.*f",argv[1].u.integer==-1 ? def_precision : (int)(argv[1].u.integer), argv[0].u.flt); result.type=STRING; } /*}}}*/ else if (argc==3 && argv[0].type==FLOAT && (argv[1].type==INT || argv[1].type==EMPTY) && argv[2].type==INT) /* float to string */ /*{{{*/ { result.u.string=malloc((argv[1].type==INT ? argv[1].u.integer : def_precision)+10); sprintf(result.u.string,argv[2].u.integer ? "%.*e" : "%.*f",argv[1].type==INT && argv[1].u.integer>=0 ? (int)argv[1].u.integer : def_precision, argv[0].u.flt); result.type=STRING; } /*}}}*/ else /* return string type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: string(location) or string(float[,[integer][,integer]])"))+1),_("Usage: string(location) or string(float[,[integer][,integer]])")); return result; } /*}}}*/ return result; } /*}}}*/ /* sum */ /*{{{*/ static Token sum_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION) /* result is sum */ /*{{{*/ { /* variables */ /*{{{*/ Location w; int x1,y1,z1; int x2,y2,z2; Token tmp; /*}}}*/ x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2); y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2); z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2); result.type=EMPTY; for (w[X]=x1; w[X]<=x2; ++(w[X])) for (w[Y]=y1; w[Y]<=y2; ++(w[Y])) for (w[Z]=z1; w[Z]<=z2; ++(w[Z])) { Token t; tmp = tadd(result, t=getvalue(upd_sheet,w)); tfree(&t); tfree(&result); result=tmp; if (result.type==EEK) return result; } } /*}}}*/ else /* result is sum type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: sum(location,location)"))+1),_("Usage: sum(location,location)")); } /*}}}*/ return result; } /*}}}*/ /* n */ /*{{{*/ static Token n_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION) /* result is number of elements */ /*{{{*/ { /* variables */ /*{{{*/ Location w; int x1,y1,z1; int x2,y2,z2; Token tmp; int n; /*}}}*/ x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2); y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2); z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2); n=0; for (w[X]=x1; w[X]<=x2; ++(w[X])) for (w[Y]=y1; w[Y]<=y2; ++(w[Y])) for (w[Z]=z1; w[Z]<=z2; ++(w[Z])) { tmp = getvalue(upd_sheet, w); if (tmp.type != EMPTY) ++n; tfree(&tmp); } result.type = INT; result.u.integer = n; } /*}}}*/ else /* result is n type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: n(location,location)"))+1),_("Usage: n(location,location)")); } /*}}}*/ return result; } /*}}}*/ /* int */ /*{{{*/ static Token int_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==1 && argv[0].type==FLOAT) /* result is integer with cutoff fractional part */ /*{{{*/ { result.type=INT; result.u.integer=(long)(argv[0].u.flt); } /*}}}*/ else if (argc==3 && argv[0].type==FLOAT && argv[1].type==INT && argv[2].type==INT) /* result is integer with given conversion */ /*{{{*/ { result.type=INT; if (argv[0].u.flt<0) { if (argv[1].u.integer<-1) result.u.integer=(long)floor(argv[0].u.flt); else if (argv[1].u.integer==-1) result.u.integer=(long)(argv[0].u.flt-0.5); else if (argv[1].u.integer==0) result.u.integer=(long)(argv[0].u.flt); else if (argv[1].u.integer==1) result.u.integer=(long)(argv[0].u.flt+0.5); else result.u.integer=(long)ceil(argv[0].u.flt); } else { if (argv[2].u.integer<-1) result.u.integer=(long)floor(argv[0].u.flt); else if (argv[2].u.integer==-1) result.u.integer=(long)(argv[0].u.flt-0.5); else if (argv[2].u.integer==0) result.u.integer=(long)(argv[0].u.flt); else if (argv[2].u.integer==1) result.u.integer=(long)(argv[0].u.flt+0.5); else result.u.integer=(long)ceil(argv[0].u.flt); } } /*}}}*/ else if (argc==1 && argv[0].type==STRING) /* result is integer */ /*{{{*/ { char *s; errno=0; result.u.integer=strtol(argv[0].u.string,&s,10); if (s==(char*)0 || *s) { result.type=EEK; result.u.err=mystrmalloc(_("int(string): invalid string")); } else if (errno==ERANGE && (result.u.integer==LONG_MAX || result.u.integer==LONG_MIN)) { result.type=EEK; result.u.err=mystrmalloc(_("int(string): domain error")); } else result.type=INT; } /*}}}*/ else /* result is int type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: int(float[,integer,integer])"))+1),_("Usage: int(float[,integer,integer])")); } /*}}}*/ return result; } /*}}}*/ /* frac */ /*{{{*/ static Token frac_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; double foo; /*}}}*/ if (argc==1 && argv[0].type==FLOAT) /* result is fractional part */ /*{{{*/ { result.type=FLOAT; result.u.flt=modf(argv[0].u.flt,&foo); } /*}}}*/ else /* result is frac type error */ /*{{{*/ { result.type=EEK; result.u.err=strcpy(malloc(strlen(_("Usage: frac(float)"))+1),_("Usage: frac(float)")); } /*}}}*/ return result; } /*}}}*/ /* len */ /*{{{*/ static Token len_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==1 && argv[0].type==STRING) /* result is length */ /*{{{*/ { result.type=INT; result.u.integer=strlen(argv[0].u.string); } /*}}}*/ else /* result is frac type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: len(string)")); } /*}}}*/ return result; } /*}}}*/ /* log */ /*{{{*/ static Token log_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ double x=-1.0,y=-1.0; Token result; /*}}}*/ /* set x and y to first two arguments */ /*{{{*/ if (argc>=1) { if (argv[0].type==FLOAT) x=argv[0].u.flt; else if (argv[0].type==INT) x=(double)argv[0].u.integer; } if (argc==2) { if (argv[1].type==FLOAT) y=argv[1].u.flt; else if (argv[1].type==INT) y=(double)argv[1].u.integer; } /*}}}*/ if (argc==1 && (argv[0].type==FLOAT || argv[0].type==INT)) /* result is ln(x) */ /*{{{*/ { result.type=FLOAT; result.u.flt=log(x); } /*}}}*/ else if (argc==2 && (argv[0].type==FLOAT || argv[0].type==INT) && (argv[1].type==FLOAT || argv[1].type==INT)) /* result is ln(x)/ln(y) */ /*{{{*/ { result.type=FLOAT; if (y==CONST_E) result.u.flt=log(x); else if (y==10.0) result.u.flt=log10(x); else result.u.flt=log(x)/log(y); } /*}}}*/ else /* result is log type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: log(float[,float])")); } /*}}}*/ return result; } /*}}}*/ /* minmax */ /*{{{*/ static Token minmax_func(int argc, const Token argv[], int min) { /* variables */ /*{{{*/ Token result, tmp; Location minloc; /*}}}*/ if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION) /* result is min/max */ /*{{{*/ { /* variables */ /*{{{*/ Location w; int x1,y1,z1; int x2,y2,z2; /*}}}*/ x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2); y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2); z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2); minloc[X] = x1; minloc[Y] = y1; minloc[Z] = z1; result = getvalue(upd_sheet, minloc); for (w[X]=x1; w[X]<=x2; ++(w[X])) for (w[Y]=y1; w[Y]<=y2; ++(w[Y])) for (w[Z]=z1; w[Z]<=z2; ++(w[Z])) { Token t; t = getvalue(upd_sheet, w); tmp = (min ? tle(result,t) : tge(result, t)); if (tmp.type==INT) /* successful comparison */ /*{{{*/ { tfree(&tmp); if (tmp.u.integer == 0) { tfree(&result); result=t; LOCATION_GETS(minloc, w); } else tfree(&t); } /*}}}*/ else /* successless comparison, return with error */ /*{{{*/ { tfree(&result); tfree(&t); return tmp; } /*}}}*/ } tfree(&result); result.type=LOCATION; LOCATION_GETS(result.u.location, minloc); return result; } /*}}}*/ else if (argc > 0) /* try to take min/max of all arguments */ { size_t i, mini; mini = 0; for (i = 1; i < argc; ++i) { tmp = (min ? tlt(argv[i], argv[mini]) : tgt(argv[i], argv[mini])); if (tmp.type == INT) /* comparison succeeded */ { if (tmp.u.integer) mini = i; tfree(&tmp); } else /* failed comparison, return the error */ return tmp; } return argv[mini]; } else /* result is min/max type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(min ? _("Usage: min(location,location) or min(val1, val2,...)") : _("Usage: max(location,location) or max(val1,val2,...)")); return result; } /*}}}*/ } /*}}}*/ /* min */ /*{{{*/ static Token min_func(int argc, const Token argv[]) { return minmax_func(argc,argv,1); } /*}}}*/ /* max */ /*{{{*/ static Token max_func(int argc, const Token argv[]) { return minmax_func(argc,argv,0); } /*}}}*/ /* abs */ /*{{{*/ static Token abs_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==1 && argv[0].type==FLOAT) /* result is absolute floating point number */ /*{{{*/ { result.type=FLOAT; result.u.flt=fabs(argv[0].u.flt); } /*}}}*/ else if (argc==1 && argv[0].type==INT) /* result is absolute integer number */ /*{{{*/ { result.type=INT; result.u.integer=(argv[0].u.integer<0 ? -argv[0].u.integer : argv[0].u.integer); } /*}}}*/ else /* result is abs type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: abs(float|integer)")); } /*}}}*/ return result; } /*}}}*/ /* $ */ /*{{{*/ static Token env_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==1 && argv[0].type==STRING) { const char *e; if ((e=getenv(argv[0].u.string))==(char*)0) e=""; result.type=STRING; result.u.string=mystrmalloc(e); } else { result.type=EEK; result.u.err=mystrmalloc(_("Usage: $(string)")); } return result; } /*}}}*/ /* float */ /*{{{*/ static Token float_func(int argc, const Token argv[]) { Token result; if (argc==1 && argv[0].type==STRING) /* convert string to float */ /*{{{*/ { char *p; result.u.flt=strtod(argv[0].u.string,&p); if (p!=argv[0].u.string && *p=='\0' && dblfinite(result.u.flt)==(const char*)0) { result.type=FLOAT; } else { result.type=EEK; result.u.err=mystrmalloc(_("Not a (finite) floating point number")); } } /*}}}*/ else /* float type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: float(string)")); } /*}}}*/ return result; } /*}}}*/ /* strftime */ /*{{{*/ static Token strftime_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==1 && argv[0].type==STRING) /* format and return string */ /*{{{*/ { time_t t; struct tm *tm; char s[1024]; t=time((time_t*)0); tm=localtime(&t); strftime(s,sizeof(s),argv[0].u.string,tm); s[sizeof(s)-1]='\0'; result.u.string=mystrmalloc(s); result.type=STRING; } /*}}}*/ else if (argc==2 && argv[0].type==STRING && argv[1].type==INT) /* format and return string */ /*{{{*/ { time_t t; struct tm *tm; char s[1024]; t=argv[1].u.integer; tm=localtime(&t); strftime(s,sizeof(s),argv[0].u.string,tm); s[sizeof(s)-1]='\0'; result.u.string=mystrmalloc(s); result.type=STRING; } /*}}}*/ else /* strftime type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: strftime(string[,integer])")); } /*}}}*/ return result; } /*}}}*/ /* clock */ /*{{{*/ static Token clock_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==2 && argv[0].type==INT && argv[1].type==LOCATION) /* clock(condition,location) */ /*{{{*/ { if (argv[0].u.integer) clk(upd_sheet, argv[1].u.location); result.type=EMPTY; } /*}}}*/ else if (argc==3 && argv[0].type==INT && argv[1].type==LOCATION && argv[2].type==LOCATION) /* clock(condition,location,location) */ /*{{{*/ { if (argv[0].u.integer) { /* variables */ /*{{{*/ Location w; int x1,y1,z1; int x2,y2,z2; /*}}}*/ x1=argv[1].u.location[0]; x2=argv[2].u.location[0]; posorder(&x1,&x2); y1=argv[1].u.location[1]; y2=argv[2].u.location[1]; posorder(&y1,&y2); z1=argv[1].u.location[2]; z2=argv[2].u.location[2]; posorder(&z1,&z2); for (w[X]=x1; w[X]<=x2; ++(w[X])) for (w[Y]=y1; w[Y]<=y2; ++(w[Y])) for (w[Z]=z1; w[Z]<=z2; ++(w[Z])) clk(upd_sheet, w); } result.type=EMPTY; } /*}}}*/ else /* wrong usage */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: clock(condition,location[,location])")); } /*}}}*/ return result; } /*}}}*/ /* poly */ /*{{{*/ static Token poly_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; int i; /*}}}*/ for (i=0; i l ) b = l; if( e > l ) e = l; n = e - b + 1; if( n >= 1024 ) n = 1024 - 1; if(n > 0) { ss[n] = '\0'; strncpy(ss, argv[0].u.string + b, n); result.type=STRING; result.u.string=mystrmalloc(ss); } else { result.type=EMPTY; } } else { result.type=EEK; result.u.err=mystrmalloc(_("Usage: substr(string,integer,integer)")); } return result; } /*}}}*/ /* strptime */ /*{{{*/ static Token strptime_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==2 && argv[0].type==STRING && argv[1].type==STRING) /* format and return string */ /*{{{*/ { time_t t; struct tm tm; t=time((time_t*)0); tm=*localtime(&t); strptime(argv[1].u.string,argv[0].u.string,&tm); result.u.integer=mktime(&tm); result.type=INT; } /*}}}*/ else /* strftime type error */ /*{{{*/ { result.type=EEK; result.u.err=mystrmalloc(_("Usage: strptime(string,string)")); } /*}}}*/ return result; } /*}}}*/ /* time */ /*{{{*/ static Token time_func(int argc, const Token argv[]) { Token result; if (argc==0) { result.type=INT; result.u.integer=time((time_t*)0); } else { result.type=EEK; result.u.err=mystrmalloc(_("Usage: time()")); } return result; } /*}}}*/ /* table of functions */ /*{{{*/ /* The order of these entries has no influence on performance, but to stay compatible, new entries should be appended. */ Tfunc tfunc[]= { { "@", abs_at_func }, { "&", abs_adr_func }, { "x", x_func }, { "y", y_func }, { "z", z_func }, { "eval", eval_func }, { "error", error_func }, { "string", string_func }, { "sum", sum_func }, { "n", n_func }, { "int", int_func }, { "frac", frac_func }, { "len", len_func }, { "min", min_func }, { "max", max_func }, { "abs", abs_func }, { "$", env_func }, { "float", float_func }, { "strftime", strftime_func }, { "clock", clock_func }, { "poly", poly_func }, { "e", e_func }, { "log", log_func }, { "sin", sin_func }, { "cos", cos_func }, { "tan", tan_func }, { "sinh", sinh_func }, { "cosh", cosh_func }, { "tanh", tanh_func }, { "asin", asin_func }, { "acos", acos_func }, { "atan", atan_func }, { "arsinh", arsinh_func }, { "arcosh", arcosh_func }, { "artanh", artanh_func }, { "deg2rad", deg2rad_func }, { "rad2deg", rad2deg_func }, { "rnd", rnd_func }, { "substr", substr_func }, { "strptime", strptime_func }, { "time", time_func }, { "bitand", bitand_func }, { "bitor", bitor_func }, { "D", rel_adr_func }, { "R", rel_at_func }, { "", (Token (*)(int, const Token[]))0 } }; /*}}}*/