1 /* vim: set ts=4 sts=4 sw=4 expandtab textwidth=112: */
2 /*
3 * This is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Library General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #define _POSIX_SOURCE /* feature test macro for fileno */
18 #define _XOPEN_SOURCE /* feature test macro for fsync */
19
20 #include <tilda-config.h>
21 #include "debug.h"
22
23 #include <confuse.h>
24 #include <glib/gi18n.h>
25 #include <stdio.h>
26 #include <stdlib.h> /* atoi */
27 #include <unistd.h> /* fsync */
28
29 #include "configsys.h"
30 #include <vte/vte.h>
31
32 static cfg_t *tc;
33
34 /* CONFIGURATION OPTIONS
35 * In this array we set the default configuration options for the
36 * configuration file.
37 */
38 static cfg_opt_t config_opts[] = {
39
40 /* strings */
41 CFG_STR("tilda_config_version", PACKAGE_VERSION, CFGF_NONE),
42 CFG_STR("command", "", CFGF_NONE),
43 CFG_STR("font", "Monospace 11", CFGF_NONE),
44 CFG_STR("key", NULL, CFGF_NONE),
45 CFG_STR("addtab_key", "<Shift><Control>t", CFGF_NONE),
46 CFG_STR("fullscreen_key", "F11", CFGF_NONE),
47 CFG_STR("toggle_transparency_key", "F12", CFGF_NONE),
48 CFG_STR("toggle_searchbar_key", "<Shift><Control>f", CFGF_NONE),
49 CFG_STR("closetab_key", "<Shift><Control>w", CFGF_NONE),
50 CFG_STR("nexttab_key", "<Control>Page_Down", CFGF_NONE),
51 CFG_STR("prevtab_key", "<Control>Page_Up", CFGF_NONE),
52 CFG_STR("movetableft_key", "<Shift><Control>Page_Up", CFGF_NONE),
53 CFG_STR("movetabright_key", "<Shift><Control>Page_Down", CFGF_NONE),
54 CFG_STR("gototab_1_key", "<Alt>1", CFGF_NONE),
55 CFG_STR("gototab_2_key", "<Alt>2", CFGF_NONE),
56 CFG_STR("gototab_3_key", "<Alt>3", CFGF_NONE),
57 CFG_STR("gototab_4_key", "<Alt>4", CFGF_NONE),
58 CFG_STR("gototab_5_key", "<Alt>5", CFGF_NONE),
59 CFG_STR("gototab_6_key", "<Alt>6", CFGF_NONE),
60 CFG_STR("gototab_7_key", "<Alt>7", CFGF_NONE),
61 CFG_STR("gototab_8_key", "<Alt>8", CFGF_NONE),
62 CFG_STR("gototab_9_key", "<Alt>9", CFGF_NONE),
63 CFG_STR("gototab_10_key", "<Alt>0", CFGF_NONE),
64 CFG_STR("copy_key", "<Shift><Control>c", CFGF_NONE),
65 CFG_STR("paste_key", "<Shift><Control>v", CFGF_NONE),
66 CFG_STR("quit_key", "<Shift><Control>q", CFGF_NONE),
67 CFG_STR("title", "Tilda", CFGF_NONE),
68 CFG_STR("background_color", "white", CFGF_NONE),
69 CFG_STR("working_dir", NULL, CFGF_NONE),
70 CFG_STR("web_browser", "xdg-open", CFGF_NONE),
71 CFG_STR("increase_font_size_key", "<Control>equal", CFGF_NONE),
72 CFG_STR("decrease_font_size_key", "<Control>minus", CFGF_NONE),
73 CFG_STR("normalize_font_size_key", "<Control>0", CFGF_NONE),
74 CFG_STR("show_on_monitor", "", CFGF_NONE),
75 CFG_STR("word_chars", DEFAULT_WORD_CHARS, CFGF_NONE),
76
77 /* ints */
78 CFG_INT("lines", 5000, CFGF_NONE),
79 CFG_INT("x_pos", 0, CFGF_NONE),
80 CFG_INT("y_pos", 0, CFGF_NONE),
81 CFG_INT("tab_pos", 0, CFGF_NONE),
82 CFG_BOOL("expand_tabs", FALSE, CFGF_NONE),
83 CFG_BOOL("show_single_tab", FALSE, CFGF_NONE),
84 CFG_INT("backspace_key", 0, CFGF_NONE),
85 CFG_INT("delete_key", 1, CFGF_NONE),
86 CFG_INT("d_set_title", 3, CFGF_NONE),
87 CFG_INT("command_exit", 2, CFGF_NONE),
88 /* Timeout in milliseconds to spawn a shell or command */
89 CFG_INT("command_timeout_ms", 3000, CFGF_NONE),
90 CFG_INT("scheme", 3, CFGF_NONE),
91 CFG_INT("slide_sleep_usec", 20000, CFGF_NONE),
92 CFG_INT("animation_orientation", 0, CFGF_NONE),
93 CFG_INT("timer_resolution", 200, CFGF_NONE),
94 CFG_INT("auto_hide_time", 2000, CFGF_NONE),
95 CFG_INT("on_last_terminal_exit", 0, CFGF_NONE),
96 CFG_BOOL("prompt_on_exit", TRUE, CFGF_NONE),
97 CFG_INT("palette_scheme", 1, CFGF_NONE),
98 CFG_INT("non_focus_pull_up_behaviour", 0, CFGF_NONE),
99 CFG_INT("cursor_shape", 0, CFGF_NONE),
100
101 /* The length of a tab title */
102 CFG_INT("title_max_length", 25, CFGF_NONE),
103
104 /* int list */
105 CFG_INT_LIST("palette", "{\
106 0x2e2e, 0x3434, 0x3636,\
107 0xcccc, 0x0000, 0x0000,\
108 0x4e4e, 0x9a9a, 0x0606,\
109 0xc4c4, 0xa0a0, 0x0000,\
110 0x3434, 0x6565, 0xa4a4,\
111 0x7575, 0x5050, 0x7b7b,\
112 0x0606, 0x9820, 0x9a9a,\
113 0xd3d3, 0xd7d7, 0xcfcf,\
114 0x5555, 0x5757, 0x5353,\
115 0xefef, 0x2929, 0x2929,\
116 0x8a8a, 0xe2e2, 0x3434,\
117 0xfcfc, 0xe9e9, 0x4f4f,\
118 0x7272, 0x9f9f, 0xcfcf,\
119 0xadad, 0x7f7f, 0xa8a8,\
120 0x3434, 0xe2e2, 0xe2e2,\
121 0xeeee, 0xeeee, 0xecec}",
122 CFGF_NONE),
123
124 /* guint16 */
125 CFG_INT("scrollbar_pos", 2, CFGF_NONE),
126 CFG_INT("back_red", 0x0000, CFGF_NONE),
127 CFG_INT("back_green", 0x0000, CFGF_NONE),
128 CFG_INT("back_blue", 0x0000, CFGF_NONE),
129 CFG_INT("text_red", 0xffff, CFGF_NONE),
130 CFG_INT("text_green", 0xffff, CFGF_NONE),
131 CFG_INT("text_blue", 0xffff, CFGF_NONE),
132 CFG_INT("cursor_red", 0xffff, CFGF_NONE),
133 CFG_INT("cursor_green", 0xffff, CFGF_NONE),
134 CFG_INT("cursor_blue", 0xffff, CFGF_NONE),
135
136 /* floats, libconfuse has a bug with floats on non english systems,
137 * see: https://github.com/martinh/libconfuse/issues/119, so we
138 * need to emulate floats by scaling values to a long value. */
139 CFG_INT ("width_percentage", G_MAXINT, CFGF_NONE),
140 CFG_INT ("height_percentage", G_MAXINT, CFGF_NONE),
141
142 /* booleans */
143 CFG_BOOL("scroll_history_infinite", FALSE, CFGF_NONE),
144 CFG_BOOL("scroll_on_output", FALSE, CFGF_NONE),
145 CFG_BOOL("notebook_border", FALSE, CFGF_NONE),
146
147 CFG_BOOL("scrollbar", FALSE, CFGF_NONE),
148 CFG_BOOL("grab_focus", TRUE, CFGF_NONE),
149 CFG_BOOL("above", TRUE, CFGF_NONE),
150 CFG_BOOL("notaskbar", TRUE, CFGF_NONE),
151 CFG_BOOL("blinks", TRUE, CFGF_NONE),
152 CFG_BOOL("scroll_on_key", TRUE, CFGF_NONE),
153 CFG_BOOL("bell", FALSE, CFGF_NONE),
154 CFG_BOOL("run_command", FALSE, CFGF_NONE),
155 CFG_BOOL("pinned", TRUE, CFGF_NONE),
156 CFG_BOOL("animation", FALSE, CFGF_NONE),
157 CFG_BOOL("hidden", FALSE, CFGF_NONE),
158 CFG_BOOL("set_as_desktop", FALSE, CFGF_NONE),
159 CFG_BOOL("centered_horizontally", FALSE, CFGF_NONE),
160 CFG_BOOL("centered_vertically", FALSE, CFGF_NONE),
161 CFG_BOOL("enable_transparency", FALSE, CFGF_NONE),
162 CFG_BOOL("auto_hide_on_focus_lost", FALSE, CFGF_NONE),
163 CFG_BOOL("auto_hide_on_mouse_leave", FALSE, CFGF_NONE),
164 /* Whether and how we limit the length of a tab title */
165 CFG_INT("title_behaviour", 2, CFGF_NONE),
166 /* Whether to set a new tab's working dir to the current tab's */
167 CFG_BOOL("inherit_working_dir", TRUE, CFGF_NONE),
168 CFG_BOOL("command_login_shell", FALSE, CFGF_NONE),
169 CFG_BOOL("start_fullscreen", FALSE, CFGF_NONE),
170 /* Whether closing a tab shows a confirmation dialog. */
171 CFG_BOOL("confirm_close_tab", TRUE, CFGF_NONE),
172
173 CFG_INT("back_alpha", 0xffff, CFGF_NONE),
174
175 /* Whether to show the full tab title as a tooltip */
176 CFG_BOOL("show_title_tooltip", FALSE, CFGF_NONE),
177
178 /**
179 * Deprecated tilda options. These options be commented out in the
180 * configuration file and will not be initialized with default values
181 * if the option is missing in the config file.
182 **/
183 CFG_INT("max_width", 0, CFGF_NODEFAULT),
184 CFG_INT("max_height", 0, CFGF_NODEFAULT),
185
186 CFG_STR("image", NULL, CFGF_NODEFAULT),
187
188 CFG_INT("show_on_monitor_number", 0, CFGF_NODEFAULT),
189 CFG_INT("transparency", 0, CFGF_NODEFAULT),
190
191 CFG_BOOL("bold", TRUE, CFGF_NODEFAULT),
192 CFG_BOOL("title_max_length_flag", FALSE, CFGF_NODEFAULT),
193 CFG_BOOL("antialias", TRUE, CFGF_NODEFAULT),
194 CFG_BOOL("double_buffer", FALSE, CFGF_NODEFAULT),
195 CFG_BOOL("scroll_background", FALSE, CFGF_NODEFAULT),
196 CFG_BOOL("use_image", FALSE, CFGF_NODEFAULT),
197
198 CFG_INT("min_width", 0, CFGF_NODEFAULT),
199 CFG_INT("min_height", 0, CFGF_NODEFAULT),
200 /* End deprecated tilda options */
201
202 CFG_END()
203 };
204
205 /* Define these here, so that we can enable a non-threadsafe version
206 * without changing the code below. */
207 #ifndef NO_THREADSAFE
208 static GMutex mutex;
209 #define config_mutex_lock() g_mutex_lock (&mutex)
210 #define config_mutex_unlock() g_mutex_unlock (&mutex)
211 #else
212 #define config_mutex_lock()
213 #define config_mutex_unlock()
214 #endif
215
216 #define CONFIG1_OLDER -1
217 #define CONFIGS_SAME 0
218 #define CONFIG1_NEWER 1
219
220 static gboolean compare_config_versions (const gchar *config1, const gchar *config2) G_GNUC_UNUSED;
221
222 static void invoke_deprecation_function(const gchar *const *deprecated_config_options,
223 guint size);
224
225 static void remove_deprecated_config_options(const gchar *const *deprecated_config_options, guint size);
226
227 /* Note: set config_file to NULL to just free the
228 * data structures, and not write out the state to
229 * a file. */
config_free(const gchar * config_file)230 gint config_free (const gchar *config_file)
231 {
232 gint ret = 0;
233
234 if (config_file != NULL)
235 ret = config_write (config_file);
236
237 cfg_free (tc);
238
239 return ret;
240 }
241
config_setint(const gchar * key,const glong val)242 gint config_setint (const gchar *key, const glong val)
243 {
244 config_mutex_lock ();
245 cfg_setint (tc, key, val);
246 config_mutex_unlock ();
247
248 return 0;
249 }
250
config_setnint(const gchar * key,const glong val,const guint idx)251 gint config_setnint(const gchar *key, const glong val, const guint idx)
252 {
253 config_mutex_lock ();
254 cfg_setnint (tc, key, val, idx);
255 config_mutex_unlock ();
256
257 return 0;
258 }
259
config_setdouble(const gchar * key,const gdouble val)260 gint config_setdouble (const gchar *key, const gdouble val) {
261 config_mutex_lock ();
262 cfg_setfloat (tc, key, val);
263 config_mutex_unlock ();
264
265 return 0;
266 }
267
config_setndouble(const gchar * key,const gdouble val,const guint idx)268 gint config_setndouble (const gchar *key, const gdouble val, const guint idx) {
269 config_mutex_lock ();
270 cfg_setnfloat (tc, key, val, idx);
271 config_mutex_unlock ();
272
273 return 0;
274 }
275
config_setstr(const gchar * key,const gchar * val)276 gint config_setstr (const gchar *key, const gchar *val)
277 {
278 config_mutex_lock ();
279 cfg_setstr (tc, key, val);
280 config_mutex_unlock ();
281
282 return 0;
283 }
284
config_setbool(const gchar * key,const gboolean val)285 gint config_setbool(const gchar *key, const gboolean val)
286 {
287 config_mutex_lock ();
288 cfg_setbool (tc, key, val);
289 config_mutex_unlock ();
290
291 return 0;
292 }
293
config_getint(const gchar * key)294 glong config_getint (const gchar *key)
295 {
296 glong temp;
297
298 config_mutex_lock ();
299 temp = cfg_getint (tc, key);
300 config_mutex_unlock ();
301
302 return temp;
303 }
304
config_getnint(const gchar * key,const guint idx)305 glong config_getnint(const gchar *key, const guint idx)
306 {
307 glong temp;
308
309 config_mutex_lock ();
310 temp = cfg_getnint (tc, key, idx);
311 config_mutex_unlock ();
312
313 return temp;
314 }
315
config_getdouble(const gchar * key)316 gdouble config_getdouble (const gchar* key) {
317 gdouble temp;
318
319 config_mutex_lock ();
320 temp = cfg_getfloat (tc, key);
321 config_mutex_unlock ();
322
323 return temp;
324 }
325
config_getndouble(const gchar * key,const guint idx)326 gdouble config_getndouble (const gchar* key, const guint idx) {
327 gdouble temp;
328
329 config_mutex_lock ();
330 temp = cfg_getnfloat (tc, key, idx);
331 config_mutex_unlock ();
332
333 return temp;
334 }
335
config_getstr(const gchar * key)336 gchar* config_getstr (const gchar *key)
337 {
338 gchar *temp;
339
340 config_mutex_lock ();
341 temp = cfg_getstr (tc, key);
342 config_mutex_unlock ();
343
344 return temp;
345 }
346
config_getbool(const gchar * key)347 gboolean config_getbool(const gchar *key)
348 {
349 gboolean temp;
350
351 config_mutex_lock ();
352 temp = cfg_getbool (tc, key);
353 config_mutex_unlock ();
354
355 return temp;
356 }
357
358 /* This will write out the current state of the config file to the disk.
359 * It's use is generally discouraged, since config_free() will also write
360 * out the configuration to disk. */
config_write(const gchar * config_file)361 gint config_write (const gchar *config_file)
362 {
363 DEBUG_FUNCTION ("config_write");
364 DEBUG_ASSERT (config_file != NULL);
365
366 gint ret = 0;
367 FILE *fp;
368
369 char *temp_config_file = g_strdup_printf ("%s.tmp", config_file);
370 fp = fopen(temp_config_file, "w");
371
372 if (fp != NULL)
373 {
374 config_mutex_lock ();
375 cfg_print (tc, fp);
376 config_mutex_unlock ();
377
378 if (fsync (fileno(fp)))
379 {
380 // Error occurred during sync
381 TILDA_PERROR ();
382 DEBUG_ERROR ("Unable to sync file");
383
384 g_printerr (_("Unable to sync the config file to disk\n"));
385 ret = 2;
386 }
387
388 if (fclose (fp))
389 {
390 // An error occurred
391 TILDA_PERROR ();
392 DEBUG_ERROR ("Unable to close config file");
393
394 g_printerr (_("Unable to close the config file\n"));
395 ret = 3;
396 }
397 if (rename(temp_config_file, config_file)) {
398 TILDA_PERROR ();
399 DEBUG_ERROR ("Unable to rename temporary config file to final config file.");
400 }
401 }
402 else
403 {
404 TILDA_PERROR ();
405 DEBUG_ERROR ("Unable to write config file");
406
407 g_printerr (_("Unable to write the config file to %s\n"), config_file);
408 ret = 4;
409 }
410
411 return ret;
412 }
413
414 /**
415 * Start up the configuration system, using the configuration file given
416 * to get the current values. If the configuration file given does not exist,
417 * go ahead and write out the default config to the file.
418 */
config_init(const gchar * config_file)419 gint config_init (const gchar *config_file)
420 {
421 DEBUG_FUNCTION ("config_init");
422
423 gint ret = 0;
424
425 // Can we use a more descriptive name than tc?
426 tc = cfg_init (config_opts, 0);
427
428 if (g_file_test (config_file,
429 G_FILE_TEST_IS_REGULAR))
430 {
431 /* Read in the existing configuration options */
432 ret = cfg_parse (tc, config_file);
433
434 if (ret == CFG_PARSE_ERROR) {
435 DEBUG_ERROR ("Problem parsing config");
436 return ret;
437 } else if (ret != CFG_SUCCESS) {
438 DEBUG_ERROR ("Problem parsing config.");
439 return ret;
440 }
441 }
442
443 /* Deprecate old config settings.
444 * This is a lame work around until we get a permanent solution to
445 * libconfuse lacking for this functionality
446 */
447 const gchar *deprecated_tilda_config_options[] = {"show_on_monitor_number",
448 "bold",
449 "title_max_length_flag",
450 "double_buffer",
451 "antialias",
452 "image",
453 "transparency",
454 "scroll_background",
455 "use_image",
456 "min_width",
457 "min_height",
458 "max_width",
459 "max_height"
460 };
461
462 invoke_deprecation_function (deprecated_tilda_config_options,
463 G_N_ELEMENTS(deprecated_tilda_config_options));
464
465 remove_deprecated_config_options(deprecated_tilda_config_options,
466 G_N_ELEMENTS(deprecated_tilda_config_options));
467
468 #ifndef NO_THREADSAFE
469 g_mutex_init(&mutex);
470 #endif
471
472 return ret;
473 }
474
config_get_configured_monitor()475 static GdkMonitor *config_get_configured_monitor ()
476 {
477 gint x_pos = (gint) config_getint ("x_pos");
478 gint y_pos = (gint) config_getint ("y_pos");
479
480 GdkDisplay *display = gdk_display_get_default ();
481
482 return gdk_display_get_monitor_at_point (display,
483 x_pos,
484 y_pos);
485 }
486
config_get_configured_window_size(GdkRectangle * rectangle)487 void config_get_configured_window_size (GdkRectangle *rectangle)
488 {
489 gdouble relative_width = GLONG_TO_DOUBLE (config_getint ("width_percentage"));
490 gdouble relative_height = GLONG_TO_DOUBLE (config_getint ("height_percentage"));
491
492 GdkMonitor *monitor = config_get_configured_monitor ();
493
494 GdkRectangle workarea;
495 gdk_monitor_get_workarea (monitor, &workarea);
496
497 rectangle->width = pixels_ratio_to_absolute (relative_width, workarea.width);
498 rectangle->height = pixels_ratio_to_absolute (relative_height, workarea.height);
499 }
500
config_get_configured_percentage(gdouble * width_percentage,gdouble * height_percentage)501 static void config_get_configured_percentage (gdouble *width_percentage,
502 gdouble *height_percentage)
503 {
504 glong windowWidth = config_getint ("max_width");
505 glong windowHeight = config_getint ("max_height");
506
507 GdkMonitor *monitor = config_get_configured_monitor ();
508
509 GdkRectangle workarea;
510 gdk_monitor_get_workarea (monitor, &workarea);
511
512 if (width_percentage) {
513 *width_percentage = pixels_absolute_to_ratio (workarea.width, windowWidth);
514 }
515
516 if (height_percentage) {
517 *height_percentage = pixels_absolute_to_ratio (workarea.height, windowHeight);
518 }
519 }
520
print_migration_info(const gchar * old_option_name,const gchar * new_option_name)521 static void print_migration_info (const gchar *old_option_name,
522 const gchar *new_option_name)
523 {
524 g_print ("Migrated deprecated value in option '%s' to '%s'.\n",
525 old_option_name, new_option_name);
526 }
527
invoke_deprecation_function(const gchar * const * deprecated_config_options,guint size)528 void invoke_deprecation_function (const gchar *const *deprecated_config_options,
529 guint size)
530 {
531 for (guint i = 0; i < size; i++)
532 {
533 const char *const option_name = deprecated_config_options[i];
534
535 /* This will still return the option even if its
536 * commented out in the config file, so we perform the extra check
537 * using `cfg_opt_size` below to determine if the option has a valid
538 * value. We do this to ensure that we only execute the migration
539 * code once. */
540 cfg_opt_t *option = cfg_getopt (tc, option_name);
541
542 if (option == NULL || cfg_opt_size (option) == 0) {
543 continue;
544 }
545
546 gdouble width_percentage;
547 gdouble height_percentage;
548
549 config_get_configured_percentage (&width_percentage,
550 &height_percentage);
551
552 if (strncmp(option_name, "max_width", sizeof("max_width")) == 0)
553 {
554 print_migration_info (option_name, "width_percentage");
555 config_setint ("width_percentage", GLONG_FROM_DOUBLE (width_percentage));
556 }
557 if (strncmp(option_name, "max_height", sizeof("max_height")) == 0)
558 {
559 print_migration_info (option_name, "height_percentage");
560 config_setint ("height_percentage", GLONG_FROM_DOUBLE (height_percentage));
561 }
562 }
563 }
564
remove_deprecated_config_options(const gchar * const * deprecated_config_options,guint size)565 void remove_deprecated_config_options(const gchar *const *deprecated_config_options, guint size) {
566 cfg_opt_t *opt;
567 for (guint i =0; i < size; i++) {
568 opt = cfg_getopt(tc, deprecated_config_options[i]);
569 if (opt->nvalues != 0) {
570 g_info("'%s' is no longer a valid config option in the current version of Tilda and has been removed from the config file.", deprecated_config_options[i]);
571 cfg_free_value(opt);
572 }
573 }
574 }
575
576 /*
577 * Compares two config versions together.
578 *
579 * Returns -1 if config1 is older than config2 (UPDATE REQUIRED)
580 * Returns 0 if config1 is equal to config2 (NORMAL USAGE)
581 * Returns 1 if config1 is newer than config2 (DISABLE WRITE)
582 */
compare_config_versions(const gchar * config1,const gchar * config2)583 static gboolean compare_config_versions (const gchar *config1, const gchar *config2)
584 {
585 DEBUG_FUNCTION ("compare_config_versions");
586 DEBUG_ASSERT (config1 != NULL);
587 DEBUG_ASSERT (config2 != NULL);
588
589 /*
590 * 1) Split apart both strings using the .'s
591 * 2) Compare the major-major version
592 * 3) Compare the major version
593 * 4) Compare the minor version
594 */
595
596 gchar **config1_tokens;
597 gchar **config2_tokens;
598 gint config1_version[3];
599 gint config2_version[3];
600 gint i;
601
602 config1_tokens = g_strsplit (config1, ".", 3);
603 config2_tokens = g_strsplit (config2, ".", 3);
604
605 for (i=0; i<3; i++)
606 {
607 config1_version[i] = atoi (config1_tokens[i]);
608 config2_version[i] = atoi (config2_tokens[i]);
609 }
610
611 g_strfreev (config1_tokens);
612 g_strfreev (config2_tokens);
613
614 /* We're done splitting things, so compare now */
615 for (i=0; i<3; i++)
616 {
617 if (config1_version[i] > config2_version[i])
618 return CONFIG1_NEWER;
619
620 if (config1_version[i] < config2_version[i])
621 return CONFIG1_OLDER;
622 }
623
624 return CONFIGS_SAME;
625 }
626