1 #include "Core.h"
2
3 namespace Upp {
4
stou(const char * s,void * endptr,unsigned base)5 unsigned stou(const char *s, void *endptr, unsigned base)
6 {
7 ASSERT(base >= 2 && base <= 36);
8 unsigned digit = ctoi(*s);
9 if(digit >= base)
10 { // error
11 if(endptr)
12 *(const char **)endptr = s;
13 return ~0;
14 }
15 unsigned value = digit;
16 while((digit = ctoi(*++s)) < base) {
17 unsigned v0 = value;
18 value = value * base + digit;
19 if(v0 > value) // overflow
20 return ~0;
21 }
22 if(endptr)
23 *(const char **)endptr = s;
24 return value;
25 }
26
stou(const wchar * s,void * endptr,unsigned base)27 unsigned stou(const wchar *s, void *endptr, unsigned base)
28 {
29 ASSERT(base >= 2 && base <= 36);
30 unsigned digit = ctoi(*s);
31 if(digit >= base)
32 { // error
33 if(endptr)
34 *(const wchar **)endptr = s;
35 return ~0;
36 }
37 unsigned value = digit;
38 while((digit = ctoi(*++s)) < base) {
39 unsigned v0 = value;
40 value = value * base + digit;
41 if(v0 > value) // overflow
42 return ~0;
43 }
44 if(endptr)
45 *(const wchar **)endptr = s;
46 return value;
47 }
48
49
stou64(const char * s,void * endptr,unsigned base)50 uint64 stou64(const char *s, void *endptr, unsigned base)
51 {
52 ASSERT(base >= 2 && base <= 36);
53 unsigned digit = ctoi(*s);
54 if(digit >= base)
55 { // error
56 if(endptr)
57 *(const char **)endptr = s;
58 return ~0;
59 }
60 uint64 value = digit;
61 while((digit = ctoi(*++s)) < base) {
62 uint64 v0 = value;
63 value = value * base + digit;
64 if(v0 > value) // overflow
65 return ~0;
66 }
67 if(endptr)
68 *(const char **)endptr = s;
69 return value;
70 }
71
ScanInt(const char * ptr,const char ** endptr,int base)72 int ScanInt(const char *ptr, const char **endptr, int base)
73 {
74 const char *s = ptr;
75 bool minus = false;
76 while(*s && (byte)*s <= ' ')
77 s++;
78 if(*s == '+' || *s == '-')
79 minus = (*s++ == '-');
80 unsigned u = stou(s, endptr, base);
81 if(~u)
82 return (minus ? -(int)u : (int)u);
83 else
84 return Null;
85 }
86
ScanInt(const wchar * ptr,const wchar ** endptr,int base)87 int ScanInt(const wchar *ptr, const wchar **endptr, int base)
88 {
89 const wchar *s = ptr;
90 bool minus = false;
91 while(*s && *s <= ' ')
92 s++;
93 if(*s == '+' || *s == '-')
94 minus = (*s++ == '-');
95 unsigned u = stou(s, endptr, base);
96 if(~u)
97 return (minus ? -(int)u : (int)u);
98 else
99 return Null;
100 }
101
ScanInt64(const char * ptr,const char ** endptr,int base)102 int64 ScanInt64(const char *ptr, const char **endptr, int base)
103 {
104 const char *s = ptr;
105 bool minus = false;
106 while(*s && *s <= ' ')
107 s++;
108 if(*s == '+' || *s == '-')
109 minus = (*s++ == '-');
110 uint64 u = stou64(s, endptr, base);
111 if(~u)
112 return (minus ? -(int64)u : (int64)u);
113 else
114 return Null;
115 }
116
117 template <class T>
ScanDoubleT(const T * p,const T ** endptr,bool accept_comma)118 double ScanDoubleT(const T *p, const T **endptr, bool accept_comma)
119 {
120 const T *begin = p;
121 while(*p && (byte)*p <= ' ')
122 p++;
123 bool neg = false;
124 if(endptr)
125 *endptr = p;
126 if(*p == '+' || *p == '-')
127 neg = (*p++ == '-');
128 if((byte)(*p - '0') >= 10 && !((*p == '.' || accept_comma && *p == ',') && (byte)(p[1] - '0') < 10)) {
129 if(endptr) *endptr = begin;
130 return Null;
131 }
132 double mantissa = 0;
133 T c;
134 int exp = 0;
135 while((byte)(*p - '0') < 10)
136 if((c = *p++) != '0') {
137 if(exp) { mantissa *= ipow10(exp); exp = 0; }
138 mantissa = 10 * mantissa + c - '0';
139 }
140 else
141 exp++;
142 int raise = exp;
143 if(*p == '.' || accept_comma && *p == ',') // decimal part
144 while((byte)((c = *++p) - '0') < 10) {
145 if(c != '0') {
146 if(raise) {
147 mantissa *= ipow10(raise);
148 exp -= raise;
149 raise = 0;
150 }
151 exp--;
152 mantissa = 10 * mantissa + c - '0';
153 if(!IsFin(mantissa))
154 return Null;
155 }
156 else
157 raise++;
158 }
159 if(*p == 'E' || *p == 'e') { // exponent
160 int vexp = ScanInt(p + 1, &p);
161 if(IsNull(vexp))
162 return Null;
163 exp += vexp;
164 }
165 if(endptr) *endptr = p;
166 if(exp) {
167 double e = ipow10(tabs(exp));
168 mantissa = (exp > 0 ? mantissa * e : mantissa / e);
169 }
170 if(!IsFin(mantissa))
171 return Null;
172 return neg ? -mantissa : mantissa;
173 }
174
ScanDouble(const char * p,const char ** endptr,bool accept_comma)175 double ScanDouble(const char *p, const char **endptr, bool accept_comma)
176 {
177 return ScanDoubleT(p, endptr, accept_comma);
178 }
179
ScanDouble(const wchar * p,const wchar ** endptr,bool accept_comma)180 double ScanDouble(const wchar *p, const wchar **endptr, bool accept_comma)
181 {
182 return ScanDoubleT(p, endptr, accept_comma);
183 }
184
Atof(const char * s)185 double Atof(const char *s)
186 {
187 return Nvl(ScanDouble(s));
188 }
189
StrIntValue(const char * s)190 Value StrIntValue(const char *s)
191 {
192 if(s && *s) {
193 const char *p;
194 int64 q = ScanInt64(s, &p);
195 if(!IsNull(q))
196 while(*p) {
197 if((byte)*p > ' ')
198 return ErrorValue(t_("Invalid number !"));
199 p++;
200 }
201 return IsNull(q) ? ErrorValue(t_("Invalid number !")) : Value(q);
202 }
203 return (int)Null;
204 }
205
StrDblValue(const char * s)206 Value StrDblValue(const char *s)
207 {
208 if(s && *s) {
209 const char *p;
210 double q = ScanDouble(s, &p);
211 if(!IsNull(q))
212 while(*p) {
213 if((byte)*p > ' ')
214 return ErrorValue(t_("Invalid number !"));
215 p++;
216 }
217 return IsNull(q) ? ErrorValue(t_("Invalid number !")) : Value(q);
218 }
219 return (double)Null;
220 }
221
Scan(dword qtype,const String & text,const Value & def,bool * hastime)222 Value Scan(dword qtype, const String& text, const Value& def, bool *hastime) {
223 Date date;
224 const char *s;
225 if(hastime)
226 *hastime = false;
227 switch(qtype) {
228 case INT64_V:
229 case INT_V:
230 case BOOL_V:
231 return StrIntValue(text);
232 case DATE_V:
233 if(text.IsEmpty()) return (Date) Null;
234 s = StrToDate(date, text, (Date)def);
235 if(s)
236 for(;;) {
237 if(IsDigit(*s))
238 break;
239 if(*s == '\0')
240 return date;
241 s++;
242 }
243 return ErrorValue(t_("Invalid date !"));
244 case TIME_V: {
245 if(text.IsEmpty()) return (Time) Null;
246 s = StrToDate(date, text, (Date)def);
247 if(s)
248 try {
249 CParser p(s);
250 Time tm = ToTime(date);
251 Time d = (Time)def;
252 tm.hour = d.hour;
253 tm.minute = d.minute;
254 tm.second = d.second;
255 if(p.IsEof())
256 return tm;
257 if(hastime)
258 *hastime = true;
259 int q = p.ReadInt();
260 if(q < 0 || q > 23)
261 throw CParser::Error("");
262 tm.hour = q;
263 if(p.IsEof())
264 return tm;
265 p.PassChar(':');
266 q = p.ReadInt();
267 if(q < 0 || q > 59)
268 throw CParser::Error("");
269 tm.minute = q;
270 if(p.IsEof())
271 return tm;
272 p.PassChar(':');
273 q = p.ReadInt();
274 if(q < 0 || q > 59)
275 throw CParser::Error("");
276 tm.second = q;
277 if(p.IsEof())
278 return tm;
279 }
280 catch(CParser::Error) {}
281 return ErrorValue(t_("Invalid time !"));
282 }
283 case STRING_V:
284 case WSTRING_V:
285 return text;
286 case DOUBLE_V:
287 return StrDblValue(text);
288 default:
289 ASSERT(0);
290 break;
291 }
292 return Null;
293 }
294
Convert()295 Convert::Convert() {}
~Convert()296 Convert::~Convert() {}
297
Format(const Value & q) const298 Value Convert::Format(const Value& q) const {
299 if(IsVoid(q) || q.IsNull()) return String();
300 switch(q.GetType()) {
301 case INT64_V:
302 return IntStr64((int64)q);
303 case INT_V:
304 case BOOL_V:
305 return IntStr((int)q);
306 case DOUBLE_V:
307 return DblStr((double)q);
308 case DATE_V:
309 return UPP::Format(Date(q));
310 case TIME_V:
311 return UPP::Format(Time(q));
312 case STRING_V:
313 case WSTRING_V:
314 return q;
315 }
316 return q.ToString();
317 }
318
Scan(const Value & text) const319 Value Convert::Scan(const Value& text) const {
320 return text;
321 };
322
Filter(int chr) const323 int Convert::Filter(int chr) const {
324 return chr;
325 }
326
StdConvert()327 const Convert& StdConvert()
328 {
329 static Convert h;
330 return h;
331 }
332
StdFormat(const Value & q)333 String StdFormat(const Value& q) {
334 return StdConvert().Format(q);
335 }
336
NotNullError()337 Value NotNullError() {
338 return ErrorValue(t_("Null value not allowed."));
339 }
340
Scan(const Value & text) const341 Value ConvertInt::Scan(const Value& text) const {
342 Value v = UPP::Scan(INT_V, text);
343 if(IsError(v)) return v;
344 if(IsNull(v)) return notnull ? NotNullError() : v;
345 int64 m = v;
346 if(m >= minval && m <= maxval) {
347 if(m >= INT_MIN && m <= INT_MAX)
348 return (int)m;
349 else
350 return v;
351 }
352 return ErrorValue(UPP::Format(t_("Number must be between %d and %d."), minval, maxval));
353 }
354
Filter(int chr) const355 int ConvertInt::Filter(int chr) const {
356 return minval >= 0 ? CharFilterDigit(chr) : CharFilterInt(chr);
357 }
358
Format(const Value & q) const359 Value ConvertDouble::Format(const Value& q) const
360 {
361 if(IsNull(q))
362 return Null;
363 return UPP::Format(pattern, (double)q);
364 }
365
Scan(const Value & txt) const366 Value ConvertDouble::Scan(const Value& txt) const {
367 String text = txt;
368 if(pattern.GetCount() && pattern != "%.10g") { // Fix text with patterns like "%2.!n EUR" (e.g. 1.2 EUR)
369 text = UPP::Filter(text, CharFilterDouble);
370 while(ToUpper(*text.Last()) == 'E')
371 text.Trim(text.GetCount() - 1);
372 }
373 Value v = UPP::Scan(DOUBLE_V, text);
374 if(IsError(v)) return v;
375 if(IsNull(v)) return notnull ? NotNullError() : v;
376 double m = v;
377 if(m >= minval && m <= maxval) return v;
378 return ErrorValue(UPP::Format(t_("Number must be between %g and %g."), minval, maxval));
379 }
380
Filter(int chr) const381 int ConvertDouble::Filter(int chr) const {
382 chr = CharFilterDouble(chr);
383 return comma && chr == '.' ? ',' : chr;
384 }
385
ConvertDouble(double minval,double maxval,bool notnull)386 ConvertDouble::ConvertDouble(double minval, double maxval, bool notnull)
387 : minval(minval), maxval(maxval), notnull(notnull)
388 {
389 pattern = "%.10g";
390 comma = false;
391 }
392
Pattern(const char * p)393 ConvertDouble& ConvertDouble::Pattern(const char *p)
394 {
395 pattern = p;
396 comma = String(Format(1.1)).Find(',') >= 0;
397 return *this;
398 }
399
default_min()400 Date& ConvertDate::default_min()
401 {
402 static Date v = Date::Low();
403 return v;
404 }
405
default_max()406 Date& ConvertDate::default_max()
407 {
408 static Date v = Date::High();
409 return v;
410 }
411
SetDefaultMinMax(Date min,Date max)412 void ConvertDate::SetDefaultMinMax(Date min, Date max)
413 {
414 default_min() = min;
415 default_max() = max;
416 }
417
ConvertDate(Date minval,Date maxval,bool notnull)418 ConvertDate::ConvertDate(Date minval, Date maxval, bool notnull)
419 : minval(minval), maxval(maxval), notnull(notnull) {
420 defaultval = Null;
421 }
422
Format(const Value & q) const423 Value ConvertDate::Format(const Value& q) const
424 {
425 if(IsDateTime(q))
426 return Convert::Format((Date)q);
427 return Convert::Format(q);
428 }
429
Scan(const Value & text) const430 Value ConvertDate::Scan(const Value& text) const {
431 Value v = UPP::Scan(DATE_V, text, defaultval);
432 if(IsError(v)) return v;
433 if(IsNull(v)) return notnull ? NotNullError() : v;
434 Date m = v;
435 Date minval = GetMin();
436 Date maxval = GetMax();
437 if(m >= minval && m <= maxval) return v;
438 return ErrorValue(t_("Date must be between ") + UPP::Format(minval) + t_("range\v and ") + UPP::Format(maxval) + ".");
439 }
440
Filter(int chr) const441 int ConvertDate::Filter(int chr) const {
442 return CharFilterDate(chr);
443 }
444
ConvertTime(Time minval,Time maxval,bool notnull)445 ConvertTime::ConvertTime(Time minval, Time maxval, bool notnull)
446 : minval(minval), maxval(maxval), notnull(notnull), seconds(true) {
447 defaultval = Null;
448 timealways = false;
449 dayend = false;
450 }
451
~ConvertTime()452 ConvertTime::~ConvertTime()
453 {
454 }
455
Scan(const Value & text) const456 Value ConvertTime::Scan(const Value& text) const
457 {
458 bool hastime;
459 Value v = UPP::Scan(TIME_V, text, defaultval, &hastime);
460 if(IsError(v)) return v;
461 if(IsNull(v)) return notnull ? NotNullError() : v;
462 Time m = v;
463 if(!hastime && dayend) {
464 m.hour = 23;
465 m.minute = 59;
466 m.second = 59;
467 v = m;
468 }
469 Time minval = GetMin();
470 Time maxval = GetMax();
471 if(m >= minval && m <= maxval) return v;
472 return ErrorValue(t_("Time must be between ") + UPP::Format(minval) + t_("range\v and ") + UPP::Format(maxval) + ".");
473 }
474
Filter(int chr) const475 int ConvertTime::Filter(int chr) const
476 {
477 if(IsDigit(chr) || chr == ' ' || chr == '.' || chr == ':')
478 return chr;
479 if(chr == ',')
480 return '.';
481 return CharFilterDate(chr);
482 }
483
Format(const Value & q) const484 Value ConvertTime::Format(const Value& q) const
485 {
486 if(IsVoid(q) || q.IsNull())
487 return String();
488 else
489 if(q.GetType() == TIME_V || timealways)
490 return ToTime((Date)q) != (Time)q || timealways ? UPP::Format(Time(q), seconds) : UPP::Format(Date(q));
491 else
492 return Convert::Format(q);
493 }
494
Scan(const Value & text) const495 Value ConvertString::Scan(const Value& text) const {
496 if(IsError(text)) return text;
497 if(text.GetType() == STRING_V) {
498 String s = text;
499 if(trimleft)
500 s = Upp::TrimLeft(s);
501 if(trimright)
502 s = Upp::TrimRight(s);
503 if(IsNull(s)) return notnull ? NotNullError() : Value(s);
504 if(s.GetLength() <= maxlen) return s;
505 }
506 if(text.GetType() == WSTRING_V) {
507 WString s = text;
508 if(trimleft)
509 s = Upp::TrimLeft(s);
510 if(trimright)
511 s = Upp::TrimRight(s);
512 if(IsNull(s)) return notnull ? NotNullError() : Value(s);
513 if(s.GetLength() <= maxlen) return s;
514 }
515 return ErrorValue(UPP::Format(t_("Please enter no more than %d characters."), maxlen));
516 }
517
StdConvertInt()518 const ConvertInt& StdConvertInt() { static ConvertInt h; return h; }
StdConvertIntNotNull()519 const ConvertInt& StdConvertIntNotNull() { static ConvertInt h(-INT_MAX, INT_MAX, true); return h; }
520
StdConvertDouble()521 const ConvertDouble& StdConvertDouble() { static ConvertDouble h; return h; }
StdConvertDoubleNotNull()522 const ConvertDouble& StdConvertDoubleNotNull() { static ConvertDouble h(DOUBLE_NULL_LIM, -DOUBLE_NULL_LIM, true); return h; }
523
StdConvertDate()524 const ConvertDate& StdConvertDate() { static ConvertDate h; return h; }
StdConvertDateNotNull()525 const ConvertDate& StdConvertDateNotNull() { static ConvertDate h(Date(0, 0, 0), Date(3000, 12, 31), true); return h; }
526
StdConvertTime()527 const ConvertTime& StdConvertTime() { static ConvertTime h; return h; }
StdConvertTimeNotNull()528 const ConvertTime& StdConvertTimeNotNull() { static ConvertTime h(Null, Null, true); return h; }
529
StdConvertString()530 const ConvertString& StdConvertString() { static ConvertString h; return h; }
StdConvertStringNotNull()531 const ConvertString& StdConvertStringNotNull() { static ConvertString h(INT_MAX, true); return h; }
532
Format(const Value & q) const533 Value MapConvert::Format(const Value& q) const {
534 return map.Get(q, default_value);
535 }
536
NoConvertClass()537 NoConvertClass::NoConvertClass() {}
538
Format(const Value & q) const539 Value NoConvertClass::Format(const Value& q) const {
540 return q;
541 }
542
NoConvert()543 const NoConvertClass& NoConvert() {
544 return Single<NoConvertClass>();
545 }
546
Scan(const Value & v) const547 Value ErrorConvertClass::Scan(const Value& v) const
548 {
549 return ErrorValue();
550 }
551
ErrorConvert()552 const ErrorConvertClass& ErrorConvert()
553 {
554 return Single<ErrorConvertClass>();
555 }
556
Format(const Value & v) const557 Value JoinConvert::Format(const Value& v) const {
558 String r;
559 ValueArray a = v;
560 for(int i = 0; i < item.GetCount(); i++) {
561 const Item& m = item[i];
562 if(m.pos < 0)
563 r << m.text;
564 else
565 r << (String) StdConvert().Format(m.convert->Format(a[m.pos]));
566 }
567 return r;
568 }
569
NextPos() const570 int JoinConvert::NextPos() const {
571 for(int i = item.GetCount() - 1; i >= 0; i--)
572 if(item[i].pos >= 0) return item[i].pos + 1;
573 return 0;
574 }
575
Add(const char * text)576 JoinConvert& JoinConvert::Add(const char *text) {
577 Item& m = item.Add();
578 m.pos = -1;
579 m.text = text;
580 return *this;
581 }
582
Add(int pos,const Convert & cv)583 JoinConvert& JoinConvert::Add(int pos, const Convert& cv) {
584 ASSERT(pos >= 0);
585 Item& m = item.Add();
586 m.pos = pos;
587 m.convert = &cv;
588 return *this;
589 }
590
Add(int pos)591 JoinConvert& JoinConvert::Add(int pos) {
592 Add(pos, StdConvert());
593 return *this;
594 }
595
Add(const Convert & cv)596 JoinConvert& JoinConvert::Add(const Convert& cv) {
597 Add(NextPos(), cv);
598 return *this;
599 }
600
Add()601 JoinConvert& JoinConvert::Add() {
602 Add(NextPos());
603 return *this;
604 }
605
Format(const Value & v) const606 Value FormatConvert::Format(const Value& v) const
607 {
608 ValueArray va;
609 if(IsValueArray(v))
610 va = v;
611 else
612 va.Add(v);
613 return UPP::Format(format, va.Get());
614 }
615
616 }
617