Add a test for g_atexit
[platform/upstream/glib.git] / glib / glib-init.c
1 /*
2  * Copyright © 2011 Canonical Limited
3  *
4  * This library is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * 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  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17  * USA.
18  *
19  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "glib-init.h"
25
26 #include "gutils.h"     /* for GDebugKey */
27 #include "gconstructor.h"
28 #include "gmem.h"       /* for g_mem_gc_friendly */
29
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <ctype.h>
34
35 /**
36  * g_mem_gc_friendly:
37  *
38  * This variable is %TRUE if the <envar>G_DEBUG</envar> environment variable
39  * includes the key <literal>gc-friendly</literal>.
40  */
41 #ifdef ENABLE_GC_FRIENDLY_DEFAULT
42 gboolean g_mem_gc_friendly = TRUE;
43 #else
44 gboolean g_mem_gc_friendly = FALSE;
45 #endif
46 GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING |
47                                   G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG;
48 GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;
49
50 static gboolean
51 debug_key_matches (const gchar *key,
52                    const gchar *token,
53                    guint        length)
54 {
55   /* may not call GLib functions: see note in g_parse_debug_string() */
56   for (; length; length--, key++, token++)
57     {
58       char k = (*key   == '_') ? '-' : tolower (*key  );
59       char t = (*token == '_') ? '-' : tolower (*token);
60
61       if (k != t)
62         return FALSE;
63     }
64
65   return *key == '\0';
66 }
67
68 /**
69  * g_parse_debug_string:
70  * @string: (allow-none): a list of debug options separated by colons, spaces, or
71  * commas, or %NULL.
72  * @keys: (array length=nkeys): pointer to an array of #GDebugKey which associate
73  *     strings with bit flags.
74  * @nkeys: the number of #GDebugKey<!-- -->s in the array.
75  *
76  * Parses a string containing debugging options
77  * into a %guint containing bit flags. This is used
78  * within GDK and GTK+ to parse the debug options passed on the
79  * command line or through environment variables.
80  *
81  * If @string is equal to <code>"all"</code>, all flags are set. Any flags
82  * specified along with <code>"all"</code> in @string are inverted; thus,
83  * <code>"all,foo,bar"</code> or <code>"foo,bar,all"</code> sets all flags
84  * except those corresponding to <code>"foo"</code> and <code>"bar"</code>.
85  *
86  * If @string is equal to <code>"help"</code>, all the available keys in @keys
87  * are printed out to standard error.
88  *
89  * Returns: the combined set of bit flags.
90  */
91 guint
92 g_parse_debug_string  (const gchar     *string,
93                        const GDebugKey *keys,
94                        guint            nkeys)
95 {
96   guint i;
97   guint result = 0;
98
99   if (string == NULL)
100     return 0;
101
102   /* this function is used during the initialisation of gmessages, gmem
103    * and gslice, so it may not do anything that causes memory to be
104    * allocated or risks messages being emitted.
105    *
106    * this means, more or less, that this code may not call anything
107    * inside GLib.
108    */
109
110   if (!strcasecmp (string, "help"))
111     {
112       /* using stdio directly for the reason stated above */
113       fprintf (stderr, "Supported debug values:");
114       for (i = 0; i < nkeys; i++)
115        fprintf (stderr, " %s", keys[i].key);
116       fprintf (stderr, " all help\n");
117     }
118   else
119     {
120       const gchar *p = string;
121       const gchar *q;
122       gboolean invert = FALSE;
123
124       while (*p)
125        {
126          q = strpbrk (p, ":;, \t");
127          if (!q)
128            q = p + strlen (p);
129
130          if (debug_key_matches ("all", p, q - p))
131            {
132              invert = TRUE;
133            }
134          else
135            {
136              for (i = 0; i < nkeys; i++)
137                if (debug_key_matches (keys[i].key, p, q - p))
138                  result |= keys[i].value;
139            }
140
141          p = q;
142          if (*p)
143            p++;
144        }
145
146       if (invert)
147         {
148           guint all_flags = 0;
149
150           for (i = 0; i < nkeys; i++)
151             all_flags |= keys[i].value;
152
153           result = all_flags & (~result);
154         }
155     }
156
157   return result;
158 }
159
160 static guint
161 g_parse_debug_envvar (const gchar     *envvar,
162                       const GDebugKey *keys,
163                       gint             n_keys,
164                       guint            default_value)
165 {
166   const gchar *value;
167
168 #ifdef OS_WIN32
169   /* "fatal-warnings,fatal-criticals,all,help" is pretty short */
170   gchar buffer[100];
171
172   if (GetEnvironmentVariable (envvar, buffer, 100) < 100)
173     value = buffer;
174   else
175     return 0;
176 #else
177   value = getenv (envvar);
178 #endif
179
180   if (value == NULL)
181     return default_value;
182
183   return g_parse_debug_string (value, keys, n_keys);
184 }
185
186 static void
187 g_messages_prefixed_init (void)
188 {
189   const GDebugKey keys[] = {
190     { "error", G_LOG_LEVEL_ERROR },
191     { "critical", G_LOG_LEVEL_CRITICAL },
192     { "warning", G_LOG_LEVEL_WARNING },
193     { "message", G_LOG_LEVEL_MESSAGE },
194     { "info", G_LOG_LEVEL_INFO },
195     { "debug", G_LOG_LEVEL_DEBUG }
196   };
197
198   g_log_msg_prefix = g_parse_debug_envvar ("G_MESSAGES_PREFIXED", keys, G_N_ELEMENTS (keys), g_log_msg_prefix);
199 }
200
201 static void
202 g_debug_init (void)
203 {
204   const GDebugKey keys[] = {
205     { "gc-friendly", 1 },
206     {"fatal-warnings",  G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL },
207     {"fatal-criticals", G_LOG_LEVEL_CRITICAL }
208   };
209   GLogLevelFlags flags;
210
211   flags = g_parse_debug_envvar ("G_DEBUG", keys, G_N_ELEMENTS (keys), 0);
212
213   g_log_always_fatal |= flags & G_LOG_LEVEL_MASK;
214
215   g_mem_gc_friendly = flags & 1;
216 }
217
218 static void
219 glib_init (void)
220 {
221   g_messages_prefixed_init ();
222   g_debug_init ();
223 }
224
225 #if defined (G_OS_WIN32)
226
227 BOOL WINAPI DllMain (HINSTANCE hinstDLL,
228                      DWORD     fdwReason,
229                      LPVOID    lpvReserved);
230
231 HMODULE glib_dll;
232
233 BOOL WINAPI
234 DllMain (HINSTANCE hinstDLL,
235          DWORD     fdwReason,
236          LPVOID    lpvReserved)
237 {
238   switch (fdwReason)
239     {
240     case DLL_PROCESS_ATTACH:
241       glib_dll = hinstDLL;
242       g_clock_win32_init ();
243 #ifdef THREADS_WIN32
244       g_thread_win32_init ();
245 #endif
246       glib_init ();
247       break;
248
249     case DLL_THREAD_DETACH:
250 #ifdef THREADS_WIN32
251       g_thread_win32_thread_detach ();
252 #endif
253       break;
254
255     default:
256       /* do nothing */
257       ;
258     }
259
260   return TRUE;
261 }
262
263 #elif defined (G_HAS_CONSTRUCTORS)
264
265 #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
266 #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(glib_init_ctor)
267 #endif
268 G_DEFINE_CONSTRUCTOR(glib_init_ctor)
269
270 static void
271 glib_init_ctor (void)
272 {
273   glib_init ();
274 }
275
276 #else
277 # error Your platform/compiler is missing constructor support
278 #endif