Ticket #1790: mc crashes on start
[midnight-commander.git] / src / hotlist.c
blob26ebfbd224da185d83742a01984fdde79b5e1d29
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/skin/skin.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 "history.h"
60 #include "strutil.h"
61 #include "util.h"
62 #include "fileloc.h"
64 #define UX 5
65 #define UY 2
67 #define BX UX
68 #define BY (LINES - 6)
70 #define BUTTONS (sizeof(hotlist_but)/sizeof(struct _hotlist_but))
71 #define LABELS 3
72 #define B_ADD_CURRENT B_USER
73 #define B_REMOVE (B_USER + 1)
74 #define B_NEW_GROUP (B_USER + 2)
75 #define B_NEW_ENTRY (B_USER + 3)
76 #define B_UP_GROUP (B_USER + 4)
77 #define B_INSERT (B_USER + 5)
78 #define B_APPEND (B_USER + 6)
79 #define B_MOVE (B_USER + 7)
81 #ifdef USE_VFS
82 #include "../vfs/gc.h"
83 #define B_FREE_ALL_VFS (B_USER + 8)
84 #define B_REFRESH_VFS (B_USER + 9)
85 #endif
87 int hotlist_has_dot_dot = 1;
89 static WListbox *l_hotlist;
90 static WListbox *l_movelist;
92 static Dlg_head *hotlist_dlg;
93 static Dlg_head *movelist_dlg;
95 static WLabel *pname, *pname_group, *movelist_group;
97 enum HotListType {
98 HL_TYPE_GROUP,
99 HL_TYPE_ENTRY,
100 HL_TYPE_COMMENT,
101 HL_TYPE_DOTDOT
104 static struct {
106 * these parameters are intended to be user configurable
108 int expanded; /* expanded view of all groups at startup */
111 * these reflect run time state
114 int loaded; /* hotlist is loaded */
115 int readonly; /* hotlist readonly */
116 int file_error; /* parse error while reading file */
117 int running; /* we are running dlg (and have to
118 update listbox */
119 int moving; /* we are in moving hotlist currently */
120 int modified; /* hotlist was modified */
121 int type; /* LIST_HOTLIST || LIST_VFSLIST */
122 } hotlist_state;
124 static struct _hotlist_but {
125 int ret_cmd, flags, y, x;
126 const char *text;
127 int type;
128 widget_pos_flags_t pos_flags;
129 } hotlist_but[] = {
130 { B_MOVE, NORMAL_BUTTON, 1, 42, N_("&Move"),
131 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
132 { B_REMOVE, NORMAL_BUTTON, 1, 30, N_("&Remove"),
133 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
134 { B_APPEND, NORMAL_BUTTON, 1, 15, N_("&Append"),
135 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
136 { B_INSERT, NORMAL_BUTTON, 1, 0, N_("&Insert"),
137 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
138 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, N_("New &Entry"),
139 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
140 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, N_("New &Group"),
141 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
142 { B_CANCEL, NORMAL_BUTTON, 0, 53, N_("&Cancel"),
143 LIST_HOTLIST | LIST_VFSLIST|LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
144 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, N_("&Up"),
145 LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
146 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, N_("&Add current"),
147 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
148 #ifdef USE_VFS
149 { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, N_("&Refresh"),
150 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
151 { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, N_("Fr&ee VFSs now"),
152 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
153 #endif
154 { B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("Change &To"),
155 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
158 /* Directory hotlist */
159 static struct hotlist{
160 enum HotListType type;
161 char *directory;
162 char *label;
163 struct hotlist *head;
164 struct hotlist *up;
165 struct hotlist *next;
166 } *hotlist = NULL;
168 static struct hotlist *current_group;
170 static void init_movelist (int, struct hotlist *);
171 static void add_new_group_cmd (void);
172 static void add_new_entry_cmd (void);
173 static void remove_from_hotlist (struct hotlist *entry);
174 static void load_hotlist (void);
175 static void add_dotdot_to_list (void);
177 #define new_hotlist() g_new0(struct hotlist, 1)
179 static void
180 hotlist_refresh (Dlg_head * dlg)
182 common_dialog_repaint (dlg);
183 tty_setcolor (COLOR_NORMAL);
184 draw_box (dlg, 2, 5, dlg->lines - (hotlist_state.moving ? 6 : 10),
185 dlg->cols - (UX * 2));
186 if (!hotlist_state.moving)
187 draw_box (dlg, dlg->lines - 8, 5, 3, dlg->cols - (UX * 2));
190 /* If current->data is 0, then we are dealing with a VFS pathname */
191 static void
192 update_path_name (void)
194 const char *text = "";
195 char *p;
196 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
197 Dlg_head *dlg = list->widget.parent;
199 if (list->current) {
200 if (list->current->data != 0) {
201 struct hotlist *hlp = (struct hotlist *) list->current->data;
203 if (hlp->type == HL_TYPE_ENTRY ||
204 hlp->type == HL_TYPE_DOTDOT)
205 text = hlp->directory;
206 else if (hlp->type == HL_TYPE_GROUP)
207 text = _("Subgroup - press ENTER to see list");
208 } else {
209 text = list->current->text;
212 if (!hotlist_state.moving)
213 label_set_text (pname,
214 str_trunc (text, dlg->cols - (UX * 2 + 4)));
216 p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
217 if (!hotlist_state.moving)
218 label_set_text (pname_group,
219 str_trunc (p, dlg->cols - (UX * 2 + 4)));
220 else
221 label_set_text (movelist_group,
222 str_trunc (p, dlg->cols - (UX * 2 + 4)));
223 g_free (p);
225 dlg_redraw (dlg);
228 #define CHECK_BUFFER \
229 do { \
230 int i; \
232 if ((i = strlen (current->label) + 3) > buflen) { \
233 g_free (buf); \
234 buf = g_malloc (buflen = 1024 * (i/1024 + 1)); \
236 buf[0] = '\0'; \
237 } while (0)
239 static void fill_listbox (void)
241 struct hotlist *current = current_group->head;
242 GString *buff = g_string_new ("");
244 while (current){
245 switch (current->type) {
246 case HL_TYPE_GROUP:
248 /* buff clean up */
249 g_string_truncate(buff, 0);
250 g_string_append(buff,"->");
251 g_string_append(buff,current->label);
252 if (hotlist_state.moving)
253 listbox_add_item (l_movelist, 0, 0, buff->str, current);
254 else
255 listbox_add_item (l_hotlist, 0, 0, buff->str, current);
257 break;
258 case HL_TYPE_DOTDOT:
259 case HL_TYPE_ENTRY:
260 if (hotlist_state.moving)
261 listbox_add_item (l_movelist, 0, 0, current->label, current);
262 else
263 listbox_add_item (l_hotlist, 0, 0, current->label, current);
264 break;
265 default:
266 break;
268 current = current->next;
270 g_string_free (buff, TRUE);
273 static void
274 unlink_entry (struct hotlist *entry)
276 struct hotlist *current = current_group->head;
278 if (current == entry)
279 current_group->head = entry->next;
280 else {
281 while (current && current->next != entry)
282 current = current->next;
283 if (current)
284 current->next = entry->next;
286 entry->next =
287 entry->up = 0;
290 #ifdef USE_VFS
291 static void add_name_to_list (const char *path)
293 listbox_add_item (l_hotlist, 0, 0, path, 0);
295 #endif /* !USE_VFS */
297 static int
298 hotlist_button_callback (int action)
300 switch (action) {
301 case B_MOVE:
303 struct hotlist *saved = current_group;
304 struct hotlist *item;
305 struct hotlist *moveto_item = 0;
306 struct hotlist *moveto_group = 0;
307 int ret;
309 if (!l_hotlist->current)
310 return MSG_NOT_HANDLED; /* empty group - nothing to do */
311 item = l_hotlist->current->data;
312 hotlist_state.moving = 1;
313 init_movelist (LIST_MOVELIST, item);
314 run_dlg (movelist_dlg);
315 ret = movelist_dlg->ret_value;
316 hotlist_state.moving = 0;
317 if (l_movelist->current)
318 moveto_item = l_movelist->current->data;
319 moveto_group = current_group;
320 destroy_dlg (movelist_dlg);
321 current_group = saved;
322 if (ret == B_CANCEL)
323 return MSG_NOT_HANDLED;
324 if (moveto_item == item)
325 return MSG_NOT_HANDLED; /* If we insert/append a before/after a
326 it hardly changes anything ;) */
327 unlink_entry (item);
328 listbox_remove_current (l_hotlist, 1);
329 item->up = moveto_group;
330 if (!moveto_group->head)
331 moveto_group->head = item;
332 else if (!moveto_item) { /* we have group with just comments */
333 struct hotlist *p = moveto_group->head;
335 /* skip comments */
336 while (p->next)
337 p = p->next;
338 p->next = item;
339 } else if (ret == B_ENTER || ret == B_APPEND)
340 if (!moveto_item->next)
341 moveto_item->next = item;
342 else {
343 item->next = moveto_item->next;
344 moveto_item->next = item;
345 } else if (moveto_group->head == moveto_item) {
346 moveto_group->head = item;
347 item->next = moveto_item;
348 } else {
349 struct hotlist *p = moveto_group->head;
351 while (p->next != moveto_item)
352 p = p->next;
353 item->next = p->next;
354 p->next = item;
356 listbox_remove_list (l_hotlist);
357 fill_listbox ();
358 repaint_screen ();
359 hotlist_state.modified = 1;
360 return MSG_NOT_HANDLED;
361 break;
363 case B_REMOVE:
364 if (l_hotlist->current && l_hotlist->current->data)
365 remove_from_hotlist (l_hotlist->current->data);
366 return MSG_NOT_HANDLED;
367 break;
369 case B_NEW_GROUP:
370 add_new_group_cmd ();
371 return MSG_NOT_HANDLED;
372 break;
374 case B_ADD_CURRENT:
375 add2hotlist_cmd ();
376 return MSG_NOT_HANDLED;
377 break;
379 case B_NEW_ENTRY:
380 add_new_entry_cmd ();
381 return MSG_NOT_HANDLED;
382 break;
384 case B_ENTER:
386 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
387 if (list->current) {
388 if (list->current->data) {
389 struct hotlist *hlp =
390 (struct hotlist *) list->current->data;
391 if (hlp->type == HL_TYPE_ENTRY)
392 return MSG_HANDLED;
393 else if (hlp->type == HL_TYPE_DOTDOT) {
394 /* Fall through - go up */
397 else {
398 listbox_remove_list (list);
399 current_group = hlp;
400 fill_listbox ();
401 return MSG_NOT_HANDLED;
403 } else
404 return MSG_HANDLED;
407 /* Fall through if list empty - just go up */
409 case B_UP_GROUP:
411 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
412 listbox_remove_list (list);
413 current_group = current_group->up;
414 fill_listbox ();
415 return MSG_NOT_HANDLED;
416 break;
419 #ifdef USE_VFS
420 case B_FREE_ALL_VFS:
421 vfs_expire (1);
422 /* fall through */
424 case B_REFRESH_VFS:
425 listbox_remove_list (l_hotlist);
426 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
427 vfs_fill_names (add_name_to_list);
428 return MSG_NOT_HANDLED;
429 #endif /* USE_VFS */
431 default:
432 return MSG_HANDLED;
433 break;
438 static cb_ret_t
439 hotlist_callback (Dlg_head *h, dlg_msg_t msg, int parm)
441 switch (msg) {
442 case DLG_DRAW:
443 hotlist_refresh (h);
444 return MSG_HANDLED;
446 case DLG_UNHANDLED_KEY:
447 switch (parm) {
448 case KEY_M_CTRL | '\n':
449 goto l1;
450 case '\n':
451 case KEY_ENTER:
452 case KEY_RIGHT:
453 if (hotlist_button_callback (B_ENTER)) {
454 h->ret_value = B_ENTER;
455 dlg_stop (h);
457 return MSG_HANDLED;
458 break;
459 case KEY_LEFT:
460 if (hotlist_state.type != LIST_VFSLIST)
461 return !hotlist_button_callback (B_UP_GROUP);
462 else
463 return MSG_NOT_HANDLED;
464 break;
465 case KEY_DC:
466 if (!hotlist_state.moving) {
467 hotlist_button_callback (B_REMOVE);
468 return MSG_HANDLED;
470 break;
472 case ALT ('\n'):
473 case ALT ('\r'):
474 if (!hotlist_state.moving) {
475 if (l_hotlist->current) {
476 if (l_hotlist->current->data) {
477 struct hotlist *hlp =
478 (struct hotlist *) l_hotlist->current->data;
479 if (hlp->type == HL_TYPE_ENTRY) {
480 char *tmp =
481 g_strconcat ("cd ", hlp->directory, (char *) NULL);
482 stuff (cmdline, tmp, 0);
483 g_free (tmp);
484 dlg_stop (h);
485 h->ret_value = B_CANCEL;
486 return MSG_HANDLED;
491 return MSG_HANDLED; /* ignore key */
493 return MSG_NOT_HANDLED;
495 case DLG_POST_KEY:
496 if (hotlist_state.moving)
497 dlg_select_widget (l_movelist);
498 else
499 dlg_select_widget (l_hotlist);
500 /* always stay on hotlist */
501 /* fall through */
503 case DLG_INIT:
504 tty_setcolor (MENU_ENTRY_COLOR);
505 update_path_name ();
506 return MSG_HANDLED;
508 case DLG_RESIZE:
509 /* simply call dlg_set_size() with new size */
510 dlg_set_size (h, LINES - 2, COLS - 6);
511 return MSG_HANDLED;
513 default:
514 return default_dlg_callback (h, msg, parm);
518 static int l_call (WListbox *list)
520 Dlg_head *dlg = list->widget.parent;
522 if (list->current){
523 if (list->current->data) {
524 struct hotlist *hlp = (struct hotlist*) list->current->data;
525 if (hlp->type == HL_TYPE_ENTRY) {
526 dlg->ret_value = B_ENTER;
527 dlg_stop (dlg);
528 return LISTBOX_DONE;
529 } else {
530 hotlist_button_callback (B_ENTER);
531 hotlist_callback (dlg, DLG_POST_KEY, '\n');
532 return LISTBOX_CONT;
534 } else {
535 dlg->ret_value = B_ENTER;
536 dlg_stop (dlg);
537 return LISTBOX_DONE;
541 hotlist_button_callback (B_UP_GROUP);
542 hotlist_callback (dlg, DLG_POST_KEY, 'u');
543 return LISTBOX_CONT;
547 * Expands all button names (once) and recalculates button positions.
548 * returns number of columns in the dialog box, which is 10 chars longer
549 * then buttonbar.
551 * If common width of the window (i.e. in xterm) is less than returned
552 * width - sorry :) (anyway this did not handled in previous version too)
554 static int
555 init_i18n_stuff(int list_type, int cols)
557 register int i;
558 static const char* cancel_but = N_("&Cancel");
560 #ifdef ENABLE_NLS
561 static int hotlist_i18n_flag = 0;
563 if (!hotlist_i18n_flag)
565 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
566 while (i--)
567 hotlist_but [i].text = _(hotlist_but [i].text);
569 cancel_but = _(cancel_but);
570 hotlist_i18n_flag = 1;
572 #endif /* ENABLE_NLS */
574 /* Dynamic resizing of buttonbars */
576 int len[2], count[2]; /* at most two lines of buttons */
577 int cur_x[2], row;
579 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
580 len[0] = len[1] = count[0] = count[1] = 0;
582 /* Count len of buttonbars, assuming 2 extra space between buttons */
583 while (i--)
585 if (! (hotlist_but[i].type & list_type))
586 continue;
588 row = hotlist_but [i].y;
589 ++count [row];
590 len [row] += str_term_width1 (hotlist_but [i].text) + 5;
591 if (hotlist_but [i].flags == DEFPUSH_BUTTON)
592 len [row] += 2;
594 len[0] -= 2;
595 len[1] -= 2;
597 cols = max(cols, max(len[0], len[1]));
599 /* arrange buttons */
601 cur_x[0] = cur_x[1] = 0;
602 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
603 while (i--)
605 if (! (hotlist_but[i].type & list_type))
606 continue;
608 row = hotlist_but [i].y;
610 if (hotlist_but [i].x != 0)
612 /* not first int the row */
613 if (!strcmp (hotlist_but [i].text, cancel_but))
614 hotlist_but [i].x =
615 cols - str_term_width1 (hotlist_but [i].text) - 13;
616 else
617 hotlist_but [i].x = cur_x [row];
620 cur_x [row] += str_term_width1 (hotlist_but [i].text) + 2
621 + (hotlist_but [i].flags == DEFPUSH_BUTTON ? 5 : 3);
625 return cols;
628 static void
629 init_hotlist (int list_type)
631 size_t i;
632 const char *title, *help_node;
633 int hotlist_cols;
635 hotlist_cols = init_i18n_stuff (list_type, COLS - 6);
637 do_refresh ();
639 hotlist_state.expanded =
640 mc_config_get_int (mc_main_config, "HotlistConfig", "expanded_view_of_groups", 0);
642 if (list_type == LIST_VFSLIST) {
643 title = _("Active VFS directories");
644 help_node = "[vfshot]"; /* FIXME - no such node */
645 } else {
646 title = _("Directory hotlist");
647 help_node = "[Hotlist]";
650 hotlist_dlg =
651 create_dlg (0, 0, LINES - 2, hotlist_cols, dialog_colors,
652 hotlist_callback, help_node, title, DLG_CENTER | DLG_REVERSE);
654 for (i = 0; i < BUTTONS; i++) {
655 if (hotlist_but[i].type & list_type)
656 add_widget_autopos (hotlist_dlg,
657 button_new (BY + hotlist_but[i].y,
658 BX + hotlist_but[i].x,
659 hotlist_but[i].ret_cmd,
660 hotlist_but[i].flags,
661 hotlist_but[i].text,
662 hotlist_button_callback),
663 hotlist_but[i].pos_flags);
666 /* We add the labels.
667 * pname will hold entry's pathname;
668 * pname_group will hold name of current group
670 pname = label_new (UY - 11 + LINES, UX + 2, "");
671 add_widget_autopos (hotlist_dlg, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
672 if (!hotlist_state.moving) {
673 add_widget_autopos (hotlist_dlg,
674 label_new (UY - 12 + LINES, UX + 1,
675 _(" Directory path ")),
676 WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT);
678 /* This one holds the displayed pathname */
679 pname_group = label_new (UY, UX + 1, _(" Directory label "));
680 add_widget (hotlist_dlg, pname_group);
682 /* get new listbox */
683 l_hotlist =
684 listbox_new (UY + 1, UX + 1, LINES - 14, COLS - 2 * UX - 8,
685 l_call);
687 /* Fill the hotlist with the active VFS or the hotlist */
688 #ifdef USE_VFS
689 if (list_type == LIST_VFSLIST) {
690 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
691 vfs_fill_names (add_name_to_list);
692 } else
693 #endif /* !USE_VFS */
694 fill_listbox ();
696 add_widget_autopos (hotlist_dlg, l_hotlist, WPOS_KEEP_ALL);
697 /* add listbox to the dialogs */
700 static void
701 init_movelist (int list_type, struct hotlist *item)
703 size_t i;
704 char *hdr = g_strdup_printf (_("Moving %s"), item->label);
705 int movelist_cols = init_i18n_stuff (list_type, COLS - 6);
707 do_refresh ();
709 movelist_dlg =
710 create_dlg (0, 0, LINES - 6, movelist_cols, dialog_colors,
711 hotlist_callback, "[Hotlist]", hdr, DLG_CENTER | DLG_REVERSE);
712 g_free (hdr);
714 for (i = 0; i < BUTTONS; i++) {
715 if (hotlist_but[i].type & list_type)
716 add_widget (movelist_dlg,
717 button_new (BY - 4 + hotlist_but[i].y,
718 BX + hotlist_but[i].x,
719 hotlist_but[i].ret_cmd,
720 hotlist_but[i].flags,
721 hotlist_but[i].text,
722 hotlist_button_callback));
725 /* We add the labels. We are interested in the last one,
726 * that one will hold the path name label
728 movelist_group = label_new (UY, UX + 1, _(" Directory label "));
729 add_widget (movelist_dlg, movelist_group);
730 /* get new listbox */
731 l_movelist =
732 listbox_new (UY + 1, UX + 1, movelist_dlg->lines - 8,
733 movelist_dlg->cols - 2 * UX - 2, l_call);
735 fill_listbox ();
737 add_widget (movelist_dlg, l_movelist);
738 /* add listbox to the dialogs */
742 * Destroy the list dialog.
743 * Don't confuse with done_hotlist() for the list in memory.
745 static void hotlist_done (void)
747 destroy_dlg (hotlist_dlg);
748 l_hotlist = NULL;
749 if (0)
750 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
751 repaint_screen ();
754 static inline char *
755 find_group_section (struct hotlist *grp)
757 return g_strconcat (grp->directory, ".Group", (char *) NULL);
761 /* 1.11.96 bor: added pos parameter to control placement of new item.
762 see widget.c, listbox_add_item()
763 now hotlist is in unsorted mode
765 enum {
766 HL_BEFORE_CURRENT = 1
767 ,HL_AFTER_CURRENT = 2
770 static struct hotlist *
771 add2hotlist (char *label, char *directory, enum HotListType type, int pos)
773 struct hotlist *new;
774 struct hotlist *current = NULL;
777 * Hotlist is neither loaded nor loading.
778 * Must be called by "Ctrl-x a" before using hotlist.
780 if (!current_group)
781 load_hotlist ();
783 if (l_hotlist && l_hotlist->current) {
784 current = l_hotlist->current->data;
786 /* Make sure `..' stays at the top of the list. */
787 if (current->type == HL_TYPE_DOTDOT)
788 pos = HL_AFTER_CURRENT;
791 new = new_hotlist ();
793 new->type = type;
794 new->label = label;
795 new->directory = directory;
796 new->up = current_group;
798 if (type == HL_TYPE_GROUP) {
799 current_group = new;
800 add_dotdot_to_list ();
801 current_group = new->up;
804 if (!current_group->head) { /* first element in group */
805 current_group->head = new;
806 } else if (pos == HL_AFTER_CURRENT) {
807 new->next = current->next;
808 current->next = new;
809 } else if (pos == HL_BEFORE_CURRENT &&
810 current == current_group->head) {
811 /* should be inserted before first item */
812 new->next = current;
813 current_group->head = new;
814 } else if (pos == HL_BEFORE_CURRENT) {
815 struct hotlist *p = current_group->head;
817 while (p->next != current)
818 p = p->next;
820 new->next = current;
821 p->next = new;
822 } else { /* append at the end */
823 struct hotlist *p = current_group->head;
825 while (p->next)
826 p = p->next;
828 p->next = new;
831 if (hotlist_state.running && type != HL_TYPE_COMMENT &&
832 type != HL_TYPE_DOTDOT) {
833 if (type == HL_TYPE_GROUP) {
834 char *lbl = g_strconcat ("->", new->label, (char *) NULL);
836 listbox_add_item (l_hotlist, pos, 0, lbl, new);
837 g_free (lbl);
838 } else
839 listbox_add_item (l_hotlist, pos, 0, new->label, new);
840 listbox_select_entry (l_hotlist, l_hotlist->current);
842 return new;
846 #ifdef ENABLE_NLS
848 * Support routine for add_new_entry_input()/add_new_group_input()
849 * Change positions of buttons (first three widgets).
851 * This is just a quick hack. Accurate procedure must take care of
852 * internationalized label lengths and total buttonbar length...assume
853 * 64 is longer anyway.
855 static void
856 add_widgets_i18n (QuickWidget* qw, int len)
858 int i, l[3], space, cur_x;
860 for (i = 0; i < 3; i++) {
861 qw [i].u.button.text = _(qw [i].u.button.text);
862 l[i] = str_term_width1 (qw [i].u.button.text) + 3;
864 space = (len - 4 - l[0] - l[1] - l[2]) / 4;
866 for (cur_x = 2 + space, i = 3; i--; cur_x += l[i] + space) {
867 qw [i].relative_x = cur_x;
868 qw [i].x_divisions = len;
871 #endif /* ENABLE_NLS */
873 static int
874 add_new_entry_input (const char *header, const char *text1, const char *text2,
875 const char *help, char **r1, char **r2)
877 #define RELATIVE_Y_BUTTONS 4
878 #define RELATIVE_Y_LABEL_PTH 3
879 #define RELATIVE_Y_INPUT_PTH 4
881 QuickWidget quick_widgets [] =
883 /* 0 */ QUICK_BUTTON (55, 80, RELATIVE_Y_BUTTONS, 0, N_("&Cancel"), B_CANCEL, NULL),
884 /* 1 */ QUICK_BUTTON (30, 80, RELATIVE_Y_BUTTONS, 0, N_("&Insert"), B_INSERT, NULL),
885 /* 2 */ QUICK_BUTTON (10, 80, RELATIVE_Y_BUTTONS, 0, N_("&Append"), B_APPEND, NULL),
886 /* 3 */ QUICK_INPUT (4, 80, RELATIVE_Y_INPUT_PTH, 0, *r2, 58, 2, "input-pth", r2),
887 /* 4 */ QUICK_LABEL (RELATIVE_Y_LABEL_PTH, 80, 3, 0, text2),
888 /* 5 */ QUICK_INPUT (4, 80, 3, 0, *r1, 58, 0, "input-lbl", r1),
889 /* 6 */ QUICK_LABEL (3, 80, 2, 0, text1),
890 QUICK_END
893 int len;
894 int i;
895 int lines1, lines2;
896 int cols1, cols2;
898 #ifdef ENABLE_NLS
899 static gboolean i18n_flag = FALSE;
900 #endif /* ENABLE_NLS */
902 msglen(text1, &lines1, &cols1);
903 msglen(text2, &lines2, &cols2);
904 len = max (str_term_width1 (header), cols1);
905 len = max (max (len, cols2) + 4, 64);
907 #ifdef ENABLE_NLS
908 if (!i18n_flag) {
909 add_widgets_i18n (quick_widgets, len);
910 i18n_flag = TRUE;
912 #endif /* ENABLE_NLS */
915 QuickDialog Quick_input =
917 len, lines1 + lines2 + 7, -1, -1, header,
918 help, quick_widgets, FALSE
921 for (i = 0; i < 7; i++)
922 quick_widgets [i].y_divisions = Quick_input.ylen;
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 i = quick_dialog (&Quick_input);
933 return (i != B_CANCEL) ? i : 0;
935 #undef RELATIVE_Y_BUTTONS
936 #undef RELATIVE_Y_LABEL_PTH
937 #undef RELATIVE_Y_INPUT_PTH
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 QuickWidget quick_widgets [] =
973 /* 0 */ QUICK_BUTTON (55, 80, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
974 /* 1 */ QUICK_BUTTON (30, 80, 1, 0, N_("&Insert"), B_INSERT, NULL),
975 /* 2 */ QUICK_BUTTON (10, 80, 1, 0, N_("&Append"), B_APPEND, NULL),
976 /* 3 */ QUICK_INPUT (4, 80, 0, 0, "", 58, 0, "input" , result),
977 /* 4 */ QUICK_LABEL (3, 80, 2, 0, label),
978 QUICK_END
981 int len;
982 int i;
983 int lines, cols;
984 int ret;
986 #ifdef ENABLE_NLS
987 static gboolean i18n_flag = FALSE;
988 #endif /* ENABLE_NLS */
990 msglen (label, &lines, &cols);
991 len = max (max (str_term_width1 (header), cols) + 4, 64);
993 #ifdef ENABLE_NLS
994 if (!i18n_flag) {
995 add_widgets_i18n (quick_widgets, len);
996 i18n_flag = TRUE;
998 #endif /* ENABLE_NLS */
1001 QuickDialog Quick_input =
1003 len, lines + 6, -1, -1, header,
1004 "[Hotlist]", quick_widgets, FALSE
1007 int relative_y[] = {1, 1, 1, 0, 2}; /* the relative_x component from the
1008 quick_widgets variable above */
1010 for (i = 0; i < 5; i++)
1011 quick_widgets[i].y_divisions = Quick_input.ylen;
1013 for (i = 0; i < 4; i++)
1014 quick_widgets[i].relative_y = relative_y[i] + 2 + lines;
1016 ret = quick_dialog (&Quick_input);
1019 return (ret != B_CANCEL) ? ret : 0;
1022 static void add_new_group_cmd (void)
1024 char *label;
1025 int ret;
1027 ret = add_new_group_input (_(" New hotlist group "), _("Name of new group"), &label);
1028 if (!ret || !label || !*label)
1029 return;
1031 if (ret == B_ENTER || ret == B_APPEND)
1032 add2hotlist (label, 0, HL_TYPE_GROUP, HL_AFTER_CURRENT);
1033 else
1034 add2hotlist (label, 0, HL_TYPE_GROUP, HL_BEFORE_CURRENT);
1036 hotlist_state.modified = 1;
1039 void add2hotlist_cmd (void)
1041 char *lc_prompt, *label;
1042 const char *cp = _("Label for \"%s\":");
1043 int l = str_term_width1 (cp);
1044 char *label_string = g_strdup (current_panel->cwd);
1046 strip_password (label_string, 1);
1048 lc_prompt = g_strdup_printf (cp, path_trunc (current_panel->cwd, COLS-2*UX-(l+8)));
1049 label = input_dialog (_(" Add to hotlist "), lc_prompt, MC_HISTORY_HOTLIST_ADD, label_string);
1050 g_free (lc_prompt);
1052 if (!label || !*label) {
1053 g_free (label_string);
1054 g_free (label);
1055 return;
1057 add2hotlist (label, label_string, HL_TYPE_ENTRY, 0);
1058 hotlist_state.modified = 1;
1061 static void remove_group (struct hotlist *grp)
1063 struct hotlist *current = grp->head;
1065 while (current) {
1066 struct hotlist *next = current->next;
1068 if (current->type == HL_TYPE_GROUP)
1069 remove_group (current);
1071 g_free (current->label);
1072 g_free (current->directory);
1073 g_free (current);
1075 current = next;
1080 static void remove_from_hotlist (struct hotlist *entry)
1082 if (entry->type == HL_TYPE_DOTDOT)
1083 return;
1085 if (confirm_directory_hotlist_delete) {
1086 char *title;
1087 int result;
1089 title = g_strconcat (_(" Remove: "),
1090 str_trunc (entry->label, 30),
1091 " ",
1092 NULL);
1094 if (safe_delete)
1095 query_set_sel (1);
1096 result = query_dialog (title,
1097 _("\n Are you sure you want to remove this entry?"),
1098 D_ERROR, 2, _("&Yes"), _("&No"));
1100 g_free (title);
1102 if (result != 0)
1103 return;
1106 if (entry->type == HL_TYPE_GROUP) {
1107 if (entry->head) {
1108 char *header;
1109 int result;
1111 header = g_strconcat (_(" Remove: "),
1112 str_trunc (entry->label, 30),
1113 " ",
1114 NULL);
1115 result = query_dialog (header, _("\n Group not empty.\n Remove it?"),
1116 D_ERROR, 2,
1117 _("&Yes"), _("&No"));
1118 g_free (header);
1120 if (result != 0)
1121 return;
1124 remove_group (entry);
1127 unlink_entry (entry);
1129 g_free (entry->label);
1130 g_free (entry->directory);
1131 g_free (entry);
1132 /* now remove list entry from screen */
1133 listbox_remove_current (l_hotlist, 1);
1134 hotlist_state.modified = 1;
1137 char *hotlist_cmd (int vfs_or_hotlist)
1139 char *target = NULL;
1141 hotlist_state.type = vfs_or_hotlist;
1142 load_hotlist ();
1144 init_hotlist (vfs_or_hotlist);
1146 /* display file info */
1147 tty_setcolor (SELECTED_COLOR);
1149 hotlist_state.running = 1;
1150 run_dlg (hotlist_dlg);
1151 hotlist_state.running = 0;
1152 save_hotlist ();
1154 switch (hotlist_dlg->ret_value) {
1155 case B_CANCEL:
1156 break;
1158 case B_ENTER:
1159 if (l_hotlist->current->data) {
1160 struct hotlist *hlp = (struct hotlist*) l_hotlist->current->data;
1161 target = g_strdup (hlp->directory);
1162 } else
1163 target = g_strdup (l_hotlist->current->text);
1164 break;
1167 hotlist_done ();
1168 return target;
1171 static void
1172 load_group (struct hotlist *grp)
1174 gchar **profile_keys, **keys;
1175 gsize len;
1176 char *group_section;
1177 struct hotlist *current = 0;
1179 group_section = find_group_section (grp);
1181 profile_keys = keys = mc_config_get_keys (mc_main_config, group_section, &len);
1183 current_group = grp;
1185 while (*profile_keys){
1186 add2hotlist (
1187 mc_config_get_string(mc_main_config, group_section, *profile_keys, ""),
1188 g_strdup (*profile_keys),
1189 HL_TYPE_GROUP,
1191 profile_keys++;
1193 g_free (group_section);
1194 g_strfreev(keys);
1196 profile_keys = keys = mc_config_get_keys (mc_main_config, grp->directory,&len);
1198 while (*profile_keys){
1199 add2hotlist (
1200 mc_config_get_string(mc_main_config,group_section,*profile_keys,""),
1201 g_strdup (*profile_keys),
1202 HL_TYPE_ENTRY,
1204 profile_keys++;
1206 g_strfreev(keys);
1208 for (current = grp->head; current; current = current->next)
1209 load_group (current);
1212 #define TKN_GROUP 0
1213 #define TKN_ENTRY 1
1214 #define TKN_STRING 2
1215 #define TKN_URL 3
1216 #define TKN_ENDGROUP 4
1217 #define TKN_COMMENT 5
1218 #define TKN_EOL 125
1219 #define TKN_EOF 126
1220 #define TKN_UNKNOWN 127
1222 static GString *tkn_buf = NULL;
1224 static char *hotlist_file_name;
1225 static FILE *hotlist_file;
1226 static time_t hotlist_file_mtime;
1228 static int hot_skip_blanks (void)
1230 int c;
1232 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1234 return c;
1238 static int hot_next_token (void)
1240 int c, ret=0;
1241 size_t l;
1244 if (tkn_buf == NULL) tkn_buf = g_string_new ("");
1245 g_string_set_size(tkn_buf,0);
1247 again:
1248 c = hot_skip_blanks ();
1249 switch (c) {
1250 case EOF:
1251 ret = TKN_EOF;
1252 break;
1253 case '\n':
1254 ret = TKN_EOL;
1255 break;
1256 case '#':
1257 while ((c = getc (hotlist_file)) != EOF && c != '\n') {
1258 g_string_append_c (tkn_buf, c);
1260 ret = TKN_COMMENT;
1261 break;
1262 case '"':
1263 while ((c = getc (hotlist_file)) != EOF && c != '"') {
1264 if (c == '\\')
1265 if ((c = getc (hotlist_file)) == EOF){
1266 g_string_free (tkn_buf, TRUE);
1267 return TKN_EOF;
1269 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1271 if (c == EOF)
1272 ret = TKN_EOF;
1273 else
1274 ret = TKN_STRING;
1275 break;
1276 case '\\':
1277 if ((c = getc (hotlist_file)) == EOF){
1278 g_string_free (tkn_buf, TRUE);
1279 return TKN_EOF;
1281 if (c == '\n')
1282 goto again;
1284 /* fall through; it is taken as normal character */
1286 default:
1287 do {
1288 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1289 } while ((c = fgetc (hotlist_file)) != EOF &&
1290 (g_ascii_isalnum (c) || !isascii (c)));
1291 if (c != EOF)
1292 ungetc (c, hotlist_file);
1293 l = tkn_buf->len;
1294 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1295 ret = TKN_GROUP;
1296 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1297 ret = TKN_ENTRY;
1298 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1299 ret = TKN_ENDGROUP;
1300 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1301 ret = TKN_URL;
1302 else
1303 ret = TKN_UNKNOWN;
1304 break;
1306 return ret;
1309 #define SKIP_TO_EOL { \
1310 int _tkn; \
1311 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
1314 #define CHECK_TOKEN(_TKN_) \
1315 if ((tkn = hot_next_token ()) != _TKN_) { \
1316 hotlist_state.readonly = 1; \
1317 hotlist_state.file_error = 1; \
1318 while (tkn != TKN_EOL && tkn != TKN_EOF) \
1319 tkn = hot_next_token (); \
1320 break; \
1323 static void
1324 hot_load_group (struct hotlist * grp)
1326 int tkn;
1327 struct hotlist *new_grp;
1328 char *label, *url;
1330 current_group = grp;
1332 while ((tkn = hot_next_token()) != TKN_ENDGROUP)
1333 switch (tkn) {
1334 case TKN_GROUP:
1335 CHECK_TOKEN(TKN_STRING);
1336 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1337 SKIP_TO_EOL;
1338 hot_load_group (new_grp);
1339 current_group = grp;
1340 break;
1341 case TKN_ENTRY:
1342 CHECK_TOKEN(TKN_STRING);
1343 label = g_strdup (tkn_buf->str);
1344 CHECK_TOKEN(TKN_URL);
1345 CHECK_TOKEN(TKN_STRING);
1346 url = g_strdup (tkn_buf->str);
1347 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1348 SKIP_TO_EOL;
1349 break;
1350 case TKN_COMMENT:
1351 label = g_strdup (tkn_buf->str);
1352 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1353 break;
1354 case TKN_EOF:
1355 hotlist_state.readonly = 1;
1356 hotlist_state.file_error = 1;
1357 return;
1358 break;
1359 case TKN_EOL:
1360 /* skip empty lines */
1361 break;
1362 default:
1363 hotlist_state.readonly = 1;
1364 hotlist_state.file_error = 1;
1365 SKIP_TO_EOL;
1366 break;
1368 SKIP_TO_EOL;
1371 static void
1372 hot_load_file (struct hotlist * grp)
1374 int tkn;
1375 struct hotlist *new_grp;
1376 char *label, *url;
1378 current_group = grp;
1380 while ((tkn = hot_next_token())!= TKN_EOF)
1381 switch (tkn) {
1382 case TKN_GROUP:
1383 CHECK_TOKEN(TKN_STRING);
1384 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1385 SKIP_TO_EOL;
1386 hot_load_group (new_grp);
1387 current_group = grp;
1388 break;
1389 case TKN_ENTRY:
1390 CHECK_TOKEN(TKN_STRING);
1391 label = g_strdup (tkn_buf->str);
1392 CHECK_TOKEN(TKN_URL);
1393 CHECK_TOKEN(TKN_STRING);
1394 url = g_strdup (tkn_buf->str);
1395 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1396 SKIP_TO_EOL;
1397 break;
1398 case TKN_COMMENT:
1399 label = g_strdup (tkn_buf->str);
1400 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1401 break;
1402 case TKN_EOL:
1403 /* skip empty lines */
1404 break;
1405 default:
1406 hotlist_state.readonly = 1;
1407 hotlist_state.file_error = 1;
1408 SKIP_TO_EOL;
1409 break;
1413 static void
1414 clean_up_hotlist_groups (const char *section)
1416 char *grp_section;
1417 gchar **profile_keys, **keys;
1418 gsize len;
1420 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1421 if (mc_config_has_group(mc_main_config, section))
1422 mc_config_del_group (mc_main_config, section);
1424 if (mc_config_has_group (mc_main_config, grp_section)) {
1425 profile_keys = keys = mc_config_get_keys (mc_main_config, grp_section,&len);
1427 while (*profile_keys) {
1428 clean_up_hotlist_groups (*profile_keys);
1429 profile_keys++;
1431 g_strfreev(keys);
1432 mc_config_del_group (mc_main_config, grp_section);
1434 g_free (grp_section);
1439 static void
1440 load_hotlist (void)
1442 int remove_old_list = 0;
1443 struct stat stat_buf;
1445 if (hotlist_state.loaded) {
1446 stat (hotlist_file_name, &stat_buf);
1447 if (hotlist_file_mtime < stat_buf.st_mtime)
1448 done_hotlist ();
1449 else
1450 return;
1453 if (!hotlist_file_name)
1454 hotlist_file_name = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HOTLIST_FILE, NULL);
1456 hotlist = new_hotlist ();
1457 hotlist->type = HL_TYPE_GROUP;
1458 hotlist->label = g_strdup (_(" Top level group "));
1459 hotlist->up = hotlist;
1461 * compatibility :-(
1463 hotlist->directory = g_strdup ("Hotlist");
1465 if ((hotlist_file = fopen (hotlist_file_name, "r")) == 0) {
1466 int result;
1468 load_group (hotlist);
1469 hotlist_state.loaded = 1;
1471 * just to be sure we got copy
1473 hotlist_state.modified = 1;
1474 result = save_hotlist ();
1475 hotlist_state.modified = 0;
1476 if (result) {
1477 remove_old_list = 1;
1478 } else {
1479 message (D_ERROR, _(" Hotlist Load "),
1480 _("MC was unable to write ~/%s file, your old hotlist entries were not deleted"),
1481 MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1483 } else {
1484 hot_load_file (hotlist);
1485 fclose (hotlist_file);
1486 hotlist_state.loaded = 1;
1489 if (remove_old_list) {
1490 GError *error = NULL;
1491 clean_up_hotlist_groups ("Hotlist");
1492 if (! mc_config_save_file (mc_main_config, &error))
1493 setup_save_config_show_error(mc_main_config->ini_path, &error);
1496 stat (hotlist_file_name, &stat_buf);
1497 hotlist_file_mtime = stat_buf.st_mtime;
1498 current_group = hotlist;
1502 static int list_level = 0;
1504 static void
1505 hot_save_group (struct hotlist *grp)
1507 struct hotlist *current = grp->head;
1508 int i;
1509 char *s;
1511 #define INDENT(n) \
1512 do { \
1513 for (i = 0; i < n; i++) \
1514 putc (' ', hotlist_file); \
1515 } while (0)
1517 for (;current; current = current->next)
1518 switch (current->type) {
1519 case HL_TYPE_GROUP:
1520 INDENT (list_level);
1521 fputs ("GROUP \"", hotlist_file);
1522 for (s = current->label; *s; s++) {
1523 if (*s == '"' || *s == '\\')
1524 putc ('\\', hotlist_file);
1525 putc (*s, hotlist_file);
1527 fputs ("\"\n", hotlist_file);
1528 list_level += 2;
1529 hot_save_group (current);
1530 list_level -= 2;
1531 INDENT (list_level);
1532 fputs ("ENDGROUP\n", hotlist_file);
1533 break;
1534 case HL_TYPE_ENTRY:
1535 INDENT(list_level);
1536 fputs ("ENTRY \"", hotlist_file);
1537 for (s = current->label; *s; s++) {
1538 if (*s == '"' || *s == '\\')
1539 putc ('\\', hotlist_file);
1540 putc (*s, hotlist_file);
1542 fputs ("\" URL \"", hotlist_file);
1543 for (s = current->directory; *s; s++) {
1544 if (*s == '"' || *s == '\\')
1545 putc ('\\', hotlist_file);
1546 putc (*s, hotlist_file);
1548 fputs ("\"\n", hotlist_file);
1549 break;
1550 case HL_TYPE_COMMENT:
1551 fprintf (hotlist_file, "#%s\n", current->label);
1552 break;
1553 case HL_TYPE_DOTDOT:
1554 /* do nothing */
1555 break;
1559 int save_hotlist (void)
1561 int saved = 0;
1562 struct stat stat_buf;
1564 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name) {
1565 mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1567 if ((hotlist_file = fopen (hotlist_file_name, "w")) != 0) {
1568 hot_save_group (hotlist);
1569 fclose (hotlist_file);
1570 stat (hotlist_file_name, &stat_buf);
1571 hotlist_file_mtime = stat_buf.st_mtime;
1572 saved = 1;
1573 hotlist_state.modified = 0;
1574 } else
1575 mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1578 return saved;
1582 * Unload list from memory.
1583 * Don't confuse with hotlist_done() for GUI.
1585 void done_hotlist (void)
1587 if (hotlist){
1588 remove_group (hotlist);
1589 g_free (hotlist->label);
1590 g_free (hotlist->directory);
1591 g_free (hotlist);
1592 hotlist = 0;
1595 hotlist_state.loaded = 0;
1597 g_free (hotlist_file_name);
1598 hotlist_file_name = 0;
1599 l_hotlist = 0;
1600 current_group = 0;
1602 if (tkn_buf){
1603 g_string_free (tkn_buf, TRUE);
1604 tkn_buf = NULL;
1608 static void
1609 add_dotdot_to_list (void)
1611 if (current_group != hotlist) {
1612 if (hotlist_has_dot_dot != 0)
1613 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, 0);