(diff_two_paths): minor optimization: exit loop ASAP.
[midnight-commander.git] / lib / serialize.c
blob1b71b970486588a511309fb9c8969687e808f0cd
1 /*
2 Provides a serialize/unserialize functionality for INI-like formats.
4 Copyright (C) 2011-2017
5 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 lib/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 G_GNUC_PRINTF (2, 3)
56 prepend_error_message (GError ** error, const char *format, ...)
58 char *prepend_str;
59 char *split_str;
60 va_list ap;
62 if ((error == NULL) || (*error == NULL))
63 return;
65 va_start (ap, format);
66 prepend_str = g_strdup_vprintf (format, ap);
67 va_end (ap);
69 split_str = g_strdup_printf ("%s: %s", prepend_str, (*error)->message);
70 g_free (prepend_str);
71 g_free ((*error)->message);
72 (*error)->message = split_str;
75 /* --------------------------------------------------------------------------------------------- */
77 static const char *
78 go_to_end_of_serialized_string (const char *non_serialized_data,
79 const char *already_serialized_part, size_t * offset)
81 size_t calculated_offset;
82 const char *semi_ptr = strchr (non_serialized_data + 1, SRLZ_DELIM_C);
84 calculated_offset = (semi_ptr - non_serialized_data) + 1 + strlen (already_serialized_part);
85 if (calculated_offset >= strlen (non_serialized_data))
86 return NULL;
88 non_serialized_data += calculated_offset;
89 *offset += calculated_offset;
91 return non_serialized_data;
94 /* --------------------------------------------------------------------------------------------- */
95 /*** public functions ****************************************************************************/
96 /* --------------------------------------------------------------------------------------------- */
98 /**
99 * Serialize some string object to string
101 * @param prefix prefix for serailization
102 * @param data data for serialization
103 * @param error contain pointer to object for handle error code and message
105 * @return serialized data as newly allocated string
108 char *
109 mc_serialize_str (const char prefix, const char *data, GError ** error)
111 if (data == NULL)
113 g_set_error (error, MC_ERROR, 0, "mc_serialize_str(): Input data is NULL.");
114 return NULL;
116 return g_strdup_printf ("%c%zu" SRLZ_DELIM_S "%s", prefix, strlen (data), data);
119 /* --------------------------------------------------------------------------------------------- */
121 * Deserialize string to string object
123 * @param prefix prefix for deserailization
124 * @param data data for deserialization
125 * @param error contain pointer to object for handle error code and message
127 * @return newly allocated string
130 #define FUNC_NAME "mc_serialize_str()"
131 char *
132 mc_deserialize_str (const char prefix, const char *data, GError ** error)
134 size_t data_len;
136 if ((data == NULL) || (strlen (data) == 0))
138 g_set_error (error, MC_ERROR, 0, FUNC_NAME ": Input data is NULL or empty.");
139 return NULL;
142 if (*data != prefix)
144 g_set_error (error, MC_ERROR, 0, FUNC_NAME ": String prefix doesn't equal to '%c'", 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, 0,
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, 0, 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, 0,
175 FUNC_NAME
176 ": Specified data length (%zu) is greater than actual data length (%zu)",
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 GString *buffer;
201 buffer = g_string_new ("");
202 groups = mc_config_get_groups (data, NULL);
204 for (group_iterator = groups; *group_iterator != NULL; group_iterator++)
206 char *serialized_str;
207 gchar **params, **param_iterator;
209 serialized_str = mc_serialize_str ('g', *group_iterator, error);
210 if (serialized_str == NULL)
212 g_string_free (buffer, TRUE);
213 g_strfreev (groups);
214 return NULL;
216 g_string_append (buffer, serialized_str);
217 g_free (serialized_str);
219 params = mc_config_get_keys (data, *group_iterator, NULL);
221 for (param_iterator = params; *param_iterator != NULL; param_iterator++)
223 char *value;
225 serialized_str = mc_serialize_str ('p', *param_iterator, error);
226 if (serialized_str == NULL)
228 g_string_free (buffer, TRUE);
229 g_strfreev (params);
230 g_strfreev (groups);
231 return NULL;
233 g_string_append (buffer, serialized_str);
234 g_free (serialized_str);
236 value = mc_config_get_string_raw (data, *group_iterator, *param_iterator, "");
237 serialized_str = mc_serialize_str ('v', value, error);
238 g_free (value);
240 if (serialized_str == NULL)
242 g_string_free (buffer, TRUE);
243 g_strfreev (params);
244 g_strfreev (groups);
245 return NULL;
248 g_string_append (buffer, serialized_str);
249 g_free (serialized_str);
252 g_strfreev (params);
254 return g_string_free (buffer, FALSE);
257 /* --------------------------------------------------------------------------------------------- */
259 * Deserialize string to mc_config_t object
261 * @param data data for serialization
262 * @param error contain pointer to object for handle error code and message
264 * @return newly allocated mc_config_t object
267 #define FUNC_NAME "mc_deserialize_config()"
268 #define prepend_error_and_exit() { \
269 prepend_error_message (error, FUNC_NAME " at %zu", current_position + 1); \
270 mc_config_deinit (ret_data); \
271 return NULL; \
274 mc_config_t *
275 mc_deserialize_config (const char *data, GError ** error)
277 char *current_group = NULL, *current_param = NULL, *current_value = NULL;
278 size_t current_position = 0;
279 mc_config_t *ret_data;
280 enum automat_status
282 WAIT_GROUP,
283 WAIT_PARAM,
284 WAIT_VALUE
285 } current_status = WAIT_GROUP;
287 ret_data = mc_config_init (NULL, FALSE);
289 while (data != NULL)
291 if ((current_status == WAIT_GROUP) && (*data == 'p') && (current_group != NULL))
292 current_status = WAIT_PARAM;
294 switch (current_status)
296 case WAIT_GROUP:
297 g_free (current_group);
299 current_group = mc_deserialize_str ('g', data, error);
300 if (current_group == NULL)
301 prepend_error_and_exit ();
303 data = go_to_end_of_serialized_string (data, current_group, &current_position);
304 current_status = WAIT_PARAM;
305 break;
306 case WAIT_PARAM:
307 g_free (current_param);
309 current_param = mc_deserialize_str ('p', data, error);
310 if (current_param == NULL)
312 g_free (current_group);
313 prepend_error_and_exit ();
316 data = go_to_end_of_serialized_string (data, current_param, &current_position);
317 current_status = WAIT_VALUE;
318 break;
319 case WAIT_VALUE:
320 current_value = mc_deserialize_str ('v', data, error);
321 if (current_value == NULL)
323 g_free (current_group);
324 g_free (current_param);
325 prepend_error_and_exit ();
327 mc_config_set_string (ret_data, current_group, current_param, current_value);
329 data = go_to_end_of_serialized_string (data, current_value, &current_position);
330 g_free (current_value);
331 current_status = WAIT_GROUP;
332 break;
333 default:
334 break;
337 g_free (current_group);
338 g_free (current_param);
340 return ret_data;
343 #undef FUNC_NAME
345 /* --------------------------------------------------------------------------------------------- */