OSDN Git Service

BUG: [ 1630762 ] Cannot save project file if including non-ascii chars
[winmerge-jp/winmerge-jp.git] / Src / ProjectFile.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    License (GPLv2+):
3 //    This program is free software; you can redistribute it and/or modify
4 //    it under the terms of the GNU General Public License as published by
5 //    the Free Software Foundation; either version 2 of the License, or (at
6 //    your option) any later version.
7 //    
8 //    This program is distributed in the hope that it will be useful, but
9 //    WITHOUT ANY WARRANTY; without even the implied warranty of
10 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //    GNU General Public License for more details.
12 //
13 //    You should have received a copy of the GNU General Public License
14 //    along with this program; if not, write to the Free Software
15 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 /////////////////////////////////////////////////////////////////////////////
17 /** 
18  * @file  ProjectFile.cpp
19  *
20  * @brief Implementation file for ProjectFile class.
21  */
22 // RCS ID line follows -- this is updated by CVS
23 // $Id$
24
25 #include "stdafx.h"
26 #include <scew/scew.h>
27
28 #include "ProjectFile.h"
29
30 // ATL conversion macro hack for UTF-8 conversion
31 #define UTF82W(lpa) (\
32         ((_lpa = lpa) == NULL) ? NULL : (\
33                 _convert = (lstrlenA(_lpa)+1),\
34                 AtlA2WHelper((LPWSTR) alloca(_convert*2), _lpa, _convert, CP_UTF8)))
35
36 #define W2UTF8(lpw) (\
37         ((_lpw = lpw) == NULL) ? NULL : (\
38                 _convert = (lstrlenW(_lpw)+1)*6,\
39                 AtlW2AHelper((LPSTR) alloca(_convert), _lpw, _convert, CP_UTF8)))
40
41 #define UTF82A(lpu) W2A(UTF82W(lpu))
42 #define A2UTF8(lpa) W2UTF8(A2W(lpa))
43 #ifdef _UNICODE
44 #  define UTF82T(lpu) UTF82W(lpu)
45 #  define T2UTF8(lpw) W2UTF8(lpw)
46 #else
47 #  define UTF82T(lpu) UTF82A(lpu)
48 #  define T2UTF8(lpw) A2UTF8(lpw)
49 #endif
50
51 // Constants for xml element names
52 const char Root_element_name[] = "project";
53 const char Paths_element_name[] = "paths";
54 const char Left_element_name[] = "left";
55 const char Right_element_name[] = "right";
56 const char Filter_element_name[] = "filter";
57 const char Subfolders_element_name[] = "subfolders";
58 const char Left_ro_element_name[] = "left-readonly";
59 const char Right_ro_element_name[] = "right-readonly";
60
61 /** 
62  * @brief Standard constructor.
63  */
64  ProjectFile::ProjectFile()
65 : m_bHasLeft(FALSE)
66 , m_bHasRight(FALSE)
67 , m_bHasFilter(FALSE)
68 , m_bHasSubfolders(FALSE)
69 , m_subfolders(-1)
70 , m_bLeftReadOnly(FALSE)
71 , m_bRightReadOnly(FALSE)
72 {
73 }
74
75 /** 
76  * @brief Open given path-file and read data from it to member variables.
77  * @param [in] path Path to project file.
78  * @param [out] sError Error string if error happened.
79  * @return TRUE if reading succeeded, FALSE if error happened.
80  */
81 BOOL ProjectFile::Read(LPCTSTR path, CString *sError)
82 {
83         BOOL loaded = FALSE;
84     scew_tree* tree = NULL;
85     scew_parser* parser = NULL;
86
87     parser = scew_parser_create();
88     scew_parser_ignore_whitespaces(parser, 1);
89
90         FILE * fp = _tfopen(path, _T("r"));
91         if (fp)
92         {
93                 if (scew_parser_load_file_fp(parser, fp))
94                 {
95                         tree = scew_parser_tree(parser);
96
97                         scew_element * root = GetRootElement(tree);
98                         if (root)
99                         {
100                                 // Currently our content is paths, so expect
101                                 // having paths in valid project file!
102                                 if (GetPathsData(root))
103                                         loaded = TRUE;
104                         };
105                 }
106                 scew_tree_free(tree);
107
108                 /* Frees the SCEW parser */
109                 scew_parser_free(parser);
110                 fclose(fp);
111         }
112         return loaded;
113 }
114
115 /** 
116  * @brief Return project file XML's root element.
117  * @param [in] tree XML tree we got from the parser.
118  * @return Root element pointer.
119  */
120 scew_element* ProjectFile::GetRootElement(scew_tree * tree)
121 {
122         scew_element * root = NULL;
123
124         if (tree != NULL)
125         {
126                 root = scew_tree_root(tree);
127         }
128
129         if (root != NULL)
130         {
131                 // Make sure we have correct root element
132                 if (strcmp(Root_element_name, scew_element_name(root)) != 0)
133                 {
134                         root = NULL;
135                 }
136         }
137         return root;
138 }
139
140 /** 
141  * @brief Reads the paths data from the XML data.
142  * This function reads the paths data inside given element in XML data.
143  * @param [in] parent Parent element for the paths data.
144  * @return TRUE if pathdata was found from the file.
145  */
146 BOOL ProjectFile::GetPathsData(scew_element * parent)
147 {
148         USES_CONVERSION;
149         BOOL bFoundPaths = FALSE;
150         scew_element *paths = NULL;
151
152         if (parent != NULL)
153         {
154                 paths = scew_element_by_name(parent, Paths_element_name);
155         }
156
157         if (paths != NULL)
158         {
159                 bFoundPaths = TRUE;
160                 scew_element *left = NULL;
161                 scew_element *right = NULL;
162                 scew_element *filter = NULL;
163                 scew_element *subfolders = NULL;
164                 scew_element *left_ro = NULL;
165                 scew_element *right_ro = NULL;
166
167                 left = scew_element_by_name(paths, Left_element_name);
168                 right = scew_element_by_name(paths, Right_element_name);
169                 filter = scew_element_by_name(paths, Filter_element_name);
170                 subfolders = scew_element_by_name(paths, Subfolders_element_name);
171                 left_ro = scew_element_by_name(paths, Left_ro_element_name);
172                 right_ro = scew_element_by_name(paths, Right_ro_element_name);
173
174                 if (left)
175                 {
176                         LPCSTR path = NULL;
177                         path = scew_element_contents(left);
178                         m_leftFile = UTF82T(path);
179                         m_bHasLeft = TRUE;
180                 }
181                 if (right)
182                 {
183                         LPCSTR path = NULL;
184                         path = scew_element_contents(right);
185                         m_rightFile = UTF82T(path);
186                         m_bHasRight = TRUE;
187                 }
188                 if (filter)
189                 {
190                         LPCSTR filtername = NULL;
191                         filtername = scew_element_contents(filter);
192                         m_filter = UTF82T(filtername);
193                         m_bHasFilter = TRUE;
194                 }
195                 if (subfolders)
196                 {
197                         LPCSTR folders = NULL;
198                         folders = scew_element_contents(subfolders);
199                         m_subfolders = atoi(folders);
200                         m_bHasSubfolders = TRUE;
201                 }
202                 if (left_ro)
203                 {
204                         LPCSTR readonly = NULL;
205                         readonly = scew_element_contents(left_ro);
206                         m_bLeftReadOnly = (atoi(readonly) != 0);
207                 }
208                 if (right_ro)
209                 {
210                         LPCSTR readonly = NULL;
211                         readonly = scew_element_contents(right_ro);
212                         m_bRightReadOnly = (atoi(readonly) != 0);
213                 }
214         }
215         return bFoundPaths;
216 }
217
218 /** 
219  * @brief Save data from member variables to path-file.
220  * @param [in] path Path to project file.
221  * @param [out] sError Error string if error happened.
222  * @return TRUE if saving succeeded, FALSE if error happened.
223  */
224 BOOL ProjectFile::Save(LPCTSTR path, CString *sError)
225 {
226         BOOL success = TRUE;
227         scew_tree* tree = NULL;
228         scew_element* root = NULL;
229         scew_element* paths = NULL;
230
231         tree = scew_tree_create();
232         root = scew_tree_add_root(tree, Root_element_name);
233         if (root != NULL)
234         {
235                 paths = AddPathsElement(root);
236         }
237         else
238                 success = FALSE;
239
240         if (paths != NULL)
241         {
242                 AddPathsContent(paths);
243         }
244         else
245                 success = FALSE;
246         
247         scew_tree_set_xml_encoding(tree, "UTF-8");
248
249         // Set the XML file standalone
250         scew_tree_set_xml_standalone(tree, 1);
251
252         FILE * fp = _tfopen(path, _T("w"));
253         if (fp)
254         {
255                 if (!scew_writer_tree_fp(tree, fp))
256                 {
257                         success = FALSE;
258                         *sError = LoadResString(IDS_FILEWRITE_ERROR);
259                 }
260                 fclose(fp);
261         }
262         else
263         {
264                 success = FALSE;
265         }
266         
267         /* Frees the SCEW tree */
268         scew_tree_free(tree);
269
270         if (success == FALSE)
271         {
272                 *sError = LoadResString(IDS_FILEWRITE_ERROR);
273         }
274         return success;
275 }
276
277 /**
278  * @brief Add paths element into XML tree.
279  * @param [in] parent Parent element for the paths element.
280  * @return pointer to added paths element.
281  */
282 scew_element* ProjectFile::AddPathsElement(scew_element * parent)
283 {
284         scew_element* element = NULL;
285         element = scew_element_add(parent, Paths_element_name);
286         return element;
287 }
288
289 /**
290  * @brief Add paths data to the XML tree.
291  * This function adds our paths data to the XML tree.
292  * @param [in] parent Parent element for paths data.
293  * @return TRUE if we succeeded, FALSE otherwise.
294  */
295 BOOL ProjectFile::AddPathsContent(scew_element * parent)
296 {
297         USES_CONVERSION;
298         scew_element* element = NULL;
299
300         if (!m_leftFile.IsEmpty())
301         {
302                 LPCTSTR path;
303                 element = scew_element_add(parent, Left_element_name);
304                 path = m_leftFile.GetBuffer(MAX_PATH);
305                 scew_element_set_contents(element, T2UTF8(path));
306                 m_leftFile.ReleaseBuffer();
307         }
308
309         if (!m_rightFile.IsEmpty())
310         {
311                 LPCTSTR path;
312                 element = scew_element_add(parent, Right_element_name);
313                 path = m_rightFile.GetBuffer(MAX_PATH);
314                 scew_element_set_contents(element, T2UTF8(path));
315                 m_rightFile.ReleaseBuffer();
316         }
317
318         if (!m_filter.IsEmpty())
319         {
320                 LPCTSTR filter;
321                 element = scew_element_add(parent, Filter_element_name);
322                 filter = m_filter.GetBuffer(MAX_PATH);
323                 scew_element_set_contents(element, T2UTF8(filter));
324                 m_filter.ReleaseBuffer();
325         }
326
327         element = scew_element_add(parent, Subfolders_element_name);
328         if (m_subfolders != 0)
329                 scew_element_set_contents(element, "1");
330         else
331                 scew_element_set_contents(element, "0");
332
333         element = scew_element_add(parent, Left_ro_element_name);
334         if (m_bLeftReadOnly)
335                 scew_element_set_contents(element, "1");
336         else
337                 scew_element_set_contents(element, "0");
338
339         element = scew_element_add(parent, Right_ro_element_name);
340         if (m_bRightReadOnly)
341                 scew_element_set_contents(element, "1");
342         else
343                 scew_element_set_contents(element, "0");
344
345         return TRUE;
346 }
347
348 /** 
349  * @brief Returns if left path is defined in project file.
350  * @return TRUE if project file has left path.
351  */
352 BOOL ProjectFile::HasLeft() const
353 {
354         return m_bHasLeft;
355 }
356
357 /** 
358  * @brief Returns if right path is defined in project file.
359  * @return TRUE if project file has right path.
360  */
361 BOOL ProjectFile::HasRight() const
362 {
363         return m_bHasRight;
364 }
365
366 /** 
367  * @brief Returns if filter is defined in project file.
368  * @return TRUE if project file has filter.
369  */
370 BOOL ProjectFile::HasFilter() const
371 {
372         return m_bHasFilter;
373 }
374
375 /** 
376  * @brief Returns if subfolder is defined in projectfile.
377  * @return TRUE if project file has subfolder definition.
378  */
379 BOOL ProjectFile::HasSubfolders() const
380 {
381         return m_bHasSubfolders;
382 }
383
384 /** 
385  * @brief Returns left path.
386  * @param [out] pReadOnly TRUE if readonly was specified for path.
387  */
388 CString ProjectFile::GetLeft(BOOL * pReadOnly /*=NULL*/) const
389 {
390         if (pReadOnly)
391                 *pReadOnly = m_bLeftReadOnly;
392         return m_leftFile;
393 }
394
395 /** 
396  * @brief Returns if left path is specified read-only.
397  */
398 BOOL ProjectFile::GetLeftReadOnly() const
399 {
400         return m_bLeftReadOnly;
401 }
402
403 /** 
404  * @brief Set left path, returns old left path.
405  * @param [in] sLeft Left path.
406  * @param [in] bReadOnly Will path be recorded read-only?
407  */
408 CString ProjectFile::SetLeft(const CString& sLeft, const BOOL * pReadOnly /*=NULL*/)
409 {
410         CString sLeftOld = GetLeft();
411         m_leftFile = sLeft;
412         if (pReadOnly)
413                 m_bLeftReadOnly = *pReadOnly;
414
415         return sLeftOld;
416 }
417
418 /** 
419  * @brief Returns right path.
420  * @param [out] pReadOnly TRUE if readonly was specified for path.
421  */
422 CString ProjectFile::GetRight(BOOL * pReadOnly /*=NULL*/) const
423 {
424         if (pReadOnly)
425                 *pReadOnly = m_bRightReadOnly;
426         return m_rightFile;
427 }
428
429 /** 
430  * @brief Returns if right path is specified read-only.
431  */
432 BOOL ProjectFile::GetRightReadOnly() const
433 {
434         return m_bRightReadOnly;
435 }
436
437 /** 
438  * @brief Set right path, returns old right path.
439  * @param [in] sRight Right path.
440  * @param [in] bReadOnly Will path be recorded read-only?
441  */
442 CString ProjectFile::SetRight(const CString& sRight, const BOOL * pReadOnly /*=NULL*/)
443 {
444         CString sRightOld = GetRight();
445         m_rightFile = sRight;
446         if (pReadOnly)
447                 m_bRightReadOnly = *pReadOnly;
448
449         return sRightOld;
450 }
451
452 /** 
453  * @brief Returns filter.
454  */
455 CString ProjectFile::GetFilter() const
456 {
457         return m_filter;
458 }
459
460 /** 
461  * @brief Set filter, returns old filter.
462  */
463 CString ProjectFile::SetFilter(const CString& sFilter)
464 {
465         CString sFilterOld = GetFilter();
466         m_filter = sFilter;
467
468         return sFilterOld;
469 }
470
471 /** 
472  * @brief Returns subfolder included -setting.
473  */
474 int ProjectFile::GetSubfolders() const
475 {
476         return m_subfolders;
477 }
478
479 /** 
480  * @brief set subfolder, returns old subfolder value.
481  */
482 int ProjectFile::SetSubfolders(const int iSubfolder)
483 {
484         int iSubfoldersOld = GetSubfolders(); 
485         m_subfolders = iSubfolder ? 1 : 0;
486
487         return iSubfoldersOld;
488 }
489
490 /** 
491  * @brief Returns left and right paths and recursive from project file
492  * 
493  * @param [out] sLeft Left path
494  * @param [out] sRight Right path
495  * @param [out] bSubFolders If TRUE subfolders included (recursive compare)
496  */
497 void ProjectFile::GetPaths(CString & sLeft, CString & sRight,
498         BOOL & bSubfolders) const
499 {
500         if (HasLeft())
501                 sLeft = GetLeft();
502         if (HasRight())
503                 sRight = GetRight();
504         if (HasSubfolders())
505                 bSubfolders = (GetSubfolders() == 1);
506 }