Moved variable declarations from in-block declarations to be MSVC compiler compliant
[opensync.git] / opensync / opensync_support.c
blob722fb4c64cabf34074bb4d3044e1e9af43a8635f
1 /*
2 * libopensync - A synchronization framework
3 * Copyright (C) 2004-2005 Armin Bauer <armin.bauer@opensync.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <libxml/xmlmemory.h>
22 #include <libxml/parser.h>
24 #include "opensync.h"
25 #include "opensync_internals.h"
27 #include "opensync-support.h"
28 #include "opensync_support_internals.h"
30 GPrivate* current_tabs = NULL;
31 GPrivate* thread_id = NULL;
32 GPrivate* trace_disabled = NULL;
33 GPrivate* trace_sensitive = NULL;
34 GPrivate* print_stderr = NULL;
35 const char *trace = NULL;
37 #ifndef _WIN32
38 #include <pthread.h>
39 #endif
41 /**
42 * @defgroup OSyncDebugAPI OpenSync Debug
43 * @ingroup OSyncPublic
44 * @brief Debug functions used by opensync
47 /*@{*/
49 /*! @brief Reset the indentation of the trace function.
51 * Use this function after when process got forked. It's up to the forked
52 * process (child) to reset the indent.
55 void osync_trace_reset_indent(void)
57 g_private_set(current_tabs, GINT_TO_POINTER(0));
60 /*! @brief Disable tracing
63 void osync_trace_disable(void)
65 if (!trace_disabled)
66 trace_disabled = g_private_new (NULL);
68 g_private_set(trace_disabled, GINT_TO_POINTER(1));
71 /*! @brief Enable tracing
74 void osync_trace_enable(void)
76 if (!trace_disabled)
77 trace_disabled = g_private_new (NULL);
79 if (!trace)
80 g_private_set(trace_disabled, GINT_TO_POINTER(1));
81 else
82 g_private_set(trace_disabled, GINT_TO_POINTER(0));
86 /*! @brief Initailize tracing
89 static void _osync_trace_init()
91 const char *noprivacy;
92 const char *error;
93 trace = g_getenv("OSYNC_TRACE");
94 if (!trace)
95 return;
97 noprivacy = g_getenv("OSYNC_NOPRIVACY");
98 if (!trace_sensitive)
99 trace_sensitive = g_private_new(NULL);
101 if (noprivacy)
102 g_private_set(trace_sensitive, GINT_TO_POINTER(1));
103 else
104 g_private_set(trace_sensitive, GINT_TO_POINTER(0));
106 error = g_getenv("OSYNC_PRINTERROR");
107 if (!print_stderr)
108 print_stderr = g_private_new(NULL);
110 if (error)
111 g_private_set(print_stderr, GINT_TO_POINTER(1));
112 else
113 g_private_set(print_stderr, GINT_TO_POINTER(0));
115 if (!g_file_test(trace, G_FILE_TEST_IS_DIR)) {
116 printf("OSYNC_TRACE argument is no directory\n");
117 return;
122 /*! @brief Used for tracing the application
124 * use this function to trace calls. The call graph will be saved into
125 * the file that is given in the OSYNC_TRACE environment variable
127 * @param type The type of the trace
128 * @param message The message to save
132 void osync_trace(OSyncTraceType type, const char *message, ...)
134 #ifdef OPENSYNC_TRACE
135 va_list arglist;
136 char *buffer = NULL;
137 int tabs = 0;
138 unsigned long int id = 0;
139 #ifdef _WIN32
140 int pid = 0;
141 char tmp_buf[1024];
142 #else
143 pid_t pid = 0;
144 #endif
145 char *logfile = NULL;
146 GString *tabstr = NULL;
147 int i = 0;
148 GTimeVal curtime;
149 char *logmessage = NULL;
150 GError *error = NULL;
151 GIOChannel *chan = NULL;
152 gsize writen;
153 const char *endline = NULL;
155 if (!g_thread_supported ()) g_thread_init (NULL);
157 if (!trace_disabled || !g_private_get(trace_disabled)) {
158 _osync_trace_init();
159 osync_trace_enable();
162 if (GPOINTER_TO_INT(g_private_get(trace_disabled)))
163 return;
165 if (!current_tabs)
166 current_tabs = g_private_new (NULL);
167 else
168 tabs = GPOINTER_TO_INT(g_private_get(current_tabs));
170 #ifdef _WIN32
171 if (!thread_id)
172 thread_id = g_private_new (NULL);
173 id = GPOINTER_TO_INT(thread_id);
174 pid = _getpid();
175 endline = "\r\n";
176 #else
177 id = (unsigned long int)pthread_self();
178 pid = getpid();
179 endline = "\n";
180 #endif
181 logfile = g_strdup_printf("%s%cThread%lu-%i.log", trace, G_DIR_SEPARATOR, id, pid);
183 va_start(arglist, message);
185 #ifdef _WIN32
186 vsnprintf(tmp_buf, 1024, message, arglist);
187 buffer = g_strdup(tmp_buf);
188 #else
189 buffer = g_strdup_vprintf(message, arglist);
190 #endif
192 tabstr = g_string_new("");
193 for (i = 0; i < tabs; i++) {
194 tabstr = g_string_append(tabstr, "\t");
197 g_get_current_time(&curtime);
198 switch (type) {
199 case TRACE_ENTRY:
200 logmessage = g_strdup_printf("[%li.%06li]\t%s>>>>>>> %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
201 tabs++;
202 break;
203 case TRACE_INTERNAL:
204 logmessage = g_strdup_printf("[%li.%06li]\t%s%s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
205 break;
206 case TRACE_SENSITIVE:
207 if (GPOINTER_TO_INT(g_private_get(trace_sensitive)))
208 logmessage = g_strdup_printf("[%li.%06li]\t%s[SENSITIVE] %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
209 else
210 logmessage = g_strdup_printf("[%li.%06li]\t%s[SENSITIVE CONTENT HIDDEN]%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, endline);
211 break;
212 case TRACE_EXIT:
213 logmessage = g_strdup_printf("[%li.%06li]%s<<<<<<< %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
214 tabs--;
215 if (tabs < 0)
216 tabs = 0;
217 break;
218 case TRACE_EXIT_ERROR:
219 logmessage = g_strdup_printf("[%li.%06li]%s<--- ERROR --- %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
220 tabs--;
221 if (tabs < 0)
222 tabs = 0;
224 if (print_stderr)
225 fprintf(stderr, "EXIT_ERROR: %s\n", buffer);
226 break;
227 case TRACE_ERROR:
228 logmessage = g_strdup_printf("[%li.%06li]%sERROR: %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
230 if (print_stderr)
231 fprintf(stderr, "ERROR: %s\n", buffer);
233 break;
235 g_free(buffer);
236 g_private_set(current_tabs, GINT_TO_POINTER(tabs));
237 va_end(arglist);
239 g_string_free(tabstr, TRUE);
241 chan = g_io_channel_new_file(logfile, "a", &error);
242 if (!chan) {
243 printf("unable to open %s for writing: %s\n", logfile, error->message);
244 return;
247 g_io_channel_set_encoding(chan, NULL, NULL);
248 if (g_io_channel_write_chars(chan, logmessage, strlen(logmessage), &writen, NULL) != G_IO_STATUS_NORMAL) {
249 printf("unable to write trace to %s\n", logfile);
250 } else
251 g_io_channel_flush(chan, NULL);
253 g_io_channel_shutdown(chan, TRUE, NULL);
254 g_io_channel_unref(chan);
255 g_free(logmessage);
256 g_free(logfile);
258 #endif /* OPENSYNC_TRACE */
261 /*! @brief Used for printing binary data
263 * Unprintable character will be printed in hex, printable are just printed
265 * @param data The data to print
266 * @param len The length to print
269 char *osync_print_binary(const unsigned char *data, int len)
271 int t;
272 GString *str = g_string_new("");
273 for (t = 0; t < len; t++) {
274 if (data[t] >= ' ' && data[t] <= 'z')
275 g_string_append_c(str, data[t]);
276 else
277 g_string_append_printf(str, " %02x ", data[t]);
279 return g_string_free(str, FALSE);
282 /*@}*/
285 * @defgroup OSyncEnvAPIMisc OpenSync Misc
286 * @ingroup OSyncPrivate
287 * @brief Some helper functions
290 /*@{*/
292 /*! @brief Stop callback function to stop thread mainloop
294 * @param data Pointer to passed callback data
295 * @returns Always FALSE
297 static gboolean osyncThreadStopCallback(gpointer data)
299 OSyncThread *thread = data;
301 g_main_loop_quit(thread->loop);
303 return FALSE;
306 /*! @brief Start callback function to emit signal when thread's mainloop got started
308 * @param data Pointer to passed callback data
309 * @returns Always FALSE
311 static gboolean osyncThreadStartCallback(gpointer data)
313 OSyncThread *thread = data;
315 g_mutex_lock(thread->started_mutex);
316 g_cond_signal(thread->started);
317 g_mutex_unlock(thread->started_mutex);
319 return FALSE;
322 /*@}*/
325 * @defgroup OSyncEnvAPIMisc OpenSync Misc
326 * @ingroup OSyncPublic
327 * @brief Some helper functions
330 /*@{*/
332 /*! @brief Writes data to a file
334 * Writes data to a file
336 * @param filename Where to save the data
337 * @param data Pointer to the data
338 * @param size Size of the data
339 * @param mode The mode to set on the file
340 * @param oserror Pointer to a error struct
341 * @returns TRUE if successful, FALSE otherwise
344 osync_bool osync_file_write(const char *filename, const char *data, unsigned int size, int mode, OSyncError **oserror)
346 osync_bool ret = FALSE;
347 GError *error = NULL;
348 gsize writen;
350 GIOChannel *chan = g_io_channel_new_file(filename, "w", &error);
351 if (!chan) {
352 osync_trace(TRACE_INTERNAL, "Unable to open file %s for writing: %s", filename, error->message);
353 osync_error_set(oserror, OSYNC_ERROR_IO_ERROR, "Unable to open file %s for writing: %s", filename, error->message);
354 return FALSE;
356 if (mode) {
357 if (g_chmod(filename, mode)) {
358 osync_trace(TRACE_INTERNAL, "Unable to set file permissions %i for file %s", mode, filename);
359 osync_error_set(oserror, OSYNC_ERROR_IO_ERROR, "Unable to set file permissions %i for file %s", mode, filename);
360 return FALSE;
364 g_io_channel_set_encoding(chan, NULL, NULL);
365 if (g_io_channel_write_chars(chan, data, size, &writen, &error) != G_IO_STATUS_NORMAL) {
366 osync_trace(TRACE_INTERNAL, "Unable to write contents of file %s: %s", filename, error->message);
367 osync_error_set(oserror, OSYNC_ERROR_IO_ERROR, "Unable to write contents of file %s: %s", filename, error->message);
368 } else {
369 g_io_channel_flush(chan, NULL);
370 ret = TRUE;
372 g_io_channel_shutdown(chan, FALSE, NULL);
373 g_io_channel_unref(chan);
374 return ret;
377 /*! @brief Reads a file
379 * Reads a file
381 * @param filename Where to read the data from
382 * @param data Pointer to the data
383 * @param size Size of the data
384 * @param oserror Pointer to a error struct
385 * @returns TRUE if successful, FALSE otherwise
388 osync_bool osync_file_read(const char *filename, char **data, unsigned int *size, OSyncError **oserror)
390 osync_bool ret = FALSE;
391 GError *error = NULL;
392 gsize sz = 0;
393 GIOChannel *chan = NULL;
395 if (!filename) {
396 osync_trace(TRACE_INTERNAL, "No file open specified");
397 osync_error_set(oserror, OSYNC_ERROR_IO_ERROR, "No file to open specified");
398 return FALSE;
401 chan = g_io_channel_new_file(filename, "r", &error);
402 if (!chan) {
403 osync_trace(TRACE_INTERNAL, "Unable to read file %s: %s", filename, error->message);
404 osync_error_set(oserror, OSYNC_ERROR_IO_ERROR, "Unable to open file %s for reading: %s", filename, error->message);
405 return FALSE;
408 g_io_channel_set_encoding(chan, NULL, NULL);
409 if (g_io_channel_read_to_end(chan, data, &sz, &error) != G_IO_STATUS_NORMAL) {
410 osync_trace(TRACE_INTERNAL, "Unable to read contents of file %s: %s", filename, error->message);
411 osync_error_set(oserror, OSYNC_ERROR_IO_ERROR, "Unable to read contents of file %s: %s", filename, error->message);
412 } else {
413 ret = TRUE;
414 if (size)
415 *size = (int)sz;
417 g_io_channel_shutdown(chan, FALSE, NULL);
418 g_io_channel_unref(chan);
419 return ret;
422 /*! @brief Returns the version of opensync
424 * Returns a string identifying the major and minor version
425 * of opensync (something like "0.11")
427 * @returns String with version
430 const char *osync_get_version(void)
432 return OPENSYNC_VERSION;
435 /*! @brief Safely tries to malloc memory
437 * Tries to malloc memory but returns an error in an OOM situation instead
438 * of aborting
440 * @param size The size in bytes to malloc
441 * @param error The error which will hold the info in case of an error
442 * @returns A pointer to the new memory or NULL in case of error
445 void *osync_try_malloc0(unsigned int size, OSyncError **error)
447 void *result = NULL;
449 #ifdef OPENSYNC_UNITTESTS
450 if (!g_getenv("OSYNC_NOMEMORY"))
451 result = g_try_malloc(size);
452 #else
453 result = g_try_malloc(size);
454 #endif /*OPENSYNC_UNITTESTS*/
456 if (!result) {
457 osync_error_set(error, OSYNC_ERROR_GENERIC, "No memory left");
458 return NULL;
460 memset(result, 0, size);
461 return result;
464 /*! @brief Frees memory
466 * Frees memory allocated by osync_try_malloc0().
468 * @param ptr Pointer to allocated memory which should get freed
471 void osync_free(void *ptr)
473 if (!ptr)
474 return;
476 g_free(ptr);
479 /*! @brief Allocates a new thread with a g_mainloop
481 * @param context Pointer to GMainContext
482 * @param error The error which will hold the info in case of an error
483 * @returns A pointer to the new allocated OSyncThread with inactive thread and mainloop
486 OSyncThread *osync_thread_new(GMainContext *context, OSyncError **error)
488 OSyncThread *thread = NULL;
489 osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, context, error);
491 thread = osync_try_malloc0(sizeof(OSyncThread), error);
492 if (!thread)
493 goto error;
495 if (!g_thread_supported ()) g_thread_init (NULL);
497 thread->started_mutex = g_mutex_new();
498 thread->started = g_cond_new();
499 thread->context = context;
500 if (thread->context)
501 g_main_context_ref(thread->context);
502 thread->loop = g_main_loop_new(thread->context, FALSE);
504 osync_trace(TRACE_EXIT, "%s: %p", __func__, thread);
505 return thread;
507 error:
508 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
509 return NULL;
512 /*! @brief Frees thread object
514 * @param thread Pointer to OSyncThread
517 void osync_thread_free(OSyncThread *thread)
519 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, thread);
520 osync_assert(thread);
522 if (thread->started_mutex)
523 g_mutex_free(thread->started_mutex);
525 if (thread->started)
526 g_cond_free(thread->started);
528 if (thread->loop)
529 g_main_loop_unref(thread->loop);
531 if (thread->context)
532 g_main_context_unref(thread->context);
534 g_free(thread);
535 osync_trace(TRACE_EXIT, "%s", __func__);
538 /*static gpointer osyncThreadStartCallback(gpointer data)
540 OSyncThread *thread = data;
542 g_mutex_lock(thread->started_mutex);
543 g_cond_signal(thread->started);
544 g_mutex_unlock(thread->started_mutex);
546 g_main_loop_run(thread->loop);
548 return NULL;
551 /*void osync_thread_start(OSyncThread *thread)
553 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, thread);
554 osync_assert(thread);
556 //Start the thread
557 g_mutex_lock(thread->started_mutex);
558 thread->thread = g_thread_create (osyncThreadStartCallback, thread, TRUE, NULL);
559 g_cond_wait(thread->started, thread->started_mutex);
560 g_mutex_unlock(thread->started_mutex);
562 osync_trace(TRACE_EXIT, "%s", __func__);
566 /*! @brief Start thread and it's mainloop
568 * @param func GThreadFunc Pointer
569 * @param userdata Custom data poitner which get supplied to thread function
570 * @param error Pointer to error struct
571 * @return Newly allocate OSyncThread object with inactive mainloop
574 OSyncThread *osync_thread_create(GThreadFunc func, void *userdata, OSyncError **error)
576 GError *gerror;
577 OSyncThread *thread;
579 osync_assert(func);
580 osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, func, userdata, error);
582 thread = osync_try_malloc0(sizeof(OSyncThread), error);
583 if (!thread)
584 goto error;
586 if (!g_thread_supported ())
587 g_thread_init (NULL);
589 thread->thread = g_thread_create(func, userdata, TRUE, &gerror);
591 if (!thread->thread) {
592 osync_error_set(error, OSYNC_ERROR_GENERIC, "%s", gerror->message);
593 g_error_free(gerror);
594 goto error;
597 osync_trace(TRACE_EXIT, "%s", __func__);
598 return thread;
600 error:
601 osync_trace(TRACE_EXIT_ERROR, "%s", __func__, osync_error_print(error));
602 return NULL;
606 /*! @brief Start thread and it's mainloop
608 * @param thread Thread object
611 void osync_thread_start(OSyncThread *thread)
613 GSource *idle = NULL;
614 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, thread);
616 g_mutex_lock(thread->started_mutex);
617 idle = g_idle_source_new();
618 g_source_set_callback(idle, osyncThreadStartCallback, thread, NULL);
619 g_source_attach(idle, thread->context);
620 thread->thread = g_thread_create ((GThreadFunc)g_main_loop_run, thread->loop, TRUE, NULL);
621 g_cond_wait(thread->started, thread->started_mutex);
622 g_mutex_unlock(thread->started_mutex);
624 osync_trace(TRACE_EXIT, "%s", __func__);
627 /*! @brief Stops thread's mainloop and joins the thread
629 * @param thread Thread object
632 void osync_thread_stop(OSyncThread *thread)
634 GSource *source = NULL;
635 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, thread);
636 osync_assert(thread);
638 source = g_idle_source_new();
639 g_source_set_callback(source, osyncThreadStopCallback, thread, NULL);
640 g_source_attach(source, thread->context);
642 g_thread_join(thread->thread);
643 thread->thread = NULL;
645 g_source_unref(source);
647 osync_trace(TRACE_EXIT, "%s", __func__);
650 /*! @brief Exit thread
652 * @param thread Thread object
653 * @param retval Return value of thread while exiting
656 void osync_thread_exit(OSyncThread *thread, int retval)
658 GSource *source = NULL;
659 osync_trace(TRACE_ENTRY, "%s(%p, %i)", __func__, thread, retval);
660 osync_assert(thread);
662 source = g_idle_source_new();
663 g_source_set_callback(source, osyncThreadStopCallback, thread, NULL);
664 g_source_attach(source, thread->context);
665 g_source_unref(source);
666 thread->thread = NULL;
668 g_thread_exit(GINT_TO_POINTER(retval));
670 osync_trace(TRACE_EXIT, "%s", __func__);
674 /*! @brief String replace
676 * @param input Input string to work on
677 * @param delimiter Delimiter
678 * @param replacement Replacement
679 * @returns Replaced/Modified string result
682 char *osync_strreplace(const char *input, const char *delimiter, const char *replacement)
684 gchar **array;
685 gchar *ret;
686 osync_return_val_if_fail(input != NULL, NULL);
687 osync_return_val_if_fail(delimiter != NULL, NULL);
688 osync_return_val_if_fail(replacement != NULL, NULL);
690 array = g_strsplit(input, delimiter, 0);
691 ret = g_strjoinv(replacement, array);
692 g_strfreev(array);
694 return ret;
697 /*! @brief Bit counting
699 * MIT HAKMEM Count, Bit counting in constant time and memory.
701 * @param u unsigned integer value to count bits
702 * @returns The bit counting result
705 int osync_bitcount(unsigned int u)
707 unsigned int uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
708 return ((uCount + (uCount >> 3)) & 030707070707) % 63;
711 /*! @brief Creates a random string
713 * Creates a random string of given length or less
715 * @param maxlength The maximum length of the string
716 * @returns The random string
719 char *osync_rand_str(int maxlength)
721 char *randchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ1234567890";
723 int length;
724 char *retchar;
725 int i = 0;
727 length = g_random_int_range(1, maxlength + 1);
728 retchar = malloc(length * sizeof(char) + 1);
729 retchar[0] = 0;
731 for (i = 0; i < length; i++) {
732 retchar[i] = randchars[g_random_int_range(0, strlen(randchars))];
733 retchar[i + 1] = 0;
736 return retchar;
739 /*! @brief Duplicates a string
741 * Duplicates a string, ending with terminating-zero: \0
743 * @param str The pointer of the string to duplicate
744 * @returns The duplicate string, caller is responsible for freeing.
747 char *osync_strdup(const char *str)
749 return g_strdup(str);
752 /*! @brief Duplicates a formated string
754 * Duplicates a formated string, ending with terminating-zero: \0
756 * @param str The pointer of the string to duplicate
757 * @param args
758 * @returns The duplicate string, caller is responsible for freeing.
761 char *osync_strdup_printf(const char *format, ...)
763 va_list ap;
764 char *str;
766 va_start(ap, format);
767 str = g_strdup_vprintf(format, ap);
768 va_end(ap);
770 return str;
773 /*@}*/