Annotation of parser3/src/classes/math.C, revision 1.114
1.1 parser 1: /** @file
2: Parser: @b math parser class.
3:
1.112 moko 4: Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com)
1.101 moko 5: Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
1.28 paf 6:
7: portions from gen_uuid.c,
8: Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
1.22 paf 9: */
1.1 parser 10:
1.34 paf 11: #include "pa_vmethod_frame.h"
1.1 parser 12: #include "pa_common.h"
1.113 moko 13: #include "pa_int.h"
1.90 moko 14: #include "pa_base64.h"
1.1 parser 15: #include "pa_vint.h"
1.2 parser 16: #include "pa_vmath.h"
1.69 moko 17: #include "pa_vfile.h"
1.1 parser 18: #include "pa_request.h"
1.19 paf 19: #include "pa_md5.h"
1.114 ! moko 20: #include "pa_sha1.h"
1.65 moko 21: #include "pa_sha2.h"
1.55 misha 22: #include "pa_random.h"
1.1 parser 23:
1.34 paf 24: #ifdef HAVE_CRYPT
1.76 moko 25: extern "C" char *crypt(const char* , const char* );
1.20 paf 26: #endif
27:
1.114 ! moko 28: volatile const char * IDENT_MATH_C="$Id: math.C,v 1.113 2026/09/15 20:48:14 moko Exp $";
1.59 moko 29:
1.1 parser 30: // defines
31:
1.20 paf 32: #define MAX_SALT 8
1.1 parser 33:
34: // class
35:
1.34 paf 36: class MMath: public Methoded {
1.81 moko 37: public: // Methoded
38: bool used_directly() { return false; }
39:
1.1 parser 40: public:
1.34 paf 41: MMath();
1.1 parser 42: };
43:
1.34 paf 44: // global variables
45:
1.81 moko 46: DECLARE_CLASS_VAR(math, new MMath);
1.34 paf 47:
1.1 parser 48: // methods
1.34 paf 49:
50: static void _random(Request& r, MethodParams& params) {
1.41 paf 51: double top=params.as_double(0, "range must be expression", r);
1.103 moko 52: if(top<1 || top>INT_MAX)
53: throw Exception(PARSER_RUNTIME, 0, "top(%.15g) must be [1..%u]", top, INT_MAX);
1.83 moko 54: r.write(*new VInt(_random(uint(top))));
1.1 parser 55: }
56:
57:
1.20 paf 58: typedef double(*math1_func_ptr)(double);
1.3 parser 59: static double frac(double param) { return param-trunc(param); }
60: static double degrees(double param) { return param /PI *180; }
61: static double radians(double param) { return param /180 *PI; }
1.1 parser 62:
1.34 paf 63: static void math1(Request& r, MethodParams& params, math1_func_ptr func) {
1.41 paf 64: double param=params.as_double(0, "parameter must be expression", r);
65: double result=func(param);
1.83 moko 66: r.write(*new VDouble(result));
1.1 parser 67: }
68:
69: #define MATH1(name) \
1.34 paf 70: static void _##name(Request& r, MethodParams& params) {\
71: math1(r, params, &name);\
1.1 parser 72: }
1.74 moko 73:
1.1 parser 74: #define MATH1P(name_parser, name_c) \
1.34 paf 75: static void _##name_parser(Request& r, MethodParams& params) {\
76: math1(r, params, &name_c);\
1.1 parser 77: }
1.74 moko 78:
79: MATH1(round)
80: MATH1(floor)
81: MATH1P(ceiling, ceil)
82: MATH1(trunc)
83: MATH1(frac)
84: MATH1P(abs, fabs)
85: MATH1(sign)
86: MATH1(exp)
87: MATH1(log)
88: MATH1(log10)
89: MATH1(sin)
90: MATH1(asin)
91: MATH1(cos)
92: MATH1(acos)
93: MATH1(tan)
94: MATH1(atan)
95: MATH1(degrees)
96: MATH1(radians)
97: MATH1(sqrt)
1.1 parser 98:
99:
100: typedef double (*math2_func_ptr)(double, double);
1.34 paf 101: static void math2(Request& r, MethodParams& params, math2_func_ptr func) {
1.41 paf 102: double a=params.as_double(0, "parameter must be expression", r);
103: double b=params.as_double(1, "parameter must be expression", r);
104: double result=func(a, b);
1.83 moko 105: r.write(*new VDouble(result));
1.1 parser 106: }
107:
108: #define MATH2(name) \
1.34 paf 109: static void _##name(Request& r, MethodParams& params) {\
110: math2(r, params, &name);\
1.1 parser 111: }
1.74 moko 112:
113: MATH2(pow)
1.105 moko 114: MATH2(atan2)
1.1 parser 115:
1.110 moko 116: static void _eq(Request& r, MethodParams& params) {
117: double a=params.as_double(0, "parameter must be expression", r);
118: double b=params.as_double(1, "parameter must be expression", r);
1.113 moko 119: uint64_t max_ulp=ULP_EQ_DEFAULT_DIST;
1.110 moko 120: if(params.count() == 3)
121: max_ulp=params.as_int(2, "max distance must be integer", r);
1.113 moko 122: r.write(VBool::get(ulp_eq_double(a, b, max_ulp)));
1.110 moko 123: }
124:
1.77 moko 125: inline bool is_salt_body_char(unsigned char c) {
1.73 moko 126: return pa_isalnum(c) || c == '.' || c=='/';
1.20 paf 127: }
1.77 moko 128:
1.34 paf 129: static size_t calc_prefix_size(const char* salt) {
1.37 paf 130: if(strlen(salt)) {
1.39 paf 131: if(!is_salt_body_char((unsigned char)salt[0])) { // $... {...
1.34 paf 132: const char* cur=salt+1; // skip
1.39 paf 133: while(is_salt_body_char((unsigned char)*cur++)) // ...$ ...}
1.20 paf 134: ;
135: return cur-salt;
136: } else
137: return 0;
138: } else
139: return 0;
140: }
1.34 paf 141: static void _crypt(Request& r, MethodParams& params) {
142: const char* password=params.as_string(0, "password must be string").cstr();
143: const char* maybe_bodyless_salt=params.as_string(1, "salt must be string").cstr();
1.20 paf 144:
145: size_t prefix_size=calc_prefix_size(maybe_bodyless_salt);
1.34 paf 146: const char* normal_salt;
1.20 paf 147: char normalize_buf[MAX_STRING];
148: if(prefix_size==strlen(maybe_bodyless_salt)) { // bodyless?
1.99 moko 149: pa_strncpy(normalize_buf, maybe_bodyless_salt, MAX_STRING-MAX_SALT);
1.20 paf 150: char *cur=normalize_buf+strlen(normalize_buf);
151: // sould add up MAX_SALT random chars
152: static unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
153: "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
154: for(int i=0; i<MAX_SALT; i++)
155: *cur++=itoa64[_random(64)];
156: *cur=0;
157: normal_salt=normalize_buf;
158: } else
159: normal_salt=maybe_bodyless_salt;
1.19 paf 160:
1.54 misha 161: /* FreeBSD style MD5 string
162: */
163: if(strncmp(normal_salt, PA_MD5PW_ID, PA_MD5PW_IDLEN) == 0) {
1.19 paf 164: const size_t sample_size=120;
1.34 paf 165: char *sample_buf=new(PointerFreeGC) char[sample_size];
166: pa_MD5Encode((const unsigned char *)password,
167: (const unsigned char *)normal_salt, sample_buf, sample_size);
168: String sample(sample_buf);
1.83 moko 169: r.write(sample);
1.54 misha 170: } else {
1.20 paf 171: #ifdef HAVE_CRYPT
1.34 paf 172: const char* static_sample_buf=crypt(password, normal_salt);
173: if(!static_sample_buf // nothing generated
174: || !static_sample_buf[0] // generated nothing
175: || strncmp(static_sample_buf, normal_salt, prefix_size)!=0) // salt prefix not preserved
1.45 misha 176: throw Exception(PARSER_RUNTIME,
1.34 paf 177: 0,
178: "crypt on this platform does not support '%.*s' salt prefix", prefix_size, normal_salt);
1.20 paf 179:
1.83 moko 180: r.write(String(pa_strdup(static_sample_buf)));
1.20 paf 181: #else
1.45 misha 182: throw Exception(PARSER_RUNTIME,
1.34 paf 183: 0,
1.19 paf 184: "salt must start with '" PA_MD5PW_ID "'");
1.20 paf 185: #endif
186: }
1.19 paf 187: }
188:
1.34 paf 189: static void _md5(Request& r, MethodParams& params) {
1.72 moko 190: const char *string=params.as_string(0, PARAMETER_MUST_BE_STRING).cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets).cstr();
1.27 paf 191:
192: PA_MD5_CTX context;
193: unsigned char digest[16];
1.34 paf 194: pa_MD5Init(&context);
195: pa_MD5Update(&context, (const unsigned char*)string, strlen(string));
196: pa_MD5Final(digest, &context);
1.27 paf 197:
1.83 moko 198: r.write(*new String(hex_string(digest, sizeof(digest), false)));
1.26 paf 199: }
200:
1.49 misha 201:
1.54 misha 202: static void _sha1(Request& r, MethodParams& params) {
1.72 moko 203: const char *string = params.as_string(0, PARAMETER_MUST_BE_STRING).cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets).cstr();
1.49 misha 204:
1.114 ! moko 205: PA_SHA1_CTX c;
1.64 moko 206: unsigned char digest[20];
1.114 ! moko 207: pa_SHA1Reset (&c);
! 208: pa_SHA1Input (&c, (const unsigned char*)string, strlen(string));
! 209: pa_SHA1ReadDigest(digest, &c);
1.54 misha 210:
1.83 moko 211: r.write(*new String(hex_string(digest, sizeof(digest), false)));
1.62 moko 212: }
213:
1.87 moko 214: String::C getData(Value& vdata, Request& r){
215: if(const String* sdata=vdata.get_string()){
216: String::Body body=sdata->cstr_to_string_body_untaint(String::L_AS_IS, r.connection(false), &r.charsets); // explode content, honor tainting changes
217: return String::C(body.cstr(), body.length());
218: } else {
1.98 moko 219: VFile *file=vdata.as_vfile();
1.87 moko 220: return String::C(file->value_ptr(),file->value_size());
221: }
222: }
223:
1.64 moko 224: void memxor(char *dest, const char *src, size_t n){
225: for (;n>0;n--) *dest++ ^= *src++;
226: }
227:
1.62 moko 228: #define IPAD 0x36
229: #define OPAD 0x5c
230:
1.109 moko 231: #define HMAC(key,keylen,init,update,final,blocklen,digestlen){ \
1.64 moko 232: unsigned char tempdigest[digestlen], keydigest[digestlen]; \
1.65 moko 233: /* Reduce the key's size, so that it becomes <= blocklen bytes. */ \
234: if (keylen > blocklen){ \
1.64 moko 235: init(&c); \
1.109 moko 236: update(&c,(const unsigned char*)key, keylen); \
1.64 moko 237: final(keydigest, &c); \
238: key = (char *)keydigest; \
239: keylen = digestlen; \
240: } \
241: /* Compute TEMP from KEY and STRING. */ \
1.65 moko 242: char block[blocklen]; \
243: memset (block, IPAD, blocklen); \
1.64 moko 244: memxor (block, key, keylen); \
245: init(&c); \
1.65 moko 246: update(&c, (const unsigned char*)block, blocklen); \
1.69 moko 247: update(&c, (const unsigned char*)data.str, data.length); \
1.64 moko 248: final(tempdigest, &c); \
249: /* Compute result from KEY and TEMP. */ \
1.65 moko 250: memset (block, OPAD, blocklen); \
1.64 moko 251: memxor (block, key, keylen); \
252: init(&c); \
1.65 moko 253: update(&c, (const unsigned char*)block, blocklen); \
1.64 moko 254: update(&c, (const unsigned char*)tempdigest, digestlen); \
1.62 moko 255: }
1.55 misha 256:
1.62 moko 257: static void _digest(Request& r, MethodParams& params) {
1.63 moko 258: const String &smethod = params.as_string(0, PARAMETER_MUST_BE_STRING);
1.69 moko 259:
1.87 moko 260: String::C data=getData(params.as_no_junction(1, "parameter must be string or file"), r);
1.62 moko 261:
1.65 moko 262: enum Method { M_MD5, M_SHA1, M_SHA256, M_SHA512 } method;
1.62 moko 263:
1.63 moko 264: if (smethod == "md5") method = M_MD5;
265: else if (smethod == "sha1" ) method = M_SHA1;
1.65 moko 266: else if (smethod == "sha256" ) method = M_SHA256;
267: else if (smethod == "sha512" ) method = M_SHA512;
1.86 moko 268: else throw Exception(PARSER_RUNTIME, &smethod, "must be 'md5' or 'sha1' or 'sha256' or 'sha512'");
1.62 moko 269:
270: const char *hmac=0;
1.109 moko 271: size_t hmac_len=0;
272:
273: enum Format { F_HEX, F_BASE64, F_FILE } format = F_HEX;
1.62 moko 274:
275: if(params.count() == 3)
276: if(HashStringValue* options=params.as_hash(2)) {
277: int valid_options=0;
278: if(Value* value=options->get("hmac")) {
1.109 moko 279: if(VFile* vfile=dynamic_cast<VFile *>(value)){
280: hmac=(const char* )vfile->value_ptr();
281: hmac_len=vfile->value_size();
282: } else {
283: hmac=value->as_string().cstr();
284: hmac_len=strlen(hmac);
285: }
1.62 moko 286: valid_options++;
287: }
1.63 moko 288: if(Value* value=options->get("format")) {
289: const String& sformat=value->as_string();
290: if (sformat == "hex") format = F_HEX;
291: else if (sformat == "base64" ) format = F_BASE64;
1.109 moko 292: else if (sformat == "file" ) format = F_FILE;
1.63 moko 293: else throw Exception(PARSER_RUNTIME, &sformat, "must be 'hex' or 'base64'");
1.62 moko 294: valid_options++;
295: }
296: if(valid_options!=options->count())
297: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
298: }
299:
300: String::C digest;
301:
1.63 moko 302: if(method == M_MD5){
1.64 moko 303: PA_MD5_CTX c;
304: if(hmac){
1.109 moko 305: HMAC(hmac, hmac_len, pa_MD5Init, pa_MD5Update, pa_MD5Final, 64, 16);
1.64 moko 306: } else {
307: pa_MD5Init(&c);
1.69 moko 308: pa_MD5Update(&c, (const unsigned char*)data.str, data.length);
1.64 moko 309: }
1.62 moko 310: char *str=(char *)pa_malloc(16);
1.64 moko 311: pa_MD5Final((unsigned char *)str, &c);
1.62 moko 312: digest = String::C(str, 16);
313: }
314:
1.63 moko 315: if(method == M_SHA1){
1.114 ! moko 316: PA_SHA1_CTX c;
1.62 moko 317: if(hmac){
1.114 ! moko 318: HMAC(hmac, hmac_len, pa_SHA1Reset, pa_SHA1Input, pa_SHA1ReadDigest, 64, 20);
1.62 moko 319: } else {
1.114 ! moko 320: pa_SHA1Reset(&c);
! 321: pa_SHA1Input(&c, (const unsigned char*)data.str, data.length);
1.62 moko 322: }
323: char *str=(char *)pa_malloc(20);
1.114 ! moko 324: pa_SHA1ReadDigest(str, &c);
1.62 moko 325: digest = String::C(str, 20);
326: }
1.49 misha 327:
1.65 moko 328: if(method == M_SHA256){
329: SHA256_CTX c;
330: if(hmac){
1.109 moko 331: HMAC(hmac, hmac_len, pa_SHA256_Init, pa_SHA256_Update, pa_SHA256_Final, 64, SHA256_DIGEST_LENGTH);
1.65 moko 332: } else {
333: pa_SHA256_Init(&c);
1.69 moko 334: pa_SHA256_Update(&c, (const unsigned char*)data.str, data.length);
1.65 moko 335: }
336: char *str=(char *)pa_malloc(SHA256_DIGEST_LENGTH);
337: pa_SHA256_Final((unsigned char *)str, &c);
338: digest = String::C(str, SHA256_DIGEST_LENGTH);
339: }
340:
341: if(method == M_SHA512){
342: SHA512_CTX c;
343: if(hmac){
1.109 moko 344: HMAC(hmac, hmac_len, pa_SHA512_Init, pa_SHA512_Update, pa_SHA512_Final, 128, SHA512_DIGEST_LENGTH);
1.65 moko 345: } else {
346: pa_SHA512_Init(&c);
1.69 moko 347: pa_SHA512_Update(&c, (const unsigned char*)data.str, data.length);
1.65 moko 348: }
349: char *str=(char *)pa_malloc(SHA512_DIGEST_LENGTH);
350: pa_SHA512_Final((unsigned char *)str, &c);
351: digest = String::C(str, SHA512_DIGEST_LENGTH);
352: }
353:
1.63 moko 354: if(format == F_HEX){
1.83 moko 355: r.write(*new String(hex_string((unsigned char *)digest.str, digest.length, false)));
1.62 moko 356: }
1.63 moko 357: if(format == F_BASE64){
1.94 moko 358: r.write(*new String(pa_base64_encode(digest.str, digest.length, Base64Options(false /*no wrap*/))));
1.62 moko 359: }
1.109 moko 360: if(format == F_FILE){
361: VFile& result=*new VFile;
362: result.set_binary(true, digest.str, digest.length);
363: r.write(result);
364: }
1.54 misha 365: }
1.49 misha 366:
1.97 moko 367: static void _uuid(Request& r, MethodParams& params) {
368: bool lower=false;
369: bool solid=false;
370:
371: if (params.count() == 1)
372: if (HashStringValue* options = params.as_hash(0)) {
373: int valid_options = 0;
374: if (Value* vlower = options->get("lower")) {
375: lower = r.process(*vlower).as_bool();
376: valid_options++;
377: }
378: if (Value* vsolid = options->get("solid")) {
379: solid = r.process(*vsolid).as_bool();
380: valid_options++;
381: }
382: if (valid_options != options->count())
383: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
384: }
385:
386: r.write(*new String(get_uuid_cstr(lower, solid)));
1.28 paf 387: }
388:
1.100 moko 389: static void _uuid7(Request& r, MethodParams& params) {
390: bool lower=false;
391: bool solid=false;
392:
393: if (params.count() == 1)
394: if (HashStringValue* options = params.as_hash(0)) {
395: int valid_options = 0;
396: if (Value* vlower = options->get("lower")) {
397: lower = r.process(*vlower).as_bool();
398: valid_options++;
399: }
400: if (Value* vsolid = options->get("solid")) {
401: solid = r.process(*vsolid).as_bool();
402: valid_options++;
403: }
404: if (valid_options != options->count())
405: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
406: }
407:
408: r.write(*new String(get_uuid7_cstr(lower, solid)));
409: }
410:
1.97 moko 411: static void _uid64(Request& r, MethodParams& params) {
412: bool lower = false;
413:
414: if (params.count() == 1)
415: if (HashStringValue* options = params.as_hash(0)) {
416: int valid_options = 0;
417: if (Value* vlower = options->get("lower")) {
418: lower = r.process(*vlower).as_bool();
419: valid_options++;
420: }
421: if (valid_options != options->count())
422: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
423: }
424:
1.30 paf 425: unsigned char id[64/8];
426: random(&id, sizeof(id));
427:
1.97 moko 428: r.write(*new String(hex_string(id, sizeof(id), !lower)));
1.30 paf 429: }
430:
1.43 misha 431: static void _crc32(Request& r, MethodParams& params) {
1.51 misha 432: const char *string=params.as_string(0, PARAMETER_MUST_BE_STRING).cstr();
1.102 moko 433: r.write(*new VDouble((uint)pa_crc32(string, strlen(string))));
1.43 misha 434: }
435:
1.89 moko 436: static const char* abc_hex = "0123456789ABCDEF";
1.87 moko 437:
1.89 moko 438: static unsigned char hex_lookup[256] = {
1.87 moko 439: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
440: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
441: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
442: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
443: 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
444: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
445: 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0
446: };
447:
1.89 moko 448: static unsigned char abc_lookup[256] = {};
449: static unsigned char abc_256_lookup[256] = {};
1.87 moko 450:
1.89 moko 451: inline unsigned char *init_abc_256() {
452: if(!abc_256_lookup[255])
453: for(int i=0; i<256; i++) abc_256_lookup[i] = (unsigned char)i;
1.87 moko 454: return abc_256_lookup;
1.57 misha 455: }
456:
457: static void _convert(Request& r, MethodParams& params) {
1.87 moko 458: String::C data=getData(params.as_no_junction(0, "parameter must be string or file"), r);
1.60 moko 459:
1.87 moko 460: bool abc_mode = true;
461: unsigned char *lookup;
462: const char *abc_from;
463: int base_from;
464:
1.89 moko 465: if(params[1].is_string()) {
1.87 moko 466: abc_from = params[1].get_string()->cstr();
467: base_from = strlen(abc_from);
468: if(base_from < 2)
469: throw Exception(PARSER_RUNTIME, 0, "alphabet 'from' must contain at least 2 characters");
470: lookup = abc_lookup;
471: memset(abc_lookup,0,sizeof(abc_lookup));
472: for(int i=0; i<base_from; i++) abc_lookup[(unsigned char)abc_from[i]] = (unsigned char)i;
473: } else {
474: base_from=params.as_int(1, "base 'from' must be integer or string", r);
475: if(base_from < 2 || base_from > 16 && base_from != 256)
476: throw Exception(PARSER_RUNTIME, 0, "base 'from' must be an integer from 2 to 16 or 256");
1.89 moko 477: if (base_from == 256) {
1.87 moko 478: abc_from = "";
479: lookup = init_abc_256();
480: } else {
481: abc_mode = false;
482: abc_from = abc_hex;
483: lookup = hex_lookup;
484: }
485: }
1.57 misha 486:
1.87 moko 487: const char *abc_to;
488: int base_to;
1.60 moko 489:
1.89 moko 490: if(params[2].is_string()) {
1.87 moko 491: abc_to=params[2].get_string()->cstr();
492: base_to=strlen(abc_to);
493: if(base_to < 2)
494: throw Exception(PARSER_RUNTIME, 0, "alphabet 'to' must contain at least 2 characters");
495: } else {
496: base_to=params.as_int(2, "base 'to' must be integer or string", r);
497: if(base_to < 2 || base_to > 16 && base_to != 256)
498: throw Exception(PARSER_RUNTIME, 0, "base 'to' must be an integer from 2 to 16 or 256");
1.89 moko 499: if (base_to == 256) {
1.87 moko 500: abc_to = (char *)init_abc_256();
501: } else {
502: abc_to = abc_hex;
503: }
504: }
505:
506: VFile* result_file = 0;
1.60 moko 507:
1.87 moko 508: if(params.count() == 4)
509: if(HashStringValue* options=params.as_hash(3)) {
510: int valid_options=0;
511: if(Value* value=options->get("format")) {
512: const String& sformat=value->as_string();
513: if (sformat == "file" ) result_file = new VFile;
514: else if (sformat != "string") throw Exception(PARSER_RUNTIME, &sformat, "must be 'string' or 'file'");
515: valid_options++;
516: }
517: if(valid_options!=options->count())
518: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
519: }
1.60 moko 520:
521: bool negative=false;
1.87 moko 522:
523: // converting digits to their numeric values
524:
525: unsigned char *src=(unsigned char *)pa_strdup(data.str, data.length);
526: const unsigned char *src_end = src + data.length;
527:
528: unsigned char *c;
529:
530: if(abc_mode){
531:
1.89 moko 532: for(c=src;c<src_end;c++) {
1.87 moko 533: unsigned char digit=lookup[*c];
534: if(!digit && *c != abc_from[0])
1.107 moko 535: throw Exception("number.format", 0, "'%c' is an invalid digit", *c);
1.87 moko 536: *c=digit;
537: }
538:
539: } else {
540: // numbers mode, allow whitespace and sign
541:
542: while(isspace(*src))
543: src++;
544:
545: if(src[0]=='-') {
546: negative=true;
547: src++;
1.104 moko 548: if(!*src || isspace(*src))
1.107 moko 549: throw Exception("number.format", 0, "'-' is an invalid number");
1.87 moko 550: } else if(src[0]=='+') {
551: src++;
1.104 moko 552: if(!*src || isspace(*src))
1.107 moko 553: throw Exception("number.format", 0, "'+' is an invalid number");
1.87 moko 554: }
555:
556: for(c=src;c<src_end;c++) {
557: unsigned char digit=lookup[*c];
558: if(!digit && *c != abc_from[0] || digit>=base_from) {
559: for(unsigned char *s=c;s<src_end;s++)
560: if(!isspace(*s))
1.107 moko 561: throw Exception("number.format", 0, "'%c' is an invalid digit", *s);
1.87 moko 562: src_end=c;
563: break;
564: }
565: *c=digit;
566: }
567:
1.60 moko 568: }
569:
1.89 moko 570: if(src==src_end) {
1.87 moko 571: if(result_file)
572: r.write(*result_file);
573: return;
574: }
575:
1.92 moko 576: // core, using log since log2 is not present in FreeBSD < 8.X
1.87 moko 577:
1.95 moko 578: Array<char> remainders((size_t)round(data.length * log((double)base_from) / log((double)base_to)) + 1);
1.57 misha 579:
1.87 moko 580: do {
581: int carry = 0;
582: unsigned char *dst = src;
583: for (c=src; c<src_end; c++) {
584: carry = carry * base_from + *c;
585: if (carry >= base_to) {
1.95 moko 586: *(dst++) = (unsigned char)(carry / base_to);
1.87 moko 587: carry %= base_to;
588: } else if (dst > src) {
589: *(dst++) = 0;
590: }
591: }
592: src_end = dst;
593: remainders += abc_to[carry];
594: } while (src_end > src);
595:
596: // result processing
597:
598: size_t result_length = negative + remainders.count();
599: char *result_str = (char *)pa_malloc_atomic(result_length+1);
1.60 moko 600: if(negative)
1.87 moko 601: result_str[0] = '-';
1.95 moko 602: for(size_t i=0; i<remainders.count(); i++)
1.87 moko 603: result_str[result_length - 1 - i] = remainders[i];
604: result_str[result_length]='\0';
605:
1.89 moko 606: if(result_file) {
1.88 moko 607: result_file->set(true /*tainted*/, 0 /*binary*/, result_str, result_length, 0, 0, &r);
1.87 moko 608: r.write(*result_file);
609: } else {
1.91 moko 610: if(memchr(result_str, 0, result_length))
611: throw Exception(PARSER_RUNTIME, 0, "Invalid \\x00 character found while converting to string. Convert to file instead.");
612:
613: fix_line_breaks(result_str, result_length);
614:
615: if(result_length)
616: r.write(*new String(result_str, String::L_TAINTED));
1.87 moko 617: }
1.57 misha 618: }
619:
1.1 parser 620: // constructor
621:
1.34 paf 622: MMath::MMath(): Methoded("math") {
1.1 parser 623: // ^FUNC(expr)
1.105 moko 624: #define ADDN(name, N) \
625: add_native_method(#name, Method::CT_STATIC, _##name, N, N)
626: #define ADD1(name) ADDN(name, 1)
1.1 parser 627:
628: ADD1(round); ADD1(floor); ADD1(ceiling);
1.3 parser 629: ADD1(trunc); ADD1(frac);
1.1 parser 630: ADD1(abs); ADD1(sign);
1.46 misha 631: ADD1(exp);
1.48 misha 632: ADD1(log); ADD1(log10);
1.87 moko 633: ADD1(sin); ADD1(asin);
634: ADD1(cos); ADD1(acos);
1.105 moko 635: ADD1(tan); ADD1(atan); ADDN(atan2, 2);
1.3 parser 636: ADD1(degrees); ADD1(radians);
1.1 parser 637: ADD1(sqrt);
1.3 parser 638: ADD1(random);
1.1 parser 639:
1.49 misha 640: // ^math:pow(x;y)
1.105 moko 641: ADDN(pow, 2);
1.1 parser 642:
1.110 moko 643: // ^math:eq(a;b[;precision])
644: add_native_method("eq", Method::CT_STATIC, _eq, 2, 3);
645:
1.49 misha 646: // ^math:crypt[password;salt]
1.105 moko 647: ADDN(crypt, 2);
1.26 paf 648:
1.49 misha 649: // ^math:md5[string]
1.26 paf 650: ADD1(md5);
1.5 parser 651:
1.49 misha 652: // ^math:sha1[string]
653: ADD1(sha1);
654:
1.71 moko 655: // ^math:digest[method;string|file;options]
1.62 moko 656: add_native_method("digest", Method::CT_STATIC, _digest, 2, 3);
657:
1.49 misha 658: // ^math:crc32[string]
1.43 misha 659: ADD1(crc32);
660:
1.49 misha 661: // ^math:uuid[]
1.97 moko 662: // ^math:uuid[options hash]
663: add_native_method("uuid", Method::CT_STATIC, _uuid, 0, 1);
1.29 paf 664:
1.100 moko 665: // ^math:uuid7[]
666: // ^math:uuid7[options hash]
667: add_native_method("uuid7", Method::CT_STATIC, _uuid7, 0, 1);
668:
1.49 misha 669: // ^math:uid64[]
1.97 moko 670: // ^math:uid64[options hash]
671: add_native_method("uid64", Method::CT_STATIC, _uid64, 0, 1);
1.57 misha 672:
1.87 moko 673: // ^math:convert[number|file](base-from)|[abc_from](base-to)|[abc_to][options]
674: add_native_method("convert", Method::CT_STATIC, _convert, 3, 4);
1.1 parser 675: }
E-mail: