better parser for broken quoted encoding (starting dot)
[chiroptera.git] / chibackend / package.d
blobea59b8e8d178d3fdf8fc211e93fc343be04bdd26
1 /* E-Mail Client
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/>.
17 module chibackend;
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;
28 public import iv.dynstring : DynStr = dynstring;
31 // ////////////////////////////////////////////////////////////////////////// //
32 // some utilities
34 // this marks the buffer as "opaque", so GC won't scan its contents
35 // this is to avoid false positives in GC
37 public void markGCOpaque (const(void)[] buf) /*nothrow @trusted*/ {
38 import core.memory : GC;
39 if (buf !is null && buf.length != 0 && GC.addrOf(buf.ptr)) {
40 { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "X000!\n"); }
41 GC.setAttr(buf.ptr, GC.BlkAttr.NO_SCAN/*|GC.BlkAttr.NO_INTERIOR*/);
42 { import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "X001!\n"); }
48 public __gshared string chiroCLIMailPath = "~/Mail";
50 public void chiroParseCommonCLIArgs (ref string[] args) {
51 for (usize idx = 1; idx < args.length; ) {
52 string arg = args[idx];
53 if (arg.length == 0) { ++idx; continue; }
54 if (arg == "--") break;
56 usize argcount = 1;
57 if (arg == "--mailpath") {
58 argcount = 2;
59 if (idx+1 >= args.length) throw new Exception("\"--mailpath\" expects argument");
60 chiroCLIMailPath = args[idx+1];
61 } else if (arg.strEquCI("--nocompress")) {
62 ChiroCompressionLevel = 0;
63 } else if (arg.strEquCI("--max")) {
64 ChiroCompressionLevel = 666;
65 } else if (arg.length == 2 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
66 ChiroCompressionLevel = arg[1]-'0';
67 } else {
68 ++idx;
69 continue;
71 foreach (usize c; idx+argcount..args.length) args[c-argcount] = args[c];
72 args.length -= argcount;
75 if (chiroCLIMailPath.length == 0) {
76 chiroCLIMailPath = "./";
77 } else if (chiroCLIMailPath[0] == '~') {
78 char[] dpath = new char[chiroCLIMailPath.length+4096];
79 dpath = expandTilde(dpath, chiroCLIMailPath);
80 while (dpath.length > 1 && dpath[$-1] == '/') dpath = dpath[0..$-1];
81 dpath ~= '/';
82 chiroCLIMailPath = cast(string)dpath; // it is safe to cast here
83 } else if (chiroCLIMailPath[$-1] == '/') {
84 chiroCLIMailPath ~= '/';
89 // ////////////////////////////////////////////////////////////////////////// //
90 public string SysTimeToRFCString() (in auto ref Imp!"std.datetime".SysTime tm) {
91 import std.datetime;
93 //Sun, 7 Dec 2014 16:04:04 +0200
94 immutable string[7] daysOfWeekNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
95 immutable string[12] monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
97 string tzstr (Duration utcOffset) {
98 import std.format : format;
99 immutable absOffset = abs(utcOffset);
100 int hours;
101 int minutes;
102 absOffset.split!("hours", "minutes")(hours, minutes);
103 return format(utcOffset < Duration.zero ? "-%02d%02d" : "+%02d%02d", hours, minutes);
106 try {
107 import std.format : format;
108 auto dateTime = cast(DateTime)tm;
109 return "%s, %d %s %d %02d:%02d:%02d %s".format(
110 daysOfWeekNames[dateTime.dayOfWeek],
111 dateTime.day,
112 monthNames[dateTime.month-1],
113 dateTime.year,
114 dateTime.hour,
115 dateTime.minute,
116 dateTime.second,
117 tzstr(tm.utcOffset),
119 } catch (Exception e) {
120 assert(0, "format() threw.");
125 // ////////////////////////////////////////////////////////////////////////// //
126 // simple exponential running average
127 struct RunningAverageExp {
128 protected:
129 double mFadeoff = 0.1; // 10%
130 double mCurrValue = 0.0;
131 ulong mStartTime = 0;
132 uint mProcessed = 0;
133 uint mTotal = 0;
134 uint mProgTrh = 1024;
135 ulong mTimerNextUpdate = 0;
137 public nothrow @trusted @nogc:
138 this (const double aFadeoff) { pragma(inline, true);mFadeoff = aFadeoff; }
140 void reset () { pragma(inline, true);mCurrValue = 0.0; }
142 @property double fadeoff () const pure { pragma(inline, true);return mFadeoff; }
143 @property void fadeoff (const double aFadeoff) { pragma(inline, true);mFadeoff = aFadeoff; }
145 void update (const double newValue) { pragma(inline, true);mCurrValue = mFadeoff*newValue+(1.0-mFadeoff)*mCurrValue; }
147 @property double value () const pure { pragma(inline, true);return mCurrValue; }
148 @property void value (const double aValue) { pragma(inline, true);mCurrValue = aValue; }
150 @property uint uintValue () const pure { pragma(inline, true);return cast(uint)mCurrValue; }
152 void startTimer (uint total) {
153 mStartTime = GetTickCount();
154 mProcessed = 0;
155 mTotal = total;
156 mCurrValue = 0.0;
157 mTimerNextUpdate = 0;
160 @property uint progressThreshold () const pure { pragma(inline, true); return mProgTrh; }
161 @property void progressThreshold (uint trh) { pragma(inline, true); mProgTrh = trh; }
163 @property uint timerTotal () const pure { pragma(inline, true); return mTotal; }
164 @property void timerTotal (uint total) { pragma(inline, true); mTotal = total; }
166 // returns `true` if it is time to show progress
167 bool updateProcessed (uint count, bool delta=true) {
168 if (!count) return false;
169 immutable uint prev = mProcessed;
170 if (delta) mProcessed += count; else mProcessed = count;
171 if (mProgTrh == 0 || prev == 0) return true;
172 return (prev/mProgTrh != mProcessed/mProgTrh);
175 // seconds
176 void getTimeETA() (out uint time, out uint eta, out uint curr, out uint total, out uint percent, ulong ctime=ulong.max) {
177 if (ctime == ulong.max) ctime = GetTickCount();
178 immutable uint secs = cast(uint)(ctime-mStartTime);
179 time = secs;
180 if (secs && mTotal && mProcessed) {
181 //secs/count*total
182 eta = cast(uint)(cast(ulong)secs*mTotal/mProcessed);
183 if (mCurrValue < eta) mCurrValue = eta;
184 update(eta);
185 if (mCurrValue < eta) mCurrValue = eta;
187 eta = cast(uint)mCurrValue;
189 curr = mProcessed;
190 total = mTotal;
191 percent = (mTotal ? cast(uint)(cast(ulong)mProcessed*100U/mTotal) : 0U);
194 bool updateProcessedWithProgress (uint count, bool delta=true, bool forceWrite=false, bool writeNL=false) {
195 if (!updateProcessed(count, delta)) {
196 if (!forceWrite) return false;
199 immutable ctime = GetTickCount();
200 if (!forceWrite && mTimerNextUpdate > ctime) return false;
201 mTimerNextUpdate = ctime+1;
203 uint secs, eta, curr, tot, prc;
204 getTimeETA(out secs, out eta, out curr, out tot, out prc, ctime);
206 char[128] buf = void;
207 uint h, m, s;
209 import core.stdc.stdio : snprintf;
210 auto len = snprintf(buf.ptr, buf.length, "\r%s[%u/%u] %3u%%", (writeNL ? "".ptr : " ".ptr), curr, tot, prc);
211 extractHMS(secs, out h, out m, out s);
212 if (len < buf.length) len += snprintf(buf.ptr+len, buf.length-len, " %02u:%02u:%02u", h, m, s);
213 extractHMS(eta, out h, out m, out s);
214 if (len < buf.length) len += snprintf(buf.ptr+len, buf.length-len, " %02u:%02u:%02u", h, m, s);
215 if (len < buf.length) len += snprintf(buf.ptr+len, buf.length-len, "\x1b[K%c", (writeNL ? '\n' : '\r'));
217 import core.sys.posix.unistd : write, STDOUT_FILENO;
218 write(STDOUT_FILENO, buf.ptr, cast(usize)len);
220 return true;
223 static extractHMS (uint secs, out uint h, out uint m, out uint s) {
224 s = secs%60; secs /= 60;
225 m = secs%60; secs /= 60;
226 h = secs;
229 static ulong GetTickCount() () {
230 import core.time;
231 immutable MonoTime ctt = MonoTime.currTime;
232 return cast(ulong)ctt.ticks/cast(ulong)ctt.ticksPerSecond;