dfm.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. /* dfm - Simple GTK+ file manager
  2. *
  3. * Copyright (c) 2011 David Stenberg
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. *
  17. */
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <errno.h>
  21. #include <dirent.h>
  22. #include <pthread.h>
  23. #include <sys/stat.h>
  24. #include <gtk/gtk.h>
  25. #include <gdk/gdkkeysyms.h>
  26. #include "version.h"
  27. #define ARRSIZE(x) (sizeof(x) / sizeof(*x))
  28. #define CLEANMASK(mask) (mask & ~(GDK_MOD2_MASK))
  29. enum ListColumns {
  30. NAME_STR,
  31. PERMS_STR,
  32. SIZE_STR,
  33. MTIME_STR,
  34. IS_DIR
  35. };
  36. enum Movement {
  37. UP,
  38. DOWN,
  39. HOME,
  40. END,
  41. PAGEUP,
  42. PAGEDOWN
  43. };
  44. enum Preferences {
  45. DOTFILES
  46. };
  47. typedef struct {
  48. GtkWidget *win;
  49. GtkWidget *scroll;
  50. GtkWidget *tree;
  51. gchar *path;
  52. gboolean show_dot;
  53. time_t mtime;
  54. } FmWindow;
  55. typedef struct {
  56. gboolean b;
  57. gint i;
  58. void *v;
  59. } Arg;
  60. typedef struct {
  61. guint mod;
  62. guint key;
  63. void (*func)(FmWindow *fw, const Arg *arg);
  64. const Arg arg;
  65. } Key;
  66. /* functions */
  67. static void action(GtkWidget *w, GtkTreePath *p, GtkTreeViewColumn *c, FmWindow *fw);
  68. static void bookmark(FmWindow *fw, const Arg *arg);
  69. static gint compare(GtkTreeModel *m, GtkTreeIter *a, GtkTreeIter *b, gpointer p);
  70. static gchar *create_perm_str(mode_t mode);
  71. static gchar *create_size_str(size_t size);
  72. static gchar *create_time_str(const char *fmt, const struct tm *time);
  73. static FmWindow *createwin();
  74. static void destroywin(GtkWidget *w, FmWindow *fw);
  75. static void dir_exec(FmWindow *fw, const Arg *arg);
  76. static gint get_mtime(const gchar *path, time_t *time);
  77. static GList *get_selected(FmWindow *fw);
  78. static gboolean keypress(GtkWidget *w, GdkEventKey *ev, FmWindow *fw);
  79. static void make_dir(FmWindow *fw, const Arg *arg);
  80. static void move_cursor(FmWindow *fw, const Arg *arg);
  81. static void mv(FmWindow *fw, const Arg *arg);
  82. static void newwin(FmWindow *fw, const Arg *arg);
  83. static void open_directory(FmWindow *fw, const char *str);
  84. static void set_path(FmWindow *fw, const Arg *arg);
  85. static gchar *prev_dir(gchar *path);
  86. static void read_files(FmWindow *fw, DIR *dir);
  87. static void reload(FmWindow *fw);
  88. static void spawn(const gchar * const *argv, const gchar *path);
  89. static gchar *text_dialog(GtkWindow *p, const gchar *title, const gchar *text);
  90. static void text_dialog_enter(GtkWidget *w, GtkDialog *dialog);
  91. static void toggle_pref(FmWindow *fw, const Arg *arg);
  92. static void update(FmWindow *fw);
  93. static void *update_thread(void *v);
  94. static int valid_filename(const char *s, int show_dot);
  95. /* variables */
  96. static gboolean show_dotfiles = FALSE;
  97. static GList *windows = NULL;
  98. #include "config.h"
  99. /* enters the selected item if directory, otherwise
  100. * executes program with the file as argument */
  101. void
  102. action(GtkWidget *w, GtkTreePath *p, GtkTreeViewColumn *c, FmWindow *fw)
  103. {
  104. GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(fw->tree));
  105. GtkTreeIter iter;
  106. gchar *name;
  107. gchar fpath[PATH_MAX];
  108. gboolean is_dir;
  109. gtk_tree_model_get_iter(model, &iter, p);
  110. gtk_tree_model_get(model, &iter, NAME_STR, &name,
  111. IS_DIR, &is_dir, -1);
  112. chdir(fw->path);
  113. realpath(name, fpath);
  114. g_free(name);
  115. if (is_dir) { /* open directory */
  116. open_directory(fw, fpath);
  117. } else { /* execute program */
  118. spawn(filecmd, fpath);
  119. }
  120. }
  121. /* open a bookmark */
  122. void
  123. bookmark(FmWindow *fw, const Arg *arg)
  124. {
  125. if (arg->i >= 0 && arg->i < ARRSIZE(bookmarks))
  126. open_directory(fw, (char *)bookmarks[arg->i]);
  127. }
  128. /* compares two rows in the tree model */
  129. gint
  130. compare(GtkTreeModel *m, GtkTreeIter *a, GtkTreeIter *b, gpointer p)
  131. {
  132. gchar *name[2];
  133. gint isdir[2];
  134. gint ret;
  135. gtk_tree_model_get(m, a, NAME_STR, &name[0], IS_DIR, &isdir[0], -1);
  136. gtk_tree_model_get(m, b, NAME_STR, &name[1], IS_DIR, &isdir[1], -1);
  137. if (isdir[0] == isdir[1])
  138. ret = g_ascii_strcasecmp(name[0], name[1]);
  139. else
  140. ret = isdir[0] ? -1 : 1;
  141. g_free(name[0]);
  142. g_free(name[1]);
  143. return ret;
  144. }
  145. /* creates a formatted permission string */
  146. gchar*
  147. create_perm_str(mode_t mode)
  148. {
  149. char *permstr[] = {
  150. "---", "--x", "-w-", "-wx",
  151. "r--", "r-x", "rw-", "rwx" };
  152. return g_strdup_printf("%s%s%s", permstr[(mode >> 6) & 7],
  153. permstr[(mode >> 3) & 7],
  154. permstr[mode & 7]);
  155. }
  156. /* creates a formatted size string */
  157. gchar*
  158. create_size_str(size_t size)
  159. {
  160. if (size < 1024)
  161. return g_strdup_printf("%i B", (int)size);
  162. else if (size < 1024*1024)
  163. return g_strdup_printf("%.1f KB", size/1024.0);
  164. else if (size < 1024*1024*1024)
  165. return g_strdup_printf("%.1f MB", size/(1024.0*1024));
  166. else
  167. return g_strdup_printf("%.1f GB", size/(1024.0*1024*1024));
  168. }
  169. /* creates a formatted time string */
  170. gchar*
  171. create_time_str(const char *fmt, const struct tm *time)
  172. {
  173. gchar buf[64];
  174. strftime(buf, sizeof(buf), fmt, time);
  175. return g_strdup(buf);
  176. }
  177. /* creates and initializes a FmWindow */
  178. FmWindow*
  179. createwin()
  180. {
  181. FmWindow *fw;
  182. GtkCellRenderer *rend;
  183. GtkListStore *store;
  184. GtkTreeSortable *sortable;
  185. fw = g_malloc(sizeof(FmWindow));
  186. fw->path = NULL;
  187. fw->show_dot = show_dotfiles;
  188. fw->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  189. gtk_window_set_default_size(GTK_WINDOW(fw->win), 640, 480);
  190. gtk_window_set_icon_name(GTK_WINDOW(fw->win), "folder");
  191. /* setup scrolled window */
  192. fw->scroll = gtk_scrolled_window_new(NULL, NULL);
  193. gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fw->scroll),
  194. GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  195. /* setup list store */
  196. store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING,
  197. G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
  198. sortable = GTK_TREE_SORTABLE(store);
  199. /* setup tree view */
  200. fw->tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
  201. gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fw->tree), TRUE);
  202. gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(fw->tree), TRUE);
  203. gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(fw->tree), TRUE);
  204. gtk_tree_selection_set_mode(
  205. gtk_tree_view_get_selection(GTK_TREE_VIEW(fw->tree)),
  206. GTK_SELECTION_MULTIPLE);
  207. rend = gtk_cell_renderer_text_new();
  208. gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(fw->tree),
  209. -1, "Name", rend, "text", NAME_STR, NULL);
  210. rend = gtk_cell_renderer_text_new();
  211. gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(fw->tree),
  212. -1, "Permissions", rend, "text", PERMS_STR, NULL);
  213. rend = gtk_cell_renderer_text_new();
  214. gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(fw->tree),
  215. -1, "Size", rend, "text", SIZE_STR, NULL);
  216. rend = gtk_cell_renderer_text_new();
  217. gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(fw->tree),
  218. -1, "Modified", rend, "text", MTIME_STR, NULL);
  219. /* expand first column */
  220. gtk_tree_view_column_set_expand(
  221. gtk_tree_view_get_column(GTK_TREE_VIEW(fw->tree), 0),
  222. TRUE);
  223. /* setup list sorting */
  224. gtk_tree_sortable_set_sort_func(sortable, NAME_STR, compare, NULL, NULL);
  225. gtk_tree_sortable_set_sort_column_id(sortable, NAME_STR, GTK_SORT_ASCENDING);
  226. /* connect signals */
  227. g_signal_connect(G_OBJECT(fw->win), "destroy",
  228. G_CALLBACK(destroywin), fw);
  229. g_signal_connect(G_OBJECT(fw->win), "key-press-event",
  230. G_CALLBACK(keypress), fw);
  231. g_signal_connect(G_OBJECT(fw->tree), "row-activated",
  232. G_CALLBACK(action), fw);
  233. /* add widgets */
  234. gtk_container_add(GTK_CONTAINER(fw->scroll), fw->tree);
  235. gtk_container_add(GTK_CONTAINER(fw->win), fw->scroll);
  236. gtk_widget_show_all(fw->win);
  237. return fw;
  238. }
  239. /* removes and deallocates a FmWindow */
  240. void
  241. destroywin(GtkWidget *w, FmWindow *fw)
  242. {
  243. if ((windows = g_list_remove(windows, fw)) == NULL)
  244. gtk_main_quit();
  245. gtk_widget_destroy(fw->tree);
  246. gtk_widget_destroy(fw->scroll);
  247. gtk_widget_destroy(fw->win);
  248. if (fw->path)
  249. g_free(fw->path);
  250. g_free(fw);
  251. }
  252. /* change directory to current, and spawns program in background */
  253. void
  254. dir_exec(FmWindow *fw, const Arg *arg)
  255. {
  256. g_return_if_fail(fw->path && arg->v);
  257. spawn(arg->v, fw->path);
  258. }
  259. /* get mtime for a file. returns 0 if ok */
  260. gint
  261. get_mtime(const gchar *path, time_t *time)
  262. {
  263. struct stat st;
  264. gint err;
  265. g_return_val_if_fail(time, 0);
  266. if ((err = stat(path, &st)) == 0)
  267. *time = st.st_mtime;
  268. return err;
  269. }
  270. /* returns a list of name for selected files (relative file names, not absolute) */
  271. GList *
  272. get_selected(FmWindow *fw)
  273. {
  274. GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(fw->tree));
  275. GtkTreeModel *model;
  276. GtkTreeIter iter;
  277. GList *lsel = gtk_tree_selection_get_selected_rows(sel, &model);
  278. GList *node = lsel;
  279. GList *files = NULL;
  280. gchar *name;
  281. while (node) {
  282. gtk_tree_model_get_iter(model, &iter, node->data);
  283. gtk_tree_model_get(model, &iter, NAME_STR, &name, -1);
  284. files = g_list_append(files, name);
  285. node = g_list_next(node);
  286. }
  287. g_list_foreach(lsel, (GFunc) gtk_tree_path_free, NULL);
  288. g_list_free(lsel);
  289. return files;
  290. }
  291. /* handles key events on the FmWindow */
  292. gboolean
  293. keypress(GtkWidget *w, GdkEventKey *ev, FmWindow *fw)
  294. {
  295. gint i;
  296. for (i = 0; i < ARRSIZE(keys); i++) {
  297. if (gdk_keyval_to_lower(ev->keyval) == keys[i].key &&
  298. CLEANMASK(ev->state) == keys[i].mod &&
  299. keys[i].func) {
  300. keys[i].func(fw, &keys[i].arg);
  301. }
  302. }
  303. return FALSE;
  304. }
  305. void
  306. make_dir(FmWindow *fw, const Arg *arg)
  307. {
  308. gchar *path;
  309. g_return_if_fail(fw->path);
  310. if ((path = text_dialog(GTK_WINDOW(fw->win), "make directory", NULL))) {
  311. if (mkdir(path, arg->i) == -1)
  312. g_warning("mkdir: %s", strerror(errno));
  313. g_free(path);
  314. }
  315. }
  316. /* moves cursor in the tree view */
  317. void
  318. move_cursor(FmWindow *fw, const Arg *arg)
  319. {
  320. /* TODO: fix this */
  321. GtkMovementStep m;
  322. gint v;
  323. gboolean ret;
  324. switch (arg->i) {
  325. case UP:
  326. m = GTK_MOVEMENT_DISPLAY_LINES;
  327. v = -1;
  328. break;
  329. case DOWN:
  330. m = GTK_MOVEMENT_DISPLAY_LINES;
  331. v = 1;
  332. break;
  333. case HOME:
  334. m = GTK_MOVEMENT_BUFFER_ENDS;
  335. v = -1;
  336. break;
  337. case END:
  338. m = GTK_MOVEMENT_BUFFER_ENDS;
  339. v = 1;
  340. break;
  341. case PAGEUP:
  342. m = GTK_MOVEMENT_PAGES;
  343. v = -1;
  344. break;
  345. case PAGEDOWN:
  346. m = GTK_MOVEMENT_PAGES;
  347. v = 1;
  348. break;
  349. default:
  350. return;
  351. }
  352. g_signal_emit_by_name(G_OBJECT(fw->tree), "move-cursor", m, v, &ret);
  353. }
  354. void
  355. mv(FmWindow *fw, const Arg *arg)
  356. {
  357. /* TODO: dummy atm */
  358. GList *files = get_selected(fw);
  359. GList *iter = files;
  360. while (iter) {
  361. printf("f: '%s'\n", (char *)iter->data);
  362. iter = g_list_next(iter);
  363. }
  364. g_list_foreach(files, (GFunc) g_free, NULL);
  365. g_list_free(files);
  366. }
  367. /* creates and inserts a new FmWindow to the window list */
  368. void
  369. newwin(FmWindow *fw, const Arg *arg)
  370. {
  371. FmWindow *new = createwin();
  372. windows = g_list_append(windows, new);
  373. open_directory(new, arg->v ? arg->v : (fw ? fw->path : NULL));
  374. }
  375. /* open and reads directory data to FmWindow */
  376. void
  377. open_directory(FmWindow *fw, const char *str)
  378. {
  379. DIR *dir;
  380. char rpath[PATH_MAX];
  381. g_return_if_fail(str);
  382. /* change to current working directory to get relative paths right */
  383. if (fw->path)
  384. chdir(fw->path);
  385. /* get clean absolute path string */
  386. realpath(str, rpath);
  387. if (!(dir = opendir(rpath))) {
  388. g_warning("%s: %s\n", rpath, g_strerror(errno));
  389. if (strcmp(rpath, "/") != 0) {
  390. /* try to go up one level and load directory */
  391. open_directory(fw, prev_dir(rpath));
  392. }
  393. return;
  394. }
  395. if (fw->path)
  396. g_free(fw->path);
  397. fw->path = g_strdup(rpath);
  398. chdir(fw->path);
  399. get_mtime(fw->path, &fw->mtime);
  400. gtk_window_set_title(GTK_WINDOW(fw->win), fw->path);
  401. read_files(fw, dir);
  402. closedir(dir);
  403. }
  404. gchar*
  405. prev_dir(gchar *path)
  406. {
  407. gchar *p;
  408. if ((p = g_strrstr(path, "/"))) {
  409. if (p == path)
  410. *(p + 1) = '\0';
  411. else
  412. *p = '\0';
  413. }
  414. return path;
  415. }
  416. /* reads files in to fw's list store from an opened DIR struct */
  417. void
  418. read_files(FmWindow *fw, DIR *dir)
  419. {
  420. struct dirent *e;
  421. struct stat st;
  422. struct tm *time;
  423. gchar *name_str;
  424. gchar *mtime_str;
  425. gchar *perms_str;
  426. gchar *size_str;
  427. GtkListStore *store = GTK_LIST_STORE(
  428. gtk_tree_view_get_model(GTK_TREE_VIEW(fw->tree)));
  429. GtkTreeIter iter;
  430. GtkTreeSortable *sortable = GTK_TREE_SORTABLE(store);
  431. /* remove previous entries */
  432. gtk_list_store_clear(store);
  433. /* disable sort to speed up insertion */
  434. gtk_tree_sortable_set_sort_column_id(sortable,
  435. GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
  436. GTK_SORT_ASCENDING);
  437. while ((e = readdir(dir))) {
  438. if (valid_filename(e->d_name, fw->show_dot)
  439. && (stat(e->d_name, &st) == 0)) {
  440. if (S_ISDIR(st.st_mode))
  441. name_str = g_strdup_printf("%s/", e->d_name);
  442. else
  443. name_str = g_strdup(e->d_name);
  444. time = localtime(&st.st_mtime);
  445. mtime_str = create_time_str(timefmt, time);
  446. perms_str = create_perm_str(st.st_mode);
  447. size_str = create_size_str(st.st_size);
  448. gtk_list_store_append(store, &iter);
  449. gtk_list_store_set(store, &iter,
  450. NAME_STR, name_str,
  451. PERMS_STR, perms_str,
  452. SIZE_STR, size_str,
  453. MTIME_STR, mtime_str,
  454. IS_DIR, S_ISDIR(st.st_mode),
  455. -1);
  456. g_free(name_str);
  457. g_free(mtime_str);
  458. g_free(perms_str);
  459. g_free(size_str);
  460. }
  461. }
  462. /* reenable sort */
  463. gtk_tree_sortable_set_sort_column_id(sortable, NAME_STR, GTK_SORT_ASCENDING);
  464. }
  465. /* reload a FmWindow */
  466. void
  467. reload(FmWindow *fw)
  468. {
  469. open_directory(fw, fw->path);
  470. }
  471. void
  472. set_path(FmWindow *fw, const Arg *arg)
  473. {
  474. char *path;
  475. if ((path = arg->v)) {
  476. open_directory(fw, path);
  477. } else if ((path = text_dialog(GTK_WINDOW(fw->win), "path", fw->path))) {
  478. open_directory(fw, path);
  479. g_free(path);
  480. }
  481. }
  482. /* change working directory and spawns a program to the background */
  483. void
  484. spawn(const gchar * const *argv, const gchar *path)
  485. {
  486. if (fork() == 0) {
  487. g_setenv("DFM_PATH", path, TRUE);
  488. chdir(path);
  489. execvp(*argv, (gchar **)argv);
  490. g_warning("spawn: %s", strerror(errno));
  491. }
  492. }
  493. gchar*
  494. text_dialog(GtkWindow *p, const gchar *title, const gchar *text)
  495. {
  496. GtkWidget *dialog = gtk_dialog_new_with_buttons(title, p,
  497. GTK_DIALOG_MODAL, NULL);
  498. GtkWidget *entry = gtk_entry_new();
  499. GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
  500. gchar *str = NULL;
  501. if (text)
  502. gtk_entry_set_text(GTK_ENTRY(entry), text);
  503. g_signal_connect(G_OBJECT(entry), "activate",
  504. G_CALLBACK(text_dialog_enter), dialog);
  505. gtk_container_add(GTK_CONTAINER(area), entry);
  506. gtk_widget_show(entry);
  507. if (gtk_dialog_run(GTK_DIALOG(dialog)) == 1)
  508. str = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
  509. /* clean up */
  510. gtk_widget_destroy(dialog);
  511. return str;
  512. }
  513. void
  514. text_dialog_enter(GtkWidget *w, GtkDialog *dialog)
  515. {
  516. gtk_dialog_response(dialog, 1);
  517. }
  518. /* toggle preferences in a FmWindow, and reload if necessary */
  519. void
  520. toggle_pref(FmWindow *fw, const Arg *arg)
  521. {
  522. switch (arg->i) {
  523. case DOTFILES:
  524. fw->show_dot = !fw->show_dot;
  525. reload(fw);
  526. break;
  527. }
  528. }
  529. void
  530. update(FmWindow *fw)
  531. {
  532. time_t mtime = 0;
  533. if (fw->path) {
  534. if (get_mtime(fw->path, &mtime) != 0 || mtime > fw->mtime) {
  535. /* directory updated or removed, reload */
  536. reload(fw);
  537. }
  538. }
  539. }
  540. void*
  541. update_thread(void *v)
  542. {
  543. GList *p;
  544. for (;;) {
  545. sleep(polltime);
  546. gdk_threads_enter();
  547. for (p = windows; p != NULL; p = g_list_next(p))
  548. update((FmWindow *)p->data);
  549. gdk_threads_leave();
  550. }
  551. return NULL;
  552. }
  553. /* returns 1 if valid filename, i.e. not '.' or '..' (or .* if show_dot = 0) */
  554. int
  555. valid_filename(const char *s, int show_dot)
  556. {
  557. return show_dot ?
  558. (g_strcmp0(s, ".") != 0 && g_strcmp0(s, "..") != 0) :
  559. *s != '.';
  560. }
  561. int
  562. main(int argc, char *argv[])
  563. {
  564. Arg arg;
  565. pthread_t u_tid;
  566. pid_t pid;
  567. gboolean silent = FALSE;
  568. int i;
  569. /* read arguments */
  570. for(i = 1; i < argc && argv[i][0] == '-'; i++) {
  571. switch(argv[i][1]) {
  572. case 'v':
  573. g_print("%s\n", VERSION);
  574. exit(EXIT_SUCCESS);
  575. case 'd':
  576. show_dotfiles = TRUE;
  577. break;
  578. case 's':
  579. silent = TRUE;
  580. break;
  581. default:
  582. g_printerr("Usage: %s [-v] [-d] [-s] PATH\n", argv[0]);
  583. exit(EXIT_FAILURE);
  584. }
  585. }
  586. arg.v = i < argc ? argv[i] : ".";
  587. if ((pid = fork()) > 0)
  588. return EXIT_SUCCESS;
  589. else if (pid < 0)
  590. printf("fork: %s\n", g_strerror(errno));
  591. if (silent) {
  592. close(STDOUT_FILENO);
  593. close(STDERR_FILENO);
  594. }
  595. /* initialize threads */
  596. g_thread_init(NULL);
  597. gdk_threads_init();
  598. gdk_threads_enter();
  599. gtk_init(&argc, &argv);
  600. newwin(NULL, &arg);
  601. /* create update thread */
  602. pthread_create(&u_tid, NULL, update_thread, NULL);
  603. gtk_main();
  604. gdk_threads_leave();
  605. return EXIT_SUCCESS;
  606. }