2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 public import chibackend
.sqbase
;
20 public import chibackend
.decode
;
21 public import chibackend
.parse
;
22 public import chibackend
.mfilter
;
23 public import chibackend
.mbuilder
;
25 private import iv
.strex
;
26 private import iv
.vfs
.util
;
29 // ////////////////////////////////////////////////////////////////////////// //
32 // this marks the buffer as "opaque", so GC won't scan its contents
33 // this is to avoid false positives in GC
35 public void markGCOpaque (const(void)[] buf) /*nothrow @trusted*/ {
36 import core.memory : GC;
37 if (buf !is null && buf.length != 0 && GC.addrOf(buf.ptr)) {
38 { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "X000!\n"); }
39 GC.setAttr(buf.ptr, GC.BlkAttr.NO_SCAN/*|GC.BlkAttr.NO_INTERIOR*/);
40 { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "X001!\n"); }
46 public __gshared string chiroCLIMailPath
= "~/Mail";
48 public void chiroParseCommonCLIArgs (ref string
[] args
) {
49 for (usize idx
= 1; idx
< args
.length
; ) {
50 string arg
= args
[idx
];
51 if (arg
.length
== 0) { ++idx
; continue; }
52 if (arg
== "--") break;
55 if (arg
== "--mailpath") {
57 if (idx
+1 >= args
.length
) throw new Exception("\"--mailpath\" expects argument");
58 chiroCLIMailPath
= args
[idx
+1];
59 } else if (arg
.strEquCI("--nocompress")) {
60 ChiroCompressionLevel
= 0;
61 } else if (arg
.strEquCI("--max")) {
62 ChiroCompressionLevel
= 666;
63 } else if (arg
.length
== 2 && arg
[0] == '-' && arg
[1] >= '0' && arg
[1] <= '9') {
64 ChiroCompressionLevel
= arg
[1]-'0';
69 foreach (usize c
; idx
+argcount
..args
.length
) args
[c
-argcount
] = args
[c
];
70 args
.length
-= argcount
;
73 if (chiroCLIMailPath
.length
== 0) {
74 chiroCLIMailPath
= "./";
75 } else if (chiroCLIMailPath
[0] == '~') {
76 char[] dpath
= new char[chiroCLIMailPath
.length
+4096];
77 dpath
= expandTilde(dpath
, chiroCLIMailPath
);
78 while (dpath
.length
> 1 && dpath
[$-1] == '/') dpath
= dpath
[0..$-1];
80 chiroCLIMailPath
= cast(string
)dpath
; // it is safe to cast here
81 } else if (chiroCLIMailPath
[$-1] == '/') {
82 chiroCLIMailPath
~= '/';
87 // ////////////////////////////////////////////////////////////////////////// //
88 public string
SysTimeToRFCString() (in auto ref Imp
!"std.datetime".SysTime tm
) {
91 //Sun, 7 Dec 2014 16:04:04 +0200
92 immutable string
[7] daysOfWeekNames
= ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
93 immutable string
[12] monthNames
= ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
95 string
tzstr (Duration utcOffset
) {
96 import std
.format
: format
;
97 immutable absOffset
= abs(utcOffset
);
100 absOffset
.split
!("hours", "minutes")(hours
, minutes
);
101 return format(utcOffset
< Duration
.zero ?
"-%02d%02d" : "+%02d%02d", hours
, minutes
);
105 import std
.format
: format
;
106 auto dateTime
= cast(DateTime
)tm
;
107 return "%s, %d %s %d %02d:%02d:%02d %s".format(
108 daysOfWeekNames
[dateTime
.dayOfWeek
],
110 monthNames
[dateTime
.month
-1],
117 } catch (Exception e
) {
118 assert(0, "format() threw.");
123 // ////////////////////////////////////////////////////////////////////////// //
124 public struct DynStr
{
130 usize alloted
= void;
132 inout(char)* ptr () inout pure nothrow @trusted @nogc { pragma(inline
, true); return cast(char*)udata
; }
133 void ptr (void *v
) pure nothrow @trusted @nogc { pragma(inline
, true); udata
= cast(usize
)v
; }
140 inout(Data
)* datap () inout pure nothrow @trusted @nogc { pragma(inline
, true); return cast(Data
*)udata
; }
141 void datap (Data
*v
) pure nothrow @trusted @nogc { pragma(inline
, true); udata
= cast(usize
)v
; }
143 void incRef () nothrow @trusted @nogc {
144 pragma(inline
, true);
145 if (udata
) ++((cast(Data
*)udata
).rc
);
148 void decRef () nothrow @trusted @nogc {
149 pragma(inline
, true);
151 if (--((cast(Data
*)udata
).rc
) == 0) {
152 import core
.stdc
.stdlib
: free
;
153 if ((cast(Data
*)udata
).udata
) free((cast(Data
*)udata
).ptr
);
160 bool isMyData (const(void)* ptr
) pure nothrow @trusted @nogc {
161 //pragma(inline, true);
162 if (!ptr ||
!udata ||
!datap
.udata
) return false;
163 immutable usize cc
= cast(usize
)ptr
;
164 return (cc
>= datap
.udata
&& cc
< datap
.udata
+datap
.alloted
);
171 nothrow @trusted @nogc:
172 this (DynStr s
) { pragma(inline
, true); udata
= s
.udata
; incRef(); }
174 this (const(char)[] s
) {
176 ensure
!false(s
.length
);
177 datap
.ptr
[0..s
.length
] = s
[];
178 datap
.used
= s
.length
;
182 this (this) { pragma(inline
, true); incRef(); }
183 ~this () { pragma(inline
, true); decRef(); }
185 void clear () { pragma(inline
, true); decRef(); }
187 void compact () nothrow @trusted @nogc {
190 if (!dp
.udata
) return;
191 if ((dp
.used|
0x1fU
)+1U > dp
.alloted
) {
192 immutable usize newsz
= (dp
.used|
0x1fU
)+1U;
193 import core
.stdc
.stdlib
: realloc
;
194 void *np
= realloc(dp
.ptr
, newsz
);
195 if (np
is null) return;
201 void ensure(bool overalloc
) (usize sz
) nothrow @trusted @nogc {
203 static if (overalloc
) {
204 if (sz
<= 256) sz
= (sz|
0x1fU
)+1U;
205 else if (sz
<= 1024) sz
= (sz|
0x3fU
)+1U;
206 else if (sz
<= 8192) sz
= (sz|
0x1ffU
)+1U;
207 else if (sz
<= 16384) sz
= (sz|
0x3ffU
)+1U;
208 else if (sz
<= 32768) sz
= (sz|
0xfffU
)+1U;
209 else sz
= (sz|
0x1fffU
)+1U;
215 import core
.stdc
.stdlib
: calloc
;
216 dp
= cast(Data
*)calloc(1, Data
.sizeof
);
217 if (dp
is null) { import core
.exception
: onOutOfMemoryErrorNoGC
; onOutOfMemoryErrorNoGC(); }
221 if (sz
> dp
.alloted
) {
222 import core
.stdc
.stdlib
: realloc
;
223 void *np
= realloc(dp
.ptr
, sz
);
224 if (np
is null) { import core
.exception
: onOutOfMemoryErrorNoGC
; onOutOfMemoryErrorNoGC(); }
230 void makeUnique () nothrow @trusted @nogc {
231 if (!udata || datap
.rc
== 1) return;
232 immutable usize oldudata
= udata
;
233 immutable usize oldused
= datap
.used
;
235 ensure
!false(oldused
);
236 immutable usize xudata
= udata
;
242 void reserve(bool overalloc
=true) (usize newsz
) { pragma(inline
, true); ensure
!overalloc(newsz
); }
244 void append(bool overalloc
=true) (const(void)[] buf
) {
245 if (buf
.length
== 0) return;
246 if (buf
.length
>= cast(usize
)0x2fff_ffff
) { import core
.exception
: onOutOfMemoryErrorNoGC
; onOutOfMemoryErrorNoGC(); }
247 if (udata
&& cast(usize
)0x3000_0000-datap
.used
< buf
.length
) { import core
.exception
: onOutOfMemoryErrorNoGC
; onOutOfMemoryErrorNoGC(); }
249 if (isMyData(buf
.ptr
)) {
252 buf
= cast(const(void)[])tmpstr
.getData();
257 if (data
.used
+buf
.length
> data
.alloted
) {
258 ensure
!overalloc(data
.used
+buf
.length
);
262 ensure
!overalloc(buf
.length
);
265 data
.ptr
[data
.used
..data
.used
+buf
.length
] = cast(const(char)[])buf
;
266 data
.used
+= buf
.length
;
268 void append (const char ch
) { pragma(inline
, true); append((&ch
)[0..1]); }
270 void set (const(void)[] buf
) {
271 pragma(inline
, true);
272 if (buf
.length
== 0) {
275 if (isMyData(buf
.ptr
)) {
279 append
!false(tmpstr
.getData
);
290 void set (const char ch
) { pragma(inline
, true); set((&ch
)[0..1]); }
292 uint length () const pure { pragma(inline
, true); return (udata ?
cast(uint)datap
.used
: 0U); }
294 const(char)[] getData () const pure { pragma(inline
, true); return (udata
&& datap
.used ? datap
.ptr
[0..datap
.used
] : null); }
295 const(ubyte)[] getBytes () const pure { pragma(inline
, true); return cast(const(ubyte)[])(udata
&& datap
.used ? datap
.ptr
[0..datap
.used
] : null); }
297 void opAssign() (const(char)[] s
) { pragma(inline
, true); set(s
); }
298 void opAssign() (const char ch
) { pragma(inline
, true); set(ch
); }
299 void opAssign() (DynStr s
) { pragma(inline
, true); if (s
.udata
!= udata
) set(s
); }
301 void opOpAssign(string op
:"~") (const(char)[] s
) { pragma(inline
, true); append(s
); }
302 void opOpAssign(string op
:"~") (const char ch
) { pragma(inline
, true); append(ch
); }
303 void opOpAssign(string op
:"~") (DynStr s
) { pragma(inline
, true); append(s
); }
305 const(char)[] opCast(T
:const(char)[]) () const pure { pragma(inline
, true); return getData(); }
306 const(ubyte)[] opCast(T
:const(ubyte)[]) () const pure { pragma(inline
, true); return getBytes(); }
308 bool opEqual (const(char)[] other
) const pure { pragma(inline
, true); return (other
[] == getBytes()[]); }
309 bool opEqual (const ref DynStr other
) const pure { pragma(inline
, true); return (other
.getBytes()[] == getBytes()[]); }
311 int opCmp (const(char)[] other
) const pure {
312 if (!udata || datap
.used
== 0) return (other
.length ?
-1 : 0);
313 if (other
.length
== 0) return 1;
314 if (datap
.ptr
== other
.ptr
&& datap
.used
== other
.length
) return 0;
315 import core
.stdc
.string
: memcmp
;
316 immutable int cres
= memcmp(datap
.ptr
, other
.ptr
, (datap
.used
< other
.length ? datap
.used
: other
.length
));
317 if (cres
!= 0) return (cres
< 0 ?
-1 : +1);
318 return (datap
.used
< other
.length ?
-1 : datap
.used
> other
.length ?
+1 : 0);
321 int opCmp (const ref DynStr other
) const { pragma(inline
, true); return opCmp(other
.getData
); }
323 void appendQEncoded (const(void)[] ss
) {
324 static bool isSpecial (immutable char ch
) pure nothrow @safe @nogc {
335 if (ss
.length
== 0) return;
336 const(char)[] s
= cast(const(char)[])ss
;
338 static immutable string hexd
= "0123456789abcdef";
340 bool needWork
= (s
[0] == '=' || s
[0] == '?');
341 if (!needWork
) foreach (char ch
; s
) if (isSpecial(ch
)) { needWork
= true; break; }
346 append("=?UTF-8?Q?"); // quoted printable
347 foreach (char ch
; s
) {
348 if (ch
<= ' ') ch
= '_';
349 if (!isSpecial(ch
) && ch
!= '=' && ch
!= '?') {
353 append(hexd
[(cast(ubyte)ch
)>>4]);
354 append(hexd
[(cast(ubyte)ch
)&0x0f]);
361 void appendB64Encoded (const(void)[] buf
, uint maxlinelen
=76) {
362 static immutable string b64alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
367 void putB64Char (immutable char ch
) nothrow @trusted @nogc {
368 if (maxlinelen
&& linelen
>= maxlinelen
) { append("\r\n"); linelen
= 0; }
373 void encodeChunk () nothrow @trusted @nogc {
374 if (btspos
== 0) return;
375 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0xfc)>>2]);
377 putB64Char(b64alphabet
.ptr
[(bts.ptr
[0]&0x03)<<4]);
378 /*static if (padding)*/ { putB64Char('='); putB64Char('='); }
381 putB64Char(b64alphabet
.ptr
[((bts.ptr
[0]&0x03)<<4)|
((bts.ptr
[1]&0xf0)>>4)]);
383 putB64Char(b64alphabet
.ptr
[(bts.ptr
[1]&0x0f)<<2]);
384 /*static if (padding)*/ putB64Char('=');
387 putB64Char(b64alphabet
.ptr
[((bts.ptr
[1]&0x0f)<<2)|
((bts.ptr
[2]&0xc0)>>6)]);
388 putB64Char(b64alphabet
.ptr
[bts.ptr
[2]&0x3f]);
394 foreach (immutable ubyte ib
; (cast(const(ubyte)[])buf
)[]) {
395 bts.ptr
[btspos
++] = ib
;
396 if (btspos
== 3) encodeChunk();
398 if (btspos
!= 0) encodeChunk();
400 if (maxlinelen
) append("\r\n");
403 void appendNum(T
) (const T v
) if (__traits(isIntegral
, T
)) {
404 import core
.stdc
.stdio
: snprintf
;
405 static if (T
.sizeof
<= 4) {
407 static if (__traits(isUnsigned
, T
)) {
408 auto len
= snprintf(buf
.ptr
, buf
.length
, "%u", cast(uint)v
);
410 auto len
= snprintf(buf
.ptr
, buf
.length
, "%d", cast(int)v
);
414 char[128] buf
= void;
415 static if (__traits(isUnsigned
, T
)) {
416 auto len
= snprintf(buf
.ptr
, buf
.length
, "%llu", cast(ulong)v
);
418 auto len
= snprintf(buf
.ptr
, buf
.length
, "%lld", cast(long)v
);
424 void removeASCIICtrls () {
425 static bool isCtrl (const char ch
) pure nothrow @safe @nogc {
426 pragma(inline
, true);
427 return (ch
< 32 && ch
!= '\t' && ch
!= '\n');
430 bool needWork
= false;
431 foreach (immutable char ch
; getData
) if (isCtrl(ch
)) { needWork
= true; break; }
432 if (!needWork
) return;
435 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
436 foreach (ref char ch
; buf
) if (isCtrl(ch
)) ch
= ' ';
440 void lowerInPlace () {
441 bool needWork
= false;
442 foreach (immutable char ch
; getData
) if (ch
>= 'A' && ch
<= 'Z') { needWork
= true; break; }
443 if (!needWork
) return;
446 char[] buf
= cast(char[])datap
.ptr
[0..datap
.used
];
447 foreach (ref char ch
; buf
) if (ch
>= 'A' && ch
<= 'Z') ch
+= 32;
452 // ////////////////////////////////////////////////////////////////////////// //
453 // simple exponential running average
454 struct RunningAverageExp
{
456 double mFadeoff
= 0.1; // 10%
457 double mCurrValue
= 0.0;
458 ulong mStartTime
= 0;
461 uint mProgTrh
= 1024;
462 ulong mTimerNextUpdate
= 0;
464 public nothrow @trusted @nogc:
465 this (const double aFadeoff
) { pragma(inline
, true);mFadeoff
= aFadeoff
; }
467 void reset () { pragma(inline
, true);mCurrValue
= 0.0; }
469 @property double fadeoff () const pure { pragma(inline
, true);return mFadeoff
; }
470 @property void fadeoff (const double aFadeoff
) { pragma(inline
, true);mFadeoff
= aFadeoff
; }
472 void update (const double newValue
) { pragma(inline
, true);mCurrValue
= mFadeoff
*newValue
+(1.0-mFadeoff
)*mCurrValue
; }
474 @property double value () const pure { pragma(inline
, true);return mCurrValue
; }
475 @property void value (const double aValue
) { pragma(inline
, true);mCurrValue
= aValue
; }
477 @property uint uintValue () const pure { pragma(inline
, true);return cast(uint)mCurrValue
; }
479 void startTimer (uint total
) {
480 mStartTime
= GetTickCount();
484 mTimerNextUpdate
= 0;
487 @property uint progressThreshold () const pure { pragma(inline
, true); return mProgTrh
; }
488 @property void progressThreshold (uint trh
) { pragma(inline
, true); mProgTrh
= trh
; }
490 @property uint timerTotal () const pure { pragma(inline
, true); return mTotal
; }
491 @property void timerTotal (uint total
) { pragma(inline
, true); mTotal
= total
; }
493 // returns `true` if it is time to show progress
494 bool updateProcessed (uint count
, bool delta
=true) {
495 if (!count
) return false;
496 immutable uint prev
= mProcessed
;
497 if (delta
) mProcessed
+= count
; else mProcessed
= count
;
498 if (mProgTrh
== 0 || prev
== 0) return true;
499 return (prev
/mProgTrh
!= mProcessed
/mProgTrh
);
503 void getTimeETA() (out uint time
, out uint eta
, out uint curr
, out uint total
, out uint percent
, ulong ctime
=ulong.max
) {
504 if (ctime
== ulong.max
) ctime
= GetTickCount();
505 immutable uint secs
= cast(uint)(ctime
-mStartTime
);
507 if (secs
&& mTotal
&& mProcessed
) {
509 eta
= cast(uint)(cast(ulong)secs
*mTotal
/mProcessed
);
510 if (mCurrValue
< eta
) mCurrValue
= eta
;
512 if (mCurrValue
< eta
) mCurrValue
= eta
;
514 eta
= cast(uint)mCurrValue
;
518 percent
= (mTotal ?
cast(uint)(cast(ulong)mProcessed
*100U/mTotal
) : 0U);
521 bool updateProcessedWithProgress (uint count
, bool delta
=true, bool forceWrite
=false, bool writeNL
=false) {
522 if (!updateProcessed(count
, delta
)) {
523 if (!forceWrite
) return false;
526 immutable ctime
= GetTickCount();
527 if (!forceWrite
&& mTimerNextUpdate
> ctime
) return false;
528 mTimerNextUpdate
= ctime
+1;
530 uint secs
, eta
, curr
, tot
, prc
;
531 getTimeETA(out secs
, out eta
, out curr
, out tot
, out prc
, ctime
);
533 char[128] buf
= void;
536 import core
.stdc
.stdio
: snprintf
;
537 auto len
= snprintf(buf
.ptr
, buf
.length
, "\r%s[%u/%u] %3u%%", (writeNL ?
"".ptr
: " ".ptr
), curr
, tot
, prc
);
538 extractHMS(secs
, out h
, out m
, out s
);
539 if (len
< buf
.length
) len
+= snprintf(buf
.ptr
+len
, buf
.length
-len
, " %02u:%02u:%02u", h
, m
, s
);
540 extractHMS(eta
, out h
, out m
, out s
);
541 if (len
< buf
.length
) len
+= snprintf(buf
.ptr
+len
, buf
.length
-len
, " %02u:%02u:%02u", h
, m
, s
);
542 if (len
< buf
.length
) len
+= snprintf(buf
.ptr
+len
, buf
.length
-len
, "\x1b[K%c", (writeNL ?
'\n' : '\r'));
544 import core
.sys
.posix
.unistd
: write
, STDOUT_FILENO
;
545 write(STDOUT_FILENO
, buf
.ptr
, cast(usize
)len
);
550 static extractHMS (uint secs
, out uint h
, out uint m
, out uint s
) {
551 s
= secs
%60; secs
/= 60;
552 m
= secs
%60; secs
/= 60;
556 static ulong GetTickCount() () {
558 immutable MonoTime ctt
= MonoTime
.currTime
;
559 return cast(ulong)ctt
.ticks
/cast(ulong)ctt
.ticksPerSecond
;