File: | build-analysis/../src/lib/s4/src/lib/s4.c |
Warning: | line 305, column 16 Assigned value is garbage or undefined |
1 | /* S4 - An XMMS2 medialib backend | |||
2 | * Copyright (C) 2009, 2010 Sivert Berg | |||
3 | * | |||
4 | * This library is free software; you can redistribute it and/or | |||
5 | * modify it under the terms of the GNU Lesser General Public | |||
6 | * License as published by the Free Software Foundation; either | |||
7 | * version 2.1 of the License, or (at your option) any later version. | |||
8 | * | |||
9 | * This library is distributed in the hope that it will be useful, | |||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
12 | * Lesser General Public License for more details. | |||
13 | */ | |||
14 | ||||
15 | #include <s4.h> | |||
16 | #include "s4_priv.h" | |||
17 | #include "logging.h" | |||
18 | #include <string.h> | |||
19 | #include <stdlib.h> | |||
20 | #include <glib/gstdio.h> | |||
21 | #include <errno(*__errno_location ()).h> | |||
22 | ||||
23 | static GPrivate _errno = G_PRIVATE_INIT (g_free){ ((void*)0), (g_free), { ((void*)0), ((void*)0) } }; | |||
24 | ||||
25 | /** | |||
26 | * | |||
27 | * @defgroup S4 S4 | |||
28 | * @brief A database backend for XMMS2 | |||
29 | * | |||
30 | * @{ | |||
31 | */ | |||
32 | ||||
33 | #define S4_MAGIC("s4db") ("s4db") | |||
34 | #define S4_MAGIC_LEN(4) (4) | |||
35 | #define S4_VERSION1 1 | |||
36 | ||||
37 | typedef struct { | |||
38 | char magic[S4_MAGIC_LEN(4)]; | |||
39 | int32_t version; | |||
40 | unsigned char uuid[16]; | |||
41 | log_number_t last_checkpoint; | |||
42 | } s4_header_t; | |||
43 | ||||
44 | /** | |||
45 | * @{ | |||
46 | * @internal | |||
47 | */ | |||
48 | ||||
49 | /** | |||
50 | * Reads strings from a file | |||
51 | * | |||
52 | * @param s4 The database to add the strings to | |||
53 | * @param file The file to read from | |||
54 | * @return A hashtable with the id as they key and the | |||
55 | * corresponding string as they values or NULL on error | |||
56 | */ | |||
57 | static GHashTable *_read_string (s4_t *s4, FILE *file) | |||
58 | { | |||
59 | size_t r; | |||
60 | int32_t id, len; | |||
61 | char *str; | |||
62 | GHashTable *ret = g_hash_table_new (NULL((void*)0), NULL((void*)0)); | |||
63 | ||||
64 | while ((r = fread (&id, sizeof (int32_t), 1, file)) == 1 && | |||
65 | id != -1 && | |||
66 | (r = fread (&len, sizeof (int32_t), 1, file)) == 1) { | |||
67 | str = malloc (len + 1); | |||
68 | r = fread (str, 1, len, file); | |||
69 | ||||
70 | if (r != len) { | |||
71 | g_hash_table_destroy (ret); | |||
72 | return NULL((void*)0); | |||
73 | } | |||
74 | ||||
75 | str[len] = '\0'; | |||
76 | g_hash_table_insert (ret, GINT_TO_POINTER (id)((gpointer) (glong) (id)), (void*)_string_lookup (s4, str)); | |||
77 | free (str); | |||
78 | } | |||
79 | ||||
80 | if (r == 0) { | |||
81 | g_hash_table_destroy (ret); | |||
82 | return NULL((void*)0); | |||
83 | } | |||
84 | ||||
85 | return ret; | |||
86 | } | |||
87 | ||||
88 | /** | |||
89 | * Reads relations from a file | |||
90 | * | |||
91 | * @param s4 The database to insert them into | |||
92 | * @param file The file to read | |||
93 | * @param strings A hashtable with string->int relationships | |||
94 | * @return -1 on error, 0 otherwise | |||
95 | */ | |||
96 | static int _read_relations (s4_t *s4, FILE *file, GHashTable *strings) | |||
97 | { | |||
98 | s4_intpair_t rec; | |||
99 | ||||
100 | while (fread (&rec, sizeof (s4_intpair_t), 1, file) == 1) { | |||
101 | const char *key_a, *key_b, *src; | |||
102 | const s4_val_t *val_a, *val_b; | |||
103 | ||||
104 | key_a = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.key_a))((gpointer) (glong) ((((rec.key_a) < 0) ? -(rec.key_a) : ( rec.key_a))))); | |||
105 | key_b = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.key_b))((gpointer) (glong) ((((rec.key_b) < 0) ? -(rec.key_b) : ( rec.key_b))))); | |||
106 | src = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.src))((gpointer) (glong) ((((rec.src) < 0) ? -(rec.src) : (rec. src))))); | |||
107 | ||||
108 | if (rec.key_a > 0) { | |||
109 | val_a = _string_lookup_val (s4, | |||
110 | g_hash_table_lookup (strings, GINT_TO_POINTER (rec.val_a)((gpointer) (glong) (rec.val_a)))); | |||
111 | } else { | |||
112 | val_a = _int_lookup_val (s4, rec.val_a); | |||
113 | } | |||
114 | if (rec.key_b > 0) { | |||
115 | val_b = _string_lookup_val (s4, | |||
116 | g_hash_table_lookup (strings, GINT_TO_POINTER (rec.val_b)((gpointer) (glong) (rec.val_b)))); | |||
117 | } else { | |||
118 | val_b = _int_lookup_val (s4, rec.val_b); | |||
119 | } | |||
120 | ||||
121 | _s4_add_internal (s4, key_a, val_a, key_b, val_b, src); | |||
122 | } | |||
123 | ||||
124 | return 0; | |||
125 | } | |||
126 | ||||
127 | /** | |||
128 | * Reads an S4 database from filename. | |||
129 | * | |||
130 | * @param s4 The s4 database to read the data into | |||
131 | * @param filename The name of the file to read from | |||
132 | * @param flags Flags passed to s4_open | |||
133 | * @return 0 on success, non-zero on error | |||
134 | */ | |||
135 | static int _read_file (s4_t *s4, const char *filename, int flags) | |||
136 | { | |||
137 | FILE *file = fopen (filename, "r"); | |||
138 | s4_header_t hdr; | |||
139 | int i; | |||
140 | ||||
141 | if (file == NULL((void*)0)) { | |||
142 | int ret = 0; | |||
143 | switch (errno(*__errno_location ())) { | |||
144 | case ENOENT2: | |||
145 | if (flags & S4_EXISTS) { | |||
146 | s4_set_errno (S4E_NOENT); | |||
147 | ret = -1; | |||
148 | } else { | |||
149 | s4_create_uuid (s4->uuid); | |||
150 | } | |||
151 | break; | |||
152 | default: | |||
153 | s4_set_errno (S4E_OPEN); | |||
154 | ret = -1; | |||
155 | break; | |||
156 | } | |||
157 | return ret; | |||
158 | } else if (flags & S4_NEW) { | |||
159 | fclose (file); | |||
160 | s4_set_errno (S4E_EXISTS); | |||
161 | return -1; | |||
162 | } | |||
163 | ||||
164 | fread (&hdr, sizeof (s4_header_t), 1, file); | |||
165 | if (strncmp (S4_MAGIC("s4db"), hdr.magic, S4_MAGIC_LEN(4))) { | |||
166 | fclose (file); | |||
167 | s4_set_errno (S4E_MAGIC); | |||
168 | return -1; | |||
169 | } | |||
170 | ||||
171 | if (hdr.version != S4_VERSION1) { | |||
172 | fclose (file); | |||
173 | s4_set_errno (S4E_VERSION); | |||
174 | return -1; | |||
175 | } | |||
176 | ||||
177 | _log_init (s4, hdr.last_checkpoint); | |||
178 | ||||
179 | for (i = 0; i < 16; i++) { | |||
180 | s4->uuid[i] = hdr.uuid[i]; | |||
181 | } | |||
182 | ||||
183 | GHashTable *strings = _read_string (s4, file); | |||
184 | if (strings == NULL((void*)0) || _read_relations (s4, file, strings) == -1) { | |||
185 | fclose (file); | |||
186 | s4_set_errno (S4E_INCONS); | |||
187 | return -1; | |||
188 | } | |||
189 | g_hash_table_destroy (strings); | |||
190 | ||||
191 | fclose (file); | |||
192 | return 0; | |||
193 | } | |||
194 | ||||
195 | int _reread_file (s4_t *s4) | |||
196 | { | |||
197 | _free_relations (s4); | |||
198 | ||||
199 | _index_free_data (s4->index_data); | |||
200 | _entry_free_data (s4->entry_data); | |||
201 | s4->index_data = _index_create_data (); | |||
202 | s4->entry_data = _entry_create_data (); | |||
203 | ||||
204 | return _read_file (s4, s4->filename, S4_EXISTS); | |||
205 | } | |||
206 | ||||
207 | /** | |||
208 | * Writes all the id->string relations in the hash table to file | |||
209 | * | |||
210 | * @param strings The hashtable holding the key-value pairs | |||
211 | * @param file The file to write to | |||
212 | */ | |||
213 | static void _write_strings (GHashTable *strings, FILE *file) | |||
214 | { | |||
215 | GHashTableIter iter; | |||
216 | void *i; | |||
217 | const char *str; | |||
218 | ||||
219 | g_hash_table_iter_init (&iter, strings); | |||
220 | while (g_hash_table_iter_next (&iter, (void**)&str, &i)) { | |||
221 | int32_t len = strlen (str); | |||
222 | int32_t str_id = GPOINTER_TO_INT (i)((gint) (glong) (i)); | |||
223 | fwrite (&str_id, sizeof (int32_t), 1, file); | |||
224 | fwrite (&len, sizeof (int32_t), 1, file); | |||
225 | fwrite (str, 1, len, file); | |||
226 | } | |||
227 | } | |||
228 | ||||
229 | /** | |||
230 | * Writes a list of int-pairs to file | |||
231 | * | |||
232 | * @param pairs A GList with pairs to write | |||
233 | * @param file The file to write to | |||
234 | */ | |||
235 | static void _write_pairs (GList *pairs, FILE *file) | |||
236 | { | |||
237 | for (; pairs != NULL((void*)0); pairs = g_list_next (pairs)((pairs) ? (((GList *)(pairs))->next) : ((void*)0))) { | |||
238 | s4_intpair_t *pair = pairs->data; | |||
239 | fwrite (pair, sizeof (s4_intpair_t), 1, file); | |||
240 | free (pair); | |||
241 | } | |||
242 | } | |||
243 | ||||
244 | typedef struct { | |||
245 | GHashTable *strings; | |||
246 | GList *pairs; | |||
247 | int new_id; | |||
248 | } save_data_t; | |||
249 | ||||
250 | /** | |||
251 | * Gets the id of a string, or gives it an unique id if it doesn't have one | |||
252 | * | |||
253 | * @param sd A structure holding the strings found so far and the next free id. | |||
254 | * @param str The string to lookup | |||
255 | * @return The id associated with the string | |||
256 | */ | |||
257 | static int _get_string_number (save_data_t *sd, const char *str) | |||
258 | { | |||
259 | int i = GPOINTER_TO_INT (g_hash_table_lookup (sd->strings, str))((gint) (glong) (g_hash_table_lookup (sd->strings, str))); | |||
260 | ||||
261 | if (i == 0) { | |||
262 | i = sd->new_id++; | |||
263 | g_hash_table_insert (sd->strings, (void*)str, GINT_TO_POINTER (i)((gpointer) (glong) (i))); | |||
264 | } | |||
265 | ||||
266 | return i; | |||
267 | } | |||
268 | ||||
269 | /* | |||
270 | * A helper function converting a resultset into a list of int-pairs. | |||
271 | */ | |||
272 | static void _result_to_pairs (s4_resultset_t *res, save_data_t *sd) | |||
273 | { | |||
274 | const s4_resultrow_t *row; | |||
275 | int row_no; | |||
276 | ||||
277 | for (row_no = 0; s4_resultset_get_row (res, row_no, &row); row_no++) { | |||
278 | int32_t va, ka, i; | |||
279 | const s4_val_t *val_a, *val_b; | |||
280 | const char *key_a, *key_b, *src, *str; | |||
281 | const s4_result_t *id_res, *val_res; | |||
282 | ||||
283 | s4_resultrow_get_col (row, 0, &id_res); | |||
284 | s4_resultrow_get_col (row, 1, &val_res); | |||
285 | ||||
286 | val_a = s4_result_get_val (id_res); | |||
287 | key_a = s4_result_get_key (id_res); | |||
288 | ||||
289 | ka = _get_string_number (sd, key_a); | |||
290 | ||||
291 | if (s4_val_get_int (val_a, &i)) { | |||
292 | va = i; | |||
293 | ka = -ka; | |||
294 | } else if (s4_val_get_str (val_a, &str)) { | |||
295 | va = _get_string_number (sd, str); | |||
296 | } | |||
297 | ||||
298 | for (; val_res != NULL((void*)0); val_res = s4_result_next (val_res)) { | |||
299 | s4_intpair_t *pair = malloc (sizeof (s4_intpair_t)); | |||
300 | ||||
301 | val_b = s4_result_get_val (val_res); | |||
302 | key_b = s4_result_get_key (val_res); | |||
303 | src = s4_result_get_src (val_res); | |||
304 | ||||
305 | pair->val_a = va; | |||
| ||||
306 | pair->key_a = ka; | |||
307 | pair->key_b = _get_string_number (sd, key_b); | |||
308 | pair->src = _get_string_number (sd, src); | |||
309 | ||||
310 | if (s4_val_get_int (val_b, &i)) { | |||
311 | pair->val_b = i; | |||
312 | pair->key_b = -pair->key_b; | |||
313 | } else if (s4_val_get_str (val_b, &str)) { | |||
314 | pair->val_b = _get_string_number (sd, str); | |||
315 | } | |||
316 | ||||
317 | sd->pairs = g_list_prepend (sd->pairs, pair); | |||
318 | } | |||
319 | } | |||
320 | } | |||
321 | ||||
322 | /** | |||
323 | * Writes the database to disk | |||
324 | * | |||
325 | * @param s4 The database to write | |||
326 | * @return non-zero on success, 0 on error | |||
327 | */ | |||
328 | static int _write_file (s4_t *s4) | |||
329 | { | |||
330 | int32_t i = -1; | |||
331 | int j; | |||
332 | FILE *file; | |||
333 | s4_header_t hdr; | |||
334 | save_data_t sd; | |||
335 | s4_condition_t *cond; | |||
336 | s4_fetchspec_t *fs; | |||
337 | s4_resultset_t *res; | |||
338 | s4_transaction_t *trans; | |||
339 | ||||
340 | _log_lock_db (s4); | |||
341 | ||||
342 | file = fopen (s4->tmp_filename, "w"); | |||
343 | if (file == NULL((void*)0)) { | |||
344 | _log_unlock_db (s4); | |||
345 | return 0; | |||
346 | } | |||
347 | ||||
348 | sd.strings = g_hash_table_new (NULL((void*)0), NULL((void*)0)); | |||
349 | sd.pairs = NULL((void*)0); | |||
350 | sd.new_id = 1; | |||
351 | ||||
352 | cond = s4_cond_new_filter (S4_FILTER_EXISTS, NULL((void*)0), NULL((void*)0), NULL((void*)0), S4_CMP_BINARY, 0); | |||
353 | ||||
354 | fs = s4_fetchspec_create (); | |||
355 | s4_fetchspec_add (fs, NULL((void*)0), NULL((void*)0), S4_FETCH_PARENT1); | |||
356 | s4_fetchspec_add (fs, NULL((void*)0), NULL((void*)0), S4_FETCH_DATA2); | |||
357 | ||||
358 | do { | |||
359 | trans = s4_begin (s4, 0); | |||
360 | res = s4_query (trans, fs, cond); | |||
361 | _transaction_writing (trans); | |||
362 | } while (!s4_commit (trans)); | |||
363 | ||||
364 | _result_to_pairs (res, &sd); | |||
365 | ||||
366 | s4_cond_free (cond); | |||
367 | s4_fetchspec_free (fs); | |||
368 | s4_resultset_free (res); | |||
369 | ||||
370 | strncpy (hdr.magic, S4_MAGIC("s4db"), S4_MAGIC_LEN(4)); | |||
371 | hdr.version = S4_VERSION1; | |||
372 | for (j = 0; j < 16; j++) { | |||
373 | hdr.uuid[j] = s4->uuid[j]; | |||
374 | } | |||
375 | hdr.last_checkpoint = _log_last_synced (s4); | |||
376 | ||||
377 | fwrite (&hdr, sizeof (s4_header_t), 1, file); | |||
378 | _write_strings (sd.strings, file); | |||
379 | fwrite (&i, sizeof (int32_t), 1, file); | |||
380 | _write_pairs (sd.pairs, file); | |||
381 | ||||
382 | g_hash_table_destroy (sd.strings); | |||
383 | g_list_free (sd.pairs); | |||
384 | ||||
385 | fclose (file); | |||
386 | ||||
387 | g_renamerename (s4->tmp_filename, s4->filename); | |||
388 | ||||
389 | _log_checkpoint (s4); | |||
390 | _log_unlock_db (s4); | |||
391 | return 1; | |||
392 | } | |||
393 | ||||
394 | static void *_sync_thread (s4_t *s4) | |||
395 | { | |||
396 | g_mutex_lock (&s4->sync_lock); | |||
397 | while (s4->sync_thread_run) { | |||
| ||||
398 | if (s4->sync_thread_run) | |||
399 | g_cond_wait (&s4->sync_cond, &s4->sync_lock); | |||
400 | g_mutex_unlock (&s4->sync_lock); | |||
401 | ||||
402 | s4_sync (s4); | |||
403 | ||||
404 | g_mutex_lock (&s4->sync_lock); | |||
405 | g_cond_broadcast (&s4->sync_finished_cond); | |||
406 | } | |||
407 | g_mutex_unlock (&s4->sync_lock); | |||
408 | ||||
409 | return NULL((void*)0); | |||
410 | } | |||
411 | ||||
412 | /* Start sync */ | |||
413 | void _start_sync (s4_t *s4) | |||
414 | { | |||
415 | g_mutex_lock (&s4->sync_lock); | |||
416 | g_cond_signal (&s4->sync_cond); | |||
417 | g_mutex_unlock (&s4->sync_lock); | |||
418 | } | |||
419 | ||||
420 | /* Start and wait for sync to finish */ | |||
421 | void _sync (s4_t *s4) | |||
422 | { | |||
423 | g_mutex_lock (&s4->sync_lock); | |||
424 | g_cond_signal (&s4->sync_cond); | |||
425 | g_cond_wait (&s4->sync_finished_cond, &s4->sync_lock); | |||
426 | g_mutex_unlock (&s4->sync_lock); | |||
427 | } | |||
428 | ||||
429 | static s4_t *_alloc (void) | |||
430 | { | |||
431 | s4_t* s4 = calloc (1, sizeof(s4_t)); | |||
432 | ||||
433 | g_mutex_init (&s4->sync_lock); | |||
434 | g_cond_init (&s4->sync_cond); | |||
435 | g_cond_init (&s4->sync_finished_cond); | |||
436 | ||||
437 | s4->const_data = _const_create_data (); | |||
438 | s4->index_data = _index_create_data (); | |||
439 | s4->entry_data = _entry_create_data (); | |||
440 | s4->log_data = _log_create_data (); | |||
441 | ||||
442 | return s4; | |||
443 | } | |||
444 | ||||
445 | /** | |||
446 | * Frees an S4 handle and everything it points at | |||
447 | * | |||
448 | * @param s4 The handle to free | |||
449 | */ | |||
450 | static void _free (s4_t *s4) | |||
451 | { | |||
452 | _free_relations (s4); | |||
453 | ||||
454 | g_mutex_clear (&s4->sync_lock); | |||
455 | g_cond_clear (&s4->sync_cond); | |||
456 | g_cond_clear (&s4->sync_finished_cond); | |||
457 | ||||
458 | _const_free_data (s4->const_data); | |||
459 | _index_free_data (s4->index_data); | |||
460 | _entry_free_data (s4->entry_data); | |||
461 | _log_free_data (s4->log_data); | |||
462 | ||||
463 | free (s4->filename); | |||
464 | g_free (s4->tmp_filename); | |||
465 | free (s4); | |||
466 | } | |||
467 | ||||
468 | /** | |||
469 | * @} | |||
470 | */ | |||
471 | ||||
472 | /** | |||
473 | * Opens an S4 database | |||
474 | * | |||
475 | * @b The different flags you can pass: | |||
476 | * <P> | |||
477 | * @b S4_NEW | |||
478 | * <BR> | |||
479 | * It will create a new file if one does not already exists. | |||
480 | * If one exists it will fail and return NULL. | |||
481 | * </P><P> | |||
482 | * @b S4_EXISTS | |||
483 | * <BR> | |||
484 | * If the file does not exists it will fail and return NULL. | |||
485 | * s4_errno may be used to get more information about what | |||
486 | * went wrong. | |||
487 | * </P><P> | |||
488 | * @b S4_MEMORY | |||
489 | * Creates a memory-only database. It will not read any files | |||
490 | * on startup or write files on shutdown. Use this if you want | |||
491 | * a temporary database. | |||
492 | * <BR> | |||
493 | * | |||
494 | * @param filename The name of the file containing the database | |||
495 | * @param indices An array of keys to have indices on | |||
496 | * @param open_flags Zero or more of the flags bitwise-or'd. | |||
497 | * @return A pointer to an s4_t, or NULL if something went wrong. | |||
498 | */ | |||
499 | s4_t *s4_open (const char *filename, const char **indices, int open_flags) | |||
500 | { | |||
501 | int i; | |||
502 | s4_t *s4; | |||
503 | ||||
504 | s4 = _alloc (); | |||
505 | ||||
506 | for (i = 0; indices != NULL((void*)0) && indices[i] != NULL((void*)0); i++) { | |||
507 | _index_add (s4, indices[i], _index_create ()); | |||
508 | } | |||
509 | ||||
510 | s4->open_flags = open_flags; | |||
511 | ||||
512 | if (open_flags & S4_MEMORY) { | |||
513 | return s4; | |||
514 | } | |||
515 | ||||
516 | s4->filename = strdup (filename); | |||
517 | s4->tmp_filename = g_strconcat (filename, ".chkpnt", NULL((void*)0)); | |||
518 | if (_read_file (s4, s4->filename, open_flags)) { | |||
519 | _free (s4); | |||
520 | return NULL((void*)0); | |||
521 | } | |||
522 | ||||
523 | if (!_log_open (s4)) { | |||
524 | _free (s4); | |||
525 | return NULL((void*)0); | |||
526 | } | |||
527 | ||||
528 | /* Write the file right away in case we have opened a new file | |||
529 | * or we redid something in the log that was not in the file | |||
530 | */ | |||
531 | s4_sync (s4); | |||
532 | ||||
533 | s4->sync_thread_run = 1; | |||
534 | s4->sync_thread = g_thread_new ("s4 sync", (GThreadFunc)_sync_thread, s4); | |||
535 | ||||
536 | return s4; | |||
537 | } | |||
538 | ||||
539 | /** | |||
540 | * Closes an open S4 database | |||
541 | * | |||
542 | * @param s4 The database to close | |||
543 | * | |||
544 | */ | |||
545 | int s4_close (s4_t* s4) | |||
546 | { | |||
547 | if (!(s4->open_flags & S4_MEMORY)) { | |||
548 | g_mutex_lock (&s4->sync_lock); | |||
549 | s4->sync_thread_run = 0; | |||
550 | g_cond_signal (&s4->sync_cond); | |||
551 | g_mutex_unlock (&s4->sync_lock); | |||
552 | g_thread_join (s4->sync_thread); | |||
553 | ||||
554 | _log_close (s4); | |||
555 | } | |||
556 | ||||
557 | _free (s4); | |||
558 | ||||
559 | return 0; | |||
560 | } | |||
561 | ||||
562 | ||||
563 | /** | |||
564 | * Writes all changes to disk | |||
565 | * | |||
566 | * @param s4 The database to sync | |||
567 | * | |||
568 | */ | |||
569 | void s4_sync (s4_t *s4) | |||
570 | { | |||
571 | if (!_write_file (s4)) { | |||
572 | S4_ERROR ("s4_sync: could not write file")g_warning ("../src/lib/s4/src/lib/s4.c" ":" "572" ": " "s4_sync: could not write file" ); | |||
573 | } | |||
574 | } | |||
575 | ||||
576 | /** | |||
577 | * Returns the last error number set. | |||
578 | * This function is thread safe, error numbers set in one thread | |||
579 | * will NOT be seen in another thread. | |||
580 | * | |||
581 | * @return The last error number set, or S4E_NOERROR if none has been set | |||
582 | */ | |||
583 | s4_errno_t s4_errno() | |||
584 | { | |||
585 | s4_errno_t *i = g_private_get (&_errno); | |||
586 | if (i == NULL((void*)0)) { | |||
587 | return S4E_NOERROR; | |||
588 | } | |||
589 | return *i; | |||
590 | } | |||
591 | ||||
592 | /** | |||
593 | * Sets errno to the given error number | |||
594 | * | |||
595 | * @param err The error number to set | |||
596 | */ | |||
597 | void s4_set_errno (s4_errno_t err) | |||
598 | { | |||
599 | s4_errno_t *i = g_private_get (&_errno); | |||
600 | if (i == NULL((void*)0)) { | |||
601 | i = malloc (sizeof (s4_errno_t)); | |||
602 | g_private_set (&_errno, i); | |||
603 | } | |||
604 | ||||
605 | *i = err; | |||
606 | } | |||
607 | ||||
608 | /** | |||
609 | * @} | |||
610 | */ |