2 * Copyright 2004-2005 Timo Hirvonen
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 #include "ui_curses.h"
30 #include <sys/types.h>
37 struct window
*browser_win
;
38 struct searchable
*browser_searchable
;
41 static LIST_HEAD(browser_head
);
43 static inline void browser_entry_to_iter(struct browser_entry
*e
, struct iter
*iter
)
45 iter
->data0
= &browser_head
;
50 /* filter out names starting with '.' except '..' */
51 static int normal_filter(const char *name
, const struct stat
*s
)
54 if (name
[1] == '.' && name
[2] == 0)
58 if (S_ISDIR(s
->st_mode
))
60 return cmus_is_supported(name
);
64 static int hidden_filter(const char *name
, const struct stat
*s
)
66 if (name
[0] == '.' && name
[1] == 0)
71 /* only works for BROWSER_ENTRY_DIR and BROWSER_ENTRY_FILE */
72 static int entry_cmp(const struct browser_entry
*a
, const struct browser_entry
*b
)
74 if (a
->type
== BROWSER_ENTRY_DIR
) {
75 if (b
->type
== BROWSER_ENTRY_FILE
)
77 if (!strcmp(a
->name
, "../"))
79 if (!strcmp(b
->name
, "../"))
81 return strcmp(a
->name
, b
->name
);
83 if (b
->type
== BROWSER_ENTRY_DIR
)
85 return strcmp(a
->name
, b
->name
);
88 static char *fullname(const char *path
, const char *name
)
95 if (path
[l1
- 1] == '/')
97 full
= xnew(char, l1
+ 1 + l2
+ 1);
98 memcpy(full
, path
, l1
);
100 memcpy(full
+ l1
+ 1, name
, l2
+ 1);
104 static void free_browser_list(void)
106 struct list_head
*item
;
108 item
= browser_head
.next
;
109 while (item
!= &browser_head
) {
110 struct list_head
*next
= item
->next
;
111 struct browser_entry
*entry
;
113 entry
= list_entry(item
, struct browser_entry
, node
);
117 list_init(&browser_head
);
120 static int add_pl_line(void *data
, const char *line
)
122 struct browser_entry
*e
;
123 int name_size
= strlen(line
) + 1;
125 e
= xmalloc(sizeof(struct browser_entry
) + name_size
);
126 memcpy(e
->name
, line
, name_size
);
127 e
->type
= BROWSER_ENTRY_PLLINE
;
128 list_add_tail(&e
->node
, &browser_head
);
132 static int do_browser_load(const char *name
)
139 if (S_ISREG(st
.st_mode
) && cmus_is_playlist(name
)) {
143 buf
= mmap_file(name
, &size
);
150 cmus_playlist_for_each(buf
, size
, 0, add_pl_line
, NULL
);
153 } else if (S_ISDIR(st
.st_mode
)) {
154 int (*filter
)(const char *, const struct stat
*) = normal_filter
;
155 struct directory dir
;
157 int root
= !strcmp(name
, "/");
160 filter
= hidden_filter
;
162 if (dir_open(&dir
, name
))
166 while ((str
= dir_read(&dir
))) {
167 struct browser_entry
*e
;
168 struct list_head
*item
;
171 if (!filter(str
, &dir
.st
))
174 /* ignore .. if we are in the root dir */
175 if (root
&& !strcmp(str
, ".."))
179 e
= xmalloc(sizeof(struct browser_entry
) + len
+ 2);
180 e
->type
= BROWSER_ENTRY_FILE
;
181 memcpy(e
->name
, str
, len
);
182 if (S_ISDIR(dir
.st
.st_mode
)) {
183 e
->type
= BROWSER_ENTRY_DIR
;
184 e
->name
[len
++] = '/';
188 item
= browser_head
.prev
;
189 while (item
!= &browser_head
) {
190 struct browser_entry
*other
;
192 other
= container_of(item
, struct browser_entry
, node
);
193 if (entry_cmp(e
, other
) >= 0)
198 list_add(&e
->node
, item
);
202 /* try to update currect working directory */
211 static int browser_load(const char *name
)
215 rc
= do_browser_load(name
);
219 window_set_contents(browser_win
, &browser_head
);
221 browser_dir
= xstrdup(name
);
225 static GENERIC_ITER_PREV(browser_get_prev
, struct browser_entry
, node
)
226 static GENERIC_ITER_NEXT(browser_get_next
, struct browser_entry
, node
)
228 static int browser_search_get_current(void *data
, struct iter
*iter
)
230 return window_get_sel(browser_win
, iter
);
233 static int browser_search_matches(void *data
, struct iter
*iter
, const char *text
)
235 char **words
= get_words(text
);
238 if (words
[0] != NULL
) {
239 struct browser_entry
*e
;
242 e
= iter_to_browser_entry(iter
);
244 if (words
[i
] == NULL
) {
245 window_set_sel(browser_win
, iter
);
249 if (u_strcasestr_filename(e
->name
, words
[i
]) == NULL
)
253 free_str_array(words
);
257 static const struct searchable_ops browser_search_ops
= {
258 .get_prev
= browser_get_prev
,
259 .get_next
= browser_get_next
,
260 .get_current
= browser_search_get_current
,
261 .matches
= browser_search_matches
264 void browser_init(void)
270 if (getcwd(cwd
, sizeof(cwd
)) == NULL
) {
275 if (do_browser_load(dir
)) {
277 do_browser_load("/");
278 browser_dir
= xstrdup("/");
283 browser_win
= window_new(browser_get_prev
, browser_get_next
);
284 window_set_contents(browser_win
, &browser_head
);
285 window_changed(browser_win
);
287 iter
.data0
= &browser_head
;
290 browser_searchable
= searchable_new(NULL
, &iter
, &browser_search_ops
);
293 void browser_exit(void)
295 searchable_free(browser_searchable
);
297 window_free(browser_win
);
301 int browser_chdir(const char *dir
)
303 if (browser_load(dir
)) {
308 void browser_up(void)
310 char *new, *ptr
, *pos
;
311 struct browser_entry
*e
;
314 if (strcmp(browser_dir
, "/") == 0)
317 ptr
= strrchr(browser_dir
, '/');
318 if (ptr
== browser_dir
) {
321 new = xstrndup(browser_dir
, ptr
- browser_dir
);
324 /* remember last position */
327 pos
= xnew(char, len
+ 2);
328 memcpy(pos
, ptr
, len
);
332 if (browser_load(new)) {
333 error_msg("could not open directory '%s': %s\n", new, strerror(errno
));
340 list_for_each_entry(e
, &browser_head
, node
) {
341 if (strcmp(e
->name
, pos
) == 0) {
344 browser_entry_to_iter(e
, &iter
);
345 window_set_sel(browser_win
, &iter
);
352 static void browser_cd(const char *dir
)
357 if (strcmp(dir
, "../") == 0) {
362 new = fullname(browser_dir
, dir
);
364 if (new[len
- 1] == '/')
366 if (browser_load(new))
367 error_msg("could not open directory '%s': %s\n", dir
, strerror(errno
));
371 static void browser_cd_playlist(const char *filename
)
373 if (browser_load(filename
))
374 error_msg("could not read playlist '%s': %s\n", filename
, strerror(errno
));
377 void browser_enter(void)
379 struct browser_entry
*e
;
383 if (!window_get_sel(browser_win
, &sel
))
385 e
= iter_to_browser_entry(&sel
);
386 len
= strlen(e
->name
);
389 if (e
->type
== BROWSER_ENTRY_DIR
) {
392 if (e
->type
== BROWSER_ENTRY_PLLINE
) {
393 cmus_play_file(e
->name
);
397 filename
= fullname(browser_dir
, e
->name
);
398 if (cmus_is_playlist(filename
)) {
399 browser_cd_playlist(filename
);
401 cmus_play_file(filename
);
408 char *browser_get_sel(void)
410 struct browser_entry
*e
;
413 if (!window_get_sel(browser_win
, &sel
))
416 e
= iter_to_browser_entry(&sel
);
417 if (e
->type
== BROWSER_ENTRY_PLLINE
)
418 return xstrdup(e
->name
);
420 return fullname(browser_dir
, e
->name
);
423 void browser_delete(void)
425 struct browser_entry
*e
;
429 if (!window_get_sel(browser_win
, &sel
))
431 e
= iter_to_browser_entry(&sel
);
432 len
= strlen(e
->name
);
435 if (e
->type
== BROWSER_ENTRY_FILE
) {
438 name
= fullname(browser_dir
, e
->name
);
439 if (yes_no_query("Delete file '%s'? [y/N]", e
->name
)) {
440 if (unlink(name
) == -1) {
441 error_msg("deleting '%s': %s", e
->name
, strerror(errno
));
443 window_row_vanishes(browser_win
, &sel
);
452 void browser_reload(void)
454 char *tmp
= xstrdup(browser_dir
);
457 struct browser_entry
*e
;
459 /* remember selection */
460 if (window_get_sel(browser_win
, &iter
)) {
461 e
= iter_to_browser_entry(&iter
);
462 sel
= xstrdup(e
->name
);
465 /* have to use tmp */
466 if (browser_load(tmp
)) {
467 error_msg("could not update contents '%s': %s\n", tmp
, strerror(errno
));
475 list_for_each_entry(e
, &browser_head
, node
) {
476 if (strcmp(e
->name
, sel
) == 0) {
477 browser_entry_to_iter(e
, &iter
);
478 window_set_sel(browser_win
, &iter
);