2007-10-14 [colin] 3.0.2cvs72
[claws.git] / src / common / xmlprops.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2002-2007 Match Grun and the Claws Mail team
4  *
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; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 /*
21  * General functions for saving properties to an XML file.
22  *
23  * The file is structured as follows:
24  *
25  *   <property-list>
26  *     <property name="first-name" value="Axle" >/
27  *     <property name="last-name"  value="Rose" >/
28  *   </property-list>
29  *              
30  * ***********************************************************************
31  */
32
33
34 #include <glib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38
39 #include "prefs.h"
40 #include "xml.h"
41 #include "mgutils.h"
42 #include "xmlprops.h"
43
44 /* Element tag names */
45 #define XMLS_ELTAG_PROP_LIST     "property-list"
46 #define XMLS_ELTAG_PROPERTY      "property"
47
48 /* Attribute tag names */
49 #define XMLS_ATTAG_NAME          "name"
50 #define XMLS_ATTAG_VALUE         "value"
51
52 static void xmlprops_clear              ( XmlProperty *props );
53
54 typedef struct _HashLoopData {
55         FILE *fp;
56         int error;
57 } HashLoopData;
58
59 /*
60  * Create new props.
61  */
62 XmlProperty *xmlprops_create( void ) {
63         XmlProperty *props;
64
65         props = g_new0( XmlProperty, 1 );
66         props->path = NULL;
67         props->encoding = NULL;
68         props->propertyTable = g_hash_table_new( g_str_hash, g_str_equal );
69         props->retVal = MGU_SUCCESS;
70         return props;
71 }
72
73 /*
74  * Properties - file path.
75  */
76 void xmlprops_set_path( XmlProperty *props, const gchar *value ) {
77         g_return_if_fail( props != NULL );
78         props->path = mgu_replace_string( props->path, value );
79 }
80
81 /*
82  * Free hash table visitor function.
83  */
84 static gint xmlprops_free_entry_vis( gpointer key, gpointer value, gpointer data ) {
85         g_free( key );
86         g_free( value );
87         key = NULL;
88         value = NULL;
89         return TRUE;
90 }
91
92 /*
93  * Clear all properties.
94  * Enter: props Property object.
95  */
96 static void xmlprops_clear( XmlProperty *props ) {
97         g_return_if_fail( props != NULL );
98         g_hash_table_foreach_remove(
99                 props->propertyTable, xmlprops_free_entry_vis, NULL );
100 }
101
102 /*
103  * Free props.
104  * Enter: props Property object.
105  */
106 void xmlprops_free( XmlProperty *props ) {
107         g_return_if_fail( props != NULL );
108
109         /* Clear property table */
110         xmlprops_clear( props );
111         g_hash_table_destroy( props->propertyTable );
112
113         /* Free up internal objects */
114         g_free( props->path );
115         g_free( props->encoding );
116
117         props->path = NULL;
118         props->encoding = NULL;
119         props->propertyTable = NULL;
120         props->retVal = 0;
121
122         g_free( props );
123 }
124
125 static int xmlprops_write_elem_s( FILE *fp, gint lvl, gchar *name ) {
126         gint i;
127         for( i = 0; i < lvl; i++ ) {
128                 if(fputs( "  ", fp ) == EOF)
129                         return -1;
130         }
131         if(fputs( "<", fp ) == EOF)
132                 return -1;
133         if(fputs( name, fp ) == EOF)
134                 return -1;
135         
136         return 0;
137 }
138
139 static int xmlprops_write_elem_e( FILE *fp, gint lvl, gchar *name ) {
140         gint i;
141         for( i = 0; i < lvl; i++ ) {
142                 if(fputs( "  ", fp ) == EOF)
143                         return -1;
144         }
145         if(fputs( "</", fp ) == EOF)
146                 return -1;
147         if(fputs( name, fp ) == EOF)
148                 return -1;
149         if(fputs( ">\n", fp ) == EOF)
150                 return -1;
151         
152         return 0;
153 }
154
155 static int xmlprops_write_attr( FILE *fp, gchar *name, gchar *value ) {
156         if(fputs( " ", fp ) == EOF)
157                 return -1;
158         if(fputs( name, fp ) == EOF)
159                 return -1;
160         if(fputs( "=\"", fp ) == EOF)
161                 return -1;
162         if(xml_file_put_escape_str( fp, value ) < 0)
163                 return -1;
164         if(fputs( "\"", fp ) == EOF)
165                 return -1;
166         
167         return 0;
168 }
169
170 static void xmlprops_write_vis( gpointer key, gpointer value, gpointer d ) {
171         HashLoopData *data = (HashLoopData *)d;
172
173         if(xmlprops_write_elem_s( data->fp, 1, XMLS_ELTAG_PROPERTY ) < 0)
174                 data->error = 1;
175         if(xmlprops_write_attr( data->fp, XMLS_ATTAG_NAME, key ) < 0)
176                 data->error = 1;
177         if(xmlprops_write_attr( data->fp, XMLS_ATTAG_VALUE, value ) < 0)
178                 data->error = 1;
179         if(fputs( " />\n", data->fp ) == EOF)
180                 data->error = 1;
181 }
182
183 static gint xmlprops_write_to( XmlProperty *props, const gchar *fileSpec ) {
184         PrefFile *pfile;
185         FILE *fp;
186         HashLoopData data;
187
188         props->retVal = MGU_OPEN_FILE;
189         pfile = prefs_write_open( fileSpec );
190         if( pfile ) {
191                 fp = pfile->fp;
192                 if(fprintf( fp, "<?xml version=\"1.0\"" ) < 0)
193                         goto revert;
194                 if( props->encoding && *props->encoding ) {
195                         if(fprintf( fp, " encoding=\"%s\"", props->encoding ) < 0)
196                                 goto revert;
197                 }
198                 if(fprintf( fp, " ?>\n" ) < 0)
199                         goto revert;
200                 if(xmlprops_write_elem_s( fp, 0, XMLS_ELTAG_PROP_LIST ) < 0)
201                         goto revert;
202                 if(fputs( ">\n", fp ) == EOF)
203                         goto revert;
204
205                 /* Output all properties */
206                 data.fp = fp;
207                 data.error = 0;
208                 g_hash_table_foreach( props->propertyTable, xmlprops_write_vis, &data );
209
210                 if (data.error)
211                         goto revert;
212
213                 if(xmlprops_write_elem_e( fp, 0, XMLS_ELTAG_PROP_LIST ) < 0)
214                         goto revert;
215                 
216                 props->retVal = MGU_SUCCESS;
217                 if( prefs_file_close( pfile ) < 0 ) {
218                         goto revert;
219                 }
220                 goto out;
221 revert:
222                 props->retVal = MGU_ERROR_WRITE;
223                 if( prefs_file_close_revert( pfile ) < 0 ) {
224                         props->retVal = MGU_ERROR_WRITE;
225                 }
226         
227         }
228 out:
229         return props->retVal;
230 }
231
232 /*
233  * Save properties to file.
234  * return: Status code.
235  */
236 gint xmlprops_save_file( XmlProperty *props ) {
237         g_return_val_if_fail( props != NULL, -1 );
238
239         props->retVal = MGU_NO_FILE;
240         if( props->path == NULL || *props->path == '\0' ) return props->retVal;
241         xmlprops_write_to( props, props->path );
242
243         return props->retVal;
244 }
245
246 static void xmlprops_save_property(
247                 XmlProperty *props, const gchar *name, const gchar *value )
248 {
249         gchar *key;
250         gchar *val;
251
252         if( strlen( name ) == 0 ) return;
253         if( strlen( value ) == 0 ) return;
254         if( g_hash_table_lookup( props->propertyTable, name ) ) return;
255         key = g_strdup( name );
256         val = g_strdup( value );
257         g_hash_table_insert( props->propertyTable, key, val );
258 }
259
260 #define ATTR_BUFSIZE 256
261
262 static void xmlprops_read_props( XmlProperty *props, XMLFile *file ) {
263         GList *attr;
264         gchar *name, *value;
265         gchar pName[ ATTR_BUFSIZE ];
266         gchar pValue[ ATTR_BUFSIZE ];
267
268         while( TRUE ) {
269                 *pName = '\0';
270                 *pValue = '\0';
271                 if (! file->level ) break;
272                 xml_parse_next_tag( file );
273                 xml_get_current_tag( file );
274                 if( xml_compare_tag( file, XMLS_ELTAG_PROPERTY ) ) {
275                         attr = xml_get_current_tag_attr( file );
276                         while( attr ) {
277                                 name = ( ( XMLAttr * ) attr->data )->name;
278                                 value = ( ( XMLAttr * ) attr->data )->value;
279                                 if( strcmp( name, XMLS_ATTAG_NAME ) == 0 ) {
280                                         strcpy( pName, value );
281                                 }
282                                 else if( strcmp( name, XMLS_ATTAG_VALUE ) == 0 ) {
283                                         strcpy( pValue, value );
284                                 }
285                                 attr = g_list_next( attr );
286                         }
287                         xmlprops_save_property( props, pName, pValue );
288                 }
289         }
290 }
291
292 #undef ATTR_BUFSIZE
293
294 /*
295  * Load properties from file.
296  * return: Status code.
297  */
298 gint xmlprops_load_file( XmlProperty *props ) {
299         XMLFile *file = NULL;
300
301         g_return_val_if_fail( props != NULL, -1 );
302         props->retVal = MGU_NO_FILE;
303         file = xml_open_file( props->path );
304         if( file == NULL ) {
305                 return props->retVal;
306         }
307
308         props->retVal = MGU_BAD_FORMAT;
309         if( xml_get_dtd( file ) == 0 ) {
310                 if( xml_parse_next_tag( file ) == 0 ) {
311                         if( xml_compare_tag( file, XMLS_ELTAG_PROP_LIST ) ) {
312                                 xmlprops_read_props( props, file );
313                                 props->retVal = MGU_SUCCESS;
314                         }
315                 }
316         }
317         xml_close_file( file );
318
319         return props->retVal;
320 }
321
322 /*
323  * Set property.
324  * Enter: props Property object.
325  *        name  Property name.
326  *        value New value to save.
327  */
328 void xmlprops_set_property(
329                 XmlProperty *props, const gchar *name, const gchar *value )
330 {
331         gchar *key = NULL;
332         gchar *val;
333
334         g_return_if_fail( props != NULL );
335         if( name == NULL || strlen( name ) == 0 ) return;
336         if( value == NULL || strlen( value ) == 0 ) return;
337         val = g_hash_table_lookup( props->propertyTable, name );
338         if( val == NULL ) {
339                 key = g_strdup( name );
340         }
341         else {
342                 g_free( val );
343         }
344         val = g_strdup( value );
345         g_hash_table_insert( props->propertyTable, key, val );
346 }
347
348 /*
349  * Set property to integer value.
350  * Enter: props Property object.
351  *        name  Property name.
352  *        value New value to save.
353  */
354 void xmlprops_set_property_i(
355                 XmlProperty *props, const gchar *name, const gint value )
356 {
357         gchar buf[32];
358
359         g_return_if_fail( props != NULL );
360         sprintf( buf, "%d", value );
361         xmlprops_set_property( props, name, buf );
362 }
363
364 /*
365  * Set property to boolean value.
366  * Enter: props Property object.
367  *        name  Property name.
368  *        value New value to save.
369  */
370 void xmlprops_set_property_b(
371                 XmlProperty *props, const gchar *name, const gboolean value )
372 {
373         g_return_if_fail( props != NULL );
374         if( value ) {
375                 xmlprops_set_property( props, name, "y" );
376         }
377         else {
378                 xmlprops_set_property( props, name, "n" );
379         }
380 }
381
382 /*
383  * Get property into a buffer.
384  * Enter:  props Property object.
385  *         name  Property name.
386  * Return: value found, or NULL if none. Should be g_free() when done.
387  */
388 void xmlprops_get_property_s(
389                 XmlProperty *props, const gchar *name, gchar *buffer ) {
390         gchar *val;
391
392         g_return_if_fail( props != NULL );
393         if( buffer == NULL ) return;
394         val = g_hash_table_lookup( props->propertyTable, name );
395         if( val ) {
396                 strcpy( buffer, val );
397         }
398 }
399
400 /*
401  * Get property as integer value.
402  * Enter:  props Property object.
403  *         name  Property name.
404  * Return: value found, or zero if not found.
405  */
406 gint xmlprops_get_property_i( XmlProperty *props, const gchar *name ) {
407         gchar *val;
408         gchar *endptr;
409         gint value;
410
411         value = 0;
412         g_return_val_if_fail( props != NULL, value );
413         val = g_hash_table_lookup( props->propertyTable, name );
414         if( val ) {
415                 endptr = NULL;
416                 value = strtol( val, &endptr, 10 );
417         }
418         return value;
419 }
420
421 /*
422  * Get property as boolean value.
423  * Enter:  props Property object.
424  *         name  Property name.
425  * Return: value found, or FALSE if not found.
426  */
427 gboolean xmlprops_get_property_b( XmlProperty *props, const gchar *name ) {
428         gchar *val;
429         gboolean value;
430
431         value = FALSE;
432         g_return_val_if_fail( props != NULL, value );
433         val = g_hash_table_lookup( props->propertyTable, name );
434         if( val ) {
435                 value = ( g_ascii_strcasecmp( val, "y" ) == 0 );
436         }
437         return value;
438 }
439
440 /*
441 * End of Source.
442 */
443
444