1 /*
2 ** 1998-05-17 -	I just have to build an Opus-look-alike GTK GUI! :)
3 ** 1998-09-11 -	Er, it like, grew, or something.
4 */
5 
6 #include "gentoo.h"
7 
8 #include <dlfcn.h>
9 #include <signal.h>
10 #include <stdlib.h>
11 #include <time.h>
12 #include <sys/wait.h>
13 
14 #if defined ENABLE_NLS
15 #include <locale.h>
16 #endif
17 
18 #include "errors.h"
19 #include "configure.h"
20 #include "dirpane.h"
21 #include "dialog.h"
22 #include "userinfo.h"
23 #include "file.h"
24 #include "fileutil.h"
25 #include "gfam.h"
26 #include "guiutil.h"
27 #include "xmlutil.h"
28 #include "strutil.h"
29 #include "types.h"
30 #include "styles.h"
31 #include "sizeutil.h"
32 #include "buttons.h"
33 #include "buttonlayout.h"
34 #include "queue.h"
35 #include "cmdseq.h"
36 #include "children.h"
37 #include "dpformat.h"
38 #include "keyboard.h"
39 #include "controls.h"
40 #include "iconutil.h"
41 #include "cmdseq_config.h"
42 #include "menus.h"
43 #include "nag_dialog.h"
44 
45 #include "cfg_module.h"
46 #include "cfg_nag.h"
47 #include "cfg_windows.h"
48 
49 /* ----------------------------------------------------------------------------------------- */
50 
51 /* 1998-05-18 -	Filter out files the user really doesn't want to see, and that recursive
52 **		directory traversing code really, really, REALLY, doesn't.
53 */
dir_filter(const gchar * name)54 static gboolean dir_filter(const gchar *name)
55 {
56 	if((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
57 		return FALSE;
58 	return TRUE;
59 }
60 
61 /* ----------------------------------------------------------------------------------------- */
62 
63 /* 2000-03-18 -	User clicked close button on window. Just react as if the Quit command was run. */
evt_main_delete(GtkWidget * wid,GdkEventAny * evt,gpointer user)64 static gboolean evt_main_delete(GtkWidget *wid, GdkEventAny *evt, gpointer user)
65 {
66 	csq_execute(user, "Quit");
67 
68 	return TRUE;
69 }
70 
71 /* 1998-10-26 -	Initialize some fields in the dp structure. */
init_pane(DirPane * dp,gint index)72 static void init_pane(DirPane *dp, gint index)
73 {
74 	dp->index = index;
75 	dp->complete.prefix[0] = '\0';
76 	dp->dir.stat = NULL;
77 	dp->dir.stat_alloc = 0;
78 	dp->dir.stat_use   = 0;
79 	dp->hist = dph_dirhistory_new();
80 	dp->dir.path[0] = '\0';
81 }
82 
evt_paned_notify_position(GObject * obj,GParamSpec * spec,gpointer user)83 static void evt_paned_notify_position(GObject *obj, GParamSpec *spec, gpointer user)
84 {
85 	MainInfo	*min = user;
86 	GdkWindow	*pwin;
87 	gint		pos, np;
88 
89 	if(!dp_realized(min))
90 		return;
91 
92 	pos = gtk_paned_get_position(GTK_PANED(obj));
93 	pwin = gtk_widget_get_window(min->gui->panes);
94 	np = min->cfg.dp_paning.orientation == DPORIENT_HORIZ ? gdk_window_get_width(pwin) : gdk_window_get_height(pwin);
95 	switch(min->cfg.dp_paning.mode)
96 	{
97 		case DPSPLIT_FREE:
98 			break;
99 		case DPSPLIT_RATIO:
100 			min->cfg.dp_paning.value = (gdouble) pos / np;
101 			break;
102 		case DPSPLIT_ABS_LEFT:
103 			min->cfg.dp_paning.value = pos;
104 			break;
105 		case DPSPLIT_ABS_RIGHT:
106 			min->cfg.dp_paning.value = np - pos;
107 			break;
108 	}
109 }
110 
111 /* 1998-05-17 -	Build a couple of dir-panes.
112 ** 1998-05-18 -	Now also puts the panes in a horizontally paned window, for extra Opusity. ;^)
113 ** 1998-08-02 -	Fixed *huge* bug where pane 1 (the right) was built using pane 0's format!!
114 */
build_dirpanes(MainInfo * min)115 static GtkWidget * build_dirpanes(MainInfo *min)
116 {
117 	GtkWidget	*pane;
118 	GuiInfo		*gui;
119 
120 	gui = min->gui;
121 
122 	gui->panes = gtk_paned_new(min->cfg.dp_paning.orientation == DPORIENT_HORIZ ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
123 	init_pane(&gui->pane[0], 0);
124 	pane = dp_build(min, &min->cfg.dp_format[0], &gui->pane[0]);
125 	gtk_paned_add1(GTK_PANED(gui->panes), pane);
126 	init_pane(&gui->pane[1], 1);
127 	pane = dp_build(min, &min->cfg.dp_format[1], &gui->pane[1]);
128 	gtk_paned_add2(GTK_PANED(gui->panes), pane);
129 	gui->sig_pane_notify = g_signal_connect(G_OBJECT(gui->panes), "notify::position", G_CALLBACK(evt_paned_notify_position), min);
130 	gtk_widget_show(gui->panes);
131 
132 	gtk_widget_grab_focus(min->gui->pane[0].view);
133 
134 	return gui->panes;
135 }
136 
evt_top_button_press_event(GtkWidget * top,GdkEventButton * evt,gpointer user)137 static void evt_top_button_press_event(GtkWidget *top, GdkEventButton *evt, gpointer user)
138 {
139 	if(evt->button == 1 && evt->type == GDK_2BUTTON_PRESS)
140 		csq_execute(user, "About");
141 }
142 
143 /* 1998-05-19 -	Create the top widgetry, returning something the caller can just add to a box. Sets the
144 **		<label> pointer to a GtkLabel which can be used later to display funny messages and stuff.
145 **		The label is internally wrapped in a GtkEventbox, which makes updating it possible without
146 **		causing instant epilepsy in everyone watching. GTK+ really is smooth.
147 ** 1998-11-26 -	Made the label focusable (?), in order to *finally* have somewhere safe to put the focus
148 **		when I don't want it on path entry widgets. Nice.
149 ** 2010-10-03 -	Added sneaky click-listener, so that we can run About on double-click.
150 */
build_top(MainInfo * min)151 static GtkWidget * build_top(MainInfo *min)
152 {
153 	GtkLabel	**label = (GtkLabel **) &min->gui->top;
154 	GtkWidget	*top, *lab;
155 
156 	if(min->cfg.errors.display == ERR_DISPLAY_TITLEBAR)
157 	{
158 		if(*label != NULL)
159 			gtk_widget_destroy(GTK_WIDGET(*label));
160 		*label = NULL;
161 		return NULL;
162 	}
163 
164 	top = gtk_event_box_new();
165 	lab = gtk_label_new("");
166 	gtk_container_add(GTK_CONTAINER(top), lab);
167 	gtk_widget_show_all(top);
168 	if(label != NULL)
169 		*label = GTK_LABEL(lab);
170 	g_signal_connect(G_OBJECT(top), "button_press_event", G_CALLBACK(evt_top_button_press_event), min);
171 
172 	return top;
173 }
174 
175 /* 2002-05-31 -	Rewritten yet again, now using the first weak version of a specialized button layout module. */
build_bottom(MainInfo * min)176 static GtkWidget * build_bottom(MainInfo *min)
177 {
178 	GtkWidget	*hbox, *sb, *cb;
179 
180 	if((sb = btn_buttonsheet_build(min, &min->cfg.buttons, "Shortcuts", FALSE, NULL, NULL)) != NULL)
181 		btn_buttonsheet_built_add_keys(min, GTK_CONTAINER(sb), NULL);
182 	if((cb = btn_buttonsheet_build(min, &min->cfg.buttons, NULL, FALSE, NULL, min)) != NULL)
183 		btn_buttonsheet_built_add_keys(min, GTK_CONTAINER(cb), NULL);
184 	if((hbox = btl_buttonlayout_pack(min->cfg.buttonlayout, cb, sb)) != NULL)
185 		gtk_widget_show_all(hbox);
186 
187 	return hbox;
188 }
189 
rebuild_top(MainInfo * min)190 void rebuild_top(MainInfo *min)
191 {
192 	GtkWidget	*top;
193 
194 	if(min->gui->top != NULL)
195 	{
196 		gtk_widget_destroy(min->gui->top);
197 		min->gui->top = NULL;
198 	}
199 	top = build_top(min);
200 	if(top != NULL)
201 	{
202 		gtk_box_pack_start(GTK_BOX(min->gui->vbox), top, FALSE, FALSE, 0);
203 		gtk_box_reorder_child(GTK_BOX(min->gui->vbox), top, 0);
204 	}
205 	dp_show_stats(min->gui->cur_pane);
206 	gui_set_main_title(min, NULL);
207 }
208 
209 /* 1998-10-26 -	Rebuild the middle part of the GUI, i.e. the panes. */
rebuild_middle(MainInfo * min)210 void rebuild_middle(MainInfo *min)
211 {
212 	GtkWidget	*left, *right;
213 
214 	gtk_container_remove(GTK_CONTAINER(min->gui->panes), min->gui->pane[0].vbox);
215 	gtk_container_remove(GTK_CONTAINER(min->gui->panes), min->gui->pane[1].vbox);
216 	gtk_widget_destroy(min->gui->panes);
217 	min->gui->panes = gtk_paned_new(min->cfg.dp_paning.orientation == DPORIENT_HORIZ ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
218 	gtk_box_pack_start(GTK_BOX(min->gui->vbox), min->gui->panes, TRUE, TRUE, 0);
219 	gtk_box_reorder_child(GTK_BOX(min->gui->vbox), min->gui->panes, 1);
220 	if((left = dp_build(min, &min->cfg.dp_format[0], &min->gui->pane[0])) != NULL)
221 		gtk_paned_add1(GTK_PANED(min->gui->panes), left);
222 	if((right = dp_build(min, &min->cfg.dp_format[1], &min->gui->pane[1])) != NULL)
223 		gtk_paned_add2(GTK_PANED(min->gui->panes), right);
224 	min->gui->sig_pane_notify = g_signal_connect(G_OBJECT(min->gui->panes), "notify::position", G_CALLBACK(evt_paned_notify_position), min);
225 	gtk_widget_show(min->gui->panes);
226 	dp_split_refresh(min);
227 }
228 
229 /* 1998-07-14 -	Rebuild the bottom part of the GUI, whose main responsibility is to contain
230 **		the button bank(s).
231 */
rebuild_bottom(MainInfo * min)232 void rebuild_bottom(MainInfo *min)
233 {
234 	gtk_widget_destroy(min->gui->bottom);
235 	if((min->gui->bottom = build_bottom(min)) != NULL)
236 	{
237 		gtk_box_pack_start(GTK_BOX(min->gui->vbox), min->gui->bottom, FALSE, FALSE, 0);
238 		gtk_widget_show(min->gui->bottom);
239 	}
240 }
241 
242 /* 2008-04-20 -	Replaced old size_allocation-tracking code with new, using configure events instead. */
evt_main_configure(GtkWidget * wid,GdkEventConfigure * req,gpointer user)243 static gboolean evt_main_configure(GtkWidget *wid, GdkEventConfigure *req, gpointer user)
244 {
245 	dp_split_refresh(user);
246 
247 	return FALSE;	/* Keep propagating. */
248 }
249 
build_gui(MainInfo * min)250 static GtkWidget * build_gui(MainInfo *min)
251 {
252 	GtkWidget	*top, *panes, *bottom;
253 
254 	min->gui = g_malloc(sizeof *min->gui);
255 	min->gui->window = NULL;
256 	min->gui->window = win_window_open(min->cfg.wininfo, WIN_MAIN);
257 	gtk_widget_set_name(min->gui->window, "gentoo");
258 	g_object_set_data(G_OBJECT(min->gui->window), "user", min);
259 	min->gui->sig_main_configure = g_signal_connect(G_OBJECT(min->gui->window), "configure_event", G_CALLBACK(evt_main_configure), min);
260 	min->gui->sig_main_delete = g_signal_connect(G_OBJECT(min->gui->window), "delete_event", G_CALLBACK(evt_main_delete), min);
261 	min->gui->pane[0].main = min->gui->pane[1].main = min;
262 	min->gui->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
263 	min->gui->kbd_ctx = kbd_context_new(min);
264 
265 	top = build_top(min);
266 	if((panes = build_dirpanes(min)) != NULL)
267 	{
268 		bottom = build_bottom(min);
269 		if(top != NULL)
270 			gtk_box_pack_start(GTK_BOX(min->gui->vbox), top,    FALSE, FALSE, 0);
271 		gtk_box_pack_start(GTK_BOX(min->gui->vbox), panes,  TRUE,  TRUE,  0);
272 		if(bottom != NULL)
273 			gtk_box_pack_start(GTK_BOX(min->gui->vbox), bottom, FALSE, FALSE, 0);
274 		gtk_widget_show_all(min->gui->vbox);
275 		min->gui->middle = panes;
276 		min->gui->bottom = bottom;
277 		min->gui->cur_pane = &min->gui->pane[0];
278 		kbd_context_attach(min->gui->kbd_ctx, GTK_WINDOW(min->gui->window));
279 		ctrl_keys_install(min->cfg.ctrlinfo, min->gui->kbd_ctx);
280 		return min->gui->vbox;
281 	}
282 	gtk_widget_destroy(top);
283 	gtk_widget_destroy(min->gui->vbox);
284 
285 	return NULL;
286 }
287 
288 /* 1998-08-23 -	Initialize the paths config data. */
init_paths(CfgInfo * cfg)289 static void init_paths(CfgInfo *cfg)
290 {
291 	const gchar	*confdir;
292 
293 	cfg->path.path[PTID_ICON]  = g_string_new(PATH_ICN);
294 	if((confdir = g_get_user_config_dir()) != NULL)
295 	{
296 		cfg->path.path[PTID_GTKRC] = g_string_new(confdir);
297 		g_string_append(cfg->path.path[PTID_GTKRC], G_DIR_SEPARATOR_S PACKAGE);
298 	}
299 	else
300 		cfg->path.path[PTID_GTKRC] = g_string_new(PATH_GRC);
301 	cfg->path.path[PTID_FSTAB] = g_string_new("/etc/fstab");
302 	cfg->path.path[PTID_MTAB]  = g_string_new("/proc/mounts");
303 
304 	cfg->path.hideinfo.mode = HIDE_NONE;
305 	cfg->path.hideinfo.hide_re_src[0] = '\0';
306 	cfg->path.hideinfo.hide_re = NULL;
307 }
308 
309 /* ----------------------------------------------------------------------------------------- */
310 
311 /* 1998-11-29 -	Load the GTK+ RC file, allowing users to configure gentoo's looks. The
312 **		file is always named ".gentoogtkrc" (too long, I know) but you can put
313 **		it anywhere as long as you given gentoo the path (in the config).
314 ** 2001-08-12 -	The dot is now optional, but is given priority if found.
315 ** 2011-07-24 -	Now we even prefer "gtkrc", without "gentoo", to match .config/gentoo location.
316 ** 2012-05-02 -	Ported to use GtkCssProvider API, for GTK+ 3.0.
317 */
load_gtk_rc(MainInfo * min)318 static void load_gtk_rc(MainInfo *min)
319 {
320 	const gchar	*names[] = { "gtkrc", "gentoogtkrc", ".gentoogtkrc" };
321 	const gchar	*name;
322 	gsize		i;
323 
324 	for(i = 0; i < sizeof names / sizeof *names; i++)
325 	{
326 		if((name = fut_locate(min->cfg.path.path[PTID_GTKRC]->str, names[i])) != NULL)
327 		{
328 			GtkCssProvider	*prov;
329 
330 			if((prov = gtk_css_provider_new()) != NULL)
331 			{
332 				if(gtk_css_provider_load_from_path(prov, name, NULL))
333 				{
334 					GdkScreen	*scr = gtk_widget_get_screen(min->gui->window);
335 
336 					gtk_style_context_add_provider_for_screen(scr, GTK_STYLE_PROVIDER(prov), GTK_STYLE_PROVIDER_PRIORITY_THEME);
337 					break;
338 				}
339 			}
340 		}
341 	}
342 }
343 
344 /* 1999-04-04 -	Initialize default (er, and non-configurable) command options. I'll
345 **		build a GUI for this stuff real soon now.
346 */
init_cmd_options(CfgInfo * cfg)347 static void init_cmd_options(CfgInfo *cfg)
348 {
349 	cfg->opt_overwrite.show_info = TRUE;
350 	g_strlcpy(cfg->opt_overwrite.datefmt, "%Y-%m-%d %H:%M.%S", sizeof cfg->opt_overwrite.datefmt);
351 }
352 
353 #if 0
354 /* 1999-05-10 -	This is for temporary key checks during development. Simpler to get a trigger this way
355 **		than to hack the controls and/or keyboard modules. Is that a sign of bad code there? Naah. :)
356 */
357 #include <gdk/gdkkeysyms.h>
358 static void evt_key_press(GtkWidget *wid, GdkEventKey *evt, gpointer user)
359 {
360 	MainInfo	*min = user;
361 
362 	printf("Someone pressed %d ('%c'), state=%04X\n", evt->keyval, evt->keyval, evt->state);
363 
364 	if(evt->keyval == GDK_v)
365 	{
366 		GtkAdjustment	*adj;
367 
368 		if((adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(min->gui->cur_pane->scwin))) != NULL)
369 			printf("Adj: lower=%g upper=%g value=%g\n", adj->lower, adj->upper, adj->value);
370 	}
371 	else if(evt->keyval == GDK_z)
372 		dp_unfocus(min->gui->cur_pane);
373 	else if(evt->keyval == GDK_d)
374 	{
375 		Dialog	*dlg;
376 		gint	ret;
377 
378 		dlg = dlg_dialog_sync_new(NULL, "Hello", "_A|_B");
379 		ret = dlg_dialog_sync_wait(dlg);
380 		dlg_dialog_sync_destroy(dlg);
381 	}
382 	else if(evt->keyval == GDK_1)
383 	{
384 		min->cfg.dp_paning.mode++;
385 		if(min->cfg.dp_paning.mode > DPSPLIT_ABS_RIGHT)
386 			min->cfg.dp_paning.mode = DPSPLIT_FREE;
387 	}
388 	else if(evt->keyval == GDK_0)
389 		gtk_widget_hide(min->gui->pane[0].scwin);
390 	else if(evt->keyval == GDK_plus)
391 		gtk_widget_show(min->gui->pane[0].scwin);
392 }
393 #endif
394 
395 /* 2002-02-19 -	Resolve what the initial directory is going to be, and go there. */
enter_initial_dirs(MainInfo * min,const gchar * dirleft,const gchar * dirright)396 static gboolean enter_initial_dirs(MainInfo *min, const gchar *dirleft, const gchar *dirright)
397 {
398 	const gchar	*dir;
399 	const gchar	*odir[2];
400 	const gchar	*cmd[] = { "ActivateLeft", "ActivateRight" };
401 	gint		i;
402 	gboolean	ok = TRUE;
403 
404 	odir[0] = dirleft;
405 	odir[1] = dirright;
406 	for(i = sizeof cmd / sizeof *cmd - 1; i >= 0; i--)
407 	{
408 		if((dir = odir[i]) == NULL)
409 		{
410 			dir = min->cfg.dp_format[i].def_path;
411 			if(!dir[0])
412 				dir = dph_history_get_first(&min->gui->pane[i]);
413 		}
414 		if(dir == NULL)
415 			dir = usr_get_home();
416 		csq_execute(min, cmd[i]);
417 		ok &= csq_execute_format(min, "DirEnter 'dir=%s'", stu_escape(dir)) != 0;
418 	}
419 
420 	return ok;
421 }
422 
423 /* 2003-11-25 -	An idle handler that should only trigger once, to run commands from command line. */
evt_idle_run(gpointer user)424 static gint evt_idle_run(gpointer user)
425 {
426 	MainInfo	*min = user;
427 
428 	if(min->run_commands != NULL)
429 	{
430 		gsize	i;
431 
432 		for(i = 0; min->run_commands[i] != NULL; i++)
433 			csq_execute(min, min->run_commands[i]);
434 	}
435 
436 	return FALSE;		/* Causes the handler to be removed, once really is enough. */
437 }
438 
cmp_list_commands(gconstpointer a,gconstpointer b)439 static gint cmp_list_commands(gconstpointer a, gconstpointer b)
440 {
441 	return strcmp(a, b);
442 }
443 
cb_list_commands(gpointer key,gpointer value,gpointer user)444 static void cb_list_commands(gpointer key, gpointer value, gpointer user)
445 {
446 	GList	**head = user;
447 
448 	*head = g_list_insert_sorted(*head, key, cmp_list_commands);
449 }
450 
do_list_commands(MainInfo * min)451 static void do_list_commands(MainInfo *min)
452 {
453 	GList	*cmdseq = NULL, *iter;
454 
455 	g_hash_table_foreach(min->cfg.commands.builtin, cb_list_commands, &cmdseq);
456 	for(iter = cmdseq; iter != NULL; iter = g_list_next(iter))
457 		printf("%s\n", (const gchar *) iter->data);
458 }
459 
main(int argc,char * argv[])460 int main(int argc, char *argv[])
461 {
462 	static MainInfo	min = { NULL };
463 
464 	gboolean	show_version = FALSE;
465 	gboolean	root_ok = FALSE;
466 	gboolean	no_rc = FALSE, no_gtk_rc = FALSE, no_dir_history = FALSE, list_commands = FALSE;
467 	gchar		*dir_left = NULL;
468 	gchar		*dir_right = NULL;
469 #if defined ENABLE_NLS
470 	gboolean	show_locale_info = FALSE;
471 	gint		i;
472 #endif
473 	GOptionEntry	option_entries[] = {
474 	{ "version", 0, 0, G_OPTION_ARG_NONE, &show_version,  N_("Report the version to standard output, and exit"), NULL },
475 #if defined ENABLE_NLS
476 	{ "locale-info", 0, 0, G_OPTION_ARG_NONE, &show_locale_info, N_("Report internal locale details, and exit"), NULL },
477 #endif
478 	{ "root-ok", 0, 0, G_OPTION_ARG_NONE, &root_ok, N_("Allows gentoo to be run by the root user. Could be dangerous!"), NULL },
479 	{ "no-rc", 0, 0, G_OPTION_ARG_NONE, &no_rc, N_("Do not load the ~/.config/gentoo/gentoorc configuration file; instead, use default values"), NULL },
480 	{ "no-gtk-rc", 0, 0, G_OPTION_ARG_NONE, &no_gtk_rc, N_("Do not load the ~/.config/gentoo/gtkrc GTK+ configuration file; instead, use system defaults"), NULL },
481 	{ "no-dir-history", 0, 0, G_OPTION_ARG_NONE, &no_dir_history, N_("Do not load the ~/.config/gentoo/dirhistory file; instead, start with empty history"), NULL },
482 	{ "run", 'r', 0, G_OPTION_ARG_STRING_ARRAY, &min.run_commands, N_("Run COMMAND, a gentoo command. Done before user interaction allowed, but after configuration "
483 			      "file has been read in. Can be used many times to run several commands in sequence"), N_("COMMAND") },
484 	{ "left", '1', 0, G_OPTION_ARG_STRING, &dir_left, N_("Use DIR as path for the left directory pane. Overrides default (and history)"), N_("DIR") },
485 	{ "right", '2', 0, G_OPTION_ARG_STRING, &dir_right, N_("Use DIR as path for the right directory pane. Overrides default (and history)"), N_("DIR") },
486 	{ "list-commands", 0, 0, G_OPTION_ARG_NONE, &list_commands, N_("Print a list of all built-in commands, and exit"), NULL },
487 	{ NULL }
488 	};
489 	GtkWidget	*box;
490 	GOptionContext	*context;
491 	GError		*err = NULL;
492 
493 #if defined ENABLE_NLS
494 	setlocale(LC_ALL, "");
495 	bindtextdomain(PACKAGE, LOCALEDIR);
496 	textdomain(PACKAGE);
497 	/* Translate help texts for command line options. */
498 	for(i = 0; option_entries[i].long_name != NULL; i++)
499 	{
500 		option_entries[i].description = _(option_entries[i].description);
501 		option_entries[i].arg_description = _(option_entries[i].arg_description);
502 	}
503 #endif
504 
505 	/* Use glib's option parser, for consistency, features and just less code in general. Nice. */
506 	context = g_option_context_new(_("- a graphical file manager using GTK+"));
507 	g_option_context_add_main_entries(context, option_entries, PACKAGE);
508 	g_option_context_add_group(context, gtk_get_option_group(TRUE));	/* Import GTK+'s options. */
509 	if(!g_option_context_parse(context, &argc, &argv, &err))
510 	{
511 		g_print(_("Failed to parse command line options: %s\n"), err->message);
512 		return EXIT_FAILURE;
513 	}
514 
515 	/* Must be run before application-level options parsing. */
516 	gtk_init(&argc, &argv);
517 
518 	min.vfs.vfs = g_vfs_get_default();
519 
520 	if(show_version)
521 	{
522 		puts(VERSION);
523 		return EXIT_SUCCESS;
524 	}
525 #if defined ENABLE_NLS
526 	if(show_locale_info)
527 	{
528 		const gchar	*enc, *cset, **fenc;
529 
530 		printf("PACKAGE=\"%s\"\n", PACKAGE);
531 		printf("LOCALEDIR=\"%s\"\n", LOCALEDIR);
532 
533 		enc = g_getenv("G_FILENAME_ENCODING");
534 		if(enc == NULL)
535 			enc = "(not set)";
536 		printf("G_FILENAME_ENCODING=\"%s\"\n", enc);
537 
538 		g_get_filename_charsets(&fenc);
539 		printf("Filename encoding: \"%s\"\n", (fenc != NULL && *fenc != NULL) ? *fenc : "(unknown)");
540 
541 		g_get_charset(&cset);
542 		if(cset == NULL)
543 			cset = "(unknown)";
544 		printf("Native charset: \"%s\"\n", cset);
545 		return EXIT_SUCCESS;
546 	}
547 #endif
548 
549 	if(geteuid() == 0 && !root_ok)
550 	{
551 		fprintf(stderr, _("%s: To allow running as root, use the '--root-ok' option\n"), argv[0]);
552 		return EXIT_FAILURE;
553 	}
554 
555 	dpf_initialize();
556 	mnu_initialize();
557 
558 	min.cfg.flags = 0;
559 	dpf_init_defaults(&min.cfg);
560 	csq_init_commands(&min);
561 
562 	if(list_commands)
563 	{
564 		do_list_commands(&min);
565 		return EXIT_SUCCESS;
566 	}
567 
568 	min.cfg.menus = mnu_menuinfo_new_default(&min);
569 	btn_buttoninfo_new_default(&min, &min.cfg.buttons);
570 	min.cfg.buttonlayout = btl_buttonlayout_new();
571 	init_paths(&min.cfg);
572 	min.cfg.style = stl_styleinfo_default();
573 	typ_init(&min.cfg);
574 	min.cfg.wininfo	 = win_wininfo_new_default(&min);
575 	min.cfg.ctrlinfo = ctrl_new_default(&min);
576 	min.cfg.errors.display = ERR_DISPLAY_STATUSBAR;
577 	cng_initialize(&min.cfg.nag);
578 	min.cfg.dir_filter = dir_filter;
579 	chd_initialize(&min);
580 	min.que = que_initialize();
581 
582 	min.ico = ico_initialize(&min);
583 
584 	min.cfg.flags = 0;
585 	if(!no_rc)
586 		cfg_load_config(&min);
587 	if(!usr_init())
588 		g_warning(_("Couldn't initialize userinfo module - username resolving won't work"));
589 
590 	if((box = build_gui(&min)) != NULL)
591 	{
592 		dp_initialize(min.gui->pane, sizeof min.gui->pane / sizeof *min.gui->pane);
593 
594 		if(!no_gtk_rc)
595 			load_gtk_rc(&min);
596 
597 		fam_initialize(&min);
598 
599 		dlg_main_window_set(GTK_WINDOW(min.gui->window));
600 		dlg_position_set(min.cfg.dialogs.pos);
601 #if 0
602 		g_signal_connect(G_OBJECT(min.gui->window), "key_press_event", G_CALLBACK(evt_key_press), &min);
603 #endif
604 		init_cmd_options(&min.cfg);
605 
606 		gtk_container_add(GTK_CONTAINER(min.gui->window), box);
607 
608 		if(!no_dir_history)
609 			dph_history_load(&min, min.gui->pane, sizeof min.gui->pane / sizeof *min.gui->pane);
610 
611 		gtk_window_set_resizable(GTK_WINDOW(min.gui->window), TRUE);
612 
613 		win_window_show(min.gui->window);
614 
615 		err_clear(&min);
616 		if(enter_initial_dirs(&min, dir_left, dir_right))
617 		{
618 			if(min.gui->top != NULL)
619 			{
620 				gchar	buf[128];
621 
622 				g_snprintf(buf, sizeof buf, _("gentoo v%s by Emil Brink <emil@obsession.se>"), VERSION);
623 				gtk_label_set_text(GTK_LABEL(min.gui->top), buf);
624 			}
625 		}
626 		else
627 			err_show(&min);
628 
629 		ndl_dialog_sync_new_wait(&min, "gio-warning", _("Development Version Warning"), _("This version of gentoo is considered somewhat new and untested.\nThere have been major changes to almost all parts of the program since the previous version.\nPlease be a bit careful, and make sure you report any problem to the author. Thanks."));
630 
631 		g_idle_add(evt_idle_run, &min);
632 		gtk_main();
633 	}
634 	chd_kill_children();
635 	fam_shutdown(&min);
636 
637 	return EXIT_SUCCESS;
638 }
639