1 /*
2 ** 1998-05-31 -	After a long wait, here it finally is - the gdtool GUI config
3 **		module! This will be *big*, I can feel it.
4 ** 1998-06-16 -	Smartened up the window handling. Now the config window is only ever
5 **		built (created) once. It is then reused! Environmentally safe.
6 **		It also allows me to use the config GUI to keep all the state,
7 **		although I'm not exactly convinced that's what I want to do...
8 ** 1998-06-22 -	Redesigned. Cut away all old notebook-page-creation code (~140
9 **		lines) and implemented a new, more modular way of doing it.
10 ** 1998-07-26 -	Added a global cache of page descriptors, avoiding having to
11 **		repeat the describe-calls all the time.
12 ** 1998-08-25 -	Implemented a slim way of having the individual page modules
13 **		notify this main module of program-wide things they want done when
14 **		config closes. As an example, it is important to rescan the directories
15 **		if the styles or types change.
16 ** 1998-08-30 -	Fixed version handling in config file, at least somewhat. Also added
17 **		a system-wide config, which is loaded if the user doesn't have one.
18 ** 1998-10-16 -	System-wide config path now configurable via a symbol.
19 ** 1999-12-24 -	Now uses the window utility module to handle the root config window,
20 **		thus making its size and position configurable and savable.
21 ** 2000-07-02 -	Translated.
22 ** 2002-07-19 -	Renamed, and recast the interface to use a tree. Cleaner-looking, and
23 **		handles nested stuff better.
24 */
25 
26 #include "gentoo.h"
27 
28 #include <stdlib.h>
29 
30 #include "cmdseq.h"
31 #include "dialog.h"
32 #include "dirpane.h"
33 #include "fileutil.h"
34 #include "iconutil.h"
35 #include "nag_dialog.h"
36 #include "window.h"
37 #include "xmlutil.h"
38 
39 #include "configure.h"
40 
41 #include "cfg_module.h"
42 
43 #include "cfg_buttonlayout.h"
44 #include "cfg_buttons.h"
45 #include "cfg_cmdcfg.h"
46 #include "cfg_cmdseq.h"
47 #include "cfg_controls.h"
48 #include "cfg_dialogs.h"
49 #include "cfg_dirpane.h"
50 #include "cfg_errors.h"
51 #include "cfg_menus.h"
52 #include "cfg_nag.h"
53 #include "cfg_paths.h"
54 #include "cfg_styles.h"
55 #include "cfg_types.h"
56 #include "cfg_windows.h"
57 
58 /* This should be set in the Makefile, and passed along using the -D
59 ** compiler option, If not, let's default to nice old Slackware style.
60 */
61 #if !defined PATH_CFG
62 #define	PATH_CFG	"/usr/local/etc/"
63 #endif
64 
65 /* ----------------------------------------------------------------------------------------- */
66 
67 typedef struct {
68 	MainInfo	*min;
69 	GtkWidget	*dlg;
70 	GtkWidget	*view;
71 	GtkTreeStore	*store;
72 	GtkTreeIter	treeiter[4];
73 	guint		level;		/* Current depth in tree, used when building. */
74 	GtkWidget	*first;		/* Keeps track of first leaf node during build, for select. */
75 	GtkWidget	*nbook;		/* A notebook widget holding all the config pages. */
76 	GtkWidget	*ok, *save, *cancel;
77 	gint		page;		/* Index of last selected page. */
78 
79 	guint32		flags;		/* Flags for stuff that need to be done when window closes. */
80 } CfgGui;
81 
82 /* A global (yuck!) vector of page descriptor functions. This is where pointers to new pages go. */
83 static const CMDescribeFunc describe_page[] = {	cdp_describe,
84 						ccs_describe, ccc_describe,
85 						cst_describe, ctp_describe,
86 						cbt_describe, cbl_describe,
87 						cpt_describe,
88 						cwn_describe, cdl_describe,
89 						cct_describe, cer_describe,
90 						cng_describe,
91 					};
92 
93 #define	CFG_PAGES	(sizeof describe_page / sizeof describe_page[0])
94 /* A global vector of the resulting page descriptors. */
95 static const CfgModule	*cfg_page[CFG_PAGES];
96 
97 
98 static CfgGui	the_cfggui = { NULL };
99 
100 /* ----------------------------------------------------------------------------------------- */
101 
102 /* 2011-07-24 -	Returns the base directory where the proper configuration file is to be found. */
get_config_dirname(void)103 static const gchar * get_config_dirname(void)
104 {
105 	static gchar	buf[1024] = "";
106 
107 	if(buf[0] == '\0')
108 	{
109 		const gchar	*confdir;
110 
111 		if((confdir = g_get_user_config_dir()) != NULL)
112 			g_snprintf(buf, sizeof buf, "%s" G_DIR_SEPARATOR_S PACKAGE, confdir);
113 	}
114 	return buf;
115 }
116 
117 /* 2011-07-24 -	Returns pointer to the proper filename for our configuration file. */
get_config_filename(const gchar * filename)118 static const gchar * get_config_filename(const gchar *filename)
119 {
120 	static gchar	buf[1024] = "";
121 
122 	if(buf[0] == '\0')
123 	{
124 		const gchar	*confdir = get_config_dirname();
125 
126 		g_snprintf(buf, sizeof buf, "%s" G_DIR_SEPARATOR_S "%s", confdir, filename);
127 	}
128 	return buf;
129 }
130 
131 /* 2011-07-24 -	Returns pointer to the old and outdated filename used by our configuration file. */
get_config_filename_old(const gchar * filename)132 static const gchar * get_config_filename_old(const gchar *filename)
133 {
134 	static gchar	buf[1024];
135 
136 	if(buf[0] == '\0')
137 	{
138 		const gchar	*home = g_getenv("HOME");
139 
140 		if(home == NULL)
141 			home = g_get_home_dir();
142 		if(home != NULL)
143 			g_snprintf(buf, sizeof buf, "%s" G_DIR_SEPARATOR_S "%s", home, filename);
144 	}
145 	return buf;
146 }
147 
148 /* ----------------------------------------------------------------------------------------- */
149 
150 /* 1998-06-26 -	Do the work of hiding the config GUI. */
hide_config(CfgGui * cgu)151 static void hide_config(CfgGui *cgu)
152 {
153 	guint	i;
154 
155 	for(i = 0; i < CFG_PAGES; i++)
156 	{
157 		if(cfg_page[i]->hide != NULL)
158 			cfg_page[i]->hide(cgu->min);
159 	}
160 	if(the_cfggui.flags & CFLG_RESET_KEYBOARD)
161 		kbd_context_clear(cgu->min->gui->kbd_ctx);
162 
163 	if(the_cfggui.flags & CFLG_REBUILD_TOP)
164 		rebuild_top(cgu->min);
165 	if(the_cfggui.flags & CFLG_REBUILD_MIDDLE)
166 		rebuild_middle(cgu->min);
167 
168 	/* The rescanning needs to happen early, since we now have potentially stale Type pointers in the panes. */
169 	if(the_cfggui.flags & CFLG_RESCAN_LEFT)
170 		dp_rescan(&cgu->min->gui->pane[0]);
171 	else if(the_cfggui.flags & CFLG_REDISP_LEFT)
172 		dp_redisplay_preserve(&cgu->min->gui->pane[0]);
173 	if(the_cfggui.flags & CFLG_RESCAN_RIGHT)
174 		dp_rescan(&cgu->min->gui->pane[1]);
175 	else if(the_cfggui.flags & CFLG_REDISP_RIGHT)
176 		dp_redisplay_preserve(&cgu->min->gui->pane[1]);
177 
178 	if(the_cfggui.flags & CFLG_REBUILD_BOTTOM)
179 	{
180 		rebuild_bottom(cgu->min);
181 		csq_execute(cgu->min, "ActivateOther");
182 		csq_execute(cgu->min, "ActivateOther");
183 	}
184 
185 	if(the_cfggui.flags & CFLG_FLUSH_ICONS)
186 		ico_flush(cgu->min);
187 
188 	if(the_cfggui.flags & CFLG_RESET_KEYBOARD)
189 		ctrl_keys_install(cgu->min->cfg.ctrlinfo, cgu->min->gui->kbd_ctx);
190 
191 	gtk_grab_remove(cgu->dlg);
192 	win_window_relink(cgu->min->cfg.wininfo, WIN_CONFIG, cgu->dlg);
193 	win_window_close(cgu->dlg);
194 }
195 
196 /* 1998-06-26 -	The user just clicked the OK button. Let all page modules know, then hide the
197 **		GUI.
198 */
evt_ok_clicked(GtkWidget * wid,gpointer user)199 static gint evt_ok_clicked(GtkWidget *wid, gpointer user)
200 {
201 	CfgGui		*cgu = user;
202 	MainInfo	*min = cgu->min;
203 	guint		i;
204 
205 	cfg_modified_set(min);
206 
207 	for(i = 0; i < CFG_PAGES; i++)
208 	{
209 		if(cfg_page[i]->accept != NULL)
210 			cfg_page[i]->accept(min);
211 	}
212 	hide_config(cgu);
213 	return TRUE;
214 }
215 
216 /* 1998-09-18 -	Broke the saving code out of the button handler, and made it globally
217 **		accessible.
218 */
cfg_save_all(MainInfo * min)219 void cfg_save_all(MainInfo *min)
220 {
221 	const gchar	*root = "GentooConfig", *rcname = get_config_filename(RCNAME);
222 	FILE		*out;
223 	guint		i;
224 	const CfgModule	*page;
225 
226 	cfg_modified_clear(min);
227 	if((out = xml_put_open(rcname)) != NULL)
228 	{
229 		xml_put_node_open(out, root);
230 		xml_put_text(out, "version", VERSION);
231 		for(i = 0; i < CFG_PAGES; i++)
232 		{
233 			if((page = describe_page[i](min)) != NULL && (page->save != NULL))
234 				page->save(min, out);
235 		}
236 		xml_put_node_close(out, root);
237 		xml_put_close(out);
238 	}
239 	else
240 		dlg_dialog_async_new_error(_("Couldn't open configuration file for output"));
241 }
242 
243 /* 1998-07-25 -	I guess it's becoming time to grow up and start outputting a config file.
244 **		XML seems to be the format of the week, so I'll just go for something like
245 **		that.
246 ** 1998-09-18 -	Broke out the actual saving code and put in in a function of its own.
247 */
evt_save_clicked(GtkWidget * wid,gpointer user)248 static gint evt_save_clicked(GtkWidget *wid, gpointer user)
249 {
250 	CfgGui		*cgu = user;
251 	MainInfo	*min = cgu->min;
252 	guint		i;
253 
254 	for(i = 0; i < CFG_PAGES; i++)
255 	{
256 		if(cfg_page[i]->accept != NULL)
257 			cfg_page[i]->accept(min);
258 	}
259 	cfg_save_all(min);
260 	hide_config(cgu);
261 
262 	return TRUE;
263 }
264 
265 /* 1998-07-12 -	User clicked the cancel button. Hide the GUI. */
evt_cancel_clicked(GtkWidget * wid,gpointer user)266 static gint evt_cancel_clicked(GtkWidget *wid, gpointer user)
267 {
268 	hide_config(user);
269 	return TRUE;
270 }
271 
272 /* 1998-05-31 -	Build the buttons at the bottom of the config window (OK, Save, Cancel). */
build_buttons(CfgGui * cgu)273 static void build_buttons(CfgGui *cgu)
274 {
275 	cgu->ok = gtk_button_new_with_label(_("OK"));
276 	g_signal_connect(G_OBJECT(cgu->ok), "clicked", G_CALLBACK(evt_ok_clicked), cgu);
277 	gtk_dialog_add_action_widget(GTK_DIALOG(cgu->dlg), cgu->ok, GTK_RESPONSE_OK);
278 	cgu->save = gtk_button_new_with_label(_("Save"));
279 	g_signal_connect(G_OBJECT(cgu->save), "clicked", G_CALLBACK(evt_save_clicked), cgu);
280 	gtk_dialog_add_action_widget(GTK_DIALOG(cgu->dlg), cgu->save, GTK_RESPONSE_OK);
281 	cgu->cancel = gtk_button_new_with_label(_("Cancel"));
282 	g_signal_connect(G_OBJECT(cgu->cancel), "clicked", G_CALLBACK(evt_cancel_clicked), cgu);
283 	gtk_dialog_add_action_widget(GTK_DIALOG(cgu->dlg), cgu->cancel, GTK_RESPONSE_CANCEL);
284 
285 	gtk_widget_set_can_default(cgu->ok, TRUE);
286 	gtk_widget_set_can_default(cgu->save, TRUE);
287 	gtk_widget_set_can_default(cgu->cancel, TRUE);
288 	gtk_widget_grab_default(cgu->ok);
289 }
290 
291 /* 1998-06-16 -	This gets called as the user clicks the close button of the config GUI.
292 **		Unlike what the function name might lead you to expect, we don't destroy the
293 **		GUI we so laborously (sp?) created. We just hide it so we can use it again
294 **		later. Neat for several reasons.
295 */
evt_cfg_delete(GtkWidget * wid,GdkEvent * evt,gpointer user)296 static gint evt_cfg_delete(GtkWidget *wid, GdkEvent *evt, gpointer user)
297 {
298 	hide_config(user);
299 
300 	return TRUE;
301 }
302 
303 /* 2004-11-2x -	Page tree cursor changed, so switch page in the notebook on the right. */
evt_view_cursor_changed(GtkWidget * view,gpointer user)304 static void evt_view_cursor_changed(GtkWidget *view, gpointer user)
305 {
306 	CfgGui		*cgu = user;
307 	GtkTreePath	*path;
308 	GtkTreeIter	iter;
309 
310 	gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, NULL);
311 	if(path == NULL)
312 		return;
313 	gtk_tree_model_get_iter(GTK_TREE_MODEL(cgu->store), &iter, path);
314 	if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(cgu->store), &iter) > 0)	/* Disregard interior nodes. */
315 		return;
316 	gtk_tree_model_get(GTK_TREE_MODEL(cgu->store), &iter, 1, &cgu->page, -1);
317 	gtk_notebook_set_current_page(GTK_NOTEBOOK(cgu->nbook), cgu->page);
318 }
319 
320 /* 1998-06-16 -	Cooled up (?) this routine a lot. It is now recursive, in a rather
321 **		interesting manner. The point is not to reconstruct the entire config-
322 **		GUI each time it is needed, but rather to just build it once and then
323 **		hide/show it as needed.
324 */
cfg_configure(MainInfo * min)325 gint cfg_configure(MainInfo *min)
326 {
327 	CfgGui		*cgu = &the_cfggui;
328 	gchar		*name = NULL;
329 	guint		i;
330 	GtkWidget	*hbox, *scwin, *simple;
331 	GtkCellRenderer	*cr;
332 	GtkTreeViewColumn *vc;
333 
334 	the_cfggui.flags = 0U;
335 
336 	/* Do the widgets already exist? Then just update and display. */
337 	if(cgu->dlg != NULL)
338 	{
339 		for(i = 0; i < CFG_PAGES; i++)
340 		{
341 			if(cfg_page[i]->update != NULL)
342 				cfg_page[i]->update(min);
343 		}
344 		win_window_show(cgu->dlg);
345 		gtk_notebook_set_current_page(GTK_NOTEBOOK(cgu->nbook), the_cfggui.page);
346 		gtk_widget_show_all(cgu->dlg);
347 		gtk_grab_add(cgu->dlg);
348 		return 1;
349 	}
350 
351 	/* Initialize the cache of page descriptors. */
352 	for(i = 0; i < CFG_PAGES; i++)
353 		cfg_page[i] = describe_page[i](min);
354 
355 	cgu->min = min;
356 	cgu->dlg = win_window_open(min->cfg.wininfo, WIN_CONFIG);
357 	gtk_window_set_modal(GTK_WINDOW(cgu->dlg), TRUE);
358 	g_signal_connect(G_OBJECT(cgu->dlg), "delete_event", G_CALLBACK(evt_cfg_delete), cgu);
359 
360 	cgu->store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT);	/* Simply store notebook page# in second column. */
361 	cgu->level = 0U;
362 
363 	cgu->first = NULL;
364 	cgu->nbook = gtk_notebook_new();
365 	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(cgu->nbook), FALSE);
366 
367 	for(i = 0; i < CFG_PAGES; i++)
368 	{
369 		if(cfg_page[i]->init != NULL)
370 		{
371 			if((simple = cfg_page[i]->init(min, &name)) != NULL)
372 			{
373 				gint	pn = gtk_notebook_append_page(GTK_NOTEBOOK(cgu->nbook), simple, NULL);
374 
375 				gtk_tree_store_append(cgu->store, &cgu->treeiter[cgu->level], NULL);
376 				gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 0, name, -1);
377 				gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 1, pn, -1);
378 			}
379 		}
380 	}
381 	hbox  = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
382 	scwin = gtk_scrolled_window_new(NULL, NULL);
383 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
384 	/* Build tree view, showing only the first column. */
385 	cgu->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(cgu->store));
386 	cr = gtk_cell_renderer_text_new();
387 	vc = gtk_tree_view_column_new_with_attributes("(ConfigPages)", cr, "text", 0, NULL);
388 	gtk_tree_view_append_column(GTK_TREE_VIEW(cgu->view), vc);
389 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cgu->view), FALSE);
390 	g_signal_connect(G_OBJECT(cgu->view), "cursor_changed", G_CALLBACK(evt_view_cursor_changed), cgu);
391 	gtk_container_add(GTK_CONTAINER(scwin), cgu->view);
392 	gtk_box_pack_start(GTK_BOX(hbox), scwin, FALSE, FALSE, 0);
393 
394 	scwin = gtk_scrolled_window_new(NULL, NULL);
395 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
396 	gtk_container_add(GTK_CONTAINER(scwin), cgu->nbook);
397 	gtk_box_pack_start(GTK_BOX(hbox), scwin, TRUE, TRUE, 0);
398 	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(cgu->dlg))), hbox, TRUE, TRUE, 0);
399 	build_buttons(cgu);
400 
401 	return cfg_configure(min);		/* Recurse! */
402 }
403 
404 /* ----------------------------------------------------------------------------------------- */
405 
goto_iterate(const CfgGui * cgu,GtkTreeIter * iter,const char * label)406 static gboolean goto_iterate(const CfgGui *cgu, GtkTreeIter *iter, const char *label)
407 {
408 	GtkTreeIter	citer;
409 	const gchar	*lh;
410 	gint		lp;
411 
412 	do
413 	{
414 		gtk_tree_model_get(GTK_TREE_MODEL(cgu->store), iter, 0, &lh, 1, &lp, -1);
415 		if(strcmp(lh, label) == 0)
416 		{
417 			GtkTreePath	*path;
418 
419 			gtk_notebook_set_current_page(GTK_NOTEBOOK(cgu->nbook), lp);
420 			path = gtk_tree_model_get_path(GTK_TREE_MODEL(cgu->store), iter);
421 			gtk_tree_view_expand_to_path(GTK_TREE_VIEW(cgu->view), path);
422 			gtk_tree_view_set_cursor(GTK_TREE_VIEW(cgu->view), path, NULL, FALSE);
423 			gtk_tree_path_free(path);
424 			return TRUE;
425 		}
426 		if(gtk_tree_model_iter_children(GTK_TREE_MODEL(cgu->store), &citer, iter))
427 		{
428 			if(goto_iterate(cgu, &citer, label))
429 				return TRUE;
430 		}
431 	} while(gtk_tree_model_iter_next(GTK_TREE_MODEL(cgu->store), iter));
432 
433 	return FALSE;
434 }
435 
436 /* 2014-12-26 -	Switch to a named configuration page. */
cfg_goto_page(const char * label)437 void cfg_goto_page(const char *label)
438 {
439 	const CfgGui	*cgu = &the_cfggui;
440 	GtkTreeIter	iter;
441 
442 	if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(cgu->store), &iter))
443 	{
444 		goto_iterate(cgu, &iter, label);
445 	}
446 }
447 
448 /* ----------------------------------------------------------------------------------------- */
449 
cfg_tree_level_begin(const gchar * label)450 void cfg_tree_level_begin(const gchar *label)
451 {
452 	CfgGui	*cgu = &the_cfggui;
453 
454 	if(cgu->level + 1 < sizeof cgu->treeiter / sizeof *cgu->treeiter)
455 	{
456 		gtk_tree_store_append(cgu->store, &cgu->treeiter[cgu->level], cgu->level > 0 ? &cgu->treeiter[cgu->level - 1] : NULL);
457 		gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 0, label, -1);
458 		cgu->level++;
459 	}
460 }
461 
cfg_tree_level_append(const gchar * label,GtkWidget * page)462 void cfg_tree_level_append(const gchar *label, GtkWidget *page)
463 {
464 	CfgGui		*cgu = &the_cfggui;
465 
466 	gtk_tree_store_append(cgu->store, &cgu->treeiter[cgu->level], &cgu->treeiter[cgu->level - 1]);
467 	gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 0, label, -1);
468 
469 	if(page != NULL)
470 	{
471 		gint	pn = gtk_notebook_append_page(GTK_NOTEBOOK(the_cfggui.nbook), page, NULL);
472 		gtk_tree_store_set(cgu->store, &cgu->treeiter[cgu->level], 1, pn, -1);
473 	}
474 }
475 
476 /* 2002-07-19 -	Replace <old> tree widget with the <new> one. Handy for cases where config widgets
477 **		are seriously rebuilt during update(), such as in cfg_cmdcfg.c.
478 */
cfg_tree_level_replace(GtkWidget * old,GtkWidget * new)479 void cfg_tree_level_replace(GtkWidget *old, GtkWidget *new)
480 {
481 	if(old)
482 	{
483 		gint	pn = gtk_notebook_page_num(GTK_NOTEBOOK(the_cfggui.nbook), old);
484 		gtk_container_remove(GTK_CONTAINER(the_cfggui.nbook), old);
485 		gtk_notebook_insert_page(GTK_NOTEBOOK(the_cfggui.nbook), new, NULL, pn);
486 		gtk_widget_show(new);
487 	}
488 }
489 
cfg_tree_level_end(void)490 void cfg_tree_level_end(void)
491 {
492 	if(the_cfggui.level > 0)
493 		the_cfggui.level--;
494 }
495 
496 /* ----------------------------------------------------------------------------------------- */
497 
load_node(const XmlNode * node,gpointer user)498 static void load_node(const XmlNode *node, gpointer user)
499 {
500 	MainInfo	*min = user;
501 	guint		i;
502 
503 	for(i = 0; i < CFG_PAGES; i++)
504 	{
505 		if(xml_node_has_name(node, cfg_page[i]->node) && cfg_page[i]->load != NULL)
506 		{
507 			cfg_page[i]->load(min, node);
508 			return;
509 		}
510 	}
511 }
512 
513 /* 1998-07-26 -	Load the entire program configuration. Pretty complex stuff, made considerably
514 **		less so by the modularization and tree organization.
515 ** 1998-08-30 -	Now keeps knowledge about config file name to itself. First checks if there
516 **		is a local config; if so, it is loaded. If not, the system-wide default
517 **		config from /etc/local/etc/ is used. If that fails, whine.
518 ** 1998-10-21 -	Now returns a set of flags, rather than the single first boolean.
519 ** 1999-08-25 -	Made the error dialog shown when no config is found a bit more informative.
520 */
cfg_load_config(MainInfo * min)521 guint32 cfg_load_config(MainInfo *min)
522 {
523 	XmlNode		*tree;
524 	gchar		name[PATH_MAX] = "";
525 	const gchar	*rcdir, *rcname;
526 	guint32		i, flags = 0UL, bad_dir = 0;
527 
528 	/* Since we know this function is called during boot, ensure the user's config directory exists. */
529 	rcdir = get_config_dirname();
530 	if(*rcdir == '\0')
531 		g_error("Failed to retrieve user's configuration directory, can't save configuration data");
532 	else if(!fut_exists(rcdir))
533 		g_mkdir_with_parents(rcdir, 0700);
534 
535 	/* Does the user seem to have a local config? */
536 	rcname = get_config_filename(RCNAME);
537 	if(!fut_can_read_named(rcname))
538 	{
539 		rcname = get_config_filename_old("." RCNAME);	/* Nope, check old location and name. */
540 		bad_dir = 1;
541 	}
542 	if(!fut_can_read_named(rcname))
543 	{
544 		g_snprintf(name, sizeof name, PATH_CFG G_DIR_SEPARATOR_S "%s", RCNAME);		/* Nope, check for global one. */
545 		rcname = name;
546 		bad_dir = 1;
547 	}
548 
549 	/* If loading from a "bad" (non-standard) directory, nag. */
550 	if(bad_dir)
551 		ndl_dialog_sync_new_wait(min, "rcpath", _("Configuration Path Notice"), _("Configuration was not loaded from the current default location.\nPress Save in the Configuration window to update."));
552 
553 	/* Initialize the cache of page descriptors. */
554 	for(i = 0; i < CFG_PAGES; i++)
555 		cfg_page[i] = describe_page[i](min);
556 
557 	if((tree = xml_tree_load(rcname)) != NULL)
558 	{
559 		const gchar	*fver;
560 
561 		if(xml_get_text(tree, "version", &fver) && strcmp(fver, VERSION) != 0)
562 			g_warning(_("Config file version (%s) doesn't match program version (%s)"), fver, VERSION);
563 		xml_node_visit_children(tree, load_node, (gpointer) min);
564 		xml_tree_destroy(tree);
565 	}
566 	else
567 	{
568 		const gchar	*rcname, *oldrcname;
569 		gchar		syscfg[PATH_MAX], whine[3 * PATH_MAX];
570 
571 		rcname = get_config_filename(RCNAME);
572 		oldrcname = get_config_filename_old("." RCNAME);
573 		g_snprintf(syscfg, sizeof syscfg, PATH_CFG G_DIR_SEPARATOR_S "%s", RCNAME);
574 		g_snprintf(whine, sizeof whine, _("Couldn't find any configuration file; checked:\n"
575 				"\"%s\",\n\"%s\" and\n\"%s\".\n"
576 				"Using built-in minimal configuration."),
577 				rcname, oldrcname, syscfg);
578 		dlg_dialog_async_new_error(whine);
579 		flags |= CLDF_NONE_FOUND;
580 	}
581 	return flags;
582 }
583 
584 /* ----------------------------------------------------------------------------------------- */
585 
586 /* 1998-08-25 -	Log a request to get something done. */
cfg_set_flags(guint32 flags)587 void cfg_set_flags(guint32 flags)
588 {
589 	the_cfggui.flags |= flags;
590 }
591 
592 /* 1999-04-09 -	Set the 'configuration modified' flag. */
cfg_modified_set(MainInfo * min)593 void cfg_modified_set(MainInfo *min)
594 {
595 	min->cfg.flags |= CFLG_CHANGED;
596 }
597 
598 /* 1999-04-09 -	Clear the 'configuration modified' flag. Use with care. */
cfg_modified_clear(MainInfo * min)599 void cfg_modified_clear(MainInfo *min)
600 {
601 	min->cfg.flags &= ~CFLG_CHANGED;
602 }
603