Update copyright year to 2015.
[cboard.git] / src / filebrowser.c
blob5fc26f4543d6f0135e2841372ab9d640b24b657a
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007-2015 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <time.h>
32 #include <pwd.h>
34 #ifdef HAVE_GLOB_H
35 #include <glob.h>
36 #endif
38 #ifdef HAVE_DIRENT_H
39 #include <dirent.h>
40 #endif
42 #include "misc.h"
43 #include "common.h"
44 #include "window.h"
45 #include "message.h"
46 #include "menu.h"
47 #include "input.h"
48 #include "filebrowser.h"
49 #include "common.h"
50 #include "conf.h"
52 static void free_file_browser()
54 int i;
56 if (!files)
57 return;
59 for (i = 0; files[i]; i++) {
60 free(files[i]->name);
61 free(files[i]->path);
62 free(files[i]->st);
63 free(files[i]);
66 free(files);
67 files = NULL;
70 static struct menu_item_s **get_file_items(WIN *win)
72 struct menu_input_s *m = win->data;
73 struct input_s *in = m->data;
74 char *path = in->arg;
75 struct menu_item_s **items = m->items;
76 char *p;
77 char pattern[255];
78 int i, n = 0;
79 struct stat st;
80 int which = 2;
81 int len;
82 int x = GLOB_ERR;
83 #ifdef HAVE_GLOB_NOMATCH
84 int ret;
85 #endif
86 glob_t g;
89 * First find hidden directories in the working directory. Then non-hidden
90 * directories, then apply the config.pattern to regular files.
92 snprintf(pattern, sizeof(pattern), "%s/.?*", path);
94 if (items) {
95 for (i = 0; items[i]; i++)
96 free(items[i]);
99 free(items);
100 items = m->items = NULL;
101 free_file_browser();
102 m->nofree = 1;
104 new_glob:
105 #ifdef HAVE_GLOB_NOMATCH
106 if ((ret = glob(pattern, x, NULL, &g)) != 0 && ret != GLOB_NOMATCH) {
107 #else
108 if (glob(pattern, x, NULL, &g) != 0) {
109 #endif
110 cmessage(ERROR_STR, ANY_KEY_STR, "glob() failed:\n%s", pattern);
111 return NULL;
114 for (i = 0; i < g.gl_pathc; i++) {
115 struct tm *tp;
116 char tbuf[16];
117 char sbuf[64];
119 if (stat(g.gl_pathv[i], &st) == -1)
120 continue;
122 if ((p = strrchr(g.gl_pathv[i], '/')) != NULL)
123 p++;
124 else
125 p = g.gl_pathv[i];
127 if (which) {
128 if (!S_ISDIR(st.st_mode))
129 continue;
131 if (p[0] == '.' && p[1] == 0)
132 continue;
134 else {
135 if (S_ISDIR(st.st_mode))
136 continue;
139 files = Realloc(files, (n + 2) * sizeof(struct file_s *));
140 files[n] = Malloc(sizeof(struct file_s));
141 files[n]->path = strdup(g.gl_pathv[i]);
142 len = strlen(p) + 2;
143 files[n]->name = Malloc(len);
144 strcpy(files[n]->name, p);
146 if (S_ISDIR(st.st_mode)) {
147 files[n]->name[len - 2] = '/';
148 files[n]->name[len - 1] = 0;
149 p = files[n]->path + strlen(files[n]->path) - 1;
151 if (*p == '.' && *(p - 1) == '.' && *(p - 2) == '/') {
152 p -= 2;
153 *p = 0;
155 if (strlen(files[n]->path)) {
156 while (*p != '/')
157 p--;
159 *p = 0;
162 if (!strlen(files[n]->path)) {
163 p = files[n]->path;
164 *p++ = '/';
165 *p = 0;
170 tp = localtime(&st.st_mtime);
171 strftime(tbuf, sizeof(tbuf), "%b %d %T", tp);
172 snprintf(sbuf, sizeof(sbuf), "%s %6i", tbuf, (int)st.st_size);
173 files[n]->st = strdup(sbuf);
174 n++;
177 which--;
178 files[n] = NULL;
180 if (which > 0) {
181 snprintf(pattern, sizeof(pattern), "%s/*", path);
182 globfree(&g);
183 goto new_glob;
185 else if (which == 0) {
186 snprintf(pattern, sizeof(pattern), "%s/%s", path, config.pattern);
187 globfree(&g);
188 goto new_glob;
191 globfree(&g);
193 for (i = 0; files[i]; i++) {
194 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
195 items[i] = Malloc(sizeof(struct menu_item_s));
196 items[i]->name = files[i]->name;
197 items[i]->value = files[i]->st;
198 items[i]->selected = 0;
201 free(win->title);
202 win->title = strdup(path);
203 items[i] = NULL;
204 m->items = items;
205 return items;
208 static void file_browser_help(struct menu_input_s *m)
210 message(_("File Browser Keys"), ANY_KEY_STR, "%s",
212 " UP/DOWN - previous/next menu item\n"
213 " HOME/END - first/last menu item\n"
214 " PGDN/PGUP - next/previous page\n"
215 " a-zA-Z0-9 - jump to item\n"
216 " ENTER - select item\n"
217 " ~ - change to home directory\n"
218 " CTRL-e - change filename expression\n"
219 " ESCAPE - abort"
223 static void file_browser_select(struct menu_input_s *m)
225 struct input_s *in = m->data;
226 char *path = in->arg;
227 struct stat st;
229 if (stat(files[m->selected]->path, &st) == -1) {
230 message(ERROR_STR, ANY_KEY_STR, "%s", strerror(errno));
231 return;
234 if (S_ISDIR(st.st_mode)) {
235 if (access(files[m->selected]->path, R_OK) != 0) {
236 cmessage(files[m->selected]->path, ANY_KEY_STR, "%s", strerror(errno));
237 return;
240 free(path);
241 path = strdup(files[m->selected]->path);
242 in->arg = path;
243 m->selected = 0;
245 if (oldwd)
246 free(oldwd);
248 oldwd = strdup(path);
249 return;
252 strncpy(in->buf, files[m->selected]->path, sizeof(in->buf)-1);
253 in->buf[sizeof(in->buf)-1] = 0;
254 set_field_buffer(in->fields[0], 0, in->buf);
255 pushkey = -1;
258 static void file_browser_finalize(WIN *win)
260 struct input_s *in = win->data;
262 free(in->arg);
263 free_file_browser();
266 static void file_browser_home(struct menu_input_s *m)
268 struct input_s *in = m->data;
269 char *path = in->arg;
270 struct passwd *pw;
272 free(path);
273 pw = getpwuid(getuid());
274 path = strdup(pw->pw_dir);
275 in->arg = path;
276 m->selected = 0;
278 if (oldwd)
279 free(oldwd);
281 oldwd = strdup(path);
284 static void do_file_browser_expression_finalize(WIN *win)
286 struct input_data_s *in = win->data;
288 if (!in->str)
289 return;
291 if (config.pattern)
292 free(config.pattern);
294 config.pattern = strdup(in->str);
295 free(in->str);
296 free(in);
297 pushkey = REFRESH_MENU;
300 static void file_browser_expression(struct menu_input_s *m)
302 struct input_data_s *in;
304 in = Calloc(1, sizeof(struct input_data_s));
305 in->efunc = do_file_browser_expression_finalize;
306 construct_input(_("Filename Expression"), config.pattern, 1, 1, NULL, NULL, NULL, 0, in, -1, -1);
309 static void file_browser_abort(struct menu_input_s *m)
311 pushkey = -1;
314 static void file_browser_print(WIN *win)
316 int i, len = 0;
317 struct menu_input_s *m = win->data;
319 for (i = 0; m->items[i]; i++) {
320 int n = strlen(m->items[i]->value);
322 if (len < n)
323 len = n;
326 mvwprintw(win->w, m->print_line, 1, "%*s %-*s", len, m->item->value,
327 win->cols - len - 3, m->item->name);
330 void file_browser(void *arg)
332 struct menu_key_s **keys = NULL;
333 struct input_s *in = arg;
334 char *p;
335 char path[FILENAME_MAX] = {0};
337 if (!oldwd && config.savedirectory) {
338 if ((p = pathfix(config.savedirectory)) == NULL)
339 return;
341 strncpy(path, p, sizeof(path)-1);
343 if (access(path, R_OK) == -1) {
344 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", path, strerror(errno));
345 getcwd(path, sizeof(path));
348 else if (!oldwd)
349 getcwd(path, sizeof(path)-1);
350 else
351 strncpy(path, oldwd, sizeof(path)-1);
353 in->arg = strdup(path);
354 add_menu_key(&keys, '\n', file_browser_select);
355 add_menu_key(&keys, KEY_F(1), file_browser_help);
356 add_menu_key(&keys, '~', file_browser_home);
357 add_menu_key(&keys, CTRL_KEY('e'), file_browser_expression);
358 add_menu_key(&keys, KEY_ESCAPE, file_browser_abort);
359 construct_menu(LINES - 4, 0, -1, -1, NULL, 0, get_file_items, keys, in,
360 file_browser_print, file_browser_finalize);
361 return;