Ticket #2726 (mcedit nonpersistent selection must reset after cursor moved)
[midnight-commander.git] / lib / serialize.c
blobc03dcd199fe191d8b5a79d7cb98c89f5b8dd48ce
1 /*
2 Provides a serialize/unserialize functionality for INI-like formats.
4 Copyright (C) 2011
5 The Free Software Foundation, Inc.
7 Written by:
8 Slava Zanko <slavazanko@gmail.com>, 2011
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 /** \file serialize.c
27 * \brief Source: serialize/unserialize functionality for INI-like formats.
30 #include <config.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
36 #include "lib/global.h"
38 #include "lib/serialize.h"
40 /*** global variables ****************************************************************************/
42 /*** file scope macro definitions ****************************************************************/
44 #define SRLZ_DELIM_C ':'
45 #define SRLZ_DELIM_S ":"
47 /*** file scope type declarations ****************************************************************/
49 /*** file scope variables ************************************************************************/
51 /*** file scope functions ************************************************************************/
52 /* --------------------------------------------------------------------------------------------- */
54 static void
55 prepend_error_message (GError ** error, const char *format, ...)
57 char *prepend_str;
58 char *split_str;
59 va_list ap;
61 if ((error == NULL) || (*error == NULL))
62 return;
64 va_start (ap, format);
65 prepend_str = g_strdup_vprintf (format, ap);
66 va_end (ap);
68 split_str = g_strdup_printf ("%s: %s", prepend_str, (*error)->message);
69 g_free (prepend_str);
70 g_free ((*error)->message);
71 (*error)->message = split_str;
74 /* --------------------------------------------------------------------------------------------- */
76 static const char *
77 go_to_end_of_serialized_string (const char *non_serialized_data,
78 const char *already_serialized_part, size_t * offset)
80 size_t calculated_offset;
81 const char *semi_ptr = strchr (non_serialized_data + 1, SRLZ_DELIM_C);
83 calculated_offset = (semi_ptr - non_serialized_data) + 1 + strlen (already_serialized_part);
84 if (calculated_offset >= strlen (non_serialized_data))
85 return NULL;
87 non_serialized_data += calculated_offset;
88 *offset += calculated_offset;
90 return non_serialized_data;
93 /* --------------------------------------------------------------------------------------------- */
94 /*** public functions ****************************************************************************/
95 /* --------------------------------------------------------------------------------------------- */
97 /**
98 * Serialize some string object to string
100 * @param prefix prefix for serailization
101 * @param data data for serialization
102 * @param error contain pointer to object for handle error code and message
104 * @return serialized data as newly allocated string
107 char *
108 mc_serialize_str (const char prefix, const char *data, GError ** error)
110 if (data == NULL)
112 g_set_error (error, MC_ERROR, -1, "mc_serialize_str(): Input data is NULL.");
113 return NULL;
115 return g_strdup_printf ("%c%zd" SRLZ_DELIM_S "%s", prefix, strlen (data), data);
118 /* --------------------------------------------------------------------------------------------- */
120 * Deserialize string to string object
122 * @param prefix prefix for deserailization
123 * @param data data for deserialization
124 * @param error contain pointer to object for handle error code and message
126 * @return newly allocated string
129 #define FUNC_NAME "mc_serialize_str()"
130 char *
131 mc_deserialize_str (const char prefix, const char *data, GError ** error)
133 size_t data_len;
135 if ((data == NULL) || (strlen (data) == 0))
137 g_set_error (error, MC_ERROR, -1, FUNC_NAME ": Input data is NULL or empty.");
138 return NULL;
141 if (*data != prefix)
143 g_set_error (error, MC_ERROR, -2, FUNC_NAME ": String prefix doesn't equal to '%c'",
144 prefix);
145 return NULL;
149 char buffer[BUF_TINY];
150 char *semi_ptr;
151 size_t semi_offset;
153 semi_ptr = strchr (data + 1, SRLZ_DELIM_C);
154 if (semi_ptr == NULL)
156 g_set_error (error, MC_ERROR, -3,
157 FUNC_NAME ": Length delimiter '%c' doesn't exists", SRLZ_DELIM_C);
158 return NULL;
160 semi_offset = semi_ptr - (data + 1);
161 if (semi_offset >= BUF_TINY)
163 g_set_error (error, MC_ERROR, -3, FUNC_NAME ": Too big string length");
164 return NULL;
166 strncpy (buffer, data + 1, semi_offset);
167 buffer[semi_offset] = '\0';
168 data_len = atol (buffer);
169 data += semi_offset + 2;
172 if (data_len > strlen (data))
174 g_set_error (error, MC_ERROR, -3,
175 FUNC_NAME
176 ": Specified data length (%zd) is greater than actual data length (%zd)",
177 data_len, strlen (data));
178 return NULL;
180 return g_strndup (data, data_len);
183 #undef FUNC_NAME
185 /* --------------------------------------------------------------------------------------------- */
187 * Serialize mc_config_t object to string
189 * @param data data for serialization
190 * @param error contain pointer to object for handle error code and message
192 * @return serialized data as newly allocated string
195 char *
196 mc_serialize_config (const mc_config_t * data, GError ** error)
198 gchar **groups, **group_iterator;
199 size_t group_count;
200 GString *buffer;
202 buffer = g_string_new ("");
203 group_iterator = groups = mc_config_get_groups (data, &group_count);
205 while (group_count-- != 0)
207 char *serialized_str;
208 gchar **params, **param_iterator;
209 size_t param_count;
211 serialized_str = mc_serialize_str ('g', *group_iterator, error);
212 if (serialized_str == NULL)
214 g_string_free (buffer, TRUE);
215 g_strfreev (groups);
216 return NULL;
218 g_string_append (buffer, serialized_str);
219 g_free (serialized_str);
221 param_iterator = params = mc_config_get_keys (data, *group_iterator, &param_count);
223 while (param_count-- != 0)
225 char *value;
226 serialized_str = mc_serialize_str ('p', *param_iterator, error);
227 if (serialized_str == NULL)
229 g_string_free (buffer, TRUE);
230 g_strfreev (params);
231 g_strfreev (groups);
232 return NULL;
234 g_string_append (buffer, serialized_str);
235 g_free (serialized_str);
237 value = mc_config_get_string_raw (data, *group_iterator, *param_iterator, "");
238 serialized_str = mc_serialize_str ('v', value, error);
239 g_free (value);
241 if (serialized_str == NULL)
243 g_string_free (buffer, TRUE);
244 g_strfreev (params);
245 g_strfreev (groups);
246 return NULL;
249 g_string_append (buffer, serialized_str);
250 g_free (serialized_str);
252 param_iterator++;
255 g_strfreev (params);
257 group_iterator++;
259 return g_string_free (buffer, FALSE);
262 /* --------------------------------------------------------------------------------------------- */
264 * Deserialize string to mc_config_t object
266 * @param data data for serialization
267 * @param error contain pointer to object for handle error code and message
269 * @return newly allocated mc_config_t object
272 #define FUNC_NAME "mc_deserialize_config()"
273 #define prepend_error_and_exit() { \
274 prepend_error_message (error, FUNC_NAME " at %lu", current_position + 1); \
275 mc_config_deinit (ret_data); \
276 return NULL; \
279 mc_config_t *
280 mc_deserialize_config (const char *data, GError ** error)
282 char *current_group = NULL, *current_param = NULL, *current_value = NULL;
283 size_t current_position = 0;
284 mc_config_t *ret_data = mc_config_init (NULL);
285 enum automat_status
287 WAIT_GROUP,
288 WAIT_PARAM,
289 WAIT_VALUE
290 } current_status = WAIT_GROUP;
292 while (data != NULL)
294 if ((current_status == WAIT_GROUP) && (*data == 'p') && (current_group != NULL))
295 current_status = WAIT_PARAM;
297 switch (current_status)
299 case WAIT_GROUP:
300 g_free (current_group);
302 current_group = mc_deserialize_str ('g', data, error);
303 if (current_group == NULL)
304 prepend_error_and_exit ();
306 data = go_to_end_of_serialized_string (data, current_group, &current_position);
307 current_status = WAIT_PARAM;
308 break;
309 case WAIT_PARAM:
310 g_free (current_param);
312 current_param = mc_deserialize_str ('p', data, error);
313 if (current_param == NULL)
315 g_free (current_group);
316 prepend_error_and_exit ();
319 data = go_to_end_of_serialized_string (data, current_param, &current_position);
320 current_status = WAIT_VALUE;
321 break;
322 case WAIT_VALUE:
323 current_value = mc_deserialize_str ('v', data, error);
324 if (current_value == NULL)
326 g_free (current_group);
327 g_free (current_param);
328 prepend_error_and_exit ();
330 mc_config_set_string (ret_data, current_group, current_param, current_value);
332 data = go_to_end_of_serialized_string (data, current_value, &current_position);
333 g_free (current_value);
334 current_status = WAIT_GROUP;
335 break;
338 g_free (current_group);
339 g_free (current_param);
341 return ret_data;
344 #undef FUNC_NAME
346 /* --------------------------------------------------------------------------------------------- */