1 /*
2 Provides a serialize/unserialize functionality for INI-like formats.
3
4 Copyright (C) 2011-2021
5 Free Software Foundation, Inc.
6
7 Written by:
8 Slava Zanko <slavazanko@gmail.com>, 2011
9
10 This file is part of the Midnight Commander.
11
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.
16
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.
21
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/>.
24 */
25
26 /** \file lib/serialize.c
27 * \brief Source: serialize/unserialize functionality for INI-like formats.
28 */
29
30 #include <config.h>
31
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35
36 #include "lib/global.h"
37
38 #include "lib/serialize.h"
39
40 /*** global variables ****************************************************************************/
41
42 /*** file scope macro definitions ****************************************************************/
43
44 #define SRLZ_DELIM_C ':'
45 #define SRLZ_DELIM_S ":"
46
47 /*** file scope type declarations ****************************************************************/
48
49 /*** file scope variables ************************************************************************/
50
51 /*** file scope functions ************************************************************************/
52 /* --------------------------------------------------------------------------------------------- */
53
54 static void
55 G_GNUC_PRINTF (2, 3)
prepend_error_message(GError ** error,const char * format,...)56 prepend_error_message (GError ** error, const char *format, ...)
57 {
58 char *prepend_str;
59 char *split_str;
60 va_list ap;
61
62 if ((error == NULL) || (*error == NULL))
63 return;
64
65 va_start (ap, format);
66 prepend_str = g_strdup_vprintf (format, ap);
67 va_end (ap);
68
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;
73 }
74
75 /* --------------------------------------------------------------------------------------------- */
76
77 static const char *
go_to_end_of_serialized_string(const char * non_serialized_data,const char * already_serialized_part,size_t * offset)78 go_to_end_of_serialized_string (const char *non_serialized_data,
79 const char *already_serialized_part, size_t * offset)
80 {
81 size_t calculated_offset;
82 const char *semi_ptr = strchr (non_serialized_data + 1, SRLZ_DELIM_C);
83
84 calculated_offset = (semi_ptr - non_serialized_data) + 1 + strlen (already_serialized_part);
85 if (calculated_offset >= strlen (non_serialized_data))
86 return NULL;
87
88 non_serialized_data += calculated_offset;
89 *offset += calculated_offset;
90
91 return non_serialized_data;
92 }
93
94 /* --------------------------------------------------------------------------------------------- */
95 /*** public functions ****************************************************************************/
96 /* --------------------------------------------------------------------------------------------- */
97
98 /**
99 * Serialize some string object to string
100 *
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
104 *
105 * @return serialized data as newly allocated string
106 */
107
108 char *
mc_serialize_str(const char prefix,const char * data,GError ** error)109 mc_serialize_str (const char prefix, const char *data, GError ** error)
110 {
111 if (data == NULL)
112 {
113 g_set_error (error, MC_ERROR, 0, "mc_serialize_str(): Input data is NULL.");
114 return NULL;
115 }
116 return g_strdup_printf ("%c%zu" SRLZ_DELIM_S "%s", prefix, strlen (data), data);
117 }
118
119 /* --------------------------------------------------------------------------------------------- */
120 /**
121 * Deserialize string to string object
122 *
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
126 *
127 * @return newly allocated string
128 */
129
130 #define FUNC_NAME "mc_serialize_str()"
131 char *
mc_deserialize_str(const char prefix,const char * data,GError ** error)132 mc_deserialize_str (const char prefix, const char *data, GError ** error)
133 {
134 size_t data_len;
135
136 if ((data == NULL) || (*data == '\0'))
137 {
138 g_set_error (error, MC_ERROR, 0, FUNC_NAME ": Input data is NULL or empty.");
139 return NULL;
140 }
141
142 if (*data != prefix)
143 {
144 g_set_error (error, MC_ERROR, 0, FUNC_NAME ": String prefix doesn't equal to '%c'", prefix);
145 return NULL;
146 }
147
148 {
149 char buffer[BUF_TINY];
150 char *semi_ptr;
151 size_t semi_offset;
152
153 semi_ptr = strchr (data + 1, SRLZ_DELIM_C);
154 if (semi_ptr == NULL)
155 {
156 g_set_error (error, MC_ERROR, 0,
157 FUNC_NAME ": Length delimiter '%c' doesn't exists", SRLZ_DELIM_C);
158 return NULL;
159 }
160 semi_offset = semi_ptr - (data + 1);
161 if (semi_offset >= BUF_TINY)
162 {
163 g_set_error (error, MC_ERROR, 0, FUNC_NAME ": Too big string length");
164 return NULL;
165 }
166 strncpy (buffer, data + 1, semi_offset);
167 buffer[semi_offset] = '\0';
168 data_len = atol (buffer);
169 data += semi_offset + 2;
170 }
171
172 if (data_len > strlen (data))
173 {
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;
179 }
180 return g_strndup (data, data_len);
181 }
182
183 #undef FUNC_NAME
184
185 /* --------------------------------------------------------------------------------------------- */
186 /**
187 * Serialize mc_config_t object to string
188 *
189 * @param data data for serialization
190 * @param error contain pointer to object for handle error code and message
191 *
192 * @return serialized data as newly allocated string
193 */
194
195 char *
mc_serialize_config(mc_config_t * data,GError ** error)196 mc_serialize_config (mc_config_t * data, GError ** error)
197 {
198 gchar **groups, **group_iterator;
199 GString *buffer;
200
201 buffer = g_string_new ("");
202 groups = mc_config_get_groups (data, NULL);
203
204 for (group_iterator = groups; *group_iterator != NULL; group_iterator++)
205 {
206 char *serialized_str;
207 gchar **params, **param_iterator;
208
209 serialized_str = mc_serialize_str ('g', *group_iterator, error);
210 if (serialized_str == NULL)
211 {
212 g_string_free (buffer, TRUE);
213 g_strfreev (groups);
214 return NULL;
215 }
216 g_string_append (buffer, serialized_str);
217 g_free (serialized_str);
218
219 params = mc_config_get_keys (data, *group_iterator, NULL);
220
221 for (param_iterator = params; *param_iterator != NULL; param_iterator++)
222 {
223 char *value;
224
225 serialized_str = mc_serialize_str ('p', *param_iterator, error);
226 if (serialized_str == NULL)
227 {
228 g_string_free (buffer, TRUE);
229 g_strfreev (params);
230 g_strfreev (groups);
231 return NULL;
232 }
233 g_string_append (buffer, serialized_str);
234 g_free (serialized_str);
235
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);
239
240 if (serialized_str == NULL)
241 {
242 g_string_free (buffer, TRUE);
243 g_strfreev (params);
244 g_strfreev (groups);
245 return NULL;
246 }
247
248 g_string_append (buffer, serialized_str);
249 g_free (serialized_str);
250 }
251
252 g_strfreev (params);
253 }
254
255 g_strfreev (groups);
256
257 return g_string_free (buffer, FALSE);
258 }
259
260 /* --------------------------------------------------------------------------------------------- */
261 /**
262 * Deserialize string to mc_config_t object
263 *
264 * @param data data for serialization
265 * @param error contain pointer to object for handle error code and message
266 *
267 * @return newly allocated mc_config_t object
268 */
269
270 #define FUNC_NAME "mc_deserialize_config()"
271 #define prepend_error_and_exit() { \
272 prepend_error_message (error, FUNC_NAME " at %zu", current_position + 1); \
273 mc_config_deinit (ret_data); \
274 return NULL; \
275 }
276
277 mc_config_t *
mc_deserialize_config(const char * data,GError ** error)278 mc_deserialize_config (const char *data, GError ** error)
279 {
280 char *current_group = NULL, *current_param = NULL, *current_value = NULL;
281 size_t current_position = 0;
282 mc_config_t *ret_data;
283 enum automat_status
284 {
285 WAIT_GROUP,
286 WAIT_PARAM,
287 WAIT_VALUE
288 } current_status = WAIT_GROUP;
289
290 ret_data = mc_config_init (NULL, FALSE);
291
292 while (data != NULL)
293 {
294 if ((current_status == WAIT_GROUP) && (*data == 'p') && (current_group != NULL))
295 current_status = WAIT_PARAM;
296
297 switch (current_status)
298 {
299 case WAIT_GROUP:
300 g_free (current_group);
301
302 current_group = mc_deserialize_str ('g', data, error);
303 if (current_group == NULL)
304 prepend_error_and_exit ();
305
306 data = go_to_end_of_serialized_string (data, current_group, ¤t_position);
307 current_status = WAIT_PARAM;
308 break;
309 case WAIT_PARAM:
310 g_free (current_param);
311
312 current_param = mc_deserialize_str ('p', data, error);
313 if (current_param == NULL)
314 {
315 g_free (current_group);
316 prepend_error_and_exit ();
317 }
318
319 data = go_to_end_of_serialized_string (data, current_param, ¤t_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)
325 {
326 g_free (current_group);
327 g_free (current_param);
328 prepend_error_and_exit ();
329 }
330 mc_config_set_string (ret_data, current_group, current_param, current_value);
331
332 data = go_to_end_of_serialized_string (data, current_value, ¤t_position);
333 g_free (current_value);
334 current_status = WAIT_GROUP;
335 break;
336 default:
337 break;
338 }
339 }
340 g_free (current_group);
341 g_free (current_param);
342
343 return ret_data;
344 }
345
346 #undef FUNC_NAME
347
348 /* --------------------------------------------------------------------------------------------- */
349