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)
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
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
26 #include <sys/types.h>
35 #ifdef HAVE_GETOPT_LONG
40 #include "collection.h"
46 #include "gui_support.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 */
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
;
80 char *home_dir
, *app_dir
;
82 /* Static prototypes */
83 static void show_features(void);
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 ""
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")
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'},
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)
171 child_died_flag
= FALSE
;
173 /* Find out which children exited and allow them to die */
178 child
= waitpid(-1, &status
, WNOHANG
);
180 if (child
== 0 || child
== -1)
183 cb
= g_hash_table_lookup(death_callbacks
,
184 GINT_TO_POINTER(child
));
187 cb
->callback(cb
->data
);
188 g_hash_table_remove(death_callbacks
,
189 GINT_TO_POINTER(child
));
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
)
203 read(source
, buf
, BUFLEN
);
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
)
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
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"));
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();
251 /* Don't pass it on to our child processes... */
256 death_callbacks
= g_hash_table_new(NULL
, NULL
);
264 ngroups
= getgroups(0, NULL
);
267 else if (ngroups
> 0)
269 supplemental_groups
= g_malloc(sizeof(gid_t
) * ngroups
);
270 getgroups(ngroups
, supplemental_groups
);
276 #ifdef HAVE_GETOPT_LONG
278 c
= getopt_long(argc
, argv
, SHORT_OPS
,
279 long_opts
, &long_index
);
281 c
= getopt(argc
, argv
, SHORT_OPS
);
285 break; /* No more options */
293 override_redirect
= TRUE
;
296 fprintf(stderr
, "ROX-Filer %s\n", VERSION
);
297 fprintf(stderr
, _(COPYING
));
301 fprintf(stderr
, _(HELP
));
302 fprintf(stderr
, _(SHORT_ONLY_WARNING
));
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
);
315 tmp
= g_strdup(VALUE
);
316 slash
= strrchr(tmp
, '/');
321 g_string_append(to_open
, "<s>");
322 g_string_append(to_open
, dir
);
328 g_string_append(to_open
, slash
);
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
);
353 gtk_init(&argc
, &argv
);
355 if (euid
== 0 || show_user
)
356 show_user_message
= g_strdup_printf( _("Running as user '%s'"),
362 tmp
= pathdup(argv
[i
++]);
364 g_string_append(to_open
, "<f>");
365 g_string_append(to_open
, tmp
);
370 if (to_open
->len
== 0)
374 dir
= g_get_current_dir();
375 g_string_sprintf(to_open
, "<d>%s", dir
);
379 option_add_int("dnd_no_hostnames", 1, NULL
);
382 if (remote_init(to_open
, new_copy
))
383 return EXIT_SUCCESS
; /* Already running */
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
);
424 sigaction(SIGPIPE
, &act
, NULL
);
426 run_list(to_open
->str
);
427 g_string_free(to_open
, TRUE
);
429 if (number_of_windows
> 0)
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"),
442 _("No (couldn't find a valid libvfs)")
447 /* Register a function to be called when process number 'child' dies. */
448 void on_child_death(gint child
, CallbackFn callback
, gpointer data
)
452 g_return_if_fail(callback
!= NULL
);
454 cb
= g_new(Callback
, 1);
456 cb
->callback
= callback
;
459 g_hash_table_insert(death_callbacks
, GINT_TO_POINTER(child
), cb
);