Fix #3095516 - PCManFM does not write preferences from main menu.
[lxde/pcmanfm.git] / src / pcmanfm.c
CommitLineData
b6e3c554
HJYP
1/*
2 * pcmanfm.c
d3451adb 3 *
7b7d923f 4 * Copyright 2009 - 2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
d3451adb 5 *
b6e3c554
HJYP
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
d3451adb 10 *
b6e3c554
HJYP
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
d3451adb 15 *
b6e3c554
HJYP
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
08e70fea 22#ifdef HAVE_CONFIG_H
b6e3c554 23#include <config.h>
08e70fea
HJYP
24#endif
25
b6e3c554 26#include <gtk/gtk.h>
1098cfe7 27#include <gdk/gdkx.h>
b6e3c554 28#include <stdio.h>
f8f2bfad
HJYP
29#include <glib/gi18n.h>
30
31#include <stdlib.h>
32#include <string.h>
33/* socket is used to keep single instance */
34#include <sys/types.h>
f8f2bfad
HJYP
35#include <signal.h>
36#include <unistd.h> /* for getcwd */
b6e3c554 37
20c0bc9a 38#include <libfm/fm-gtk.h>
4d55886c 39#include "app-config.h"
b6e3c554 40#include "main-win.h"
8505a8ef 41#include "desktop.h"
71f82759 42#include "volume-manager.h"
c5fccf1d 43#include "pref.h"
df6826e0 44#include "pcmanfm.h"
cc448d7f 45#include "single-inst.h"
f8f2bfad 46
94435bc3 47static int signal_pipe[2] = {-1, -1};
f8f2bfad 48gboolean daemon_mode = FALSE;
3d141640 49static guint save_config_idle = 0;
f8f2bfad 50
f8f2bfad 51static char** files_to_open = NULL;
cc448d7f 52static int n_files_to_open = 0;
f970e846 53static char* profile = NULL;
f8f2bfad
HJYP
54static gboolean no_desktop = FALSE;
55static gboolean show_desktop = FALSE;
56static gboolean desktop_off = FALSE;
57static gboolean desktop_running = FALSE;
107cdb22 58/* static gboolean new_tab = FALSE; */
f8f2bfad
HJYP
59static int show_pref = 0;
60static gboolean desktop_pref = FALSE;
61static char* set_wallpaper = NULL;
107cdb22 62static char* wallpaper_mode = NULL;
f8f2bfad 63static gboolean find_files = FALSE;
5b890032 64static char* ipc_cwd = NULL;
f8f2bfad
HJYP
65
66static int n_pcmanfm_ref = 0;
67
68static GOptionEntry opt_entries[] =
8505a8ef 69{
cda6259f 70 /* options only acceptable by first pcmanfm instance. These options are not passed through IPC */
a48f9fc8 71 { "profile", 'p', 0, G_OPTION_ARG_STRING, &profile, N_("Name of configuration profile"), "<profile name>" },
cda6259f
HJYP
72 { "daemon-mode", 'd', 0, G_OPTION_ARG_NONE, &daemon_mode, N_("Run PCManFM as a daemon"), NULL },
73 { "no-desktop", '\0', 0, G_OPTION_ARG_NONE, &no_desktop, N_("No function. Just to be compatible with nautilus"), NULL },
74
75 /* options that are acceptable for every instance of pcmanfm and will be passed through IPC. */
f8f2bfad
HJYP
76 { "desktop", '\0', 0, G_OPTION_ARG_NONE, &show_desktop, N_("Launch desktop manager"), NULL },
77 { "desktop-off", '\0', 0, G_OPTION_ARG_NONE, &desktop_off, N_("Turn off desktop manager if it's running"), NULL },
f8f2bfad 78 { "desktop-pref", '\0', 0, G_OPTION_ARG_NONE, &desktop_pref, N_("Open desktop preference dialog"), NULL },
f970e846 79 { "set-wallpaper", 'w', 0, G_OPTION_ARG_FILENAME, &set_wallpaper, N_("Set desktop wallpaper"), N_("<image file>") },
107cdb22 80 { "wallpaper-mode", '\0', 0, G_OPTION_ARG_STRING, &wallpaper_mode, N_("Set mode of desktop wallpaper. <mode>=(color|stretch|fit|center|tile)"), N_("<mode>") },
a48f9fc8 81 { "show-pref", '\0', 0, G_OPTION_ARG_INT, &show_pref, N_("Open preference dialog. 'n' is number of the page you want to show (1, 2, 3...)."), "n" },
1d071341 82 /* { "find-files", 'f', 0, G_OPTION_ARG_NONE, &find_files, N_("Open Find Files utility"), NULL }, */
f8f2bfad
HJYP
83 {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &files_to_open, NULL, N_("[FILE1, FILE2,...]")},
84 { NULL }
85};
86
107cdb22
HJYP
87static const char* valid_wallpaper_modes[] = {"color", "stretch", "fit", "center", "tile"};
88
f8f2bfad 89static gboolean pcmanfm_run();
b6e3c554 90
94435bc3
HJYP
91/* it's not safe to call gtk+ functions in unix signal handler
92 * since the process is interrupted here and the state of gtk+ is unpredictable. */
93static void unix_signal_handler(int sig_num)
94{
95 /* postpond the signal handling by using a pipe */
96 write(signal_pipe[1], &sig_num, sizeof(sig_num));
97}
98
99static gboolean on_unix_signal(GIOChannel* ch, GIOCondition cond, gpointer user_data)
100{
101 int sig_num;
102 g_io_channel_read_chars(ch, &sig_num, 1, NULL, NULL);
103 switch(sig_num)
104 {
105 case SIGTERM:
106 default:
107 gtk_main_quit();
108 }
109 return TRUE;
110}
111
1098cfe7 112static void single_inst_cb(const char* cwd, int screen_num)
cc448d7f 113{
cda6259f
HJYP
114 g_free(ipc_cwd);
115 ipc_cwd = g_strdup(cwd);
116
117 if(files_to_open)
cc448d7f 118 {
cda6259f 119 int i;
6033d802 120 n_files_to_open = g_strv_length(files_to_open);
cda6259f
HJYP
121 /* canonicalize filename if needed. */
122 for(i = 0; i < n_files_to_open; ++i)
cc448d7f 123 {
cda6259f
HJYP
124 char* file = files_to_open[i];
125 char* scheme = g_uri_parse_scheme(file);
6033d802 126 g_debug("file: %s", file);
cda6259f 127 if(scheme) /* a valid URI */
cc448d7f 128 {
cda6259f
HJYP
129 /* FIXME: should we canonicalize URIs? and how about file:///? */
130 g_free(scheme);
131 }
132 else /* a file path */
133 {
134 files_to_open[i] = fm_canonicalize_filename(file, cwd);
135 g_free(file);
cc448d7f 136 }
cc448d7f 137 }
cc448d7f 138 }
cda6259f 139 pcmanfm_run();
cc448d7f
HJYP
140}
141
b6e3c554
HJYP
142int main(int argc, char** argv)
143{
4d55886c 144 FmConfig* config;
f8f2bfad 145 GError* err = NULL;
cda6259f 146
f8f2bfad
HJYP
147#ifdef ENABLE_NLS
148 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
149 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
150 textdomain ( GETTEXT_PACKAGE );
151#endif
152
153 /* initialize GTK+ and parse the command line arguments */
154 if(G_UNLIKELY(!gtk_init_with_args(&argc, &argv, "", opt_entries, GETTEXT_PACKAGE, &err)))
155 {
156 g_printf("%s\n", err->message);
c5fccf1d 157 g_error_free(err);
f8f2bfad
HJYP
158 return 1;
159 }
160
cc448d7f 161 /* ensure that there is only one instance of pcmanfm. */
1098cfe7 162 switch(single_inst_init("pcmanfm", single_inst_cb, opt_entries + 3, gdk_x11_get_default_screen()))
cc448d7f
HJYP
163 {
164 case SINGLE_INST_CLIENT: /* we're not the first instance. */
cda6259f 165 single_inst_finalize();
cc448d7f
HJYP
166 gdk_notify_startup_complete();
167 return 0;
168 case SINGLE_INST_ERROR: /* error happened. */
169 single_inst_finalize();
170 return 1;
171 }
f8f2bfad 172
94435bc3
HJYP
173 if(pipe(signal_pipe) == 0)
174 {
175 GIOChannel* ch = g_io_channel_unix_new(signal_pipe[0]);
176 g_io_add_watch(ch, G_IO_IN|G_IO_PRI, (GIOFunc)on_unix_signal, NULL);
177 g_io_channel_unref(ch);
178
179 /* intercept signals */
ae2947d9
HJYP
180 // signal( SIGPIPE, SIG_IGN );
181 signal( SIGHUP, unix_signal_handler );
94435bc3 182 signal( SIGTERM, unix_signal_handler );
ae2947d9
HJYP
183 signal( SIGPOLL, unix_signal_handler );
184 signal( SIGHUP, unix_signal_handler );
94435bc3 185 }
b6e3c554 186
f970e846 187 config = fm_app_config_new(); /* this automatically load libfm config file. */
17d12cc9 188
f970e846 189 /* load pcmanfm-specific config file */
17d12cc9 190 fm_app_config_load_from_profile(FM_APP_CONFIG(config), profile);
f970e846 191
94435bc3 192 fm_gtk_init(config);
f8f2bfad
HJYP
193 /* the main part */
194 if(pcmanfm_run())
195 {
71f82759 196 fm_volume_manager_init();
94435bc3 197 gtk_main();
f8f2bfad
HJYP
198 if(desktop_running)
199 fm_desktop_manager_finalize();
3d141640
HJYP
200
201 pcmanfm_save_config(TRUE);
202 if(save_config_idle)
203 {
204 g_source_remove(save_config_idle);
205 save_config_idle = 0;
206 }
71f82759 207 fm_volume_manager_finalize();
f8f2bfad 208 }
cc448d7f
HJYP
209
210 single_inst_finalize();
f8f2bfad 211 fm_gtk_finalize();
ae2947d9 212
f8f2bfad 213 g_object_unref(config);
94435bc3 214 return 0;
f8f2bfad
HJYP
215}
216
ee12f37e
HJYP
217static FmJobErrorAction on_file_info_job_error(FmFileInfoJob* job, GError* err, FmJobErrorSeverity severity, gpointer user_data)
218{
16a17d6b 219 if(err->domain == G_IO_ERROR)
ee12f37e 220 {
16a17d6b
HJYP
221 if(err->code == G_IO_ERROR_NOT_MOUNTED)
222 {
223 if(fm_mount_path(NULL, fm_file_info_job_get_current(job), TRUE))
224 return FM_JOB_RETRY;
225 }
226 else if(err->code == G_IO_ERROR_FAILED_HANDLED)
227 return FM_JOB_CONTINUE;
ee12f37e 228 }
16a17d6b 229 fm_show_error(NULL, err->message);
ee12f37e
HJYP
230 return FM_JOB_CONTINUE;
231}
f8f2bfad
HJYP
232
233gboolean pcmanfm_run()
234{
235 gboolean ret = TRUE;
f8f2bfad 236
f8f2bfad
HJYP
237 if(!files_to_open)
238 {
239 /* Launch desktop manager */
240 if(show_desktop)
241 {
242 if(!desktop_running)
243 {
244 fm_desktop_manager_init();
245 desktop_running = TRUE;
246 }
19abb8bf 247 show_desktop = FALSE;
f8f2bfad
HJYP
248 return TRUE;
249 }
250 else if(desktop_off)
251 {
252 if(desktop_running)
253 {
254 desktop_running = FALSE;
255 fm_desktop_manager_finalize();
256 }
19abb8bf 257 desktop_off = FALSE;
f8f2bfad
HJYP
258 return FALSE;
259 }
260 else if(show_pref > 0)
261 {
c5fccf1d
HJYP
262 fm_edit_preference(NULL, show_pref - 1);
263 show_pref = 0;
f8f2bfad
HJYP
264 return TRUE;
265 }
266 else if(desktop_pref)
267 {
c5fccf1d 268 fm_desktop_preference();
19abb8bf 269 desktop_pref = FALSE;
f8f2bfad
HJYP
270 return TRUE;
271 }
107cdb22 272 else
f8f2bfad 273 {
107cdb22
HJYP
274 gboolean need_to_exit = (wallpaper_mode || set_wallpaper);
275 gboolean wallpaper_changed = FALSE;
276 if(set_wallpaper) /* a new wallpaper is assigned */
277 {
278 /* g_debug("\'%s\'", set_wallpaper); */
279 /* Make sure this is a support image file. */
280 if(gdk_pixbuf_get_file_info(set_wallpaper, NULL, NULL))
281 {
282 if(app_config->wallpaper)
283 g_free(app_config->wallpaper);
284 app_config->wallpaper = set_wallpaper;
285 set_wallpaper = NULL;
286 if(! wallpaper_mode) /* if wallpaper mode is not specified */
287 {
288 /* do not use solid color mode; otherwise wallpaper won't be shown. */
289 if(app_config->wallpaper_mode == FM_WP_COLOR)
290 app_config->wallpaper_mode = FM_WP_FIT;
291 }
292 wallpaper_changed = TRUE;
293 }
294 }
295
296 if(wallpaper_mode)
297 {
298 int i = 0;
299 for(i = 0; i < G_N_ELEMENTS(valid_wallpaper_modes); ++i)
300 {
301 if(strcmp(valid_wallpaper_modes[i], wallpaper_mode) == 0)
302 {
303 if(i != app_config->wallpaper_mode)
304 {
305 app_config->wallpaper_mode = i;
306 wallpaper_changed = TRUE;
307 }
308 break;
309 }
310 }
311 g_free(wallpaper_mode);
312 wallpaper_mode = NULL;
313 }
314
315 if(wallpaper_changed)
f8f2bfad 316 {
4b3e1fe9 317 fm_config_emit_changed(FM_CONFIG(app_config), "wallpaper");
17d12cc9 318 fm_app_config_save_profile(app_config, profile);
f8f2bfad 319 }
107cdb22
HJYP
320
321 if(need_to_exit)
322 return FALSE;
f8f2bfad
HJYP
323 }
324 }
325
326 if(G_UNLIKELY(find_files))
327 {
328 /* FIXME: find files */
329 }
330 else
331 {
df6826e0
HJYP
332 if(files_to_open)
333 {
334 char** filename;
0a053694 335 FmJob* job = fm_file_info_job_new(NULL, 0);
cacf261a 336 FmPath* cwd = NULL;
d951adb8 337 GList* infos;
df6826e0
HJYP
338 for(filename=files_to_open; *filename; ++filename)
339 {
cacf261a
HJYP
340 FmPath* path;
341 if( **filename == '/' || strstr(*filename, ":/") ) /* absolute path or URI */
342 path = fm_path_new(*filename);
0d8dce1a
HJYP
343 else if( strcmp(*filename, "~") == 0 ) /* special case for home dir */
344 {
345 path = fm_path_get_home();
346 fm_main_win_add_win(NULL, path);
347 continue;
348 }
cacf261a
HJYP
349 else /* basename */
350 {
351 if(G_UNLIKELY(!cwd))
352 {
353 /* FIXME: This won't work if those filenames are passed via IPC since the receiving process has different cwd. */
354 char* cwd_str = g_get_current_dir();
355 cwd = fm_path_new(cwd_str);
356 g_free(cwd_str);
357 }
358 path = fm_path_new_relative(cwd, *filename);
359 }
4b3e1fe9 360 fm_file_info_job_add(FM_FILE_INFO_JOB(job), path);
df6826e0
HJYP
361 fm_path_unref(path);
362 }
cacf261a
HJYP
363 if(cwd)
364 fm_path_unref(cwd);
ee12f37e 365 g_signal_connect(job, "error", G_CALLBACK(on_file_info_job_error), NULL);
a48f9fc8 366 fm_job_run_sync_with_mainloop(job);
d951adb8
HJYP
367 infos = fm_list_peek_head_link(FM_FILE_INFO_JOB(job)->file_infos);
368 fm_launch_files_simple(NULL, NULL, infos, pcmanfm_open_folder, NULL);
df6826e0 369 g_object_unref(job);
cacf261a 370 ret = (n_pcmanfm_ref >= 1); /* if there is opened window, return true to run the main loop. */
2b46b521
HJYP
371
372 g_strfreev(files_to_open);
373 files_to_open = NULL;
df6826e0
HJYP
374 }
375 else
f8f2bfad 376 {
3d141640
HJYP
377 if(!daemon_mode)
378 {
379 FmPath* path;
380 char* cwd = ipc_cwd ? ipc_cwd : g_get_current_dir();
381 path = fm_path_new_for_path(cwd);
382 fm_main_win_add_win(NULL, path);
383 fm_path_unref(path);
384 g_free(cwd);
385 ipc_cwd = NULL;
386 }
f8f2bfad
HJYP
387 }
388 }
389 return ret;
390}
391
392/* After opening any window/dialog/tool, this should be called. */
393void pcmanfm_ref()
394{
395 ++n_pcmanfm_ref;
396 /* g_debug("ref: %d", n_pcmanfm_ref); */
397}
398
399/* After closing any window/dialog/tool, this should be called.
400 * If the last window is closed and we are not a deamon, pcmanfm will quit.
401 */
402void pcmanfm_unref()
403{
404 --n_pcmanfm_ref;
405 /* g_debug("unref: %d, daemon_mode=%d, desktop_running=%d", n_pcmanfm_ref, daemon_mode, desktop_running); */
406 if( 0 == n_pcmanfm_ref && !daemon_mode && !desktop_running )
407 gtk_main_quit();
b6e3c554 408}
df6826e0
HJYP
409
410gboolean pcmanfm_open_folder(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err)
411{
df6826e0
HJYP
412 GList* l = folder_infos;
413 for(; l; l=l->next)
414 {
415 FmFileInfo* fi = (FmFileInfo*)l->data;
416 fm_main_win_open_in_last_active(fi->path);
417 }
418 return TRUE;
419}
dcecce78 420
3d141640 421static gboolean on_save_config_idle(gpointer user_data)
dcecce78 422{
3d141640
HJYP
423 pcmanfm_save_config(TRUE);
424 save_config_idle = 0;
425 return FALSE;
426}
427
428void pcmanfm_save_config(gboolean immediate)
429{
430 if(immediate)
431 {
432 fm_config_save(fm_config, NULL);
433 fm_app_config_save_profile(app_config, profile);
434 }
435 else
436 {
437 /* install an idle handler to save the config file. */
438 if( 0 == save_config_idle)
439 save_config_idle = g_idle_add_full(G_PRIORITY_LOW, (GSourceFunc)on_save_config_idle, NULL, NULL);
440 }
dcecce78 441}
c56a211a
HJYP
442
443void pcmanfm_open_folder_in_terminal(GtkWindow* parent, FmPath* dir)
444{
445 GAppInfo* app;
c56a211a
HJYP
446 char** argv;
447 int argc;
448 if(!fm_config->terminal)
449 {
450 fm_show_error(parent, _("Terminal emulator is not set."));
451 fm_edit_preference(parent, PREF_ADVANCED);
452 return;
453 }
454 if(!g_shell_parse_argv(fm_config->terminal, &argc, &argv, NULL))
455 return;
456 app = g_app_info_create_from_commandline(argv[0], NULL, 0, NULL);
457 g_strfreev(argv);
458 if(app)
459 {
460 GError* err = NULL;
461 GAppLaunchContext* ctx = gdk_app_launch_context_new();
462 char* cwd_str;
463
464 if(fm_path_is_native(dir))
465 cwd_str = fm_path_to_str(dir);
466 else
467 {
468 GFile* gf = fm_path_to_gfile(dir);
469 cwd_str = g_file_get_path(gf);
470 g_object_unref(gf);
471 }
99476ede 472 gdk_app_launch_context_set_screen(GDK_APP_LAUNCH_CONTEXT(ctx), parent ? gtk_widget_get_screen(GTK_WIDGET(parent)) : gdk_screen_get_default());
c56a211a
HJYP
473 gdk_app_launch_context_set_timestamp(GDK_APP_LAUNCH_CONTEXT(ctx), gtk_get_current_event_time());
474 g_chdir(cwd_str); /* FIXME: currently we don't have better way for this. maybe a wrapper script? */
475 g_free(cwd_str);
476 if(!g_app_info_launch(app, NULL, ctx, &err))
477 {
478 fm_show_error(parent, err->message);
479 g_error_free(err);
480 }
481 g_object_unref(ctx);
482 g_object_unref(app);
483 }
484}
7cb06c27
HJYP
485
486/* FIXME: Need to load content of ~/Templates and list available templates in popup menus. */
487void pcmanfm_create_new(GtkWindow* parent, FmPath* cwd, const char* templ)
488{
489 GError* err = NULL;
490 FmPath* dest;
491 char* basename;
492_retry:
493 basename = fm_get_user_input(parent, _("Create New..."), _("Enter a name for the newly created file:"), _("New"));
494 if(!basename)
495 return;
496
497 dest = fm_path_new_child(cwd, basename);
498 g_free(basename);
499
500 if( templ == TEMPL_NAME_FOLDER )
501 {
502 GFile* gf = fm_path_to_gfile(dest);
503 if(!g_file_make_directory(gf, NULL, &err))
504 {
98daf506 505 if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
7cb06c27
HJYP
506 {
507 fm_path_unref(dest);
508 g_error_free(err);
509 g_object_unref(gf);
510 err = NULL;
511 goto _retry;
512 }
513 fm_show_error(parent, err->message);
514 g_error_free(err);
515 }
516
517 if(!err) /* select the newly created file */
518 {
519 /*FIXME: this doesn't work since the newly created file will
520 * only be shown after file-created event was fired on its
521 * folder's monitor and after FmFolder handles it in idle
522 * handler. So, we cannot select it since it's not yet in
523 * the folder model now. */
524 /* fm_folder_view_select_file_path(fv, dest); */
525 }
526 g_object_unref(gf);
527 }
528 else if( templ == TEMPL_NAME_BLANK )
529 {
530 GFile* gf = fm_path_to_gfile(dest);
531 GFileOutputStream* f = g_file_create(gf, G_FILE_CREATE_NONE, NULL, &err);
532 if(f)
533 {
534 g_output_stream_close(G_OUTPUT_STREAM(f), NULL, NULL);
535 g_object_unref(f);
536 }
537 else
538 {
98daf506 539 if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
7cb06c27
HJYP
540 {
541 fm_path_unref(dest);
542 g_error_free(err);
543 g_object_unref(gf);
544 err = NULL;
545 goto _retry;
546 }
547 fm_show_error(parent, err->message);
548 g_error_free(err);
549 }
550
551 if(!err) /* select the newly created file */
552 {
553 /*FIXME: this doesn't work since the newly created file will
554 * only be shown after file-created event was fired on its
555 * folder's monitor and after FmFolder handles it in idle
556 * handler. So, we cannot select it since it's not yet in
557 * the folder model now. */
558 /* fm_folder_view_select_file_path(fv, dest); */
559 }
560 g_object_unref(gf);
561 }
562 else /* templates in ~/Templates */
563 {
564 FmPath* dir = fm_path_new(g_get_user_special_dir(G_USER_DIRECTORY_TEMPLATES));
565 FmPath* template = fm_path_new_child(dir, templ);
9afd9e19 566 fm_copy_file(parent, template, cwd);
7cb06c27
HJYP
567 fm_path_unref(template);
568 }
569 fm_path_unref(dest);
570}
92f8ca49
HJYP
571
572char* pcmanfm_get_profile_dir(gboolean create)
573{
574 char* dir = g_build_filename(g_get_user_config_dir(), "pcmanfm", profile ? profile : "default", NULL);
575 if(create)
576 g_mkdir_with_parents(dir, 0700);
577 return dir;
578}