1
2 /**************************************************************************
3 * Copyright (C) 2007-2015 Ruben Pollan Bella <meskio@sindominio.net> *
4 * *
5 * This file is part of TuDu. *
6 * *
7 * TuDu is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; version 3 of the License. *
10 * *
11 * TuDu is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 **************************************************************************/
19
20 #include "parser.h"
21
Parser(const char * path)22 Parser::Parser(const char* path)
23 {
24 file.imbue(locale(""));
25 file.open(path);
26 }
27
~Parser()28 Parser::~Parser()
29 {
30 file.close();
31 }
32
parse(ToDo & todo,Sched & sched)33 bool Parser::parse(ToDo& todo, Sched& sched)
34 {
35 wchar_t ch;
36 bool tag = false, att = false;
37 iToDo iterator(todo);
38
39 if (!file) return false; //if the file is not open
40
41 str = L"";
42 collect_text = false;
43 deadline = false;
44 scheduled = false;
45 while (file.get(ch))
46 {
47 switch (ch)
48 {
49 case L'<':
50 if (tag) return false;
51 tag = true;
52 if (collect_text)
53 {
54 txt = str;
55 str = L"";
56 }
57 else collect_text = true;
58 break;
59 case L'>':
60 if (!tag) return false;
61 tag = false;
62 collect_text = false;
63 if (!att) ptag(iterator,sched);
64 else {
65 patt(iterator);
66 att = false;
67 }
68 str = L"";
69 break;
70 case L'\t':
71 case L' ':
72 if (tag)
73 {
74 if (!att)
75 {
76 ptag(iterator,sched);
77 att = true;
78 str = L"";
79 } else if (str.length()) {
80 patt(iterator);
81 str = L"";
82 }
83 } else
84 if (collect_text) str += ch;
85 break;
86 case L'&':
87 if (collect_text) str += amp();
88 break;
89 default:
90 if (collect_text) str += ch;
91 }
92 }
93
94 // in case of empty file create an empty task
95 if (!todo.haveChild())
96 iterator.addChild(new ToDo());
97
98 return true;
99 }
100
amp()101 wchar_t Parser::amp()
102 {
103 wchar_t ch;
104 wstring str = L"";
105
106 file.get(ch);
107 while (L';' != ch)
108 {
109 str += ch;
110 file.get(ch);
111 }
112 if (L"amp" == str) return L'&';
113 if (L"lt" == str) return L'<';
114 if (L"gt" == str) return L'>';
115 else return L' ';
116 }
117
ptag(iToDo & iterator,Sched & sched)118 void Parser::ptag(iToDo& iterator, Sched& sched)
119 {
120 static bool first_todo = true;
121
122 if (L"todo" == str)
123 {
124 if (first_todo) first_todo = false;
125 else {
126 iterator.addChild(new ToDo());
127 iterator.in();
128 }
129 }
130 if (L"/todo" == str)
131 {
132 iterator.out();
133 }
134 if (L"title" == str)
135 {
136 collect_text = true;
137 }
138 if (L"/title" == str)
139 {
140 iterator->getTitle() = txt;
141 }
142 if (L"deadline" == str)
143 {
144 deadline = true;
145 }
146 if (L"/deadline" == str)
147 {
148 deadline = false;
149 }
150 if (L"day" == str)
151 {
152 collect_text = true;
153 }
154 if (L"/day" == str)
155 {
156 char num[3];
157 wcstombs(num, txt.c_str(), 3);
158 int day = atoi(num);
159 if (deadline)
160 {
161 iterator->deadline().day() = day;
162 }
163 else if (scheduled)
164 iterator->sched().day() = day;
165 }
166 if (L"month" == str)
167 {
168 collect_text = true;
169 }
170 if (L"/month" == str)
171 {
172 char num[3];
173 wcstombs(num, txt.c_str(), 3);
174 int month = atoi(num);
175 if (deadline)
176 {
177 iterator->deadline().month() = month;
178 }
179 else if (scheduled)
180 iterator->sched().month() = month;
181 }
182 if (L"year" == str)
183 {
184 collect_text = true;
185 }
186 if (L"/year" == str)
187 {
188 char num[5];
189 wcstombs(num, txt.c_str(), 5);
190 int year = atoi(num);
191 if (deadline)
192 {
193 iterator->deadline().year() = year;
194 }
195 else if (scheduled)
196 iterator->sched().year() = year;
197 }
198 if (L"position" == str)
199 {
200 collect_text = true;
201 }
202 if (L"/position" == str)
203 {
204 char num[8];
205 wcstombs(num, txt.c_str(), 8);
206 iterator->schedPosition() = atoi(num);
207 }
208 if (L"priority" == str)
209 {
210 collect_text = true;
211 }
212 if (L"/priority" == str)
213 {
214 char num[2];
215 wcstombs(num, txt.c_str(), 2);
216 iterator->priority() = atoi(num);
217 }
218 if (L"category" == str)
219 {
220 collect_text = true;
221 }
222 if (L"/category" == str)
223 {
224 iterator->addCategory(txt);
225 }
226 if (L"text" == str)
227 {
228 collect_text = true;
229 }
230 if (L"/text" == str)
231 {
232 if (L'\n' == txt[0]) txt.erase(0,1);
233 iterator->getText() = txt;
234 }
235 if (L"scheduled" == str)
236 {
237 scheduled = true;
238 }
239 if (L"/scheduled" == str)
240 {
241 if (iterator->sched().valid())
242 sched.add(&(*iterator));
243 scheduled = false;
244 }
245 }
246
patt(iToDo & iterator)247 void Parser::patt(iToDo& iterator)
248 {
249 wstring name, data;
250 int eq_pos = str.find(L"=");
251
252 name = str.substr(0, eq_pos);
253 eq_pos++;
254 data = str.substr(eq_pos, str.length()-eq_pos);
255 if (L"done" == name)
256 {
257 if (L"\"yes\"" == data) iterator->done() = true;
258 else iterator->done() = false;
259 }
260 else if (L"collapse" == name)
261 {
262 if (L"\"yes\"" == data) iterator->getCollapse() = true;
263 else iterator->getCollapse() = false;
264 }
265 }
266
267
Writer(const char * pathToSave,ToDo & t)268 Writer::Writer(const char* pathToSave, ToDo& t): todo(t)
269 {
270 strncpy(path, pathToSave, 255);
271 path[255] = '\0';
272 file.imbue(locale(""));
273 }
274
~Writer()275 Writer::~Writer() {}
276
save()277 bool Writer::save()
278 {
279 file.open(path);
280 file << "<?xml version=\"1.0\"?>" << endl;
281
282 /* there was an error writing */
283 if (file.fail()) return false;
284
285 file << "<!DOCTYPE tudu SYSTEM \"" << PATH_DTD << "\">" << endl;
286 file << "<todo>" << endl;
287 i = new iToDo(todo);
288 i->sort(L"");
289 while(--(*i));
290 _save();
291 delete i;
292 file << "</todo>" << endl;
293 file.close();
294
295 return true;
296 }
297
298 #define putTabs(num) \
299 for (int j = 0; j < num+1; j++) file << "\t"
300
_save()301 void Writer::_save()
302 {
303 for (; !((*i).end()); ++(*i))
304 {
305 putTabs((*i).depth());
306 file << "<todo done=\"";
307 if ((*i)->done()) file << "yes";
308 else file << "no";
309 file << "\" collapse=\"";
310 if ((*i)->getCollapse()) file << "yes";
311 else file << "no";
312 file << "\">" << endl;
313 if (L"" != (*i)->getTitle())
314 {
315 wstring str = (*i)->getTitle();
316
317 putTabs((*i).depth()+1);
318 amp(str);
319 file << "<title>" << str
320 << "</title>" << endl;
321 }
322 if ((*i)->deadline().valid())
323 {
324 Date &date = (*i)->deadline();
325
326 putTabs((*i).depth()+1);
327 file << "<deadline>" << endl;
328 putTabs((*i).depth()+2);
329 file << "<day>" << date.day() << "</day>" << endl;
330 putTabs((*i).depth()+2);
331 file << "<month>" << date.month() << "</month>" << endl;
332 putTabs((*i).depth()+2);
333 char str[10]; /* fixing problem with locales that prints 2,010 */
334 sprintf(str,"%i",date.year());
335 file << "<year>" << str << "</year>" << endl;
336 putTabs((*i).depth()+1);
337 file << "</deadline>" << endl;
338 }
339 if ((*i)->priority())
340 {
341 putTabs((*i).depth()+1);
342 file << "<priority>" << (*i)->priority() <<
343 "</priority>" << endl;
344 }
345 if (!(*i)->getCategories().empty())
346 {
347 for (set<wstring>::iterator it = (*i)->getCategories().begin();
348 it != (*i)->getCategories().end(); it++)
349 {
350 putTabs((*i).depth()+1);
351 file << "<category>" << *it <<
352 "</category>" << endl;
353 }
354 }
355 if ((*i)->getText() != L"")
356 {
357 wstring str = (*i)->getText().getStr();
358
359 putTabs((*i).depth()+1);
360 amp(str);
361 file << "<text>" << str;
362 file << "</text>" << endl;
363 }
364 if ((*i)->sched().valid())
365 {
366 Date &date = (*i)->sched();
367
368 putTabs((*i).depth()+1);
369 file << "<scheduled>" << endl;
370 putTabs((*i).depth()+2);
371 file << "<day>" << date.day() << "</day>" << endl;
372 putTabs((*i).depth()+2);
373 file << "<month>" << date.month() << "</month>" << endl;
374 putTabs((*i).depth()+2);
375 char str[10]; /* fixing problem with locales that prints 2,010 */
376 sprintf(str,"%i",date.year());
377 file << "<year>" << str << "</year>" << endl;
378 putTabs((*i).depth()+2);
379 file << "<position>" << (*i)->schedPosition() << "</position>" << endl;
380 putTabs((*i).depth()+1);
381 file << "</scheduled>" << endl;
382 }
383 (*i).in();
384 _save();
385 (*i).out();
386 putTabs((*i).depth());
387 file << "</todo>" << endl;
388 }
389 }
390
391 #define replace(orig, alt) \
392 { \
393 index = 0; \
394 while ((index = str.find(orig,index)) != wstring::npos) \
395 { \
396 str.erase(index,1); \
397 str.insert(index, alt); \
398 index += 4; \
399 } \
400 } while (0)
401
amp(wstring & str)402 void Writer::amp(wstring& str)
403 {
404 wstring::size_type index;
405 replace(L'&', L"&");
406 replace(L'<', L"<");
407 replace(L'>', L">");
408 }
409