1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
14 #include "k8strutils.h"
17 #include "k8history.h"
18 //#include "k8jshistory.h"
24 ///////////////////////////////////////////////////////////////////////////////
25 #define KHISTORY_DATE_FORMAT "yyyy/MM/dd HH:mm:ss"
27 static QString dateToString (const QDateTime &dt) {
28 if (dt.isNull()) return QDateTime::currentDateTime().toString(KHISTORY_DATE_FORMAT);
29 return dt.toString(KHISTORY_DATE_FORMAT);
34 ///////////////////////////////////////////////////////////////////////////////
35 const uchar
*HistoryMessage::parse (const uchar
*ptr
, int *maxleft
, const QString
&myUni
) {
38 const uchar
*res
= K8JSON::parseRecord(rec
, ptr
, maxleft
);
39 if (!res
) return 0; // alas
40 if (rec
.type() != QVariant::Map
) return res
; // alas
41 QVariantMap
m(rec
.toMap());
42 uni
= m
["uni"].toString().trimmed();
43 action
= m
["action"].toString();
44 text
= m
["text"].toString();
49 date
= QDateTime::fromTime_t(rec
.toUInt()).toLocalTime();
51 default: date
= QDateTime::currentDateTime();
53 if (!myUni
.compare(uni
, Qt::CaseInsensitive
)) type
= Outgoing
;
54 else if (!uni
.compare("info", Qt::CaseInsensitive
)) type
= Info
;
55 else if (!uni
.compare("server", Qt::CaseInsensitive
)) type
= Server
;
61 "uni": "psyc://psyced.org/~lynX",
62 "timestamp": 1235744956, //2009/02/27 16:29:16
64 "text": "and that friend is the president now"
68 ///////////////////////////////////////////////////////////////////////////////
69 HistoryFile::HistoryFile (const QString
&fname
, const QString
&myUni
) :
70 mCount(-1), mFile(fname
+".json"), mMap(0), mSize(-1), mMyUni(myUni
)
77 HistoryFile::~HistoryFile () {
82 bool HistoryFile::isOpen () const { return mFile
.isOpen(); }
83 //bool HistoryFile::exists () const { return mFile.exists(); }
84 int HistoryFile::count () { return mCount
; }
87 void HistoryFile::remove () {
93 //bool HistoryFile::open (bool allowCreate)
94 bool HistoryFile::open (OpenMode newmode
) {
97 if (!mFile
.exists()) {
98 if (newmode
!= Write
) return false;
99 QString
dir(mFile
.fileName());
100 int idx
= dir
.lastIndexOf('/');
108 if (!mFile
.open(newmode
==Write
?QIODevice::ReadWrite
:QIODevice::ReadOnly
)) return false;
110 mFile
.write("//Dyskinesia JS history file\n");
111 mFile
.write("//'timestamp' is UTC unixtime, but commented date is local\n");
113 mSize
= mFile
.size();
114 if (!(mMap
= mFile
.map(0, mSize
))) {
118 if (!scanMessages()) {
126 bool HistoryFile::scanMessages () {
127 //fprintf(stderr, "scanning...\n");
130 const uchar
*sj
= K8JSON::skipBlanks(mMap
, &len
);
132 // no messages at all
133 //fprintf(stderr, "empty file.\n");
139 //fprintf(stderr, "not a list (%u).\n", sj[-1]);
144 sj
= K8JSON::skipBlanks(sj
, &len
);
145 if (!sj
|| *sj
!= '{') {
147 //fprintf(stderr, "bad (%u) (%c).\n", sj[0], sj[0]);
150 mMsgOfs
<< (int)(sj
-mMap
); // save current message start
151 //fprintf(stderr, "ofs: 0x%08x\n", mMsgOfs[mCount]);
153 sj
= K8JSON::skipRec(sj
, &len
);
154 if (!sj
) return false; // invalid ON file
159 mMsgOfs
<< (int)(sj
-mMap
); // save last message end
161 fprintf(stderr, "---------------------\n");
162 for (int f = 0; f < mMsgOfs.count(); f++) {
163 fprintf(stderr, "%3i: 0x%08x\n", f, (uint)(mMsgOfs[f]));
167 default: return false;
169 //fprintf(stderr, " end: 0x%08x\n", (uint)(sj-mMap));
170 len
--; sj
++; // skip ','
172 return false; // no eof
176 void HistoryFile::close () {
177 if (mFile
.isOpen()) {
178 if (mMap
) mFile
.unmap(mMap
);
181 mCount
= -1; mSize
= -1;
188 *FIXME: check for errors!
190 bool HistoryFile::append (const HistoryMessage
&msg
) {
191 if (!mFile
.isOpen()) return false;
196 mFile
.resize(mMsgOfs
[mCount
]); // trunc file
197 mFile
.seek(mFile
.size());
199 // new file, no entries
201 } else mFile
.write(",");
202 // save start of the new message
203 mMsgOfs
[mCount
] = mFile
.size();
206 QByteArray
ba(QString::number(mCount
).toAscii());
209 mFile
.write("\n \"uni\": ");
210 ba
= K8JSON::quote(msg
.uni
).toUtf8();
213 mFile
.write(",\n \"timestamp\": ");
214 QDateTime
utc(msg
.date
.toUTC());
215 ba
= QString::number(utc
.toTime_t()).toAscii();
218 ba
= msg
.date
.toString(KHISTORY_DATE_FORMAT
).toAscii();
222 if (!msg
.action
.isEmpty()) {
223 mFile
.write("\"action\": ");
224 ba
= K8JSON::quote(msg
.action
).toUtf8();
228 mFile
.write("\"text\": ");
229 ba
= K8JSON::quote(msg
.text
).toUtf8();
231 mFile
.write("\n }\n");
234 // save end of the written message
235 mMsgOfs
<< mFile
.size();
238 if (!(mMap
= mFile
.map(0, mSize
))) {
247 bool HistoryFile::read (int idx
, HistoryMessage
&msg
) {
248 if (!mMap
) return false;
249 if (idx
< 0) idx
+= mCount
;
250 if (idx
< 0 || idx
>= mCount
) return false;
251 int left
= mSize
-(int)mMsgOfs
[idx
];
252 if (!msg
.parse(mMap
+mMsgOfs
[idx
], &left
, mMyUni
)) return false;