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"&amp;");
406 	replace(L'<', L"&lt;");
407 	replace(L'>', L"&gt;");
408 }
409