1 /*
2 * Copyright © 2011 Canonical Limited
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, 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, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Ryan Lortie <desrt@desrt.ca>
18 */
19
20 #include "config.h"
21
22 #include "glib-init.h"
23 #include "gmacros.h"
24 #include "gtypes.h"
25 #include "gutils.h" /* for GDebugKey */
26 #include "gconstructor.h"
27 #include "gmem.h" /* for g_mem_gc_friendly */
28
29 #include <string.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <ctype.h>
33
34 /* This seems as good a place as any to make static assertions about platform
35 * assumptions we make throughout GLib. */
36
37 /* We do not support 36-bit bytes or other historical curiosities. */
38 G_STATIC_ASSERT (CHAR_BIT == 8);
39
40 /* We assume that data pointers are the same size as function pointers... */
41 G_STATIC_ASSERT (sizeof (gpointer) == sizeof (GFunc));
42 G_STATIC_ASSERT (G_ALIGNOF (gpointer) == G_ALIGNOF (GFunc));
43 /* ... and that all function pointers are the same size. */
44 G_STATIC_ASSERT (sizeof (GFunc) == sizeof (GCompareDataFunc));
45 G_STATIC_ASSERT (G_ALIGNOF (GFunc) == G_ALIGNOF (GCompareDataFunc));
46
47 /* We assume that "small" enums (those where all values fit in INT32_MIN
48 * to INT32_MAX) are exactly int-sized. In particular, we assume that if
49 * an enum has no members that exceed the range of char/short, the
50 * compiler will make it int-sized anyway, so adding a member later that
51 * *does* exceed the range of char/short is not an ABI break. */
52 typedef enum {
53 TEST_CHAR_0 = 0
54 } TestChar;
55 typedef enum {
56 TEST_SHORT_0 = 0,
57 TEST_SHORT_256 = 256
58 } TestShort;
59 typedef enum {
60 TEST_INT32_MIN = G_MININT32,
61 TEST_INT32_MAX = G_MAXINT32
62 } TestInt;
63 G_STATIC_ASSERT (sizeof (TestChar) == sizeof (int));
64 G_STATIC_ASSERT (sizeof (TestShort) == sizeof (int));
65 G_STATIC_ASSERT (sizeof (TestInt) == sizeof (int));
66 G_STATIC_ASSERT (G_ALIGNOF (TestChar) == G_ALIGNOF (int));
67 G_STATIC_ASSERT (G_ALIGNOF (TestShort) == G_ALIGNOF (int));
68 G_STATIC_ASSERT (G_ALIGNOF (TestInt) == G_ALIGNOF (int));
69
70 /**
71 * g_mem_gc_friendly:
72 *
73 * This variable is %TRUE if the `G_DEBUG` environment variable
74 * includes the key `gc-friendly`.
75 */
76 gboolean g_mem_gc_friendly = FALSE;
77
78 GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING |
79 G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG;
80 GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;
81
82 static gboolean
debug_key_matches(const gchar * key,const gchar * token,guint length)83 debug_key_matches (const gchar *key,
84 const gchar *token,
85 guint length)
86 {
87 /* may not call GLib functions: see note in g_parse_debug_string() */
88 for (; length; length--, key++, token++)
89 {
90 char k = (*key == '_') ? '-' : tolower (*key );
91 char t = (*token == '_') ? '-' : tolower (*token);
92
93 if (k != t)
94 return FALSE;
95 }
96
97 return *key == '\0';
98 }
99
100 /* The GVariant documentation indirectly says that int is at least 32 bits
101 * (by saying that b, y, n, q, i, u, h are promoted to int). On any
102 * reasonable platform, int is in fact *exactly* 32 bits long, because
103 * otherwise, {signed char, short, int} wouldn't be sufficient to provide
104 * {int8_t, int16_t, int32_t}. */
105 G_STATIC_ASSERT (sizeof (int) == sizeof (gint32));
106
107 /**
108 * g_parse_debug_string:
109 * @string: (nullable): a list of debug options separated by colons, spaces, or
110 * commas, or %NULL.
111 * @keys: (array length=nkeys): pointer to an array of #GDebugKey which associate
112 * strings with bit flags.
113 * @nkeys: the number of #GDebugKeys in the array.
114 *
115 * Parses a string containing debugging options
116 * into a %guint containing bit flags. This is used
117 * within GDK and GTK+ to parse the debug options passed on the
118 * command line or through environment variables.
119 *
120 * If @string is equal to "all", all flags are set. Any flags
121 * specified along with "all" in @string are inverted; thus,
122 * "all,foo,bar" or "foo,bar,all" sets all flags except those
123 * corresponding to "foo" and "bar".
124 *
125 * If @string is equal to "help", all the available keys in @keys
126 * are printed out to standard error.
127 *
128 * Returns: the combined set of bit flags.
129 */
130 guint
g_parse_debug_string(const gchar * string,const GDebugKey * keys,guint nkeys)131 g_parse_debug_string (const gchar *string,
132 const GDebugKey *keys,
133 guint nkeys)
134 {
135 guint i;
136 guint result = 0;
137
138 if (string == NULL)
139 return 0;
140
141 /* this function is used during the initialisation of gmessages, gmem
142 * and gslice, so it may not do anything that causes memory to be
143 * allocated or risks messages being emitted.
144 *
145 * this means, more or less, that this code may not call anything
146 * inside GLib.
147 */
148
149 if (!strcasecmp (string, "help"))
150 {
151 /* using stdio directly for the reason stated above */
152 fprintf (stderr, "Supported debug values:");
153 for (i = 0; i < nkeys; i++)
154 fprintf (stderr, " %s", keys[i].key);
155 fprintf (stderr, " all help\n");
156 }
157 else
158 {
159 const gchar *p = string;
160 const gchar *q;
161 gboolean invert = FALSE;
162
163 while (*p)
164 {
165 q = strpbrk (p, ":;, \t");
166 if (!q)
167 q = p + strlen (p);
168
169 if (debug_key_matches ("all", p, q - p))
170 {
171 invert = TRUE;
172 }
173 else
174 {
175 for (i = 0; i < nkeys; i++)
176 if (debug_key_matches (keys[i].key, p, q - p))
177 result |= keys[i].value;
178 }
179
180 p = q;
181 if (*p)
182 p++;
183 }
184
185 if (invert)
186 {
187 guint all_flags = 0;
188
189 for (i = 0; i < nkeys; i++)
190 all_flags |= keys[i].value;
191
192 result = all_flags & (~result);
193 }
194 }
195
196 return result;
197 }
198
199 static guint
g_parse_debug_envvar(const gchar * envvar,const GDebugKey * keys,gint n_keys,guint default_value)200 g_parse_debug_envvar (const gchar *envvar,
201 const GDebugKey *keys,
202 gint n_keys,
203 guint default_value)
204 {
205 const gchar *value;
206
207 #ifdef OS_WIN32
208 /* "fatal-warnings,fatal-criticals,all,help" is pretty short */
209 gchar buffer[100];
210
211 if (GetEnvironmentVariable (envvar, buffer, 100) < 100)
212 value = buffer;
213 else
214 return 0;
215 #else
216 value = getenv (envvar);
217 #endif
218
219 if (value == NULL)
220 return default_value;
221
222 return g_parse_debug_string (value, keys, n_keys);
223 }
224
225 static void
g_messages_prefixed_init(void)226 g_messages_prefixed_init (void)
227 {
228 const GDebugKey keys[] = {
229 { "error", G_LOG_LEVEL_ERROR },
230 { "critical", G_LOG_LEVEL_CRITICAL },
231 { "warning", G_LOG_LEVEL_WARNING },
232 { "message", G_LOG_LEVEL_MESSAGE },
233 { "info", G_LOG_LEVEL_INFO },
234 { "debug", G_LOG_LEVEL_DEBUG }
235 };
236
237 g_log_msg_prefix = g_parse_debug_envvar ("G_MESSAGES_PREFIXED", keys, G_N_ELEMENTS (keys), g_log_msg_prefix);
238 }
239
240 static void
g_debug_init(void)241 g_debug_init (void)
242 {
243 const GDebugKey keys[] = {
244 { "gc-friendly", 1 },
245 {"fatal-warnings", G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL },
246 {"fatal-criticals", G_LOG_LEVEL_CRITICAL }
247 };
248 GLogLevelFlags flags;
249
250 flags = g_parse_debug_envvar ("G_DEBUG", keys, G_N_ELEMENTS (keys), 0);
251
252 g_log_always_fatal |= flags & G_LOG_LEVEL_MASK;
253
254 g_mem_gc_friendly = flags & 1;
255 }
256
257 void
glib_init(void)258 glib_init (void)
259 {
260 static gboolean glib_inited;
261
262 if (glib_inited)
263 return;
264
265 glib_inited = TRUE;
266
267 g_messages_prefixed_init ();
268 g_debug_init ();
269 g_quark_init ();
270 }
271
272 #if defined (G_OS_WIN32)
273
274 BOOL WINAPI DllMain (HINSTANCE hinstDLL,
275 DWORD fdwReason,
276 LPVOID lpvReserved);
277
278 HMODULE glib_dll;
279
280 BOOL WINAPI
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)281 DllMain (HINSTANCE hinstDLL,
282 DWORD fdwReason,
283 LPVOID lpvReserved)
284 {
285 switch (fdwReason)
286 {
287 case DLL_PROCESS_ATTACH:
288 glib_dll = hinstDLL;
289 g_crash_handler_win32_init ();
290 g_clock_win32_init ();
291 #ifdef THREADS_WIN32
292 g_thread_win32_init ();
293 #endif
294 glib_init ();
295 /* must go after glib_init */
296 g_console_win32_init ();
297 break;
298
299 case DLL_THREAD_DETACH:
300 #ifdef THREADS_WIN32
301 g_thread_win32_thread_detach ();
302 #endif
303 break;
304
305 case DLL_PROCESS_DETACH:
306 #ifdef THREADS_WIN32
307 if (lpvReserved == NULL)
308 g_thread_win32_process_detach ();
309 #endif
310 g_crash_handler_win32_deinit ();
311 break;
312
313 default:
314 /* do nothing */
315 ;
316 }
317
318 return TRUE;
319 }
320
321 #elif defined (G_HAS_CONSTRUCTORS)
322
323 #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
324 #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(glib_init_ctor)
325 #endif
G_DEFINE_CONSTRUCTOR(glib_init_ctor)326 G_DEFINE_CONSTRUCTOR(glib_init_ctor)
327
328 static void
329 glib_init_ctor (void)
330 {
331 glib_init ();
332 }
333
334 #else
335 # error Your platform/compiler is missing constructor support
336 #endif
337