Midnight Commander for Win32
[reactos.git] / rosapps / mc / src / hotlist.c
1 /* Directory hotlist -- for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997 the Free Software Foundation.
3
4 Written by:
5 1994 Radek Doulik
6 1995 Janne Kukonlehto
7 1996 Andrej Borsenkow
8 1997 Norbert Warmuth
9
10 Janne did the original Hotlist code, Andrej made the groupable
11 hotlist; the move hotlist and revamped the file format and made
12 it stronger.
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 */
28
29 #include <config.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdlib.h> /* For malloc() */
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #ifdef SCO_FLAVOR
41 # include <sys/timeb.h> /* alex: for struct timeb, used in time.h */
42 #endif /* SCO_FLAVOR */
43 #include <time.h>
44 #ifndef OS2_NT
45 # include <grp.h>
46 # include <pwd.h>
47 #else
48 # include <io.h>
49 #endif
50 #include "tty.h"
51 #include "mad.h"
52 #include "util.h" /* Needed for the externs */
53 #include "win.h"
54 #include "color.h"
55 #include "dlg.h"
56 #include "widget.h"
57 #include "dialog.h" /* For do_refresh() */
58 #include "setup.h" /* For profile_bname */
59 #include "profile.h" /* Load/save directories hotlist */
60
61 #include "../vfs/vfs.h"
62 /* Needed for the extern declarations of integer parameters */
63 #include "wtools.h"
64 #include "dir.h"
65 #include "panel.h" /* Needed for the externs */
66 #include "file.h"
67 #include "main.h"
68 #include "global.h"
69 #include "hotlist.h"
70 #include "key.h"
71 #include "command.h"
72
73 #ifdef HAVE_TK
74 # include "tkwidget.h"
75 #endif
76
77 #define UX 5
78 #define UY 2
79
80 #define BX UX
81 #define BY LINES-6
82
83 #define BUTTONS (sizeof(hotlist_but)/sizeof(struct _hotlist_but))
84 #define LABELS 3
85 #define B_ADD_CURRENT B_USER
86 #define B_REMOVE (B_USER + 1)
87 #define B_NEW_GROUP (B_USER + 2)
88 #define B_NEW_ENTRY (B_USER + 3)
89 #define B_UP_GROUP (B_USER + 4)
90 #define B_INSERT (B_USER + 5)
91 #define B_APPEND (B_USER + 6)
92 #define B_MOVE (B_USER + 7)
93
94 static WListbox *l_hotlist;
95 static WListbox *l_movelist;
96
97 static Dlg_head *hotlist_dlg;
98 static Dlg_head *movelist_dlg;
99
100 static WLabel *pname, *pname_group, *movelist_group;
101
102 enum HotListType {
103 HL_TYPE_GROUP,
104 HL_TYPE_ENTRY,
105 HL_TYPE_COMMENT
106 };
107
108 static struct {
109 /*
110 * these parameters are intended to be user configurable
111 */
112 int expanded; /* expanded view of all groups at startup */
113
114 /*
115 * these reflect run time state
116 */
117
118 int loaded; /* hotlist is loaded */
119 int readonly; /* hotlist readonly */
120 int file_error; /* parse error while reading file */
121 int running; /* we are running dlg (and have to
122 update listbox */
123 int moving; /* we are in moving hotlist currently */
124 int modified; /* hotlist was modified */
125 int type; /* LIST_HOTLIST || LIST_VFSLIST */
126 } hotlist_state;
127
128 struct _hotlist_but {
129 int ret_cmd, flags, y, x;
130 char *text;
131 char *tkname;
132 int type;
133 } hotlist_but[] = {
134 { B_MOVE, NORMAL_BUTTON, 1, 42, N_("&Move"), "move", LIST_HOTLIST},
135 { B_REMOVE, NORMAL_BUTTON, 1, 30, N_("&Remove"), "r", LIST_HOTLIST},
136 { B_APPEND, NORMAL_BUTTON, 1, 15, N_("&Append"), "e", LIST_MOVELIST},
137 { B_INSERT, NORMAL_BUTTON, 1, 0, N_("&Insert"), "g", LIST_MOVELIST},
138 { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, N_("New &Entry"), "e", LIST_HOTLIST},
139 { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, N_("New &Group"), "g", LIST_HOTLIST},
140 { B_CANCEL, NORMAL_BUTTON, 0, 53, N_("&Cancel"), "cc", LIST_HOTLIST|LIST_VFSLIST|LIST_MOVELIST},
141 { B_UP_GROUP, NORMAL_BUTTON, 0, 42, N_("&Up"), "up", LIST_HOTLIST|LIST_MOVELIST},
142 { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, N_("&Add current"),"ad", LIST_HOTLIST},
143 { B_ENTER, DEFPUSH_BUTTON, 0, 0, N_("Change &To"), "ct", LIST_HOTLIST|LIST_VFSLIST|LIST_MOVELIST},
144 };
145
146 /* Directory hotlist */
147 static struct hotlist{
148 enum HotListType type;
149 char *directory;
150 char *label;
151 struct hotlist *head;
152 struct hotlist *up;
153 struct hotlist *next;
154 } *hotlist = NULL;
155
156 struct hotlist *current_group;
157
158 static void remove_from_hotlist (struct hotlist *entry);
159 void add_new_group_cmd (void);
160
161 static struct hotlist *new_hotlist (void)
162 {
163 struct hotlist *hl;
164
165 hl = xmalloc (sizeof (struct hotlist), "new-hotlist");
166 hl->type = 0;
167 hl->directory =
168 hl->label = 0;
169 hl->head =
170 hl->up =
171 hl->next = 0;
172
173 return hl;
174 }
175
176 #ifndef HAVE_X
177 static void hotlist_refresh (Dlg_head *dlg)
178 {
179 dialog_repaint (dlg, COLOR_NORMAL, COLOR_HOT_NORMAL);
180 attrset (COLOR_NORMAL);
181 draw_box (dlg, 2, 5,
182 dlg->lines - (hotlist_state.moving ? 6 : 10),
183 dlg->cols - (UX*2));
184 if (!hotlist_state.moving)
185 draw_box (dlg, dlg->lines-8, 5, 3, dlg->cols - (UX*2));
186 }
187 #endif
188
189 /* If current->data is 0, then we are dealing with a VFS pathname */
190 static INLINE void update_path_name ()
191 {
192 char *text, *p;
193 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
194 Dlg_head *dlg = hotlist_state.moving ? movelist_dlg : hotlist_dlg;
195
196 if (list->current){
197 if (list->current->data != 0) {
198 struct hotlist *hlp = (struct hotlist *)list->current->data;
199
200 if (hlp->type == HL_TYPE_ENTRY)
201 text = hlp->directory;
202 else
203 text = _("Subgroup - press ENTER to see list");
204
205 #ifndef HAVE_X
206 p = copy_strings (" ", current_group->label, " ", (char *)0);
207 if (!hotlist_state.moving)
208 label_set_text (pname_group, name_trunc (p, dlg->cols - (UX*2+4)));
209 else
210 label_set_text (movelist_group, name_trunc (p, dlg->cols - (UX*2+4)));
211 free (p);
212 #endif
213 } else {
214 text = list->current->text;
215 }
216 } else {
217 text = "";
218 }
219 if (!hotlist_state.moving)
220 label_set_text (pname, name_trunc (text, dlg->cols - (UX*2+4)));
221 dlg_redraw (dlg);
222 }
223
224 #define CHECK_BUFFER \
225 do { \
226 int i; \
227 \
228 if ((i = strlen (current->label) + 3) > buflen) { \
229 free (buf); \
230 buf = xmalloc (buflen = 1024 * (i/1024 + 1), "fill_listbox"); \
231 } \
232 buf[0] = '\0'; \
233 } while (0)
234
235 static void fill_listbox (void)
236 {
237 struct hotlist *current = current_group->head;
238 static char *buf;
239 static int buflen;
240
241 if (!buf)
242 buf = xmalloc (buflen = 1024, "fill_listbox");
243 buf[0] = '\0';
244
245 while (current){
246 switch (current->type) {
247 case HL_TYPE_GROUP:
248 {
249 CHECK_BUFFER;
250 strcat (strcat (buf, "->"), current->label);
251 if (hotlist_state.moving)
252 listbox_add_item (l_movelist, 0, 0, buf, current);
253 else
254 listbox_add_item (l_hotlist, 0, 0, buf, current);
255 }
256 break;
257 case HL_TYPE_ENTRY:
258 if (hotlist_state.moving)
259 listbox_add_item (l_movelist, 0, 0, current->label, current);
260 else
261 listbox_add_item (l_hotlist, 0, 0, current->label, current);
262 break;
263 }
264 current = current->next;
265 }
266 }
267
268 #undef CHECK_BUFFER
269
270 static void
271 unlink_entry (struct hotlist *entry)
272 {
273 struct hotlist *current = current_group->head;
274
275 if (current == entry)
276 current_group->head = entry->next;
277 else
278 while (current && current->next != entry)
279 current = current->next;
280 if (current)
281 current->next = entry->next;
282 entry->next =
283 entry->up = 0;
284 }
285
286 static void add_new_entry_cmd (void);
287 static void init_movelist (int, struct hotlist *);
288
289 static int hotlist_button_callback (int action, void *data)
290 {
291 switch (action) {
292 case B_MOVE:
293 {
294 struct hotlist *saved = current_group;
295 struct hotlist *item;
296 struct hotlist *moveto_item = 0;
297 struct hotlist *moveto_group = 0;
298 int ret;
299
300 if (!l_hotlist->current)
301 return 0; /* empty group - nothing to do */
302 item = l_hotlist->current->data;
303 hotlist_state.moving = 1;
304 init_movelist (LIST_MOVELIST, item);
305 run_dlg (movelist_dlg);
306 ret = movelist_dlg->ret_value;
307 hotlist_state.moving = 0;
308 if (l_movelist->current)
309 moveto_item = l_movelist->current->data;
310 moveto_group = current_group;
311 destroy_dlg (movelist_dlg);
312 current_group = saved;
313 if (ret == B_CANCEL)
314 return 0;
315 if (moveto_item == item)
316 return 0; /* If we insert/append a before/after a
317 it hardly changes anything ;) */
318 unlink_entry (item);
319 listbox_remove_current (l_hotlist, 1);
320 item->up = moveto_group;
321 if (!moveto_group->head)
322 moveto_group->head = item;
323 else if (!moveto_item) { /* we have group with just comments */
324 struct hotlist *p = moveto_group->head;
325
326 /* skip comments */
327 while (p->next)
328 p = p->next;
329 p->next = item;
330 } else if (ret == B_ENTER || ret == B_APPEND)
331 if (!moveto_item->next)
332 moveto_item->next = item;
333 else {
334 item->next = moveto_item->next;
335 moveto_item->next = item;
336 }
337 else if (moveto_group->head == moveto_item) {
338 moveto_group->head = item;
339 item->next = moveto_item;
340 } else {
341 struct hotlist *p = moveto_group->head;
342
343 while (p->next != moveto_item)
344 p = p->next;
345 item->next = p->next;
346 p->next = item;
347 }
348 listbox_remove_list (l_hotlist);
349 fill_listbox ();
350 #ifdef HAVE_X
351 x_listbox_select_nth (l_hotlist, 0);
352 #endif
353 repaint_screen ();
354 hotlist_state.modified = 1;
355 return 0;
356 break;
357 }
358 case B_REMOVE:
359 if (l_hotlist->current)
360 remove_from_hotlist (l_hotlist->current->data);
361 return 0;
362 break;
363
364 case B_NEW_GROUP:
365 add_new_group_cmd ();
366 return 0;
367 break;
368
369 case B_ADD_CURRENT:
370 add2hotlist_cmd ();
371 return 0;
372 break;
373
374 case B_NEW_ENTRY:
375 add_new_entry_cmd ();
376 return 0;
377 break;
378
379 case B_ENTER:
380 {
381 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
382 if (list->current){
383 if (list->current->data) {
384 struct hotlist *hlp = (struct hotlist*) list->current->data;
385 if (hlp->type == HL_TYPE_ENTRY)
386 return 1;
387 else {
388 listbox_remove_list (list);
389 current_group = hlp;
390 fill_listbox ();
391 return 0;
392 }
393 } else
394 return 1;
395 }
396 }
397 /* Fall through if list empty - just go up */
398
399 case B_UP_GROUP:
400 {
401 WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
402 listbox_remove_list (list);
403 current_group = current_group->up;
404 fill_listbox ();
405 #ifdef HAVE_X
406 x_listbox_select_nth (list, 0);
407 #endif
408 return 0;
409 break;
410 }
411
412 default:
413 return 1;
414 break;
415
416 }
417 }
418
419 static int hotlist_callback (Dlg_head * h, int Par, int Msg)
420 {
421 switch (Msg) {
422 #ifndef HAVE_X
423 case DLG_DRAW:
424 hotlist_refresh (h);
425 break;
426 #endif
427
428 case DLG_UNHANDLED_KEY:
429 switch (Par) {
430 case '\n':
431 if (ctrl_pressed())
432 goto l1;
433 case KEY_ENTER:
434 case KEY_RIGHT:
435 if (hotlist_button_callback (B_ENTER, 0)) {
436 h->ret_value = B_ENTER;
437 dlg_stop (h);
438 };
439 return 1;
440 break;
441 case KEY_LEFT:
442 if (hotlist_state.type != LIST_VFSLIST )
443 return !hotlist_button_callback (B_UP_GROUP, 0);
444 else
445 return 0;
446 break;
447 l1:
448 case ALT('\n'):
449 case ALT('\r'):
450 if (!hotlist_state.moving)
451 {
452 if (l_hotlist->current){
453 if (l_hotlist->current->data) {
454 struct hotlist *hlp = (struct hotlist*) l_hotlist->current->data;
455 if (hlp->type == HL_TYPE_ENTRY) {
456 char *tmp = copy_strings( "cd ", hlp->directory, NULL);
457 stuff (input_w (cmdline), tmp, 0);
458 free (tmp);
459 dlg_stop (h);
460 h->ret_value = B_CANCEL;
461 return 1;
462 }
463 }
464 }
465 }
466 return 1; /* ignore key */
467 default:
468 return 0;
469 }
470 break;
471
472 case DLG_POST_KEY:
473 if (hotlist_state.moving)
474 dlg_select_widget (movelist_dlg, l_movelist);
475 else
476 dlg_select_widget (hotlist_dlg, l_hotlist);
477 /* always stay on hotlist */
478 /* fall through */
479
480 case DLG_INIT:
481 attrset (MENU_ENTRY_COLOR);
482 update_path_name ();
483 break;
484 }
485 return 0;
486 }
487
488 static int l_call (void *l)
489 {
490 WListbox *list = (WListbox *) l;
491 Dlg_head *dlg = hotlist_state.moving ? movelist_dlg : hotlist_dlg;
492
493 if (list->current){
494 if (list->current->data) {
495 struct hotlist *hlp = (struct hotlist*) list->current->data;
496 if (hlp->type == HL_TYPE_ENTRY) {
497 dlg->ret_value = B_ENTER;
498 dlg_stop (dlg);
499 return listbox_finish;
500 } else {
501 hotlist_button_callback (B_ENTER, (void *)0);
502 hotlist_callback (dlg, '\n', DLG_POST_KEY);
503 return listbox_nothing;
504 }
505 } else {
506 dlg->ret_value = B_ENTER;
507 dlg_stop (dlg);
508 return listbox_finish;
509 }
510 }
511
512 hotlist_button_callback (B_UP_GROUP, (void *)0);
513 hotlist_callback (dlg, 'u', DLG_POST_KEY);
514 return listbox_nothing;
515 }
516
517 static void add_name_to_list (char *path)
518 {
519 listbox_add_item (l_hotlist, 0, 0, path, 0);
520 }
521
522 /*
523 * Expands all button names (once) and recalculates button positions.
524 * returns number of columns in the dialog box, which is 10 chars longer
525 * then buttonbar.
526 *
527 * If common width of the window (i.e. in xterm) is less than returned
528 * width - sorry :) (anyway this did not handled in previous version too)
529 */
530 static int
531 init_i18n_stuff(int list_type, int cols)
532 {
533 register int i;
534 static char* cancel_but = "&Cancel";
535
536 #ifdef ENABLE_NLS
537 static int hotlist_i18n_flag = 0;
538
539 if (!hotlist_i18n_flag)
540 {
541 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
542 while (i--)
543 hotlist_but [i].text = _(hotlist_but [i].text);
544
545 cancel_but = _(cancel_but);
546 hotlist_i18n_flag = 1;
547 }
548 #endif /* ENABLE_NLS */
549
550 /* Dynamic resizing of buttonbars */
551 {
552 int len[2], count[2]; /* at most two lines of buttons */
553 int cur_x[2], row;
554
555 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
556 len[0] = len[1] = count[0] = count[1] = 0;
557
558 /* Count len of buttonbars, assuming 2 extra space between buttons */
559 while (i--)
560 {
561 if (! (hotlist_but[i].type & list_type))
562 continue;
563
564 row = hotlist_but [i].y;
565 ++count [row];
566 len [row] += strlen (hotlist_but [i].text) + 5;
567 if (hotlist_but [i].flags == DEFPUSH_BUTTON)
568 len [row] += 2;
569 }
570 len[0] -= 2;
571 len[1] -= 2;
572
573 cols = max(cols, max(len[0], len[1]));
574
575 /* arrange buttons */
576
577 cur_x[0] = cur_x[1] = 0;
578 i = sizeof (hotlist_but) / sizeof (hotlist_but [0]);
579 while (i--)
580 {
581 if (! (hotlist_but[i].type & list_type))
582 continue;
583
584 row = hotlist_but [i].y;
585
586 if (hotlist_but [i].x != 0)
587 {
588 /* not first int the row */
589 if (!strcmp (hotlist_but [i].text, cancel_but))
590 hotlist_but [i].x =
591 cols - strlen (hotlist_but [i].text) - 13;
592 else
593 hotlist_but [i].x = cur_x [row];
594 }
595
596 cur_x [row] += strlen (hotlist_but [i].text) + 2
597 + (hotlist_but [i].flags == DEFPUSH_BUTTON ? 5 : 3);
598 }
599 }
600
601 return cols;
602 }
603
604 static void init_hotlist (int list_type)
605 {
606 int i;
607 int hotlist_cols = init_i18n_stuff (list_type, COLS - 6);
608
609 do_refresh ();
610
611 hotlist_state.expanded = GetPrivateProfileInt ("HotlistConfig",
612 "expanded_view_of_groups", 0, profile_name);
613
614 hotlist_dlg = create_dlg (0, 0, LINES-2, hotlist_cols, dialog_colors,
615 hotlist_callback,
616 list_type == LIST_VFSLIST ? "[vfshot]" : "[Hotlist]",
617 list_type == LIST_VFSLIST ? "vfshot" : "hotlist",
618 DLG_CENTER|DLG_GRID);
619 x_set_dialog_title (hotlist_dlg,
620 list_type == LIST_VFSLIST ? _("Active VFS directories") : _("Directory hotlist"));
621
622 #define XTRACT(i) BY+hotlist_but[i].y, BX+hotlist_but[i].x, hotlist_but[i].ret_cmd, hotlist_but[i].flags, hotlist_but[i].text, hotlist_button_callback, 0, hotlist_but[i].tkname
623
624 for (i = 0; i < BUTTONS; i++){
625 if (hotlist_but[i].type & list_type)
626 add_widgetl (hotlist_dlg, button_new (XTRACT (i)), (i == BUTTONS - 1) ?
627 XV_WLAY_CENTERROW : XV_WLAY_RIGHTOF);
628 }
629 #undef XTRACT
630
631 /* We add the labels.
632 * pname will hold entry's pathname;
633 * pname_group will hold name of current group
634 */
635 pname = label_new (UY-11+LINES, UX+2, "", "the-lab");
636 add_widget (hotlist_dlg, pname);
637 #ifndef HAVE_X
638 if (!hotlist_state.moving) {
639 add_widget (hotlist_dlg, label_new (UY-12+LINES, UX+1, _(" Directory path "), NULL));
640
641 /* This one holds the displayed pathname */
642 pname_group = label_new (UY, UX+1, _(" Directory label "), NULL);
643 add_widget (hotlist_dlg, pname_group);
644 }
645 #endif
646 /* get new listbox */
647 l_hotlist = listbox_new (UY + 1, UX + 1, COLS-2*UX-8, LINES-14, listbox_cback, l_call, "listbox");
648
649 /* Fill the hotlist with the active VFS or the hotlist */
650 if (list_type == LIST_VFSLIST){
651 listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
652 vfs_fill_names (add_name_to_list);
653 } else
654 fill_listbox ();
655
656 add_widgetl (hotlist_dlg, l_hotlist, XV_WLAY_EXTENDWIDTH);
657 /* add listbox to the dialogs */
658 }
659
660 static void init_movelist (int list_type, struct hotlist *item)
661 {
662 int i;
663 char *hdr = copy_strings (_("Moving "), item->label, 0);
664 int movelist_cols = init_i18n_stuff (list_type, COLS - 6);
665
666 do_refresh ();
667
668 movelist_dlg = create_dlg (0, 0, LINES-6, movelist_cols, dialog_colors,
669 hotlist_callback, "[Hotlist]",
670 "movelist",
671 DLG_CENTER|DLG_GRID);
672 x_set_dialog_title (movelist_dlg, hdr);
673 free (hdr);
674
675 #define XTRACT(i) BY-4+hotlist_but[i].y, BX+hotlist_but[i].x, hotlist_but[i].ret_cmd, hotlist_but[i].flags, hotlist_but[i].text, hotlist_button_callback, 0, hotlist_but[i].tkname
676
677 for (i = 0; i < BUTTONS; i++){
678 if (hotlist_but[i].type & list_type)
679 add_widgetl (movelist_dlg, button_new (XTRACT (i)), (i == BUTTONS - 1) ?
680 XV_WLAY_CENTERROW : XV_WLAY_RIGHTOF);
681 }
682
683 #undef XTRACT
684
685 /* We add the labels. We are interested in the last one,
686 * that one will hold the path name label
687 */
688 #ifndef HAVE_X
689 movelist_group = label_new (UY, UX+1, _(" Directory label "), NULL);
690 add_widget (movelist_dlg, movelist_group);
691 #endif
692 /* get new listbox */
693 l_movelist = listbox_new (UY + 1, UX + 1,
694 movelist_dlg->cols - 2*UX - 2, movelist_dlg->lines - 8,
695 listbox_cback, l_call, "listbox");
696
697 fill_listbox ();
698
699 add_widgetl (movelist_dlg, l_movelist, XV_WLAY_EXTENDWIDTH);
700 /* add listbox to the dialogs */
701 }
702
703 static void hotlist_done (void)
704 {
705 destroy_dlg (hotlist_dlg);
706 if (0)
707 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
708 repaint_screen ();
709 }
710
711 static char *
712 find_group_section (struct hotlist *grp)
713 {
714 return copy_strings (grp->directory, ".Group", (char *)0);
715
716 }
717
718
719 /* 1.11.96 bor: added pos parameter to control placement of new item.
720 see widget.c, listbox_add_item()
721 now hotlist is in unsorted mode
722 */
723 static struct hotlist *
724 add2hotlist (char *label, char *directory, enum HotListType type, int pos)
725 {
726 struct hotlist *current;
727 struct hotlist *new;
728
729 if (l_hotlist && l_hotlist->current)
730 current = l_hotlist->current->data;
731
732 new = new_hotlist ();
733
734 new->type = type;
735 new->label = label;
736 new->directory = directory;
737 new->up = current_group;
738
739 if (!current_group->head) { /* first element in group */
740 current_group->head = new;
741 } else if (pos == 2) { /* should be appended after current*/
742 new->next = current->next;
743 current->next = new;
744 } else if (pos == 1 &&
745 current == current_group->head) {
746 /* should be inserted before first item */
747 new->next = current;
748 current_group->head = new;
749 } else if (pos == 1) { /* befor current */
750 struct hotlist *p = current_group->head;
751
752 while (p->next != current)
753 p = p->next;
754
755 new->next = current;
756 p->next = new;
757 } else { /* append at the end */
758 struct hotlist *p = current_group->head;
759
760 while (p->next)
761 p = p->next;
762
763 p->next = new;
764 }
765
766 if (hotlist_state.running && type != HL_TYPE_COMMENT) {
767 if (type == HL_TYPE_GROUP) {
768 char *lbl = copy_strings ("->", new->label, (char *)0);
769
770 listbox_add_item (l_hotlist, pos, 0, lbl, new);
771 free (lbl);
772 } else
773 listbox_add_item (l_hotlist, pos, 0, new->label, new);
774 listbox_select_entry (l_hotlist, l_hotlist->current);
775 }
776 return new;
777
778 }
779
780 /*
781 * Support routine for add_new_entry_input()/add_new_group_input()
782 * Change positions of buttons (first three widgets).
783 *
784 * This is just a quick hack. Accurate procedure must take care of
785 * internationalized label lengths and total buttonbar length...assume
786 * 64 is longer anyway.
787 */
788 static void add_widgets_i18n(QuickWidget* qw, int len)
789 {
790 int i, l[3], space, cur_x;
791
792 for (i = 0; i < 3; i++)
793 {
794 qw [i].text = _(qw [i].text);
795 l[i] = strlen (qw [i].text) + 3;
796 }
797 space = (len - 4 - l[0] - l[1] - l[2]) / 4;
798
799 for (cur_x = 2 + space, i = 3; i--; cur_x += l[i] + space)
800 {
801 qw [i].relative_x = cur_x;
802 qw [i].x_divisions = len;
803 }
804 }
805
806 static int add_new_entry_input (char *header, char *text1, char *text2, char *help, char **r1, char **r2)
807 {
808 QuickDialog Quick_input;
809 QuickWidget quick_widgets [] = {
810 { quick_button, 55, 80, 4, 0, N_("&Cancel"), 0, B_CANCEL, 0, 0,
811 XV_WLAY_DONTCARE, "button-cancel" },
812 { quick_button, 30, 80, 4, 0, N_("&Insert"), 0, B_INSERT, 0, 0,
813 XV_WLAY_DONTCARE, "button-insert" },
814 { quick_button, 10, 80, 4, 0, N_("&Append"), 0, B_APPEND, 0, 0,
815 XV_WLAY_DONTCARE, "button-append" },
816 { quick_input, 4, 80, 4, 0, "",58, 0, 0, 0, XV_WLAY_BELOWCLOSE, "input-pth" },
817 { quick_label, 3, 80, 3, 0, 0, 0, 0, 0, 0, XV_WLAY_DONTCARE, "label-pth" },
818 { quick_input, 4, 80, 3, 0, "", 58, 0, 0, 0, XV_WLAY_BELOWCLOSE, "input-lbl" },
819 { quick_label, 3, 80, 2, 0, 0, 0, 0, 0, 0, XV_WLAY_DONTCARE, "label-lbl" },
820 { 0 } };
821
822 int len;
823 int i;
824 int lines1, lines2;
825 char *my_str1, *my_str2;
826
827 #ifdef ENABLE_NLS
828 static int i18n_flag = 0;
829 #endif /* ENABLE_NLS */
830
831 len = max (strlen (header), msglen (text1, &lines1));
832 len = max (len, msglen (text2, &lines2)) + 4;
833 len = max (len, 64);
834
835 #ifdef ENABLE_NLS
836 if (!i18n_flag)
837 {
838 add_widgets_i18n(quick_widgets, len);
839 i18n_flag = 1;
840 }
841 #endif /* ENABLE_NLS */
842
843 Quick_input.xlen = len;
844 Quick_input.xpos = -1;
845 Quick_input.title = header;
846 Quick_input.help = help;
847 Quick_input.class = "hotlist_new_entry";
848 Quick_input.i18n = 0;
849 quick_widgets [6].text = text1;
850 quick_widgets [4].text = text2;
851 quick_widgets [5].text = *r1;
852 quick_widgets [3].text = *r2;
853
854 for (i = 0; i < 7; i++)
855 quick_widgets [i].y_divisions = lines1+lines2+7;
856 Quick_input.ylen = lines1 + lines2 + 7;
857
858 quick_widgets [0].relative_y += (lines1 + lines2);
859 quick_widgets [1].relative_y += (lines1 + lines2);
860 quick_widgets [2].relative_y += (lines1 + lines2);
861 quick_widgets [3].relative_y += (lines1);
862 quick_widgets [4].relative_y += (lines1);
863
864 quick_widgets [5].str_result = &my_str1;
865 quick_widgets [3].str_result = &my_str2;
866
867 Quick_input.widgets = quick_widgets;
868 if ((i = quick_dialog (&Quick_input)) != B_CANCEL){
869 *r1 = *(quick_widgets [5].str_result);
870 *r2 = *(quick_widgets [3].str_result);
871 return i;
872 } else
873 return 0;
874 }
875
876 static void add_new_entry_cmd (void)
877 {
878 char *title = 0, *url = 0;
879 int ret;
880
881 /* Take current directory as default value for input fields */
882 title = url = cpanel->cwd;
883
884 ret = add_new_entry_input (_("New hotlist entry"), _("Directory label"), _("Directory path"),
885 "[Hotlist]", &title, &url);
886
887 if (!ret || !title || !*title || !url || !*url)
888 return;
889
890 if (ret == B_ENTER || ret == B_APPEND)
891 add2hotlist (strdup (title), strdup (url), HL_TYPE_ENTRY, 2);
892 else
893 add2hotlist (strdup (title), strdup (url), HL_TYPE_ENTRY, 1);
894
895 hotlist_state.modified = 1;
896 }
897
898 static int add_new_group_input (char *header, char *label, char **result)
899 {
900 int ret;
901 QuickDialog Quick_input;
902 QuickWidget quick_widgets [] = {
903 { quick_button, 55, 80, 1, 0, N_("&Cancel"), 0, B_CANCEL, 0, 0,
904 XV_WLAY_DONTCARE, "button-cancel" },
905 { quick_button, 30, 80, 1, 0, N_("&Insert"), 0, B_INSERT, 0, 0,
906 XV_WLAY_DONTCARE, "button-insert" },
907 { quick_button, 10, 80, 1, 0, N_("&Append"), 0, B_APPEND, 0, 0,
908 XV_WLAY_DONTCARE, "button-append" },
909 { quick_input, 4, 80, 0, 0, "", 58, 0, 0, 0, XV_WLAY_BELOWCLOSE, "input" },
910 { quick_label, 3, 80, 2, 0, 0, 0, 0, 0, 0, XV_WLAY_DONTCARE, "label" },
911 { 0 } };
912
913 int len;
914 int i;
915 int lines;
916 char *my_str;
917
918 #ifdef ENABLE_NLS
919 static int i18n_flag = 0;
920 #endif /* ENABLE_NLS */
921
922 len = max (strlen (header), msglen (label, &lines)) + 4;
923 len = max (len, 64);
924
925 #ifdef ENABLE_NLS
926 if (!i18n_flag)
927 {
928 add_widgets_i18n(quick_widgets, len);
929 i18n_flag = 1;
930 }
931 #endif /* ENABLE_NLS */
932
933 Quick_input.xlen = len;
934 Quick_input.xpos = -1;
935 Quick_input.title = header;
936 Quick_input.help = "[Hotlist]";
937 Quick_input.class = "hotlist_new_group";
938 Quick_input.i18n = 0;
939 quick_widgets [4].text = label;
940
941 for (i = 0; i < 5; i++)
942 quick_widgets [i].y_divisions = lines+6;
943 Quick_input.ylen = lines + 6;
944
945 for (i = 0; i < 4; i++)
946 quick_widgets [i].relative_y += 2 + lines;
947
948 quick_widgets [3].str_result = &my_str;
949 quick_widgets [3].text = "";
950
951 Quick_input.widgets = quick_widgets;
952 if ((ret = quick_dialog (&Quick_input)) != B_CANCEL){
953 *result = *(quick_widgets [3].str_result);
954 return ret;
955 } else
956 return 0;
957 }
958
959 void add_new_group_cmd (void)
960 {
961 char *label;
962 int ret;
963
964 ret = add_new_group_input (_(" New hotlist group "), _("Name of new group"), &label);
965 if (!ret || !label || !*label)
966 return;
967
968 if (ret == B_ENTER || ret == B_APPEND)
969 add2hotlist (label, 0, HL_TYPE_GROUP, 2);
970 else
971 add2hotlist (label, 0, HL_TYPE_GROUP, 1);
972
973 hotlist_state.modified = 1;
974 }
975
976 void add2hotlist_cmd (void)
977 {
978 char *prompt, *label;
979 char* cp = _("Label for \"%s\":");
980 int l = strlen(cp);
981
982 prompt = xmalloc (strlen (cpanel->cwd) + l, "add2hotlist_cmd");
983 sprintf (prompt, cp, name_trunc (cpanel->cwd, COLS-2*UX-(l+8)));
984 label = input_dialog (_(" Add to hotlist "), prompt, cpanel->cwd);
985 free (prompt);
986 if (!label || !*label)
987 return;
988
989 add2hotlist (label, strdup (cpanel->cwd), HL_TYPE_ENTRY, 0);
990 hotlist_state.modified = 1;
991 }
992
993 static void remove_group (struct hotlist *grp)
994 {
995 struct hotlist *current = grp->head;
996
997 while (current) {
998 struct hotlist *next = current->next;
999
1000 if (current->type == HL_TYPE_GROUP)
1001 remove_group (current);
1002
1003 if (current->label)
1004 free (current->label);
1005 if (current->directory)
1006 free (current->directory);
1007 free (current);
1008
1009 current = next;
1010 }
1011
1012 }
1013
1014 static void remove_from_hotlist (struct hotlist *entry)
1015 {
1016 if (entry->type == HL_TYPE_GROUP) {
1017 if (entry->head) {
1018 char *header;
1019 int result;
1020
1021 header = copy_strings (_(" Remove: "),
1022 name_trunc (entry->label, 30),
1023 " ",
1024 0);
1025 result = query_dialog (header, _("\n Group not empty.\n Remove it?"),
1026 D_ERROR, 2,
1027 _("&No"), _("&Yes"));
1028 free (header);
1029
1030 if (!result)
1031 return;
1032 }
1033
1034 remove_group (entry);
1035 }
1036
1037 unlink_entry (entry);
1038
1039 if (entry->label)
1040 free (entry->label);
1041 if (entry->directory)
1042 free (entry->directory);
1043 free (entry);
1044 /* now remove list entry from screen */
1045 listbox_remove_current (l_hotlist, 1);
1046 hotlist_state.modified = 1;
1047 }
1048
1049 char *hotlist_cmd (int vfs_or_hotlist)
1050 {
1051 char *target = NULL;
1052
1053 hotlist_state.type = vfs_or_hotlist;
1054 load_hotlist ();
1055
1056 init_hotlist (vfs_or_hotlist);
1057
1058 /* display file info */
1059 attrset (SELECTED_COLOR);
1060
1061 hotlist_state.running = 1;
1062 run_dlg (hotlist_dlg);
1063 hotlist_state.running = 0;
1064 save_hotlist ();
1065
1066 switch (hotlist_dlg->ret_value) {
1067 case B_CANCEL:
1068 break;
1069
1070 case B_ENTER:
1071 if (l_hotlist->current->data) {
1072 struct hotlist *hlp = (struct hotlist*) l_hotlist->current->data;
1073 target = strdup (hlp->directory);
1074 } else
1075 target = strdup (l_hotlist->current->text);
1076 break;
1077 }
1078
1079 hotlist_done ();
1080 return target;
1081 }
1082
1083 void load_group (struct hotlist *grp)
1084 {
1085 void *profile_keys;
1086 char *key, *value;
1087 char *group_section;
1088 struct hotlist *current = 0;
1089
1090 group_section = find_group_section (grp);
1091
1092 profile_keys = profile_init_iterator (group_section, profile_name);
1093
1094 current_group = grp;
1095
1096 while (profile_keys){
1097 profile_keys = profile_iterator_next (profile_keys, &key, &value);
1098 add2hotlist (strdup (value), strdup (key), HL_TYPE_GROUP, 0);
1099 }
1100 free (group_section);
1101
1102 profile_keys = profile_init_iterator (grp->directory, profile_name);
1103
1104 while (profile_keys){
1105 profile_keys = profile_iterator_next (profile_keys, &key, &value);
1106 add2hotlist (strdup (value), strdup (key), HL_TYPE_ENTRY, 0);
1107 }
1108
1109 for (current = grp->head; current; current = current->next)
1110 load_group (current);
1111 }
1112
1113 #define TKN_GROUP 0
1114 #define TKN_ENTRY 1
1115 #define TKN_STRING 2
1116 #define TKN_URL 3
1117 #define TKN_ENDGROUP 4
1118 #define TKN_COMMENT 5
1119 #define TKN_EOL 125
1120 #define TKN_EOF 126
1121 #define TKN_UNKNOWN 127
1122
1123 static char *tkn_buf;
1124 static int tkn_buf_length;
1125 static int tkn_length;
1126
1127 static char *hotlist_file_name;
1128 static FILE *hotlist_file;
1129 static time_t hotlist_file_mtime;
1130
1131 static int hot_skip_blanks ()
1132 {
1133 int c;
1134
1135 while ((c = getc (hotlist_file)) != EOF && c != '\n' && isspace (c))
1136 ;
1137 return c;
1138
1139 }
1140
1141 static int hot_next_token ()
1142 {
1143 int c;
1144
1145 #define CHECK_BUF() \
1146 do { \
1147 if (tkn_length == tkn_buf_length) \
1148 tkn_buf = tkn_buf ? (realloc (tkn_buf, tkn_buf_length += 1024)) \
1149 : (malloc (tkn_buf_length = 1024)); \
1150 } while (0)
1151
1152 tkn_length = 0;
1153
1154 again:
1155 c = hot_skip_blanks ();
1156 switch (c) {
1157 case EOF:
1158 return TKN_EOF;
1159 break;
1160 case '\n':
1161 return TKN_EOL;
1162 break;
1163 case '#':
1164 while ((c = getc (hotlist_file)) != EOF && c != '\n') {
1165 if (c == EOF)
1166 return TKN_EOF;
1167 if (c != '\n') {
1168 CHECK_BUF();
1169 tkn_buf[tkn_length++] = c == '\n' ? ' ' : c;
1170 }
1171 }
1172 CHECK_BUF();
1173 tkn_buf[tkn_length] = '\0';
1174 return TKN_COMMENT;
1175 break;
1176 case '"':
1177 while ((c = getc (hotlist_file)) != EOF && c != '"') {
1178 if (c == '\\')
1179 if ((c = getc (hotlist_file)) == EOF)
1180 return TKN_EOF;
1181 CHECK_BUF();
1182 tkn_buf[tkn_length++] = c == '\n' ? ' ' : c;
1183 }
1184 if (c == EOF)
1185 return TKN_EOF;
1186 CHECK_BUF();
1187 tkn_buf[tkn_length] = '\0';
1188 return TKN_STRING;
1189 break;
1190 case '\\':
1191 if ((c = getc (hotlist_file)) == EOF)
1192 return TKN_EOF;
1193 if (c == '\n')
1194 goto again;
1195
1196 /* fall through; it is taken as normal character */
1197
1198 default:
1199 do {
1200 CHECK_BUF();
1201 tkn_buf[tkn_length++] = toupper(c);
1202 } while ((c = fgetc (hotlist_file)) != EOF && isalnum (c));
1203 if (c != EOF)
1204 ungetc (c, hotlist_file);
1205 CHECK_BUF();
1206 tkn_buf[tkn_length] = '\0';
1207 if (strncmp (tkn_buf, "GROUP", tkn_length) == 0)
1208 return TKN_GROUP;
1209 else if (strncmp (tkn_buf, "ENTRY", tkn_length) == 0)
1210 return TKN_ENTRY;
1211 else if (strncmp (tkn_buf, "ENDGROUP", tkn_length) == 0)
1212 return TKN_ENDGROUP;
1213 else if (strncmp (tkn_buf, "URL", tkn_length) == 0)
1214 return TKN_URL;
1215 else
1216 return TKN_UNKNOWN;
1217 break;
1218 }
1219 }
1220
1221 #define SKIP_TO_EOL { \
1222 int _tkn; \
1223 while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
1224 }
1225
1226 #define CHECK_TOKEN(_TKN_) \
1227 if ((tkn = hot_next_token ()) != _TKN_) { \
1228 hotlist_state.readonly = 1; \
1229 hotlist_state.file_error = 1; \
1230 while (tkn != TKN_EOL && tkn != TKN_EOF) \
1231 tkn = hot_next_token (); \
1232 break; \
1233 }
1234
1235 static void
1236 hot_load_group (struct hotlist * grp)
1237 {
1238 int tkn;
1239 struct hotlist *new_grp;
1240 char *label, *url;
1241
1242 current_group = grp;
1243
1244 while ((tkn = hot_next_token()) != TKN_ENDGROUP)
1245 switch (tkn) {
1246 case TKN_GROUP:
1247 CHECK_TOKEN(TKN_STRING);
1248 new_grp = add2hotlist (strdup (tkn_buf), 0, HL_TYPE_GROUP, 0);
1249 SKIP_TO_EOL;
1250 hot_load_group (new_grp);
1251 current_group = grp;
1252 break;
1253 case TKN_ENTRY:
1254 CHECK_TOKEN(TKN_STRING);
1255 label = strdup (tkn_buf);
1256 CHECK_TOKEN(TKN_URL);
1257 CHECK_TOKEN(TKN_STRING);
1258 url = strdup (tkn_buf);
1259 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1260 SKIP_TO_EOL;
1261 break;
1262 case TKN_COMMENT:
1263 label = strdup (tkn_buf);
1264 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1265 break;
1266 case TKN_EOF:
1267 hotlist_state.readonly = 1;
1268 hotlist_state.file_error = 1;
1269 return;
1270 break;
1271 case TKN_EOL:
1272 /* skip empty lines */
1273 break;
1274 default:
1275 hotlist_state.readonly = 1;
1276 hotlist_state.file_error = 1;
1277 SKIP_TO_EOL;
1278 break;
1279 }
1280 SKIP_TO_EOL;
1281 }
1282
1283 static void
1284 hot_load_file (struct hotlist * grp)
1285 {
1286 int tkn;
1287 struct hotlist *new_grp;
1288 char *label, *url;
1289
1290 current_group = grp;
1291
1292 while ((tkn = hot_next_token())!= TKN_EOF)
1293 switch (tkn) {
1294 case TKN_GROUP:
1295 CHECK_TOKEN(TKN_STRING);
1296 new_grp = add2hotlist (strdup (tkn_buf), 0, HL_TYPE_GROUP, 0);
1297 SKIP_TO_EOL;
1298 hot_load_group (new_grp);
1299 current_group = grp;
1300 break;
1301 case TKN_ENTRY:
1302 CHECK_TOKEN(TKN_STRING);
1303 label = strdup (tkn_buf);
1304 CHECK_TOKEN(TKN_URL);
1305 CHECK_TOKEN(TKN_STRING);
1306 url = strdup (tkn_buf);
1307 add2hotlist (label, url, HL_TYPE_ENTRY, 0);
1308 SKIP_TO_EOL;
1309 break;
1310 case TKN_COMMENT:
1311 label = strdup (tkn_buf);
1312 add2hotlist (label, 0, HL_TYPE_COMMENT, 0);
1313 break;
1314 case TKN_EOL:
1315 /* skip empty lines */
1316 break;
1317 default:
1318 hotlist_state.readonly = 1;
1319 hotlist_state.file_error = 1;
1320 SKIP_TO_EOL;
1321 break;
1322 }
1323 }
1324
1325 void
1326 clean_up_hotlist_groups (char *section)
1327 {
1328 char *grp_section;
1329 void *profile_keys;
1330 char *key, *value;
1331
1332 grp_section = copy_strings (section, ".Group", (char *)0);
1333 if (profile_has_section (section, profile_name))
1334 profile_clean_section (section, profile_name);
1335 if (profile_has_section (grp_section, profile_name)) {
1336 profile_keys = profile_init_iterator (grp_section, profile_name);
1337
1338 while (profile_keys) {
1339 profile_keys = profile_iterator_next (profile_keys, &key, &value);
1340 clean_up_hotlist_groups (key);
1341 }
1342 profile_clean_section (grp_section, profile_name);
1343 }
1344 free (grp_section);
1345 }
1346
1347
1348
1349 void load_hotlist (void)
1350 {
1351 char *grp_section;
1352 int has_old_list = 0;
1353 int remove_old_list = 0;
1354 struct stat stat_buf;
1355
1356 if (hotlist_state.loaded) {
1357 stat (hotlist_file_name, &stat_buf);
1358 if (hotlist_file_mtime < stat_buf.st_mtime)
1359 done_hotlist ();
1360 else
1361 return;
1362 }
1363
1364 if (!hotlist_file_name)
1365 hotlist_file_name = concat_dir_and_file (home_dir, HOTLIST_FILENAME);
1366
1367 hotlist = new_hotlist ();
1368 hotlist->type = HL_TYPE_GROUP;
1369 hotlist->label = strdup (_(" Top level group "));
1370 hotlist->up = hotlist;
1371 /*
1372 * compatibility :-(
1373 */
1374 hotlist->directory = strdup ("Hotlist");
1375
1376 grp_section = copy_strings ("Hotlist", ".Group", (char *)0);
1377 has_old_list = profile_has_section ("Hotlist", profile_name) ||
1378 profile_has_section (grp_section, profile_name);
1379 free (grp_section);
1380
1381 if ((hotlist_file = fopen (hotlist_file_name, "r")) == 0) {
1382 int result;
1383 char *msg;
1384
1385 msg = copy_strings (_("Hotlist is now kept in file ~/"), HOTLIST_FILENAME,
1386 _("MC will load hotlist from ~/"), PROFILE_NAME,
1387 _(" and then delete [Hotlist] section there"), NULL);
1388 message (0, _(" Hotlist Load "), msg);
1389 free (msg);
1390
1391 load_group (hotlist);
1392 hotlist_state.loaded = 1;
1393 /*
1394 * just to be shure we got copy
1395 */
1396 hotlist_state.modified = 1;
1397 result = save_hotlist ();
1398 hotlist_state.modified = 0;
1399 if (result) {
1400 remove_old_list = 1;
1401 } else {
1402 char *msg;
1403
1404 msg = copy_strings (_("MC was unable to write ~/"), HOTLIST_FILENAME,
1405 _(" file, your old hotlist entries were not deleted"), NULL);
1406
1407 message (D_ERROR, _(" Hotlist Load "), msg);
1408 free (msg);
1409 }
1410 } else {
1411 hot_load_file (hotlist);
1412 fclose (hotlist_file);
1413 hotlist_state.loaded = 1;
1414 if (has_old_list) {
1415 int result;
1416 char *msg;
1417
1418 msg = copy_strings (
1419 _("You have ~/"), HOTLIST_FILENAME, _(" file and [Hotlist] section in ~/"), PROFILE_NAME, "\n",
1420 _("Your ~/"), HOTLIST_FILENAME, _(" most probably was created\n"),
1421 _("by an earlier development version of MC\nand is more actual than ~/"),
1422 PROFILE_NAME, _(" entries\n\n"),
1423 _("You can choose between\n\n"
1424 " Remove - remove old hotlist entries from ~/"), PROFILE_NAME, "\n",
1425 _(" Keep - keep your old entries; you will be asked\n"
1426 " the same question next time\n"
1427 " Merge - add old entries to hotlist as group \"Entries from ~/"),
1428 PROFILE_NAME, "\"\n\n", NULL);
1429
1430 result = query_dialog (_(" Hotlist Load "),
1431 msg, D_ERROR, 3, _("&Remove"), _("&Keep"), _("&Merge"));
1432 if (result == 0)
1433 remove_old_list = 1;
1434 else if (result == 2) {
1435 struct hotlist *grp = hotlist->head;
1436 struct hotlist *old;
1437
1438 hotlist->head = 0;
1439 load_group (hotlist);
1440
1441 old = new_hotlist ();
1442 old->type = HL_TYPE_GROUP;
1443 old->label = copy_strings (_(" Entries from ~/"), PROFILE_NAME, NULL);
1444 old->up = hotlist;
1445 old->head = hotlist->head;
1446 old->next = grp;
1447 hotlist->head = old;
1448 hotlist_state.modified = 1;
1449 if (!save_hotlist ()){
1450 char *str;
1451
1452 str = copy_strings (_("MC was unable to write ~/"), HOTLIST_FILENAME,
1453 _(" file your old hotlist entries were not deleted"), NULL);
1454
1455 message (D_ERROR, _(" Hotlist Load "), str);
1456 free (str);
1457 } else
1458 remove_old_list = 1;
1459 hotlist_state.modified = 0;
1460 }
1461 }
1462 }
1463
1464 if (remove_old_list) {
1465 clean_up_hotlist_groups ("Hotlist");
1466 sync_profiles ();
1467 }
1468
1469 stat (hotlist_file_name, &stat_buf);
1470 hotlist_file_mtime = stat_buf.st_mtime;
1471 current_group = hotlist;
1472 }
1473
1474 void save_group (struct hotlist *grp)
1475 {
1476 struct hotlist *current = grp->head;
1477 char *group_section;
1478
1479 group_section = find_group_section (grp);
1480
1481 profile_clean_section (group_section, profile_name);
1482 for (;current && current->type == HL_TYPE_GROUP; current = current->next){
1483 WritePrivateProfileString (group_section,
1484 current->directory,
1485 current->label,
1486 profile_name);
1487 }
1488 free (group_section);
1489
1490 for (current = grp->head;
1491 current && current->type == HL_TYPE_GROUP;
1492 current = current->next)
1493 save_group (current);
1494
1495 profile_clean_section (grp->directory, profile_name);
1496 for (;current; current = current->next){
1497 WritePrivateProfileString (grp->directory,
1498 current->directory,
1499 current->label,
1500 profile_name);
1501 }
1502 }
1503
1504 static int list_level = 0;
1505
1506 void hot_save_group (struct hotlist *grp)
1507 {
1508 struct hotlist *current = grp->head;
1509 int i;
1510 char *s;
1511
1512 #define INDENT(n) \
1513 do { \
1514 for (i = 0; i < n; i++) \
1515 putc (' ', hotlist_file); \
1516 } while (0)
1517
1518 for (;current; current = current->next)
1519 switch (current->type) {
1520 case HL_TYPE_GROUP:
1521 INDENT (list_level);
1522 fputs ("GROUP \"", hotlist_file);
1523 for (s = current->label; *s; s++) {
1524 if (*s == '"')
1525 putc ('\\', hotlist_file);
1526 else if (*s == '\\')
1527 putc ('\\', hotlist_file);
1528 putc (*s, hotlist_file);
1529 }
1530 fputs ("\"\n", hotlist_file);
1531 list_level += 2;
1532 hot_save_group (current);
1533 list_level -= 2;
1534 INDENT (list_level);
1535 fputs ("ENDGROUP\n", hotlist_file);
1536 break;
1537 case HL_TYPE_ENTRY:
1538 INDENT(list_level);
1539 fputs ("ENTRY \"", hotlist_file);
1540 for (s = current->label; *s; s++) {
1541 if (*s == '"')
1542 putc ('\\', hotlist_file);
1543 else if (*s == '\\')
1544 putc ('\\', hotlist_file);
1545 putc (*s, hotlist_file);
1546 }
1547 fputs ("\" URL \"", hotlist_file);
1548 for (s = current->directory; *s; s++) {
1549 if (*s == '"')
1550 putc ('\\', hotlist_file);
1551 else if (*s == '\\')
1552 putc ('\\', hotlist_file);
1553 putc (*s, hotlist_file);
1554 }
1555 fputs ("\"\n", hotlist_file);
1556 break;
1557 case HL_TYPE_COMMENT:
1558 fprintf (hotlist_file, "#%s\n", current->label);
1559 break;
1560
1561 }
1562 }
1563
1564 int save_hotlist (void)
1565 {
1566 int saved = 0;
1567 struct stat stat_buf;
1568
1569 if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name) {
1570 char *fbak = copy_strings (hotlist_file_name, ".bak", 0);
1571
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 fflush (hotlist_file);
1580 fclose (hotlist_file);
1581 stat (hotlist_file_name, &stat_buf);
1582 hotlist_file_mtime = stat_buf.st_mtime;
1583 saved = 1;
1584 hotlist_state.modified = 0;
1585 } else
1586 rename (fbak, hotlist_file_name);
1587 free (fbak);
1588 }
1589
1590 return saved;
1591 }
1592
1593 void done_hotlist (void)
1594 {
1595 remove_group (hotlist);
1596 hotlist_state.loaded = 0;
1597 if (hotlist->label)
1598 free (hotlist->label);
1599 if (hotlist->directory)
1600 free (hotlist->directory);
1601 free (hotlist);
1602 if (hotlist_file_name)
1603 free (hotlist_file_name);
1604 hotlist_file_name = 0;
1605 hotlist = current_group = 0;
1606 l_hotlist = 0;
1607 current_group = 0;
1608 }
1609
1610