r496: Many internal changes to the Options system.
[rox-filer.git] / ROX-Filer / src / main.c
blob76ca1d7dd419c5d814294de478ee8a15ae60ed19
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <signal.h>
28 #include <string.h>
29 #include <sys/wait.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <pwd.h>
33 #include <grp.h>
35 #ifdef HAVE_GETOPT_LONG
36 # include <getopt.h>
37 #endif
39 #include <gtk/gtk.h>
40 #include "collection.h"
42 #include "global.h"
44 #include "main.h"
45 #include "support.h"
46 #include "gui_support.h"
47 #include "filer.h"
48 #include "display.h"
49 #include "mount.h"
50 #include "menu.h"
51 #include "dnd.h"
52 #include "options.h"
53 #include "choices.h"
54 #include "type.h"
55 #include "pixmaps.h"
56 #include "dir.h"
57 #include "action.h"
58 #include "i18n.h"
59 #include "remote.h"
60 #include "pinboard.h"
61 #include "panel.h"
62 #include "run.h"
63 #include "toolbar.h"
64 #include "bind.h"
65 #include "icon.h"
66 #include "appmenu.h"
68 int number_of_windows = 0; /* Quit when this reaches 0 again... */
69 static int to_wakeup_pipe = -1; /* Write here to get noticed */
71 uid_t euid;
72 gid_t egid;
73 int ngroups; /* Number of supplemental groups */
74 gid_t *supplemental_groups = NULL;
76 /* Message to display at the top of each filer window */
77 guchar *show_user_message = NULL;
79 int home_dir_len;
80 char *home_dir, *app_dir;
82 /* Static prototypes */
83 static void show_features(void);
86 #define COPYING \
87 N_("Copyright (C) 2000 Thomas Leonard.\n" \
88 "ROX-Filer comes with ABSOLUTELY NO WARRANTY,\n" \
89 "to the extent permitted by law.\n" \
90 "You may redistribute copies of ROX-Filer\n" \
91 "under the terms of the GNU General Public License.\n" \
92 "For more information about these matters, " \
93 "see the file named COPYING.\n")
95 #ifdef HAVE_GETOPT_LONG
96 # define USAGE N_("Try `ROX-Filer/AppRun --help' for more information.\n")
97 # define SHORT_ONLY_WARNING ""
98 #else
99 # define USAGE N_("Try `ROX-Filer/AppRun -h' for more information.\n")
100 # define SHORT_ONLY_WARNING \
101 _("NOTE: Your system does not support long options - \n" \
102 "you must use the short versions instead.\n\n")
103 #endif
105 #define HELP N_("Usage: ROX-Filer/AppRun [OPTION]... [FILE]...\n" \
106 "Open each directory or file listed, or the current working\n" \
107 "directory if no arguments are given.\n\n" \
108 " -b, --bottom=PANEL open PAN as a bottom-edge panel\n" \
109 " -d, --dir=DIR open DIR as directory (not application)\n" \
110 " -h, --help display this help and exit\n" \
111 " -l, --left=PANEL open PAN as a left-edge panel\n" \
112 " -n, --new start a new filer, even if already running\n" \
113 " -o, --override override window manager control of panels\n" \
114 " -p, --pinboard=PIN use pinboard PIN as the pinboard\n" \
115 " -r, --right=PANEL open PAN as a right-edge panel\n" \
116 " -s, --show=FILE open a directory showing FILE\n" \
117 " -t, --top=PANEL open PANEL as a top-edge panel\n" \
118 " -u, --user show user name in each window \n" \
119 " -x, --examine=FILE FILE has changed - re-examine it\n" \
120 " -v, --version display the version information and exit\n" \
121 "\nThe latest version can be found at:\n" \
122 "\thttp://rox.sourceforge.net\n" \
123 "\nReport bugs to <tal197@users.sourceforge.net>.\n")
125 #define SHORT_OPS "d:t:b:l:r:op:s:hvnux:"
127 #ifdef HAVE_GETOPT_LONG
128 static struct option long_opts[] =
130 {"dir", 1, NULL, 'd'},
131 {"top", 1, NULL, 't'},
132 {"bottom", 1, NULL, 'b'},
133 {"left", 1, NULL, 'l'},
134 {"override", 0, NULL, 'o'},
135 {"pinboard", 1, NULL, 'p'},
136 {"right", 1, NULL, 'r'},
137 {"help", 0, NULL, 'h'},
138 {"version", 0, NULL, 'v'},
139 {"user", 0, NULL, 'u'},
140 {"new", 0, NULL, 'n'},
141 {"show", 1, NULL, 's'},
142 {"examine", 1, NULL, 'x'},
143 {NULL, 0, NULL, 0},
145 #endif
147 /* Take control of panels away from WM? */
148 gboolean override_redirect = FALSE;
150 /* Always start a new filer, even if one seems to be already running */
151 gboolean new_copy = FALSE;
153 /* Maps child PIDs to Callback pointers */
154 static GHashTable *death_callbacks = NULL;
155 static gboolean child_died_flag = FALSE;
157 /* This is called as a signal handler; simply ensures that
158 * child_died_callback() will get called later.
160 static void child_died(int signum)
162 child_died_flag = TRUE;
163 write(to_wakeup_pipe, "\0", 1); /* Wake up! */
166 static void child_died_callback(void)
168 int status;
169 gint child;
171 child_died_flag = FALSE;
173 /* Find out which children exited and allow them to die */
176 Callback *cb;
178 child = waitpid(-1, &status, WNOHANG);
180 if (child == 0 || child == -1)
181 return;
183 cb = g_hash_table_lookup(death_callbacks,
184 GINT_TO_POINTER(child));
185 if (cb)
187 cb->callback(cb->data);
188 g_hash_table_remove(death_callbacks,
189 GINT_TO_POINTER(child));
192 } while (1);
195 #define BUFLEN 40
196 /* When data is written to_wakeup_pipe, this gets called from the event
197 * loop some time later. Useful for getting out of signal handlers, etc.
199 void wake_up_cb(gpointer data, gint source, GdkInputCondition condition)
201 char buf[BUFLEN];
203 read(source, buf, BUFLEN);
205 if (child_died_flag)
206 child_died_callback();
209 /* The value that goes with an option */
210 #define VALUE (*optarg == '=' ? optarg + 1 : optarg)
212 int main(int argc, char **argv)
214 int wakeup_pipe[2];
215 int i;
216 struct sigaction act;
217 guchar *tmp, *dir, *slash;
218 gboolean show_user = FALSE;
220 /* This is a list of \0 separated strings. Each string starts with a
221 * character indicating what kind of operation to perform:
223 * fFILE open this file (or directory)
224 * dDIR open DIR as a directory (not as an application)
225 * eDIR shift-open this file (edit)
226 * pPIN display this pinboard
227 * [lrtb]PANEL open PANEL as a {left, right, top, bottom} panel
228 * sFILE open a directory to show FILE
229 * xFILE eXamine FILE
231 GString *to_open;
233 to_open = g_string_new(NULL);
235 home_dir = g_get_home_dir();
236 home_dir_len = strlen(home_dir);
237 app_dir = g_strdup(getenv("APP_DIR"));
239 choices_init();
240 i18n_init();
242 if (!app_dir)
244 g_warning("APP_DIR environment variable was unset!\n"
245 "Use the AppRun script to invoke ROX-Filer...\n");
246 app_dir = g_get_current_dir();
248 #ifdef HAVE_UNSETENV
249 else
251 /* Don't pass it on to our child processes... */
252 unsetenv("APP_DIR");
254 #endif
256 death_callbacks = g_hash_table_new(NULL, NULL);
258 #ifdef HAVE_LIBVFS
259 mc_vfs_init();
260 #endif
262 euid = geteuid();
263 egid = getegid();
264 ngroups = getgroups(0, NULL);
265 if (ngroups < 0)
266 ngroups = 0;
267 else if (ngroups > 0)
269 supplemental_groups = g_malloc(sizeof(gid_t) * ngroups);
270 getgroups(ngroups, supplemental_groups);
273 while (1)
275 int c;
276 #ifdef HAVE_GETOPT_LONG
277 int long_index;
278 c = getopt_long(argc, argv, SHORT_OPS,
279 long_opts, &long_index);
280 #else
281 c = getopt(argc, argv, SHORT_OPS);
282 #endif
284 if (c == EOF)
285 break; /* No more options */
287 switch (c)
289 case 'n':
290 new_copy = TRUE;
291 break;
292 case 'o':
293 override_redirect = TRUE;
294 break;
295 case 'v':
296 fprintf(stderr, "ROX-Filer %s\n", VERSION);
297 fprintf(stderr, _(COPYING));
298 show_features();
299 return EXIT_SUCCESS;
300 case 'h':
301 fprintf(stderr, _(HELP));
302 fprintf(stderr, _(SHORT_ONLY_WARNING));
303 return EXIT_SUCCESS;
304 case 'd':
305 case 'x':
306 /* Argument is a path */
307 tmp = pathdup(VALUE);
308 g_string_append_c(to_open, '<');
309 g_string_append_c(to_open, c);
310 g_string_append_c(to_open, '>');
311 g_string_append(to_open, tmp);
312 g_free(tmp);
313 break;
314 case 's':
315 tmp = g_strdup(VALUE);
316 slash = strrchr(tmp, '/');
317 if (slash)
318 *slash = '\0';
320 dir = pathdup(tmp);
321 g_string_append(to_open, "<s>");
322 g_string_append(to_open, dir);
323 g_free(dir);
325 if (slash)
327 *slash = '/';
328 g_string_append(to_open, slash);
331 g_free(tmp);
332 break;
333 case 'l':
334 case 'r':
335 case 't':
336 case 'b':
337 case 'p':
338 /* Argument is a leaf (or starts with /) */
339 g_string_append_c(to_open, '<');
340 g_string_append_c(to_open, c);
341 g_string_append_c(to_open, '>');
342 g_string_append(to_open, VALUE);
343 break;
344 case 'u':
345 show_user = TRUE;
346 break;
347 default:
348 printf(_(USAGE));
349 return EXIT_FAILURE;
353 gtk_init(&argc, &argv);
355 if (euid == 0 || show_user)
356 show_user_message = g_strdup_printf( _("Running as user '%s'"),
357 user_name(euid));
359 i = optind;
360 while (i < argc)
362 tmp = pathdup(argv[i++]);
364 g_string_append(to_open, "<f>");
365 g_string_append(to_open, tmp);
367 g_free(tmp);
370 if (to_open->len == 0)
372 guchar *dir;
374 dir = g_get_current_dir();
375 g_string_sprintf(to_open, "<d>%s", dir);
376 g_free(dir);
379 option_add_int("dnd_no_hostnames", 1, NULL);
381 gui_support_init();
382 if (remote_init(to_open, new_copy))
383 return EXIT_SUCCESS; /* Already running */
384 pixmaps_init();
386 dnd_init();
387 bind_init();
388 dir_init();
389 menu_init();
390 filer_init();
391 toolbar_init();
392 display_init();
393 mount_init();
394 options_init();
395 type_init();
396 action_init();
397 appmenu_init();
399 icon_init();
400 pinboard_init();
401 panel_init();
403 options_load();
405 pipe(wakeup_pipe);
406 close_on_exec(wakeup_pipe[0], TRUE);
407 close_on_exec(wakeup_pipe[1], TRUE);
408 gdk_input_add(wakeup_pipe[0], GDK_INPUT_READ, wake_up_cb, NULL);
409 to_wakeup_pipe = wakeup_pipe[1];
411 /* If the pipe is full then we're going to get woken up anyway... */
412 set_blocking(to_wakeup_pipe, FALSE);
414 /* Let child processes die */
415 act.sa_handler = child_died;
416 sigemptyset(&act.sa_mask);
417 act.sa_flags = SA_NOCLDSTOP;
418 sigaction(SIGCHLD, &act, NULL);
420 /* Ignore SIGPIPE - check for EPIPE errors instead */
421 act.sa_handler = SIG_IGN;
422 sigemptyset(&act.sa_mask);
423 act.sa_flags = 0;
424 sigaction(SIGPIPE, &act, NULL);
426 run_list(to_open->str);
427 g_string_free(to_open, TRUE);
429 if (number_of_windows > 0)
430 gtk_main();
432 return EXIT_SUCCESS;
435 static void show_features(void)
437 g_printerr("\n-- %s --\n\n", _("features set at compile time"));
438 g_printerr("%s... %s\n", _("VFS support"),
439 #ifdef HAVE_LIBVFS
440 _("Yes")
441 #else
442 _("No (couldn't find a valid libvfs)")
443 #endif
447 /* Register a function to be called when process number 'child' dies. */
448 void on_child_death(gint child, CallbackFn callback, gpointer data)
450 Callback *cb;
452 g_return_if_fail(callback != NULL);
454 cb = g_new(Callback, 1);
456 cb->callback = callback;
457 cb->data = data;
459 g_hash_table_insert(death_callbacks, GINT_TO_POINTER(child), cb);