Removed src/pipethrough.* files.
[midnight-commander.git] / src / hotlist.c
blob5fdff38f18c3d2337129f91704d1ed51099386c6
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 "lib/global.h"
45 #include "lib/tty/tty.h" /* COLS */
46 #include "lib/skin.h"
47 #include "lib/tty/key.h" /* KEY_M_CTRL */
48 #include "lib/mcconfig.h" /* Load/save directories hotlist */
49 #include "lib/fileloc.h"
50 #include "lib/strutil.h"
52 #include "dialog.h"
53 #include "widget.h"
54 #include "setup.h" /* For profile_bname */
55 #include "wtools.h" /* QuickDialog */
56 #include "panel.h" /* current_panel */
57 #include "main.h" /* update_panels() */
58 #include "layout.h" /* repaint_screen() */
59 #include "hotlist.h"
60 #include "command.h" /* cmdline */
61 #include "history.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 ENABLE_VFS
81 #include "lib/vfs/mc-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"),
130 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
131 { B_REMOVE, NORMAL_BUTTON, 1, 30, N_("&Remove"),
132 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
133 { B_APPEND, NORMAL_BUTTON, 1, 15, N_("&Append"),
134 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
135 { B_INSERT, NORMAL_BUTTON, 1, 0, N_("&Insert"),
136 LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
137 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, N_("New &Entry"),
138 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
139 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, N_("New &Group"),
140 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
141 { B_CANCEL, NORMAL_BUTTON, 0, 53, N_("&Cancel"),
142 LIST_HOTLIST | LIST_VFSLIST|LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
143 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, N_("&Up"),
144 LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
145 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, N_("&Add current"),
146 LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
147 #ifdef ENABLE_VFS
148 { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, N_("&Refresh"),
149 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
150 { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, N_("Fr&ee VFSs now"),
151 LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
152 #endif
153 { B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("Change &To"),
154 LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
157 /* Directory hotlist */
158 static struct hotlist{
159 enum HotListType type;
160 char *directory;
161 char *label;
162 struct hotlist *head;
163 struct hotlist *up;
164 struct hotlist *next;
165 } *hotlist = NULL;
167 static struct hotlist *current_group;
169 static void init_movelist (int, struct hotlist *);
170 static void add_new_group_cmd (void);
171 static void add_new_entry_cmd (void);
172 static void remove_from_hotlist (struct hotlist *entry);
173 static void load_hotlist (void);
174 static void add_dotdot_to_list (void);
176 #define new_hotlist() g_new0(struct hotlist, 1)
178 static void
179 hotlist_refresh (Dlg_head * dlg)
181 common_dialog_repaint (dlg);
182 tty_setcolor (COLOR_NORMAL);
183 draw_box (dlg, 2, 5, dlg->lines - (hotlist_state.moving ? 6 : 10),
184 dlg->cols - (UX * 2));
185 if (!hotlist_state.moving)
186 draw_box (dlg, dlg->lines - 8, 5, 3, dlg->cols - (UX * 2));
189 /* If current->data is 0, then we are dealing with a VFS pathname */
190 static void
191 update_path_name (void)
193 const char *text = "";
194 char *p;
195 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
196 Dlg_head *dlg = list->widget.parent;
198 if (list->current) {
199 if (list->current->data != 0) {
200 struct hotlist *hlp = (struct hotlist *) list->current->data;
202 if (hlp->type == HL_TYPE_ENTRY ||
203 hlp->type == HL_TYPE_DOTDOT)
204 text = hlp->directory;
205 else if (hlp->type == HL_TYPE_GROUP)
206 text = _("Subgroup - press ENTER to see list");
207 } else {
208 text = list->current->text;
211 if (!hotlist_state.moving)
212 label_set_text (pname,
213 str_trunc (text, dlg->cols - (UX * 2 + 4)));
215 p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
216 if (!hotlist_state.moving)
217 label_set_text (pname_group,
218 str_trunc (p, dlg->cols - (UX * 2 + 4)));
219 else
220 label_set_text (movelist_group,
221 str_trunc (p, dlg->cols - (UX * 2 + 4)));
222 g_free (p);
224 dlg_redraw (dlg);
227 #define CHECK_BUFFER \
228 do { \
229 int i; \
231 if ((i = strlen (current->label) + 3) > buflen) { \
232 g_free (buf); \
233 buf = g_malloc (buflen = 1024 * (i/1024 + 1)); \
235 buf[0] = '\0'; \
236 } while (0)
238 static void fill_listbox (void)
240 struct hotlist *current = current_group->head;
241 GString *buff = g_string_new ("");
243 while (current){
244 switch (current->type) {
245 case HL_TYPE_GROUP:
247 /* buff clean up */
248 g_string_truncate(buff, 0);
249 g_string_append(buff,"->");
250 g_string_append(buff,current->label);
251 if (hotlist_state.moving)
252 listbox_add_item (l_movelist, 0, 0, buff->str, current);
253 else
254 listbox_add_item (l_hotlist, 0, 0, buff->str, current);
256 break;
257 case HL_TYPE_DOTDOT:
258 case HL_TYPE_ENTRY:
259 if (hotlist_state.moving)
260 listbox_add_item (l_movelist, 0, 0, current->label, current);
261 else
262 listbox_add_item (l_hotlist, 0, 0, current->label, current);
263 break;
264 default:
265 break;
267 current = current->next;
269 g_string_free (buff, TRUE);
272 static void
273 unlink_entry (struct hotlist *entry)
275 struct hotlist *current = current_group->head;
277 if (current == entry)
278 current_group->head = entry->next;
279 else {
280 while (current && current->next != entry)
281 current = current->next;
282 if (current)
283 current->next = entry->next;
285 entry->next =
286 entry->up = 0;
289 #ifdef ENABLE_VFS
290 static void add_name_to_list (const char *path)
292 listbox_add_item (l_hotlist, 0, 0, path, 0);
294 #endif /* !ENABLE_VFS */
296 static int
297 hotlist_button_callback (int action)
299 switch (action) {
300 case B_MOVE:
302 struct hotlist *saved = current_group;
303 struct hotlist *item;
304 struct hotlist *moveto_item = 0;
305 struct hotlist *moveto_group = 0;
306 int ret;
308 if (!l_hotlist->current)
309 return MSG_NOT_HANDLED; /* empty group - nothing to do */
310 item = l_hotlist->current->data;
311 hotlist_state.moving = 1;
312 init_movelist (LIST_MOVELIST, item);
313 run_dlg (movelist_dlg);
314 ret = movelist_dlg->ret_value;
315 hotlist_state.moving = 0;
316 if (l_movelist->current)
317 moveto_item = l_movelist->current->data;
318 moveto_group = current_group;
319 destroy_dlg (movelist_dlg);
320 current_group = saved;
321 if (ret == B_CANCEL)
322 return MSG_NOT_HANDLED;
323 if (moveto_item == item)
324 return MSG_NOT_HANDLED; /* If we insert/append a before/after a
325 it hardly changes anything ;) */
326 unlink_entry (item);
327 listbox_remove_current (l_hotlist, 1);
328 item->up = moveto_group;
329 if (!moveto_group->head)
330 moveto_group->head = item;
331 else if (!moveto_item) { /* we have group with just comments */
332 struct hotlist *p = moveto_group->head;
334 /* skip comments */
335 while (p->next)
336 p = p->next;
337 p->next = item;
338 } else if (ret == B_ENTER || ret == B_APPEND)
339 if (!moveto_item->next)
340 moveto_item->next = item;
341 else {
342 item->next = moveto_item->next;
343 moveto_item->next = item;
344 } else if (moveto_group->head == moveto_item) {
345 moveto_group->head = item;
346 item->next = moveto_item;
347 } else {
348 struct hotlist *p = moveto_group->head;
350 while (p->next != moveto_item)
351 p = p->next;
352 item->next = p->next;
353 p->next = item;
355 listbox_remove_list (l_hotlist);
356 fill_listbox ();
357 repaint_screen ();
358 hotlist_state.modified = 1;
359 return MSG_NOT_HANDLED;
360 break;
362 case B_REMOVE:
363 if (l_hotlist->current && l_hotlist->current->data)
364 remove_from_hotlist (l_hotlist->current->data);
365 return MSG_NOT_HANDLED;
366 break;
368 case B_NEW_GROUP:
369 add_new_group_cmd ();
370 return MSG_NOT_HANDLED;
371 break;
373 case B_ADD_CURRENT:
374 add2hotlist_cmd ();
375 return MSG_NOT_HANDLED;
376 break;
378 case B_NEW_ENTRY:
379 add_new_entry_cmd ();
380 return MSG_NOT_HANDLED;
381 break;
383 case B_ENTER:
385 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
386 if (list->current) {
387 if (list->current->data) {
388 struct hotlist *hlp =
389 (struct hotlist *) list->current->data;
390 if (hlp->type == HL_TYPE_ENTRY)
391 return MSG_HANDLED;
392 else if (hlp->type == HL_TYPE_DOTDOT) {
393 /* Fall through - go up */
396 else {
397 listbox_remove_list (list);
398 current_group = hlp;
399 fill_listbox ();
400 return MSG_NOT_HANDLED;
402 } else
403 return MSG_HANDLED;
406 /* Fall through if list empty - just go up */
408 case B_UP_GROUP:
410 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
411 listbox_remove_list (list);
412 current_group = current_group->up;
413 fill_listbox ();
414 return MSG_NOT_HANDLED;
415 break;
418 #ifdef ENABLE_VFS
419 case B_FREE_ALL_VFS:
420 vfs_expire (1);
421 /* fall through */
423 case B_REFRESH_VFS:
424 listbox_remove_list (l_hotlist);
425 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
426 vfs_fill_names (add_name_to_list);
427 return MSG_NOT_HANDLED;
428 #endif /* ENABLE_VFS */
430 default:
431 return MSG_HANDLED;
432 break;
437 static cb_ret_t
438 hotlist_callback (Dlg_head *h, Widget *sender,
439 dlg_msg_t msg, int parm, void *data)
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, sender, msg, parm, data);
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, NULL, DLG_POST_KEY, '\n', NULL);
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, NULL, DLG_POST_KEY, 'u', NULL);
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 ENABLE_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 /* !ENABLE_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 " ", (char *) NULL);
1093 if (safe_delete)
1094 query_set_sel (1);
1095 result = query_dialog (title,
1096 _("\n Are you sure you want to remove this entry?"),
1097 D_ERROR, 2, _("&Yes"), _("&No"));
1099 g_free (title);
1101 if (result != 0)
1102 return;
1105 if (entry->type == HL_TYPE_GROUP) {
1106 if (entry->head) {
1107 char *header;
1108 int result;
1110 header = g_strconcat (_(" Remove: "),
1111 str_trunc (entry->label, 30),
1112 " ", (char *) NULL);
1113 result = query_dialog (header, _("\n Group not empty.\n Remove it?"),
1114 D_ERROR, 2,
1115 _("&Yes"), _("&No"));
1116 g_free (header);
1118 if (result != 0)
1119 return;
1122 remove_group (entry);
1125 unlink_entry (entry);
1127 g_free (entry->label);
1128 g_free (entry->directory);
1129 g_free (entry);
1130 /* now remove list entry from screen */
1131 listbox_remove_current (l_hotlist, 1);
1132 hotlist_state.modified = 1;
1135 char *hotlist_cmd (int vfs_or_hotlist)
1137 char *target = NULL;
1139 hotlist_state.type = vfs_or_hotlist;
1140 load_hotlist ();
1142 init_hotlist (vfs_or_hotlist);
1144 /* display file info */
1145 tty_setcolor (SELECTED_COLOR);
1147 hotlist_state.running = 1;
1148 run_dlg (hotlist_dlg);
1149 hotlist_state.running = 0;
1150 save_hotlist ();
1152 switch (hotlist_dlg->ret_value) {
1153 case B_CANCEL:
1154 break;
1156 case B_ENTER:
1157 if (l_hotlist->current->data) {
1158 struct hotlist *hlp = (struct hotlist*) l_hotlist->current->data;
1159 target = g_strdup (hlp->directory);
1160 } else
1161 target = g_strdup (l_hotlist->current->text);
1162 break;
1165 hotlist_done ();
1166 return target;
1169 static void
1170 load_group (struct hotlist *grp)
1172 gchar **profile_keys, **keys;
1173 gsize len;
1174 char *group_section;
1175 struct hotlist *current = 0;
1177 group_section = find_group_section (grp);
1179 profile_keys = keys = mc_config_get_keys (mc_main_config, group_section, &len);
1181 current_group = grp;
1183 while (*profile_keys){
1184 add2hotlist (
1185 mc_config_get_string(mc_main_config, group_section, *profile_keys, ""),
1186 g_strdup (*profile_keys),
1187 HL_TYPE_GROUP,
1189 profile_keys++;
1191 g_free (group_section);
1192 g_strfreev(keys);
1194 profile_keys = keys = mc_config_get_keys (mc_main_config, grp->directory,&len);
1196 while (*profile_keys){
1197 add2hotlist (
1198 mc_config_get_string(mc_main_config,group_section,*profile_keys,""),
1199 g_strdup (*profile_keys),
1200 HL_TYPE_ENTRY,
1202 profile_keys++;
1204 g_strfreev(keys);
1206 for (current = grp->head; current; current = current->next)
1207 load_group (current);
1210 #define TKN_GROUP 0
1211 #define TKN_ENTRY 1
1212 #define TKN_STRING 2
1213 #define TKN_URL 3
1214 #define TKN_ENDGROUP 4
1215 #define TKN_COMMENT 5
1216 #define TKN_EOL 125
1217 #define TKN_EOF 126
1218 #define TKN_UNKNOWN 127
1220 static GString *tkn_buf = NULL;
1222 static char *hotlist_file_name;
1223 static FILE *hotlist_file;
1224 static time_t hotlist_file_mtime;
1226 static int hot_skip_blanks (void)
1228 int c;
1230 while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1232 return c;
1236 static int hot_next_token (void)
1238 int c, ret=0;
1239 size_t l;
1242 if (tkn_buf == NULL) tkn_buf = g_string_new ("");
1243 g_string_set_size(tkn_buf,0);
1245 again:
1246 c = hot_skip_blanks ();
1247 switch (c) {
1248 case EOF:
1249 ret = TKN_EOF;
1250 break;
1251 case '\n':
1252 ret = TKN_EOL;
1253 break;
1254 case '#':
1255 while ((c = getc (hotlist_file)) != EOF && c != '\n') {
1256 g_string_append_c (tkn_buf, c);
1258 ret = TKN_COMMENT;
1259 break;
1260 case '"':
1261 while ((c = getc (hotlist_file)) != EOF && c != '"') {
1262 if (c == '\\')
1263 if ((c = getc (hotlist_file)) == EOF){
1264 g_string_free (tkn_buf, TRUE);
1265 return TKN_EOF;
1267 g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1269 if (c == EOF)
1270 ret = TKN_EOF;
1271 else
1272 ret = TKN_STRING;
1273 break;
1274 case '\\':
1275 if ((c = getc (hotlist_file)) == EOF){
1276 g_string_free (tkn_buf, TRUE);
1277 return TKN_EOF;
1279 if (c == '\n')
1280 goto again;
1282 /* fall through; it is taken as normal character */
1284 default:
1285 do {
1286 g_string_append_c (tkn_buf, g_ascii_toupper (c));
1287 } while ((c = fgetc (hotlist_file)) != EOF &&
1288 (g_ascii_isalnum (c) || !isascii (c)));
1289 if (c != EOF)
1290 ungetc (c, hotlist_file);
1291 l = tkn_buf->len;
1292 if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1293 ret = TKN_GROUP;
1294 else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1295 ret = TKN_ENTRY;
1296 else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1297 ret = TKN_ENDGROUP;
1298 else if (strncmp (tkn_buf->str, "URL", l) == 0)
1299 ret = TKN_URL;
1300 else
1301 ret = TKN_UNKNOWN;
1302 break;
1304 return ret;
1307 #define SKIP_TO_EOL { \
1308 int _tkn; \
1309 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
1312 #define CHECK_TOKEN(_TKN_) \
1313 if ((tkn = hot_next_token ()) != _TKN_) { \
1314 hotlist_state.readonly = 1; \
1315 hotlist_state.file_error = 1; \
1316 while (tkn != TKN_EOL && tkn != TKN_EOF) \
1317 tkn = hot_next_token (); \
1318 break; \
1321 static void
1322 hot_load_group (struct hotlist * grp)
1324 int tkn;
1325 struct hotlist *new_grp;
1326 char *label, *url;
1328 current_group = grp;
1330 while ((tkn = hot_next_token()) != TKN_ENDGROUP)
1331 switch (tkn) {
1332 case TKN_GROUP:
1333 CHECK_TOKEN(TKN_STRING);
1334 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1335 SKIP_TO_EOL;
1336 hot_load_group (new_grp);
1337 current_group = grp;
1338 break;
1339 case TKN_ENTRY:
1340 CHECK_TOKEN(TKN_STRING);
1341 label = g_strdup (tkn_buf->str);
1342 CHECK_TOKEN(TKN_URL);
1343 CHECK_TOKEN(TKN_STRING);
1344 url = g_strdup (tkn_buf->str);
1345 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1346 SKIP_TO_EOL;
1347 break;
1348 case TKN_COMMENT:
1349 label = g_strdup (tkn_buf->str);
1350 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1351 break;
1352 case TKN_EOF:
1353 hotlist_state.readonly = 1;
1354 hotlist_state.file_error = 1;
1355 return;
1356 break;
1357 case TKN_EOL:
1358 /* skip empty lines */
1359 break;
1360 default:
1361 hotlist_state.readonly = 1;
1362 hotlist_state.file_error = 1;
1363 SKIP_TO_EOL;
1364 break;
1366 SKIP_TO_EOL;
1369 static void
1370 hot_load_file (struct hotlist * grp)
1372 int tkn;
1373 struct hotlist *new_grp;
1374 char *label, *url;
1376 current_group = grp;
1378 while ((tkn = hot_next_token())!= TKN_EOF)
1379 switch (tkn) {
1380 case TKN_GROUP:
1381 CHECK_TOKEN(TKN_STRING);
1382 new_grp = add2hotlist (g_strdup (tkn_buf->str), 0, HL_TYPE_GROUP, 0);
1383 SKIP_TO_EOL;
1384 hot_load_group (new_grp);
1385 current_group = grp;
1386 break;
1387 case TKN_ENTRY:
1388 CHECK_TOKEN(TKN_STRING);
1389 label = g_strdup (tkn_buf->str);
1390 CHECK_TOKEN(TKN_URL);
1391 CHECK_TOKEN(TKN_STRING);
1392 url = g_strdup (tkn_buf->str);
1393 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1394 SKIP_TO_EOL;
1395 break;
1396 case TKN_COMMENT:
1397 label = g_strdup (tkn_buf->str);
1398 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1399 break;
1400 case TKN_EOL:
1401 /* skip empty lines */
1402 break;
1403 default:
1404 hotlist_state.readonly = 1;
1405 hotlist_state.file_error = 1;
1406 SKIP_TO_EOL;
1407 break;
1411 static void
1412 clean_up_hotlist_groups (const char *section)
1414 char *grp_section;
1415 gchar **profile_keys, **keys;
1416 gsize len;
1418 grp_section = g_strconcat (section, ".Group", (char *) NULL);
1419 if (mc_config_has_group(mc_main_config, section))
1420 mc_config_del_group (mc_main_config, section);
1422 if (mc_config_has_group (mc_main_config, grp_section)) {
1423 profile_keys = keys = mc_config_get_keys (mc_main_config, grp_section,&len);
1425 while (*profile_keys) {
1426 clean_up_hotlist_groups (*profile_keys);
1427 profile_keys++;
1429 g_strfreev(keys);
1430 mc_config_del_group (mc_main_config, grp_section);
1432 g_free (grp_section);
1437 static void
1438 load_hotlist (void)
1440 int remove_old_list = 0;
1441 struct stat stat_buf;
1443 if (hotlist_state.loaded) {
1444 stat (hotlist_file_name, &stat_buf);
1445 if (hotlist_file_mtime < stat_buf.st_mtime)
1446 done_hotlist ();
1447 else
1448 return;
1451 if (!hotlist_file_name)
1452 hotlist_file_name = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HOTLIST_FILE, NULL);
1454 hotlist = new_hotlist ();
1455 hotlist->type = HL_TYPE_GROUP;
1456 hotlist->label = g_strdup (_(" Top level group "));
1457 hotlist->up = hotlist;
1459 * compatibility :-(
1461 hotlist->directory = g_strdup ("Hotlist");
1463 if ((hotlist_file = fopen (hotlist_file_name, "r")) == 0) {
1464 int result;
1466 load_group (hotlist);
1467 hotlist_state.loaded = 1;
1469 * just to be sure we got copy
1471 hotlist_state.modified = 1;
1472 result = save_hotlist ();
1473 hotlist_state.modified = 0;
1474 if (result) {
1475 remove_old_list = 1;
1476 } else {
1477 message (D_ERROR, _(" Hotlist Load "),
1478 _("MC was unable to write ~/%s file, your old hotlist entries were not deleted"),
1479 MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1481 } else {
1482 hot_load_file (hotlist);
1483 fclose (hotlist_file);
1484 hotlist_state.loaded = 1;
1487 if (remove_old_list) {
1488 GError *error = NULL;
1489 clean_up_hotlist_groups ("Hotlist");
1490 if (! mc_config_save_file (mc_main_config, &error))
1491 setup_save_config_show_error(mc_main_config->ini_path, &error);
1494 stat (hotlist_file_name, &stat_buf);
1495 hotlist_file_mtime = stat_buf.st_mtime;
1496 current_group = hotlist;
1500 static int list_level = 0;
1502 static void
1503 hot_save_group (struct hotlist *grp)
1505 struct hotlist *current = grp->head;
1506 int i;
1507 char *s;
1509 #define INDENT(n) \
1510 do { \
1511 for (i = 0; i < n; i++) \
1512 putc (' ', hotlist_file); \
1513 } while (0)
1515 for (;current; current = current->next)
1516 switch (current->type) {
1517 case HL_TYPE_GROUP:
1518 INDENT (list_level);
1519 fputs ("GROUP \"", hotlist_file);
1520 for (s = current->label; *s; s++) {
1521 if (*s == '"' || *s == '\\')
1522 putc ('\\', hotlist_file);
1523 putc (*s, hotlist_file);
1525 fputs ("\"\n", hotlist_file);
1526 list_level += 2;
1527 hot_save_group (current);
1528 list_level -= 2;
1529 INDENT (list_level);
1530 fputs ("ENDGROUP\n", hotlist_file);
1531 break;
1532 case HL_TYPE_ENTRY:
1533 INDENT(list_level);
1534 fputs ("ENTRY \"", hotlist_file);
1535 for (s = current->label; *s; s++) {
1536 if (*s == '"' || *s == '\\')
1537 putc ('\\', hotlist_file);
1538 putc (*s, hotlist_file);
1540 fputs ("\" URL \"", hotlist_file);
1541 for (s = current->directory; *s; s++) {
1542 if (*s == '"' || *s == '\\')
1543 putc ('\\', hotlist_file);
1544 putc (*s, hotlist_file);
1546 fputs ("\"\n", hotlist_file);
1547 break;
1548 case HL_TYPE_COMMENT:
1549 fprintf (hotlist_file, "#%s\n", current->label);
1550 break;
1551 case HL_TYPE_DOTDOT:
1552 /* do nothing */
1553 break;
1557 int save_hotlist (void)
1559 int saved = 0;
1560 struct stat stat_buf;
1562 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name) {
1563 mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1565 if ((hotlist_file = fopen (hotlist_file_name, "w")) != 0) {
1566 hot_save_group (hotlist);
1567 fclose (hotlist_file);
1568 stat (hotlist_file_name, &stat_buf);
1569 hotlist_file_mtime = stat_buf.st_mtime;
1570 saved = 1;
1571 hotlist_state.modified = 0;
1572 } else
1573 mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1576 return saved;
1580 * Unload list from memory.
1581 * Don't confuse with hotlist_done() for GUI.
1583 void done_hotlist (void)
1585 if (hotlist){
1586 remove_group (hotlist);
1587 g_free (hotlist->label);
1588 g_free (hotlist->directory);
1589 g_free (hotlist);
1590 hotlist = 0;
1593 hotlist_state.loaded = 0;
1595 g_free (hotlist_file_name);
1596 hotlist_file_name = 0;
1597 l_hotlist = 0;
1598 current_group = 0;
1600 if (tkn_buf){
1601 g_string_free (tkn_buf, TRUE);
1602 tkn_buf = NULL;
1606 static void
1607 add_dotdot_to_list (void)
1609 if (current_group != hotlist) {
1610 if (hotlist_has_dot_dot != 0)
1611 add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, 0);