De-inline a few functions which are large
[midnight-commander.git] / src / hotlist.c
blob27cc88c57044c876ef7db7814961f3cd43dfeb3f
1 /* Directory hotlist -- for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
3 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5 Written by:
6 1994 Radek Doulik
7 1995 Janne Kukonlehto
8 1996 Andrej Borsenkow
9 1997 Norbert Warmuth
11 Janne did the original Hotlist code, Andrej made the groupable
12 hotlist; the move hotlist and revamped the file format and made
13 it stronger.
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 /** \file hotlist.c
31 * \brief Source: directory hotlist
34 #include <config.h>
36 #include <ctype.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
43 #include "global.h"
45 #include "../src/tty/tty.h" /* COLS */
46 #include "../src/tty/color.h"
47 #include "../src/tty/key.h" /* KEY_M_CTRL */
49 #include "dialog.h"
50 #include "widget.h"
51 #include "setup.h" /* For profile_bname */
52 #include "../src/mcconfig/mcconfig.h" /* Load/save directories hotlist */
53 #include "wtools.h" /* QuickDialog */
54 #include "panel.h" /* current_panel */
55 #include "main.h" /* update_panels() */
56 #include "layout.h" /* repaint_screen() */
57 #include "hotlist.h"
58 #include "command.h" /* cmdline */
59 #include "glibcompat.h" /* g_strlcpy for glib < 2.0 */
60 #include "history.h"
61 #include "strutil.h"
63 #define UX 5
64 #define UY 2
66 #define BX UX
67 #define BY (LINES - 6)
69 #define BUTTONS (sizeof(hotlist_but)/sizeof(struct _hotlist_but))
70 #define LABELS 3
71 #define B_ADD_CURRENT B_USER
72 #define B_REMOVE (B_USER + 1)
73 #define B_NEW_GROUP (B_USER + 2)
74 #define B_NEW_ENTRY (B_USER + 3)
75 #define B_UP_GROUP (B_USER + 4)
76 #define B_INSERT (B_USER + 5)
77 #define B_APPEND (B_USER + 6)
78 #define B_MOVE (B_USER + 7)
80 #ifdef USE_VFS
81 #include "../vfs/gc.h"
82 #define B_FREE_ALL_VFS (B_USER + 8)
83 #define B_REFRESH_VFS (B_USER + 9)
84 #endif
86 int hotlist_has_dot_dot = 1;
88 static WListbox *l_hotlist;
89 static WListbox *l_movelist;
91 static Dlg_head *hotlist_dlg;
92 static Dlg_head *movelist_dlg;
94 static WLabel *pname, *pname_group, *movelist_group;
96 enum HotListType {
97 HL_TYPE_GROUP,
98 HL_TYPE_ENTRY,
99 HL_TYPE_COMMENT,
100 HL_TYPE_DOTDOT
103 static struct {
105 * these parameters are intended to be user configurable
107 int expanded; /* expanded view of all groups at startup */
110 * these reflect run time state
113 int loaded; /* hotlist is loaded */
114 int readonly; /* hotlist readonly */
115 int file_error; /* parse error while reading file */
116 int running; /* we are running dlg (and have to
117 update listbox */
118 int moving; /* we are in moving hotlist currently */
119 int modified; /* hotlist was modified */
120 int type; /* LIST_HOTLIST || LIST_VFSLIST */
121 } hotlist_state;
123 static struct _hotlist_but {
124 int ret_cmd, flags, y, x;
125 const char *text;
126 int type;
127 widget_pos_flags_t pos_flags;
128 } hotlist_but[] = {
129 { B_MOVE, NORMAL_BUTTON, 1, 42, N_("&Move"), LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
130 { B_REMOVE, NORMAL_BUTTON, 1, 30, N_("&Remove"), LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
131 { B_APPEND, NORMAL_BUTTON, 1, 15, N_("&Append"), LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
132 { B_INSERT, NORMAL_BUTTON, 1, 0, N_("&Insert"), LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
133 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, N_("New &Entry"), LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
134 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, N_("New &Group"), LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
135 { B_CANCEL, NORMAL_BUTTON, 0, 53, N_("&Cancel"), LIST_HOTLIST | LIST_VFSLIST|LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
136 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, N_("&Up"), LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
137 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, N_("&Add current"), LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
138 #ifdef USE_VFS
139 { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, N_("&Refresh"), LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
140 { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, N_("Fr&ee VFSs now"), LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
141 #endif
142 { B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("Change &To"), LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
145 /* Directory hotlist */
146 static struct hotlist{
147 enum HotListType type;
148 char *directory;
149 char *label;
150 struct hotlist *head;
151 struct hotlist *up;
152 struct hotlist *next;
153 } *hotlist = NULL;
155 static struct hotlist *current_group;
157 static void init_movelist (int, struct hotlist *);
158 static void add_new_group_cmd (void);
159 static void add_new_entry_cmd (void);
160 static void remove_from_hotlist (struct hotlist *entry);
161 static void load_hotlist (void);
162 static void add_dotdot_to_list (void);
164 #define new_hotlist() g_new0(struct hotlist, 1)
166 static void
167 hotlist_refresh (Dlg_head * dlg)
169 common_dialog_repaint (dlg);
170 tty_setcolor (COLOR_NORMAL);
171 draw_box (dlg, 2, 5, dlg->lines - (hotlist_state.moving ? 6 : 10),
172 dlg->cols - (UX * 2));
173 if (!hotlist_state.moving)
174 draw_box (dlg, dlg->lines - 8, 5, 3, dlg->cols - (UX * 2));
177 /* If current->data is 0, then we are dealing with a VFS pathname */
178 static void
179 update_path_name (void)
181 const char *text = "";
182 char *p;
183 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
184 Dlg_head *dlg = list->widget.parent;
186 if (list->current) {
187 if (list->current->data != 0) {
188 struct hotlist *hlp = (struct hotlist *) list->current->data;
190 if (hlp->type == HL_TYPE_ENTRY ||
191 hlp->type == HL_TYPE_DOTDOT)
192 text = hlp->directory;
193 else if (hlp->type == HL_TYPE_GROUP)
194 text = _("Subgroup - press ENTER to see list");
195 } else {
196 text = list->current->text;
199 if (!hotlist_state.moving)
200 label_set_text (pname,
201 str_trunc (text, dlg->cols - (UX * 2 + 4)));
203 p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
204 if (!hotlist_state.moving)
205 label_set_text (pname_group,
206 str_trunc (p, dlg->cols - (UX * 2 + 4)));
207 else
208 label_set_text (movelist_group,
209 str_trunc (p, dlg->cols - (UX * 2 + 4)));
210 g_free (p);
212 dlg_redraw (dlg);
215 #define CHECK_BUFFER \
216 do { \
217 int i; \
219 if ((i = strlen (current->label) + 3) > buflen) { \
220 g_free (buf); \
221 buf = g_malloc (buflen = 1024 * (i/1024 + 1)); \
223 buf[0] = '\0'; \
224 } while (0)
226 static void fill_listbox (void)
228 struct hotlist *current = current_group->head;
229 GString *buff = g_string_new ("");
231 while (current){
232 switch (current->type) {
233 case HL_TYPE_GROUP:
235 /* buff clean up */
236 g_string_truncate(buff, 0);
237 g_string_append(buff,"->");
238 g_string_append(buff,current->label);
239 if (hotlist_state.moving)
240 listbox_add_item (l_movelist, 0, 0, buff->str, current);
241 else
242 listbox_add_item (l_hotlist, 0, 0, buff->str, current);
244 break;
245 case HL_TYPE_DOTDOT:
246 case HL_TYPE_ENTRY:
247 if (hotlist_state.moving)
248 listbox_add_item (l_movelist, 0, 0, current->label, current);
249 else
250 listbox_add_item (l_hotlist, 0, 0, current->label, current);
251 break;
252 default:
253 break;
255 current = current->next;
257 g_string_free (buff, TRUE);
260 static void
261 unlink_entry (struct hotlist *entry)
263 struct hotlist *current = current_group->head;
265 if (current == entry)
266 current_group->head = entry->next;
267 else {
268 while (current && current->next != entry)
269 current = current->next;
270 if (current)
271 current->next = entry->next;
273 entry->next =
274 entry->up = 0;
277 #ifdef USE_VFS
278 static void add_name_to_list (const char *path)
280 listbox_add_item (l_hotlist, 0, 0, path, 0);
282 #endif /* !USE_VFS */
284 static int
285 hotlist_button_callback (int action)
287 switch (action) {
288 case B_MOVE:
290 struct hotlist *saved = current_group;
291 struct hotlist *item;
292 struct hotlist *moveto_item = 0;
293 struct hotlist *moveto_group = 0;
294 int ret;
296 if (!l_hotlist->current)
297 return MSG_NOT_HANDLED; /* empty group - nothing to do */
298 item = l_hotlist->current->data;
299 hotlist_state.moving = 1;
300 init_movelist (LIST_MOVELIST, item);
301 run_dlg (movelist_dlg);
302 ret = movelist_dlg->ret_value;
303 hotlist_state.moving = 0;
304 if (l_movelist->current)
305 moveto_item = l_movelist->current->data;
306 moveto_group = current_group;
307 destroy_dlg (movelist_dlg);
308 current_group = saved;
309 if (ret == B_CANCEL)
310 return MSG_NOT_HANDLED;
311 if (moveto_item == item)
312 return MSG_NOT_HANDLED; /* If we insert/append a before/after a
313 it hardly changes anything ;) */
314 unlink_entry (item);
315 listbox_remove_current (l_hotlist, 1);
316 item->up = moveto_group;
317 if (!moveto_group->head)
318 moveto_group->head = item;
319 else if (!moveto_item) { /* we have group with just comments */
320 struct hotlist *p = moveto_group->head;
322 /* skip comments */
323 while (p->next)
324 p = p->next;
325 p->next = item;
326 } else if (ret == B_ENTER || ret == B_APPEND)
327 if (!moveto_item->next)
328 moveto_item->next = item;
329 else {
330 item->next = moveto_item->next;
331 moveto_item->next = item;
332 } else if (moveto_group->head == moveto_item) {
333 moveto_group->head = item;
334 item->next = moveto_item;
335 } else {
336 struct hotlist *p = moveto_group->head;
338 while (p->next != moveto_item)
339 p = p->next;
340 item->next = p->next;
341 p->next = item;
343 listbox_remove_list (l_hotlist);
344 fill_listbox ();
345 repaint_screen ();
346 hotlist_state.modified = 1;
347 return MSG_NOT_HANDLED;
348 break;
350 case B_REMOVE:
351 if (l_hotlist->current && l_hotlist->current->data)
352 remove_from_hotlist (l_hotlist->current->data);
353 return MSG_NOT_HANDLED;
354 break;
356 case B_NEW_GROUP:
357 add_new_group_cmd ();
358 return MSG_NOT_HANDLED;
359 break;
361 case B_ADD_CURRENT:
362 add2hotlist_cmd ();
363 return MSG_NOT_HANDLED;
364 break;
366 case B_NEW_ENTRY:
367 add_new_entry_cmd ();
368 return MSG_NOT_HANDLED;
369 break;
371 case B_ENTER:
373 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
374 if (list->current) {
375 if (list->current->data) {
376 struct hotlist *hlp =
377 (struct hotlist *) list->current->data;
378 if (hlp->type == HL_TYPE_ENTRY)
379 return MSG_HANDLED;
380 else if (hlp->type == HL_TYPE_DOTDOT) {
381 /* Fall through - go up */
384 else {
385 listbox_remove_list (list);
386 current_group = hlp;
387 fill_listbox ();
388 return MSG_NOT_HANDLED;
390 } else
391 return MSG_HANDLED;
394 /* Fall through if list empty - just go up */
396 case B_UP_GROUP:
398 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
399 listbox_remove_list (list);
400 current_group = current_group->up;
401 fill_listbox ();
402 return MSG_NOT_HANDLED;
403 break;
406 #ifdef USE_VFS
407 case B_FREE_ALL_VFS:
408 vfs_expire (1);
409 /* fall through */
411 case B_REFRESH_VFS:
412 listbox_remove_list (l_hotlist);
413 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
414 vfs_fill_names (add_name_to_list);
415 return MSG_NOT_HANDLED;
416 #endif /* USE_VFS */
418 default:
419 return MSG_HANDLED;
420 break;
425 static cb_ret_t
426 hotlist_callback (Dlg_head *h, dlg_msg_t msg, int parm)
428 switch (msg) {
429 case DLG_DRAW:
430 hotlist_refresh (h);
431 return MSG_HANDLED;
433 case DLG_UNHANDLED_KEY:
434 switch (parm) {
435 case KEY_M_CTRL | '\n':
436 goto l1;
437 case '\n':
438 case KEY_ENTER:
439 case KEY_RIGHT:
440 if (hotlist_button_callback (B_ENTER)) {
441 h->ret_value = B_ENTER;
442 dlg_stop (h);
444 return MSG_HANDLED;
445 break;
446 case KEY_LEFT:
447 if (hotlist_state.type != LIST_VFSLIST)
448 return !hotlist_button_callback (B_UP_GROUP);
449 else
450 return MSG_NOT_HANDLED;
451 break;
452 case KEY_DC:
453 if (!hotlist_state.moving) {
454 hotlist_button_callback (B_REMOVE);
455 return MSG_HANDLED;
457 break;
459 case ALT ('\n'):
460 case ALT ('\r'):
461 if (!hotlist_state.moving) {
462 if (l_hotlist->current) {
463 if (l_hotlist->current->data) {
464 struct hotlist *hlp =
465 (struct hotlist *) l_hotlist->current->data;
466 if (hlp->type == HL_TYPE_ENTRY) {
467 char *tmp =
468 g_strconcat ("cd ", hlp->directory, (char *) NULL);
469 stuff (cmdline, tmp, 0);
470 g_free (tmp);
471 dlg_stop (h);
472 h->ret_value = B_CANCEL;
473 return MSG_HANDLED;
478 return MSG_HANDLED; /* ignore key */
480 return MSG_NOT_HANDLED;
482 case DLG_POST_KEY:
483 if (hotlist_state.moving)
484 dlg_select_widget (l_movelist);
485 else
486 dlg_select_widget (l_hotlist);
487 /* always stay on hotlist */
488 /* fall through */
490 case DLG_INIT:
491 tty_setcolor (MENU_ENTRY_COLOR);
492 update_path_name ();
493 return MSG_HANDLED;
495 case DLG_RESIZE:
496 /* simply call dlg_set_size() with new size */
497 dlg_set_size (h, LINES - 2, COLS - 6);
498 return MSG_HANDLED;
500 default:
501 return default_dlg_callback (h, msg, parm);
505 static int l_call (WListbox *list)
507 Dlg_head *dlg = list->widget.parent;
509 if (list->current){
510 if (list->current->data) {
511 struct hotlist *hlp = (struct hotlist*) list->current->data;
512 if (hlp->type == HL_TYPE_ENTRY) {
513 dlg->ret_value = B_ENTER;
514 dlg_stop (dlg);
515 return LISTBOX_DONE;
516 } else {
517 hotlist_button_callback (B_ENTER);
518 hotlist_callback (dlg, DLG_POST_KEY, '\n');
519 return LISTBOX_CONT;
521 } else {
522 dlg->ret_value = B_ENTER;
523 dlg_stop (dlg);
524 return LISTBOX_DONE;
528 hotlist_button_callback (B_UP_GROUP);
529 hotlist_callback (dlg, DLG_POST_KEY, 'u');
530 return LISTBOX_CONT;
534 * Expands all button names (once) and recalculates button positions.
535 * returns number of columns in the dialog box, which is 10 chars longer
536 * then buttonbar.
538 * If common width of the window (i.e. in xterm) is less than returned
539 * width - sorry :) (anyway this did not handled in previous version too)
541 static int
542 init_i18n_stuff(int list_type, int cols)
544 register int i;
545 static const char* cancel_but = N_("&Cancel");
547 #ifdef ENABLE_NLS
548 static int hotlist_i18n_flag = 0;
550 if (!hotlist_i18n_flag)
552 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
553 while (i--)
554 hotlist_but [i].text = _(hotlist_but [i].text);
556 cancel_but = _(cancel_but);
557 hotlist_i18n_flag = 1;
559 #endif /* ENABLE_NLS */
561 /* Dynamic resizing of buttonbars */
563 int len[2], count[2]; /* at most two lines of buttons */
564 int cur_x[2], row;
566 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
567 len[0] = len[1] = count[0] = count[1] = 0;
569 /* Count len of buttonbars, assuming 2 extra space between buttons */
570 while (i--)
572 if (! (hotlist_but[i].type & list_type))
573 continue;
575 row = hotlist_but [i].y;
576 ++count [row];
577 len [row] += str_term_width1 (hotlist_but [i].text) + 5;
578 if (hotlist_but [i].flags == DEFPUSH_BUTTON)
579 len [row] += 2;
581 len[0] -= 2;
582 len[1] -= 2;
584 cols = max(cols, max(len[0], len[1]));
586 /* arrange buttons */
588 cur_x[0] = cur_x[1] = 0;
589 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
590 while (i--)
592 if (! (hotlist_but[i].type & list_type))
593 continue;
595 row = hotlist_but [i].y;
597 if (hotlist_but [i].x != 0)
599 /* not first int the row */
600 if (!strcmp (hotlist_but [i].text, cancel_but))
601 hotlist_but [i].x =
602 cols - str_term_width1 (hotlist_but [i].text) - 13;
603 else
604 hotlist_but [i].x = cur_x [row];
607 cur_x [row] += str_term_width1 (hotlist_but [i].text) + 2
608 + (hotlist_but [i].flags == DEFPUSH_BUTTON ? 5 : 3);
612 return cols;
615 static void
616 init_hotlist (int list_type)
618 size_t i;
619 const char *title, *help_node;
620 int hotlist_cols;
622 hotlist_cols = init_i18n_stuff (list_type, COLS - 6);
624 do_refresh ();
626 hotlist_state.expanded =
627 mc_config_get_int (mc_main_config, "HotlistConfig", "expanded_view_of_groups", 0);
629 if (list_type == LIST_VFSLIST) {
630 title = _("Active VFS directories");
631 help_node = "[vfshot]"; /* FIXME - no such node */
632 } else {
633 title = _("Directory hotlist");
634 help_node = "[Hotlist]";
637 hotlist_dlg =
638 create_dlg (0, 0, LINES - 2, hotlist_cols, dialog_colors,
639 hotlist_callback, help_node, title, DLG_CENTER | DLG_REVERSE);
641 for (i = 0; i < BUTTONS; i++) {
642 if (hotlist_but[i].type & list_type)
643 add_widget_autopos (hotlist_dlg,
644 button_new (BY + hotlist_but[i].y,
645 BX + hotlist_but[i].x,
646 hotlist_but[i].ret_cmd,
647 hotlist_but[i].flags,
648 hotlist_but[i].text,
649 hotlist_button_callback),
650 hotlist_but[i].pos_flags);
653 /* We add the labels.
654 * pname will hold entry's pathname;
655 * pname_group will hold name of current group
657 pname = label_new (UY - 11 + LINES, UX + 2, "");
658 add_widget_autopos (hotlist_dlg, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
659 if (!hotlist_state.moving) {
660 add_widget_autopos (hotlist_dlg,
661 label_new (UY - 12 + LINES, UX + 1,
662 _(" Directory path ")),
663 WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
665 /* This one holds the displayed pathname */
666 pname_group = label_new (UY, UX + 1, _(" Directory label "));
667 add_widget (hotlist_dlg, pname_group);
669 /* get new listbox */
670 l_hotlist =
671 listbox_new (UY + 1, UX + 1, LINES - 14, COLS - 2 * UX - 8,
672 l_call);
674 /* Fill the hotlist with the active VFS or the hotlist */
675 #ifdef USE_VFS
676 if (list_type == LIST_VFSLIST) {
677 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
678 vfs_fill_names (add_name_to_list);
679 } else
680 #endif /* !USE_VFS */
681 fill_listbox ();
683 add_widget_autopos (hotlist_dlg, l_hotlist, WPOS_KEEP_ALL);
684 /* add listbox to the dialogs */
687 static void
688 init_movelist (int list_type, struct hotlist *item)
690 size_t i;
691 char *hdr = g_strdup_printf (_("Moving %s"), item->label);
692 int movelist_cols = init_i18n_stuff (list_type, COLS - 6);
694 do_refresh ();
696 movelist_dlg =
697 create_dlg (0, 0, LINES - 6, movelist_cols, dialog_colors,
698 hotlist_callback, "[Hotlist]", hdr, DLG_CENTER | DLG_REVERSE);
699 g_free (hdr);
701 for (i = 0; i < BUTTONS; i++) {
702 if (hotlist_but[i].type & list_type)
703 add_widget (movelist_dlg,
704 button_new (BY - 4 + hotlist_but[i].y,
705 BX + hotlist_but[i].x,
706 hotlist_but[i].ret_cmd,
707 hotlist_but[i].flags,
708 hotlist_but[i].text,
709 hotlist_button_callback));
712 /* We add the labels. We are interested in the last one,
713 * that one will hold the path name label
715 movelist_group = label_new (UY, UX + 1, _(" Directory label "));
716 add_widget (movelist_dlg, movelist_group);
717 /* get new listbox */
718 l_movelist =
719 listbox_new (UY + 1, UX + 1, movelist_dlg->lines - 8,
720 movelist_dlg->cols - 2 * UX - 2, l_call);
722 fill_listbox ();
724 add_widget (movelist_dlg, l_movelist);
725 /* add listbox to the dialogs */
729 * Destroy the list dialog.
730 * Don't confuse with done_hotlist() for the list in memory.
732 static void hotlist_done (void)
734 destroy_dlg (hotlist_dlg);
735 l_hotlist = NULL;
736 if (0)
737 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
738 repaint_screen ();
741 static inline char *
742 find_group_section (struct hotlist *grp)
744 return g_strconcat (grp->directory, ".Group", (char *) NULL);
748 /* 1.11.96 bor: added pos parameter to control placement of new item.
749 see widget.c, listbox_add_item()
750 now hotlist is in unsorted mode
752 enum {
753 HL_BEFORE_CURRENT = 1
754 ,HL_AFTER_CURRENT = 2
757 static struct hotlist *
758 add2hotlist (char *label, char *directory, enum HotListType type, int pos)
760 struct hotlist *new;
761 struct hotlist *current = NULL;
764 * Hotlist is neither loaded nor loading.
765 * Must be called by "Ctrl-x a" before using hotlist.
767 if (!current_group)
768 load_hotlist ();
770 if (l_hotlist && l_hotlist->current) {
771 current = l_hotlist->current->data;
773 /* Make sure `..' stays at the top of the list. */
774 if (current->type == HL_TYPE_DOTDOT)
775 pos = HL_AFTER_CURRENT;
778 new = new_hotlist ();
780 new->type = type;
781 new->label = label;
782 new->directory = directory;
783 new->up = current_group;
785 if (type == HL_TYPE_GROUP) {
786 current_group = new;
787 add_dotdot_to_list ();
788 current_group = new->up;
791 if (!current_group->head) { /* first element in group */
792 current_group->head = new;
793 } else if (pos == HL_AFTER_CURRENT) {
794 new->next = current->next;
795 current->next = new;
796 } else if (pos == HL_BEFORE_CURRENT &&
797 current == current_group->head) {
798 /* should be inserted before first item */
799 new->next = current;
800 current_group->head = new;
801 } else if (pos == HL_BEFORE_CURRENT) {
802 struct hotlist *p = current_group->head;
804 while (p->next != current)
805 p = p->next;
807 new->next = current;
808 p->next = new;
809 } else { /* append at the end */
810 struct hotlist *p = current_group->head;
812 while (p->next)
813 p = p->next;
815 p->next = new;
818 if (hotlist_state.running && type != HL_TYPE_COMMENT &&
819 type != HL_TYPE_DOTDOT) {
820 if (type == HL_TYPE_GROUP) {
821 char *lbl = g_strconcat ("->", new->label, (char *) NULL);
823 listbox_add_item (l_hotlist, pos, 0, lbl, new);
824 g_free (lbl);
825 } else
826 listbox_add_item (l_hotlist, pos, 0, new->label, new);
827 listbox_select_entry (l_hotlist, l_hotlist->current);
829 return new;
833 #ifdef ENABLE_NLS
835 * Support routine for add_new_entry_input()/add_new_group_input()
836 * Change positions of buttons (first three widgets).
838 * This is just a quick hack. Accurate procedure must take care of
839 * internationalized label lengths and total buttonbar length...assume
840 * 64 is longer anyway.
842 static void add_widgets_i18n(QuickWidget* qw, int len)
844 int i, l[3], space, cur_x;
846 for (i = 0; i < 3; i++)
848 qw [i].text = _(qw [i].text);
849 l[i] = str_term_width1 (qw [i].text) + 3;
851 space = (len - 4 - l[0] - l[1] - l[2]) / 4;
853 for (cur_x = 2 + space, i = 3; i--; cur_x += l[i] + space)
855 qw [i].relative_x = cur_x;
856 qw [i].x_divisions = len;
859 #endif /* ENABLE_NLS */
861 static int
862 add_new_entry_input (const char *header, const char *text1, const char *text2,
863 const char *help, char **r1, char **r2)
865 #define RELATIVE_Y_BUTTONS 4
866 #define RELATIVE_Y_LABEL_PTH 3
867 #define RELATIVE_Y_INPUT_PTH 4
869 QuickDialog Quick_input;
870 static QuickWidget quick_widgets [] = {
871 { quick_button, 55, 80, RELATIVE_Y_BUTTONS, 0, N_("&Cancel"), 0, B_CANCEL,
872 0, 0, NULL , NULL, NULL},
873 { quick_button, 30, 80, RELATIVE_Y_BUTTONS, 0, N_("&Insert"), 0, B_INSERT,
874 0, 0, NULL , NULL, NULL},
875 { quick_button, 10, 80, RELATIVE_Y_BUTTONS, 0, N_("&Append"), 0, B_APPEND,
876 0, 0, NULL , NULL, NULL},
877 { quick_input, 4, 80, RELATIVE_Y_INPUT_PTH, 0, "",58, 0,
878 0, 0, "input-pth" , NULL, NULL},
879 { quick_label, RELATIVE_Y_LABEL_PTH, 80, 3, 0, 0, 0, 0,
880 0, 0, NULL , NULL, NULL},
881 { quick_input, 4, 80, 3, 0, "", 58, 0,
882 0, 0, "input-lbl" , NULL, NULL},
883 { quick_label, 3, 80, 2, 0, 0, 0, 0,
884 0, 0, NULL , NULL, NULL},
885 NULL_QuickWidget };
887 int len;
888 int i;
889 int lines1, lines2;
890 int cols1, cols2;
892 #ifdef ENABLE_NLS
893 static int i18n_flag = 0;
894 #endif /* ENABLE_NLS */
896 msglen(text1, &lines1, &cols1);
897 msglen(text2, &lines2, &cols2);
898 len = max (str_term_width1 (header), cols1);
899 len = max (len, cols2) + 4;
900 len = max (len, 64);
902 #ifdef ENABLE_NLS
903 if (!i18n_flag)
905 add_widgets_i18n(quick_widgets, len);
906 i18n_flag = 1;
908 #endif /* ENABLE_NLS */
910 Quick_input.xlen = len;
911 Quick_input.xpos = -1;
912 Quick_input.title = header;
913 Quick_input.help = help;
914 Quick_input.i18n = 0;
915 quick_widgets [6].text = text1;
916 quick_widgets [4].text = text2;
917 quick_widgets [5].text = *r1;
918 quick_widgets [3].text = *r2;
920 for (i = 0; i < 7; i++)
921 quick_widgets [i].y_divisions = lines1+lines2+7;
922 Quick_input.ylen = lines1 + lines2 + 7;
924 quick_widgets [0].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
925 quick_widgets [1].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
926 quick_widgets [2].relative_y = RELATIVE_Y_BUTTONS + (lines1 + lines2);
927 quick_widgets [3].relative_y = RELATIVE_Y_INPUT_PTH + (lines1);
928 quick_widgets [4].relative_y = RELATIVE_Y_LABEL_PTH + (lines1);
930 quick_widgets [5].str_result = r1;
931 quick_widgets [3].str_result = r2;
933 Quick_input.widgets = quick_widgets;
934 if ((i = quick_dialog (&Quick_input)) != B_CANCEL){
935 return i;
936 } else
937 return 0;
940 static void add_new_entry_cmd (void)
942 char *title, *url, *to_free;
943 int ret;
945 /* Take current directory as default value for input fields */
946 to_free = title = url = strip_password (g_strdup (current_panel->cwd), 1);
948 ret = add_new_entry_input (_("New hotlist entry"), _("Directory label"),
949 _("Directory path"), "[Hotlist]", &title, &url);
950 g_free (to_free);
952 if (!ret)
953 return;
954 if (!title || !*title || !url || !*url) {
955 g_free (title);
956 g_free (url);
957 return;
960 if (ret == B_ENTER || ret == B_APPEND)
961 add2hotlist (title, url, HL_TYPE_ENTRY, HL_AFTER_CURRENT);
962 else
963 add2hotlist (title, url, HL_TYPE_ENTRY, HL_BEFORE_CURRENT);
965 hotlist_state.modified = 1;
968 static int
969 add_new_group_input (const char *header, const char *label, char **result)
971 int ret;
972 QuickDialog Quick_input;
973 static QuickWidget quick_widgets [] = {
974 { quick_button, 55, 80, 1, 0, N_("&Cancel"), 0, B_CANCEL, 0, 0,
975 NULL , NULL, NULL},
976 { quick_button, 30, 80, 1, 0, N_("&Insert"), 0, B_INSERT, 0, 0,
977 NULL , NULL, NULL},
978 { quick_button, 10, 80, 1, 0, N_("&Append"), 0, B_APPEND, 0, 0,
979 NULL , NULL, NULL},
980 { quick_input, 4, 80, 0, 0, "", 58, 0, 0, 0, "input" , NULL, NULL},
981 { quick_label, 3, 80, 2, 0, 0, 0, 0, 0, 0, NULL , NULL, NULL},
982 NULL_QuickWidget };
983 int relative_y[] = {1, 1, 1, 0, 2}; /* the relative_x component from the
984 quick_widgets variable above */
985 int len;
986 int i;
987 int lines, cols;
989 #ifdef ENABLE_NLS
990 static int i18n_flag = 0;
991 #endif /* ENABLE_NLS */
993 msglen (label, &lines, &cols);
994 len = max (str_term_width1 (header), cols) + 4;
995 len = max (len, 64);
997 #ifdef ENABLE_NLS
998 if (!i18n_flag)
1000 add_widgets_i18n(quick_widgets, len);
1001 i18n_flag = 1;
1003 #endif /* ENABLE_NLS */
1005 Quick_input.xlen = len;
1006 Quick_input.xpos = -1;
1007 Quick_input.title = header;
1008 Quick_input.help = "[Hotlist]";
1009 Quick_input.i18n = 0;
1010 quick_widgets [4].text = label;
1012 for (i = 0; i < 5; i++)
1013 quick_widgets [i].y_divisions = lines+6;
1014 Quick_input.ylen = lines + 6;
1016 for (i = 0; i < 4; i++)
1017 quick_widgets [i].relative_y = relative_y[i] + 2 + lines;
1019 quick_widgets [3].str_result = result;
1020 quick_widgets [3].text = "";
1022 Quick_input.widgets = quick_widgets;
1023 if ((ret = quick_dialog (&Quick_input)) != B_CANCEL){
1024 return ret;
1025 } else
1026 return 0;
1029 static void add_new_group_cmd (void)
1031 char *label;
1032 int ret;
1034 ret = add_new_group_input (_(" New hotlist group "), _("Name of new group"), &label);
1035 if (!ret || !label || !*label)
1036 return;
1038 if (ret == B_ENTER || ret == B_APPEND)
1039 add2hotlist (label, 0, HL_TYPE_GROUP, HL_AFTER_CURRENT);
1040 else
1041 add2hotlist (label, 0, HL_TYPE_GROUP, HL_BEFORE_CURRENT);
1043 hotlist_state.modified = 1;
1046 void add2hotlist_cmd (void)
1048 char *prompt, *label;
1049 const char *cp = _("Label for \"%s\":");
1050 int l = str_term_width1 (cp);
1051 char *label_string = g_strdup (current_panel->cwd);
1053 strip_password (label_string, 1);
1055 prompt = g_strdup_printf (cp, path_trunc (current_panel->cwd, COLS-2*UX-(l+8)));
1056 label = input_dialog (_(" Add to hotlist "), prompt, MC_HISTORY_HOTLIST_ADD, label_string);
1057 g_free (prompt);
1059 if (!label || !*label) {
1060 g_free (label_string);
1061 g_free (label);
1062 return;
1064 add2hotlist (label, label_string, HL_TYPE_ENTRY, 0);
1065 hotlist_state.modified = 1;
1068 static void remove_group (struct hotlist *grp)
1070 struct hotlist *current = grp->head;
1072 while (current) {
1073 struct hotlist *next = current->next;
1075 if (current->type == HL_TYPE_GROUP)
1076 remove_group (current);
1078 g_free (current->label);
1079 g_free (current->directory);
1080 g_free (current);
1082 current = next;
1087 static void remove_from_hotlist (struct hotlist *entry)
1089 if (entry->type == HL_TYPE_DOTDOT)
1090 return;
1092 if (confirm_directory_hotlist_delete) {
1093 char *title;
1094 int result;
1096 title = g_strconcat (_(" Remove: "),
1097 str_trunc (entry->label, 30),
1098 " ",
1099 NULL);
1101 if (safe_delete)
1102 query_set_sel (1);
1103 result = query_dialog (title,
1104 _("\n Are you sure you want to remove this entry?"),
1105 D_ERROR, 2, _("&Yes"), _("&No"));
1107 g_free (title);
1109 if (result != 0)
1110 return;
1113 if (entry->type == HL_TYPE_GROUP) {
1114 if (entry->head) {
1115 char *header;
1116 int result;
1118 header = g_strconcat (_(" Remove: "),
1119 str_trunc (entry->label, 30),
1120 " ",
1121 NULL);
1122 result = query_dialog (header, _("\n Group not empty.\n Remove it?"),
1123 D_ERROR, 2,
1124 _("&Yes"), _("&No"));
1125 g_free (header);
1127 if (result != 0)
1128 return;
1131 remove_group (entry);
1134 unlink_entry (entry);
1136 g_free (entry->label);
1137 g_free (entry->directory);
1138 g_free (entry);
1139 /* now remove list entry from screen */
1140 listbox_remove_current (l_hotlist, 1);
1141 hotlist_state.modified = 1;
1144 char *hotlist_cmd (int vfs_or_hotlist)
1146 char *target = NULL;
1148 hotlist_state.type = vfs_or_hotlist;
1149 load_hotlist ();
1151 init_hotlist (vfs_or_hotlist);
1153 /* display file info */
1154 tty_setcolor (SELECTED_COLOR);
1156 hotlist_state.running = 1;
1157 run_dlg (hotlist_dlg);
1158 hotlist_state.running = 0;
1159 save_hotlist ();
1161 switch (hotlist_dlg->ret_value) {
1162 case B_CANCEL:
1163 break;
1165 case B_ENTER:
1166 if (l_hotlist->current->data) {
1167 struct hotlist *hlp = (struct hotlist*) l_hotlist->current->data;
1168 target = g_strdup (hlp->directory);
1169 } else
1170 target = g_strdup (l_hotlist->current->text);
1171 break;
1174 hotlist_done ();
1175 return target;
1178 static void
1179 load_group (struct hotlist *grp)
1181 gchar **profile_keys, **keys;
1182 gsize len;
1183 char *group_section;
1184 struct hotlist *current = 0;
1186 group_section = find_group_section (grp);
1188 profile_keys = keys = mc_config_get_keys (mc_main_config, group_section, &len);
1190 current_group = grp;
1192 while (*profile_keys){
1193 add2hotlist (
1194 mc_config_get_string(mc_main_config, group_section, *profile_keys, ""),
1195 g_strdup (*profile_keys),
1196 HL_TYPE_GROUP,
1198 profile_keys++;
1200 g_free (group_section);
1201 g_strfreev(keys);
1203 profile_keys = keys = mc_config_get_keys (mc_main_config, grp->directory,&len);
1205 while (*profile_keys){
1206 add2hotlist (
1207 mc_config_get_string(mc_main_config,group_section,*profile_keys,""),
1208 g_strdup (*profile_keys),
1209 HL_TYPE_ENTRY,
1211 profile_keys++;
1213 g_strfreev(keys);
1215 for (current = grp->head; current; current = current->next)
1216 load_group (current);
1219 #define TKN_GROUP 0
1220 #define TKN_ENTRY 1
1221 #define TKN_STRING 2
1222 #define TKN_URL 3
1223 #define TKN_ENDGROUP 4
1224 #define TKN_COMMENT 5
1225 #define TKN_EOL 125
1226 #define TKN_EOF 126
1227 #define TKN_UNKNOWN 127
1229 static GString *tkn_buf = NULL;
1231 static char *hotlist_file_name;
1232 static FILE *hotlist_file;
1233 static time_t hotlist_file_mtime;
1235 static int hot_skip_blanks (void)
1237 int c;
1239 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1241 return c;
1245 static int hot_next_token (void)
1247 int c, ret=0;
1248 size_t l;
1251 if (tkn_buf == NULL) tkn_buf = g_string_new ("");
1252 g_string_set_size(tkn_buf,0);
1254 again:
1255 c = hot_skip_blanks ();
1256 switch (c) {
1257 case EOF:
1258 ret = TKN_EOF;
1259 break;
1260 case '\n':
1261 ret = TKN_EOL;
1262 break;
1263 case '#':
1264 while ((c = getc (hotlist_file)) != EOF && c != '\n') {
1265 g_string_append_c (tkn_buf, c);
1267 ret = TKN_COMMENT;
1268 break;
1269 case '"':
1270 while ((c = getc (hotlist_file)) != EOF && c != '"') {
1271 if (c == '\\')
1272 if ((c = getc (hotlist_file)) == EOF){
1273 g_string_free (tkn_buf, TRUE);
1274 return TKN_EOF;
1276 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1278 if (c == EOF)
1279 ret = TKN_EOF;
1280 else
1281 ret = TKN_STRING;
1282 break;
1283 case '\\':
1284 if ((c = getc (hotlist_file)) == EOF){
1285 g_string_free (tkn_buf, TRUE);
1286 return TKN_EOF;
1288 if (c == '\n')
1289 goto again;
1291 /* fall through; it is taken as normal character */
1293 default:
1294 do {
1295 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1296 } while ((c = fgetc (hotlist_file)) != EOF &&
1297 (g_ascii_isalnum (c) || !isascii (c)));
1298 if (c != EOF)
1299 ungetc (c, hotlist_file);
1300 l = tkn_buf->len;
1301 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1302 ret = TKN_GROUP;
1303 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1304 ret = TKN_ENTRY;
1305 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1306 ret = TKN_ENDGROUP;
1307 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1308 ret = TKN_URL;
1309 else
1310 ret = TKN_UNKNOWN;
1311 break;
1313 return ret;
1316 #define SKIP_TO_EOL { \
1317 int _tkn; \
1318 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
1321 #define CHECK_TOKEN(_TKN_) \
1322 if ((tkn = hot_next_token ()) != _TKN_) { \
1323 hotlist_state.readonly = 1; \
1324 hotlist_state.file_error = 1; \
1325 while (tkn != TKN_EOL && tkn != TKN_EOF) \
1326 tkn = hot_next_token (); \
1327 break; \
1330 static void
1331 hot_load_group (struct hotlist * grp)
1333 int tkn;
1334 struct hotlist *new_grp;
1335 char *label, *url;
1337 current_group = grp;
1339 while ((tkn = hot_next_token()) != TKN_ENDGROUP)
1340 switch (tkn) {
1341 case TKN_GROUP:
1342 CHECK_TOKEN(TKN_STRING);
1343 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1344 SKIP_TO_EOL;
1345 hot_load_group (new_grp);
1346 current_group = grp;
1347 break;
1348 case TKN_ENTRY:
1349 CHECK_TOKEN(TKN_STRING);
1350 label = g_strdup (tkn_buf->str);
1351 CHECK_TOKEN(TKN_URL);
1352 CHECK_TOKEN(TKN_STRING);
1353 url = g_strdup (tkn_buf->str);
1354 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1355 SKIP_TO_EOL;
1356 break;
1357 case TKN_COMMENT:
1358 label = g_strdup (tkn_buf->str);
1359 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1360 break;
1361 case TKN_EOF:
1362 hotlist_state.readonly = 1;
1363 hotlist_state.file_error = 1;
1364 return;
1365 break;
1366 case TKN_EOL:
1367 /* skip empty lines */
1368 break;
1369 default:
1370 hotlist_state.readonly = 1;
1371 hotlist_state.file_error = 1;
1372 SKIP_TO_EOL;
1373 break;
1375 SKIP_TO_EOL;
1378 static void
1379 hot_load_file (struct hotlist * grp)
1381 int tkn;
1382 struct hotlist *new_grp;
1383 char *label, *url;
1385 current_group = grp;
1387 while ((tkn = hot_next_token())!= TKN_EOF)
1388 switch (tkn) {
1389 case TKN_GROUP:
1390 CHECK_TOKEN(TKN_STRING);
1391 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1392 SKIP_TO_EOL;
1393 hot_load_group (new_grp);
1394 current_group = grp;
1395 break;
1396 case TKN_ENTRY:
1397 CHECK_TOKEN(TKN_STRING);
1398 label = g_strdup (tkn_buf->str);
1399 CHECK_TOKEN(TKN_URL);
1400 CHECK_TOKEN(TKN_STRING);
1401 url = g_strdup (tkn_buf->str);
1402 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1403 SKIP_TO_EOL;
1404 break;
1405 case TKN_COMMENT:
1406 label = g_strdup (tkn_buf->str);
1407 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1408 break;
1409 case TKN_EOL:
1410 /* skip empty lines */
1411 break;
1412 default:
1413 hotlist_state.readonly = 1;
1414 hotlist_state.file_error = 1;
1415 SKIP_TO_EOL;
1416 break;
1420 static void
1421 clean_up_hotlist_groups (const char *section)
1423 char *grp_section;
1424 gchar **profile_keys, **keys;
1425 gsize len;
1427 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1428 if (mc_config_has_group(mc_main_config, section))
1429 mc_config_del_group (mc_main_config, section);
1431 if (mc_config_has_group (mc_main_config, grp_section)) {
1432 profile_keys = keys = mc_config_get_keys (mc_main_config, grp_section,&len);
1434 while (*profile_keys) {
1435 clean_up_hotlist_groups (*profile_keys);
1436 profile_keys++;
1438 g_strfreev(keys);
1439 mc_config_del_group (mc_main_config, grp_section);
1441 g_free (grp_section);
1446 static void
1447 load_hotlist (void)
1449 int remove_old_list = 0;
1450 struct stat stat_buf;
1452 if (hotlist_state.loaded) {
1453 stat (hotlist_file_name, &stat_buf);
1454 if (hotlist_file_mtime < stat_buf.st_mtime)
1455 done_hotlist ();
1456 else
1457 return;
1460 if (!hotlist_file_name)
1461 hotlist_file_name = concat_dir_and_file (home_dir, HOTLIST_FILENAME);
1463 hotlist = new_hotlist ();
1464 hotlist->type = HL_TYPE_GROUP;
1465 hotlist->label = g_strdup (_(" Top level group "));
1466 hotlist->up = hotlist;
1468 * compatibility :-(
1470 hotlist->directory = g_strdup ("Hotlist");
1472 if ((hotlist_file = fopen (hotlist_file_name, "r")) == 0) {
1473 int result;
1475 load_group (hotlist);
1476 hotlist_state.loaded = 1;
1478 * just to be sure we got copy
1480 hotlist_state.modified = 1;
1481 result = save_hotlist ();
1482 hotlist_state.modified = 0;
1483 if (result) {
1484 remove_old_list = 1;
1485 } else {
1486 message (D_ERROR, _(" Hotlist Load "),
1487 _("MC was unable to write ~/%s file, your old hotlist entries were not deleted"),
1488 HOTLIST_FILENAME);
1490 } else {
1491 hot_load_file (hotlist);
1492 fclose (hotlist_file);
1493 hotlist_state.loaded = 1;
1496 if (remove_old_list) {
1497 clean_up_hotlist_groups ("Hotlist");
1498 mc_config_save_file (mc_main_config);
1501 stat (hotlist_file_name, &stat_buf);
1502 hotlist_file_mtime = stat_buf.st_mtime;
1503 current_group = hotlist;
1507 static int list_level = 0;
1509 static void
1510 hot_save_group (struct hotlist *grp)
1512 struct hotlist *current = grp->head;
1513 int i;
1514 char *s;
1516 #define INDENT(n) \
1517 do { \
1518 for (i = 0; i < n; i++) \
1519 putc (' ', hotlist_file); \
1520 } while (0)
1522 for (;current; current = current->next)
1523 switch (current->type) {
1524 case HL_TYPE_GROUP:
1525 INDENT (list_level);
1526 fputs ("GROUP \"", hotlist_file);
1527 for (s = current->label; *s; s++) {
1528 if (*s == '"' || *s == '\\')
1529 putc ('\\', hotlist_file);
1530 putc (*s, hotlist_file);
1532 fputs ("\"\n", hotlist_file);
1533 list_level += 2;
1534 hot_save_group (current);
1535 list_level -= 2;
1536 INDENT (list_level);
1537 fputs ("ENDGROUP\n", hotlist_file);
1538 break;
1539 case HL_TYPE_ENTRY:
1540 INDENT(list_level);
1541 fputs ("ENTRY \"", hotlist_file);
1542 for (s = current->label; *s; s++) {
1543 if (*s == '"' || *s == '\\')
1544 putc ('\\', hotlist_file);
1545 putc (*s, hotlist_file);
1547 fputs ("\" URL \"", hotlist_file);
1548 for (s = current->directory; *s; s++) {
1549 if (*s == '"' || *s == '\\')
1550 putc ('\\', hotlist_file);
1551 putc (*s, hotlist_file);
1553 fputs ("\"\n", hotlist_file);
1554 break;
1555 case HL_TYPE_COMMENT:
1556 fprintf (hotlist_file, "#%s\n", current->label);
1557 break;
1558 case HL_TYPE_DOTDOT:
1559 /* do nothing */
1560 break;
1564 int save_hotlist (void)
1566 int saved = 0;
1567 struct stat stat_buf;
1569 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name) {
1570 char *fbak = g_strconcat (hotlist_file_name, ".bak", (char *) NULL);
1572 rename (hotlist_file_name, fbak);
1573 if ((hotlist_file = fopen (hotlist_file_name, "w")) != 0) {
1574 if (stat (fbak, &stat_buf) == 0)
1575 chmod (hotlist_file_name, stat_buf.st_mode);
1576 else
1577 chmod (hotlist_file_name, S_IRUSR | S_IWUSR);
1578 hot_save_group (hotlist);
1579 fclose (hotlist_file);
1580 stat (hotlist_file_name, &stat_buf);
1581 hotlist_file_mtime = stat_buf.st_mtime;
1582 saved = 1;
1583 hotlist_state.modified = 0;
1584 } else
1585 rename (fbak, hotlist_file_name);
1586 g_free (fbak);
1589 return saved;
1593 * Unload list from memory.
1594 * Don't confuse with hotlist_done() for GUI.
1596 void done_hotlist (void)
1598 if (hotlist){
1599 remove_group (hotlist);
1600 g_free (hotlist->label);
1601 g_free (hotlist->directory);
1602 g_free (hotlist);
1603 hotlist = 0;
1606 hotlist_state.loaded = 0;
1608 g_free (hotlist_file_name);
1609 hotlist_file_name = 0;
1610 l_hotlist = 0;
1611 current_group = 0;
1613 if (tkn_buf){
1614 g_string_free (tkn_buf, TRUE);
1615 tkn_buf = NULL;
1619 static void
1620 add_dotdot_to_list (void)
1622 if (current_group != hotlist) {
1623 if (hotlist_has_dot_dot != 0)
1624 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, 0);