root/src/filemanager/hotlist.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. update_path_name
  2. fill_listbox
  3. unlink_entry
  4. add_name_to_list
  5. hotlist_run_cmd
  6. hotlist_button_callback
  7. hotlist_handle_key
  8. hotlist_callback
  9. hotlist_listbox_callback
  10. init_i18n_stuff
  11. init_hotlist
  12. init_movelist
  13. hotlist_done
  14. find_group_section
  15. add2hotlist
  16. add_new_entry_input
  17. add_new_entry_cmd
  18. add_new_group_input
  19. add_new_group_cmd
  20. remove_group
  21. remove_from_hotlist
  22. load_group
  23. hot_skip_blanks
  24. hot_next_token
  25. hot_load_group
  26. hot_load_file
  27. clean_up_hotlist_groups
  28. load_hotlist
  29. hot_save_group
  30. add_dotdot_to_list
  31. add2hotlist_cmd
  32. hotlist_show
  33. save_hotlist
  34. done_hotlist

   1 /*
   2    Directory hotlist -- for the Midnight Commander
   3 
   4    Copyright (C) 1994-2019
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Radek Doulik, 1994
   9    Janne Kukonlehto, 1995
  10    Andrej Borsenkow, 1996
  11    Norbert Warmuth, 1997
  12    Andrew Borodin <aborodin@vmail.ru>, 2012, 2013
  13 
  14    Janne did the original Hotlist code, Andrej made the groupable
  15    hotlist; the move hotlist and revamped the file format and made
  16    it stronger.
  17 
  18    This file is part of the Midnight Commander.
  19 
  20    The Midnight Commander is free software: you can redistribute it
  21    and/or modify it under the terms of the GNU General Public License as
  22    published by the Free Software Foundation, either version 3 of the License,
  23    or (at your option) any later version.
  24 
  25    The Midnight Commander is distributed in the hope that it will be useful,
  26    but WITHOUT ANY WARRANTY; without even the implied warranty of
  27    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  28    GNU General Public License for more details.
  29 
  30    You should have received a copy of the GNU General Public License
  31    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  32  */
  33 
  34 /** \file hotlist.c
  35  *  \brief Source: directory hotlist
  36  */
  37 
  38 #include <config.h>
  39 
  40 #include <ctype.h>
  41 #include <stdio.h>
  42 #include <string.h>
  43 #include <sys/types.h>
  44 #include <sys/stat.h>
  45 #include <unistd.h>
  46 
  47 #include "lib/global.h"
  48 
  49 #include "lib/tty/tty.h"        /* COLS */
  50 #include "lib/tty/key.h"        /* KEY_M_CTRL */
  51 #include "lib/skin.h"           /* colors */
  52 #include "lib/mcconfig.h"       /* Load/save directories hotlist */
  53 #include "lib/fileloc.h"
  54 #include "lib/strutil.h"
  55 #include "lib/vfs/vfs.h"
  56 #include "lib/util.h"
  57 #include "lib/widget.h"
  58 
  59 #include "src/setup.h"          /* For profile_bname */
  60 #include "src/history.h"
  61 
  62 #include "midnight.h"           /* current_panel */
  63 #include "command.h"            /* cmdline */
  64 
  65 #include "hotlist.h"
  66 
  67 /*** global variables ****************************************************************************/
  68 
  69 /*** file scope macro definitions ****************************************************************/
  70 
  71 #define UX 3
  72 #define UY 2
  73 
  74 #define B_ADD_CURRENT   B_USER
  75 #define B_REMOVE        (B_USER + 1)
  76 #define B_NEW_GROUP     (B_USER + 2)
  77 #define B_NEW_ENTRY     (B_USER + 3)
  78 #define B_ENTER_GROUP   (B_USER + 4)
  79 #define B_UP_GROUP      (B_USER + 5)
  80 #define B_INSERT        (B_USER + 6)
  81 #define B_APPEND        (B_USER + 7)
  82 #define B_MOVE          (B_USER + 8)
  83 #ifdef ENABLE_VFS
  84 #define B_FREE_ALL_VFS  (B_USER + 9)
  85 #define B_REFRESH_VFS   (B_USER + 10)
  86 #endif
  87 
  88 #define TKN_GROUP   0
  89 #define TKN_ENTRY   1
  90 #define TKN_STRING  2
  91 #define TKN_URL     3
  92 #define TKN_ENDGROUP 4
  93 #define TKN_COMMENT  5
  94 #define TKN_EOL      125
  95 #define TKN_EOF      126
  96 #define TKN_UNKNOWN  127
  97 
  98 #define SKIP_TO_EOL \
  99 { \
 100     int _tkn; \
 101     while ((_tkn = hot_next_token ()) != TKN_EOF && _tkn != TKN_EOL) ; \
 102 }
 103 
 104 #define CHECK_TOKEN(_TKN_) \
 105 tkn = hot_next_token (); \
 106 if (tkn != _TKN_) \
 107 { \
 108     hotlist_state.readonly = TRUE; \
 109     hotlist_state.file_error = TRUE; \
 110     while (tkn != TKN_EOL && tkn != TKN_EOF) \
 111         tkn = hot_next_token (); \
 112     break; \
 113 }
 114 
 115 /*** file scope type declarations ****************************************************************/
 116 
 117 enum HotListType
 118 {
 119     HL_TYPE_GROUP,
 120     HL_TYPE_ENTRY,
 121     HL_TYPE_COMMENT,
 122     HL_TYPE_DOTDOT
 123 };
 124 
 125 static struct
 126 {
 127     /*
 128      * these parameters are intended to be user configurable
 129      */
 130     int expanded;               /* expanded view of all groups at startup */
 131 
 132     /*
 133      * these reflect run time state
 134      */
 135 
 136     gboolean loaded;            /* hotlist is loaded */
 137     gboolean readonly;          /* hotlist readonly */
 138     gboolean file_error;        /* parse error while reading file */
 139     gboolean running;           /* we are running dlg (and have to
 140                                    update listbox */
 141     gboolean moving;            /* we are in moving hotlist currently */
 142     gboolean modified;          /* hotlist was modified */
 143     hotlist_t type;             /* LIST_HOTLIST || LIST_VFSLIST */
 144 } hotlist_state;
 145 
 146 /* Directory hotlist */
 147 struct hotlist
 148 {
 149     enum HotListType type;
 150     char *directory;
 151     char *label;
 152     struct hotlist *head;
 153     struct hotlist *up;
 154     struct hotlist *next;
 155 };
 156 
 157 /*** file scope variables ************************************************************************/
 158 
 159 static gboolean hotlist_has_dot_dot = TRUE;
 160 
 161 static WDialog *hotlist_dlg, *movelist_dlg;
 162 static WGroupbox *hotlist_group, *movelist_group;
 163 static WListbox *l_hotlist, *l_movelist;
 164 static WLabel *pname;
 165 
 166 static struct
 167 {
 168     int ret_cmd, flags, y, x, len;
 169     const char *text;
 170     int type;
 171     widget_pos_flags_t pos_flags;
 172 } hotlist_but[] =
 173 {
 174     /* *INDENT-OFF* */
 175     { B_ENTER, DEFPUSH_BUTTON, 0, 0, 0, N_("Change &to"),
 176             LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 177 #ifdef ENABLE_VFS
 178     { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, 0, N_("&Free VFSs now"),
 179             LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 180     { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, 0, N_("&Refresh"),
 181             LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 182 #endif
 183     { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, 0, N_("&Add current"),
 184             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 185     { B_UP_GROUP, NORMAL_BUTTON, 0, 42, 0, N_("&Up"),
 186             LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 187     { B_CANCEL, NORMAL_BUTTON, 0, 53, 0, N_("&Cancel"),
 188             LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_RIGHT | WPOS_KEEP_BOTTOM },
 189     { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, 0, N_("New &group"),
 190             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 191     { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, 0, N_("New &entry"),
 192             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 193     { B_INSERT, NORMAL_BUTTON, 1, 0, 0, N_("&Insert"),
 194             LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 195     { B_APPEND, NORMAL_BUTTON, 1, 15, 0, N_("A&ppend"),
 196             LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 197     { B_REMOVE, NORMAL_BUTTON, 1, 30, 0, N_("&Remove"),
 198             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 199     { B_MOVE, NORMAL_BUTTON, 1, 42, 0, N_("&Move"),
 200             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
 201     /* *INDENT-ON* */
 202 };
 203 
 204 static const size_t hotlist_but_num = G_N_ELEMENTS (hotlist_but);
 205 
 206 static struct hotlist *hotlist = NULL;
 207 
 208 static struct hotlist *current_group;
 209 
 210 static GString *tkn_buf = NULL;
 211 
 212 static char *hotlist_file_name;
 213 static FILE *hotlist_file;
 214 static time_t hotlist_file_mtime;
 215 
 216 static int list_level = 0;
 217 
 218 /* --------------------------------------------------------------------------------------------- */
 219 /*** file scope functions ************************************************************************/
 220 /* --------------------------------------------------------------------------------------------- */
 221 
 222 static void init_movelist (struct hotlist *item);
 223 static void add_new_group_cmd (void);
 224 static void add_new_entry_cmd (void);
 225 static void remove_from_hotlist (struct hotlist *entry);
 226 static void load_hotlist (void);
 227 static void add_dotdot_to_list (void);
 228 
 229 /* --------------------------------------------------------------------------------------------- */
 230 /** If current->data is 0, then we are dealing with a VFS pathname */
 231 
 232 static void
 233 update_path_name (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 234 {
 235     const char *text = "";
 236     char *p;
 237     WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
 238     Widget *w = WIDGET (list);
 239 
 240     if (!listbox_is_empty (list))
 241     {
 242         char *ctext = NULL;
 243         void *cdata = NULL;
 244 
 245         listbox_get_current (list, &ctext, &cdata);
 246         if (cdata == NULL)
 247             text = ctext;
 248         else
 249         {
 250             struct hotlist *hlp = (struct hotlist *) cdata;
 251 
 252             if (hlp->type == HL_TYPE_ENTRY || hlp->type == HL_TYPE_DOTDOT)
 253                 text = hlp->directory;
 254             else if (hlp->type == HL_TYPE_GROUP)
 255                 text = _("Subgroup - press ENTER to see list");
 256         }
 257     }
 258 
 259     p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
 260     if (hotlist_state.moving)
 261         groupbox_set_title (movelist_group, str_trunc (p, w->cols - 2));
 262     else
 263     {
 264         groupbox_set_title (hotlist_group, str_trunc (p, w->cols - 2));
 265         label_set_text (pname, str_trunc (text, w->cols));
 266     }
 267     g_free (p);
 268 }
 269 
 270 /* --------------------------------------------------------------------------------------------- */
 271 
 272 static void
 273 fill_listbox (WListbox * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 274 {
 275     struct hotlist *current;
 276     GString *buff;
 277 
 278     buff = g_string_new ("");
 279 
 280     for (current = current_group->head; current != NULL; current = current->next)
 281         switch (current->type)
 282         {
 283         case HL_TYPE_GROUP:
 284             {
 285                 /* buff clean up */
 286                 g_string_truncate (buff, 0);
 287                 g_string_append (buff, "->");
 288                 g_string_append (buff, current->label);
 289                 listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, buff->str, current, FALSE);
 290             }
 291             break;
 292         case HL_TYPE_DOTDOT:
 293         case HL_TYPE_ENTRY:
 294             listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, current->label, current, FALSE);
 295         default:
 296             break;
 297         }
 298 
 299     g_string_free (buff, TRUE);
 300 }
 301 
 302 /* --------------------------------------------------------------------------------------------- */
 303 
 304 static void
 305 unlink_entry (struct hotlist *entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
 306 {
 307     struct hotlist *current = current_group->head;
 308 
 309     if (current == entry)
 310         current_group->head = entry->next;
 311     else
 312     {
 313         while (current != NULL && current->next != entry)
 314             current = current->next;
 315         if (current != NULL)
 316             current->next = entry->next;
 317     }
 318     entry->next = entry->up = NULL;
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 
 323 #ifdef ENABLE_VFS
 324 static void
 325 add_name_to_list (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 326 {
 327     listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, path, NULL, FALSE);
 328 }
 329 #endif /* !ENABLE_VFS */
 330 
 331 /* --------------------------------------------------------------------------------------------- */
 332 
 333 static int
 334 hotlist_run_cmd (int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 335 {
 336     switch (action)
 337     {
 338     case B_MOVE:
 339         {
 340             struct hotlist *saved = current_group;
 341             struct hotlist *item = NULL;
 342             struct hotlist *moveto_item = NULL;
 343             struct hotlist *moveto_group = NULL;
 344             int ret;
 345 
 346             if (listbox_is_empty (l_hotlist))
 347                 return 0;       /* empty group - nothing to do */
 348 
 349             listbox_get_current (l_hotlist, NULL, (void **) &item);
 350             init_movelist (item);
 351             hotlist_state.moving = TRUE;
 352             ret = dlg_run (movelist_dlg);
 353             hotlist_state.moving = FALSE;
 354             listbox_get_current (l_movelist, NULL, (void **) &moveto_item);
 355             moveto_group = current_group;
 356             dlg_destroy (movelist_dlg);
 357             current_group = saved;
 358             if (ret == B_CANCEL)
 359                 return 0;
 360             if (moveto_item == item)
 361                 return 0;       /* If we insert/append a before/after a
 362                                    it hardly changes anything ;) */
 363             unlink_entry (item);
 364             listbox_remove_current (l_hotlist);
 365             item->up = moveto_group;
 366             if (moveto_group->head == NULL)
 367                 moveto_group->head = item;
 368             else if (moveto_item == NULL)
 369             {                   /* we have group with just comments */
 370                 struct hotlist *p = moveto_group->head;
 371 
 372                 /* skip comments */
 373                 while (p->next != NULL)
 374                     p = p->next;
 375                 p->next = item;
 376             }
 377             else if (ret == B_ENTER || ret == B_APPEND)
 378             {
 379                 if (moveto_item->next == NULL)
 380                     moveto_item->next = item;
 381                 else
 382                 {
 383                     item->next = moveto_item->next;
 384                     moveto_item->next = item;
 385                 }
 386             }
 387             else if (moveto_group->head == moveto_item)
 388             {
 389                 moveto_group->head = item;
 390                 item->next = moveto_item;
 391             }
 392             else
 393             {
 394                 struct hotlist *p = moveto_group->head;
 395 
 396                 while (p->next != moveto_item)
 397                     p = p->next;
 398                 item->next = p->next;
 399                 p->next = item;
 400             }
 401             listbox_remove_list (l_hotlist);
 402             fill_listbox (l_hotlist);
 403             repaint_screen ();
 404             hotlist_state.modified = TRUE;
 405             return 0;
 406         }
 407     case B_REMOVE:
 408         {
 409             struct hotlist *entry = NULL;
 410 
 411             listbox_get_current (l_hotlist, NULL, (void **) &entry);
 412             remove_from_hotlist (entry);
 413         }
 414         return 0;
 415 
 416     case B_NEW_GROUP:
 417         add_new_group_cmd ();
 418         return 0;
 419 
 420     case B_ADD_CURRENT:
 421         add2hotlist_cmd ();
 422         return 0;
 423 
 424     case B_NEW_ENTRY:
 425         add_new_entry_cmd ();
 426         return 0;
 427 
 428     case B_ENTER:
 429     case B_ENTER_GROUP:
 430         {
 431             WListbox *list;
 432             void *data;
 433             struct hotlist *hlp;
 434 
 435             list = hotlist_state.moving ? l_movelist : l_hotlist;
 436             listbox_get_current (list, NULL, &data);
 437 
 438             if (data == NULL)
 439                 return 1;
 440 
 441             hlp = (struct hotlist *) data;
 442 
 443             if (hlp->type == HL_TYPE_ENTRY)
 444                 return (action == B_ENTER ? 1 : 0);
 445             if (hlp->type != HL_TYPE_DOTDOT)
 446             {
 447                 listbox_remove_list (list);
 448                 current_group = hlp;
 449                 fill_listbox (list);
 450                 return 0;
 451             }
 452             MC_FALLTHROUGH;     /* go up */
 453         }
 454         MC_FALLTHROUGH;         /* if list empty - just go up */
 455 
 456     case B_UP_GROUP:
 457         {
 458             WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
 459 
 460             listbox_remove_list (list);
 461             current_group = current_group->up;
 462             fill_listbox (list);
 463             return 0;
 464         }
 465 
 466 #ifdef ENABLE_VFS
 467     case B_FREE_ALL_VFS:
 468         vfs_expire (TRUE);
 469         MC_FALLTHROUGH;
 470 
 471     case B_REFRESH_VFS:
 472         listbox_remove_list (l_hotlist);
 473         listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
 474                           FALSE);
 475         vfs_fill_names (add_name_to_list);
 476         return 0;
 477 #endif /* ENABLE_VFS */
 478 
 479     default:
 480         return 1;
 481     }
 482 }
 483 
 484 /* --------------------------------------------------------------------------------------------- */
 485 
 486 static int
 487 hotlist_button_callback (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 488 {
 489     int ret;
 490 
 491     (void) button;
 492     ret = hotlist_run_cmd (action);
 493     update_path_name ();
 494     return ret;
 495 }
 496 
 497 /* --------------------------------------------------------------------------------------------- */
 498 
 499 static inline cb_ret_t
 500 hotlist_handle_key (WDialog * h, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 501 {
 502     switch (key)
 503     {
 504     case KEY_M_CTRL | '\n':
 505         goto l1;
 506 
 507     case '\n':
 508     case KEY_ENTER:
 509         if (hotlist_button_callback (NULL, B_ENTER) != 0)
 510         {
 511             h->ret_value = B_ENTER;
 512             dlg_stop (h);
 513         }
 514         return MSG_HANDLED;
 515 
 516     case KEY_RIGHT:
 517         /* enter to the group */
 518         if (hotlist_state.type == LIST_VFSLIST)
 519             return MSG_NOT_HANDLED;
 520         return hotlist_button_callback (NULL, B_ENTER_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
 521 
 522     case KEY_LEFT:
 523         /* leave the group */
 524         if (hotlist_state.type == LIST_VFSLIST)
 525             return MSG_NOT_HANDLED;
 526         return hotlist_button_callback (NULL, B_UP_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
 527 
 528     case KEY_DC:
 529         if (hotlist_state.moving)
 530             return MSG_NOT_HANDLED;
 531         hotlist_button_callback (NULL, B_REMOVE);
 532         return MSG_HANDLED;
 533 
 534       l1:
 535     case ALT ('\n'):
 536     case ALT ('\r'):
 537         if (!hotlist_state.moving)
 538         {
 539             void *ldata = NULL;
 540 
 541             listbox_get_current (l_hotlist, NULL, &ldata);
 542 
 543             if (ldata != NULL)
 544             {
 545                 struct hotlist *hlp = (struct hotlist *) ldata;
 546 
 547                 if (hlp->type == HL_TYPE_ENTRY)
 548                 {
 549                     char *tmp;
 550 
 551                     tmp = g_strconcat ("cd ", hlp->directory, (char *) NULL);
 552                     input_insert (cmdline, tmp, FALSE);
 553                     g_free (tmp);
 554                     h->ret_value = B_CANCEL;
 555                     dlg_stop (h);
 556                 }
 557             }
 558         }
 559         return MSG_HANDLED;     /* ignore key */
 560 
 561     default:
 562         return MSG_NOT_HANDLED;
 563     }
 564 }
 565 
 566 /* --------------------------------------------------------------------------------------------- */
 567 
 568 static cb_ret_t
 569 hotlist_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 570 {
 571     WDialog *h = DIALOG (w);
 572 
 573     switch (msg)
 574     {
 575     case MSG_INIT:
 576     case MSG_NOTIFY:           /* MSG_NOTIFY is fired by the listbox to tell us the item has changed. */
 577         update_path_name ();
 578         return MSG_HANDLED;
 579 
 580     case MSG_UNHANDLED_KEY:
 581         return hotlist_handle_key (h, parm);
 582 
 583     case MSG_POST_KEY:
 584         /*
 585          * The code here has two purposes:
 586          *
 587          * (1) Always stay on the hotlist.
 588          *
 589          * Activating a button using its hotkey (and even pressing ENTER, as
 590          * there's a "default button") moves the focus to the button. But we
 591          * want to stay on the hotlist, to be able to use the usual keys (up,
 592          * down, etc.). So we do `widget_select (lst)`.
 593          *
 594          * (2) Refresh the hotlist.
 595          *
 596          * We may have run a command that changed the contents of the list.
 597          * We therefore need to refresh it. So we do `widget_redraw (lst)`.
 598          */
 599         {
 600             Widget *lst;
 601 
 602             lst = WIDGET (h == hotlist_dlg ? l_hotlist : l_movelist);
 603 
 604             /* widget_select() already redraws the widget, but since it's a
 605              * no-op if the widget is already selected ("focused"), we have
 606              * to call widget_redraw() separately. */
 607             if (!widget_get_state (lst, WST_FOCUSED))
 608                 widget_select (lst);
 609             else
 610                 widget_redraw (lst);
 611         }
 612         return MSG_HANDLED;
 613 
 614     case MSG_RESIZE:
 615         /* simply call dlg_set_size() with new size */
 616         dlg_set_size (h, LINES - (h == hotlist_dlg ? 2 : 6), COLS - 6);
 617         return MSG_HANDLED;
 618 
 619     default:
 620         return dlg_default_callback (w, sender, msg, parm, data);
 621     }
 622 }
 623 
 624 /* --------------------------------------------------------------------------------------------- */
 625 
 626 static lcback_ret_t
 627 hotlist_listbox_callback (WListbox * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 628 {
 629     WDialog *dlg = WIDGET (list)->owner;
 630 
 631     if (!listbox_is_empty (list))
 632     {
 633         void *data = NULL;
 634 
 635         listbox_get_current (list, NULL, &data);
 636 
 637         if (data != NULL)
 638         {
 639             struct hotlist *hlp = (struct hotlist *) data;
 640 
 641             if (hlp->type == HL_TYPE_ENTRY)
 642             {
 643                 dlg->ret_value = B_ENTER;
 644                 dlg_stop (dlg);
 645                 return LISTBOX_DONE;
 646             }
 647             else
 648             {
 649                 hotlist_button_callback (NULL, B_ENTER);
 650                 send_message (dlg, NULL, MSG_POST_KEY, '\n', NULL);
 651                 return LISTBOX_CONT;
 652             }
 653         }
 654         else
 655         {
 656             dlg->ret_value = B_ENTER;
 657             dlg_stop (dlg);
 658             return LISTBOX_DONE;
 659         }
 660     }
 661 
 662     hotlist_button_callback (NULL, B_UP_GROUP);
 663     send_message (dlg, NULL, MSG_POST_KEY, 'u', NULL);
 664     return LISTBOX_CONT;
 665 }
 666 
 667 /* --------------------------------------------------------------------------------------------- */
 668 /**
 669  * Expands all button names (once) and recalculates button positions.
 670  * returns number of columns in the dialog box, which is 10 chars longer
 671  * then buttonbar.
 672  *
 673  * If common width of the window (i.e. in xterm) is less than returned
 674  * width - sorry :)  (anyway this did not handled in previous version too)
 675  */
 676 
 677 static int
 678 init_i18n_stuff (int list_type, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 679 {
 680     size_t i;
 681 
 682     static gboolean i18n_flag = FALSE;
 683 
 684     if (!i18n_flag)
 685     {
 686         for (i = 0; i < hotlist_but_num; i++)
 687         {
 688 #ifdef ENABLE_NLS
 689             hotlist_but[i].text = _(hotlist_but[i].text);
 690 #endif /* ENABLE_NLS */
 691             hotlist_but[i].len = str_term_width1 (hotlist_but[i].text) + 3;
 692             if (hotlist_but[i].flags == DEFPUSH_BUTTON)
 693                 hotlist_but[i].len += 2;
 694         }
 695 
 696         i18n_flag = TRUE;
 697     }
 698 
 699     /* Dynamic resizing of buttonbars */
 700     {
 701         int len[2], count[2];   /* at most two lines of buttons */
 702         int cur_x[2];
 703 
 704         len[0] = len[1] = 0;
 705         count[0] = count[1] = 0;
 706         cur_x[0] = cur_x[1] = 0;
 707 
 708         /* Count len of buttonbars, assuming 1 extra space between buttons */
 709         for (i = 0; i < hotlist_but_num; i++)
 710             if ((hotlist_but[i].type & list_type) != 0)
 711             {
 712                 int row;
 713 
 714                 row = hotlist_but[i].y;
 715                 ++count[row];
 716                 len[row] += hotlist_but[i].len + 1;
 717             }
 718 
 719         (len[0])--;
 720         (len[1])--;
 721 
 722         cols = MAX (cols, MAX (len[0], len[1]));
 723 
 724         /* arrange buttons */
 725         for (i = 0; i < hotlist_but_num; i++)
 726             if ((hotlist_but[i].type & list_type) != 0)
 727             {
 728                 int row;
 729 
 730                 row = hotlist_but[i].y;
 731 
 732                 if (hotlist_but[i].x != 0)
 733                 {
 734                     /* not first int the row */
 735                     if (hotlist_but[i].ret_cmd == B_CANCEL)
 736                         hotlist_but[i].x = cols - hotlist_but[i].len - 6;
 737                     else
 738                         hotlist_but[i].x = cur_x[row];
 739                 }
 740 
 741                 cur_x[row] += hotlist_but[i].len + 1;
 742             }
 743     }
 744 
 745     return cols;
 746 }
 747 
 748 /* --------------------------------------------------------------------------------------------- */
 749 
 750 static void
 751 init_hotlist (hotlist_t list_type)
     /* [previous][next][first][last][top][bottom][index][help]  */
 752 {
 753     size_t i;
 754     const char *title, *help_node;
 755     int lines, cols;
 756     int y;
 757     int dh = 0;
 758     WGroupbox *path_box;
 759     Widget *hotlist_widget;
 760 
 761     do_refresh ();
 762 
 763     lines = LINES - 2;
 764     cols = init_i18n_stuff (list_type, COLS - 6);
 765 
 766     hotlist_state.expanded =
 767         mc_config_get_int (mc_global.main_config, "HotlistConfig", "expanded_view_of_groups", 0);
 768 
 769 #ifdef ENABLE_VFS
 770     if (list_type == LIST_VFSLIST)
 771     {
 772         title = _("Active VFS directories");
 773         help_node = "[vfshot]"; /* FIXME - no such node */
 774         dh = 1;
 775     }
 776     else
 777 #endif /* !ENABLE_VFS */
 778     {
 779         title = _("Directory hotlist");
 780         help_node = "[Hotlist]";
 781     }
 782 
 783     hotlist_dlg =
 784         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
 785                     NULL, help_node, title);
 786 
 787     y = UY;
 788     hotlist_group = groupbox_new (y, UX, lines - 10 + dh, cols - 2 * UX, _("Top level group"));
 789     hotlist_widget = WIDGET (hotlist_group);
 790     add_widget_autopos (hotlist_dlg, hotlist_widget, WPOS_KEEP_ALL, NULL);
 791 
 792     l_hotlist =
 793         listbox_new (y + 1, UX + 1, hotlist_widget->lines - 2, hotlist_widget->cols - 2, FALSE,
 794                      hotlist_listbox_callback);
 795 
 796     /* Fill the hotlist with the active VFS or the hotlist */
 797 #ifdef ENABLE_VFS
 798     if (list_type == LIST_VFSLIST)
 799     {
 800         listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
 801                           FALSE);
 802         vfs_fill_names (add_name_to_list);
 803     }
 804     else
 805 #endif /* !ENABLE_VFS */
 806         fill_listbox (l_hotlist);
 807 
 808     /* insert before groupbox to view scrollbar */
 809     add_widget_autopos (hotlist_dlg, l_hotlist, WPOS_KEEP_ALL, NULL);
 810 
 811     y += hotlist_widget->lines;
 812 
 813     path_box = groupbox_new (y, UX, 3, hotlist_widget->cols, _("Directory path"));
 814     add_widget_autopos (hotlist_dlg, path_box, WPOS_KEEP_BOTTOM | WPOS_KEEP_HORZ, NULL);
 815 
 816     pname = label_new (y + 1, UX + 2, "");
 817     add_widget_autopos (hotlist_dlg, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT, NULL);
 818     y += WIDGET (path_box)->lines;
 819 
 820     add_widget_autopos (hotlist_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
 821 
 822     for (i = 0; i < hotlist_but_num; i++)
 823         if ((hotlist_but[i].type & list_type) != 0)
 824             add_widget_autopos (hotlist_dlg,
 825                                 button_new (y + hotlist_but[i].y, UX + hotlist_but[i].x,
 826                                             hotlist_but[i].ret_cmd, hotlist_but[i].flags,
 827                                             hotlist_but[i].text, hotlist_button_callback),
 828                                 hotlist_but[i].pos_flags, NULL);
 829 
 830     widget_select (WIDGET (l_hotlist));
 831 }
 832 
 833 /* --------------------------------------------------------------------------------------------- */
 834 
 835 static void
 836 init_movelist (struct hotlist *item)
     /* [previous][next][first][last][top][bottom][index][help]  */
 837 {
 838     size_t i;
 839     char *hdr;
 840     int lines, cols;
 841     int y;
 842     Widget *movelist_widget;
 843 
 844     do_refresh ();
 845 
 846     lines = LINES - 6;
 847     cols = init_i18n_stuff (LIST_MOVELIST, COLS - 6);
 848 
 849     hdr = g_strdup_printf (_("Moving %s"), item->label);
 850 
 851     movelist_dlg =
 852         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
 853                     NULL, "[Hotlist]", hdr);
 854 
 855     g_free (hdr);
 856 
 857     y = UY;
 858     movelist_group = groupbox_new (y, UX, lines - 7, cols - 2 * UX, _("Directory label"));
 859     movelist_widget = WIDGET (movelist_group);
 860     add_widget_autopos (movelist_dlg, movelist_widget, WPOS_KEEP_ALL, NULL);
 861 
 862     l_movelist =
 863         listbox_new (y + 1, UX + 1, movelist_widget->lines - 2, movelist_widget->cols - 2, FALSE,
 864                      hotlist_listbox_callback);
 865     fill_listbox (l_movelist);
 866     /* insert before groupbox to view scrollbar */
 867     add_widget_autopos (movelist_dlg, l_movelist, WPOS_KEEP_ALL, NULL);
 868 
 869     y += movelist_widget->lines;
 870 
 871     add_widget_autopos (movelist_dlg, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
 872 
 873     for (i = 0; i < hotlist_but_num; i++)
 874         if ((hotlist_but[i].type & LIST_MOVELIST) != 0)
 875             add_widget_autopos (movelist_dlg,
 876                                 button_new (y + hotlist_but[i].y, UX + hotlist_but[i].x,
 877                                             hotlist_but[i].ret_cmd, hotlist_but[i].flags,
 878                                             hotlist_but[i].text, hotlist_button_callback),
 879                                 hotlist_but[i].pos_flags, NULL);
 880 
 881     widget_select (WIDGET (l_movelist));
 882 }
 883 
 884 /* --------------------------------------------------------------------------------------------- */
 885 /**
 886  * Destroy the list dialog.
 887  * Don't confuse with done_hotlist() for the list in memory.
 888  */
 889 
 890 static void
 891 hotlist_done (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 892 {
 893     dlg_destroy (hotlist_dlg);
 894     l_hotlist = NULL;
 895 #if 0
 896     update_panels (UP_OPTIMIZE, UP_KEEPSEL);
 897 #endif
 898     repaint_screen ();
 899 }
 900 
 901 /* --------------------------------------------------------------------------------------------- */
 902 
 903 static inline char *
 904 find_group_section (struct hotlist *grp)
     /* [previous][next][first][last][top][bottom][index][help]  */
 905 {
 906     return g_strconcat (grp->directory, ".Group", (char *) NULL);
 907 }
 908 
 909 /* --------------------------------------------------------------------------------------------- */
 910 
 911 static struct hotlist *
 912 add2hotlist (char *label, char *directory, enum HotListType type, listbox_append_t pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 913 {
 914     struct hotlist *new;
 915     struct hotlist *current = NULL;
 916 
 917     /*
 918      * Hotlist is neither loaded nor loading.
 919      * Must be called by "Ctrl-x a" before using hotlist.
 920      */
 921     if (current_group == NULL)
 922         load_hotlist ();
 923 
 924     listbox_get_current (l_hotlist, NULL, (void **) &current);
 925 
 926     /* Make sure '..' stays at the top of the list. */
 927     if ((current != NULL) && (current->type == HL_TYPE_DOTDOT))
 928         pos = LISTBOX_APPEND_AFTER;
 929 
 930     new = g_new0 (struct hotlist, 1);
 931 
 932     new->type = type;
 933     new->label = label;
 934     new->directory = directory;
 935     new->up = current_group;
 936 
 937     if (type == HL_TYPE_GROUP)
 938     {
 939         current_group = new;
 940         add_dotdot_to_list ();
 941         current_group = new->up;
 942     }
 943 
 944     if (current_group->head == NULL)
 945     {
 946         /* first element in group */
 947         current_group->head = new;
 948     }
 949     else if (pos == LISTBOX_APPEND_AFTER)
 950     {
 951         new->next = current->next;
 952         current->next = new;
 953     }
 954     else if (pos == LISTBOX_APPEND_BEFORE && current == current_group->head)
 955     {
 956         /* should be inserted before first item */
 957         new->next = current;
 958         current_group->head = new;
 959     }
 960     else if (pos == LISTBOX_APPEND_BEFORE)
 961     {
 962         struct hotlist *p = current_group->head;
 963 
 964         while (p->next != current)
 965             p = p->next;
 966 
 967         new->next = current;
 968         p->next = new;
 969     }
 970     else
 971     {                           /* append at the end */
 972         struct hotlist *p = current_group->head;
 973 
 974         while (p->next != NULL)
 975             p = p->next;
 976 
 977         p->next = new;
 978     }
 979 
 980     if (hotlist_state.running && type != HL_TYPE_COMMENT && type != HL_TYPE_DOTDOT)
 981     {
 982         if (type == HL_TYPE_GROUP)
 983         {
 984             char *lbl;
 985 
 986             lbl = g_strconcat ("->", new->label, (char *) NULL);
 987             listbox_add_item (l_hotlist, pos, 0, lbl, new, FALSE);
 988             g_free (lbl);
 989         }
 990         else
 991             listbox_add_item (l_hotlist, pos, 0, new->label, new, FALSE);
 992         listbox_select_entry (l_hotlist, l_hotlist->pos);
 993     }
 994 
 995     return new;
 996 }
 997 
 998 /* --------------------------------------------------------------------------------------------- */
 999 
1000 static int
1001 add_new_entry_input (const char *header, const char *text1, const char *text2,
     /* [previous][next][first][last][top][bottom][index][help]  */
1002                      const char *help, char **r1, char **r2)
1003 {
1004     quick_widget_t quick_widgets[] = {
1005         /* *INDENT-OFF* */
1006         QUICK_LABELED_INPUT (text1, input_label_above, *r1, "input-lbl", r1, NULL,
1007                              FALSE, FALSE, INPUT_COMPLETE_NONE),
1008         QUICK_SEPARATOR (FALSE),
1009         QUICK_LABELED_INPUT (text2, input_label_above, *r2, "input-lbl", r2, NULL,
1010                              FALSE, FALSE, INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD),
1011         QUICK_START_BUTTONS (TRUE, TRUE),
1012             QUICK_BUTTON (N_("&Append"), B_APPEND, NULL, NULL),
1013             QUICK_BUTTON (N_("&Insert"), B_INSERT, NULL, NULL),
1014             QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1015         QUICK_END
1016         /* *INDENT-ON* */
1017     };
1018 
1019     quick_dialog_t qdlg = {
1020         -1, -1, 64,
1021         header, help,
1022         quick_widgets, NULL, NULL
1023     };
1024 
1025     int ret;
1026 
1027     ret = quick_dialog (&qdlg);
1028 
1029     return (ret != B_CANCEL) ? ret : 0;
1030 }
1031 
1032 /* --------------------------------------------------------------------------------------------- */
1033 
1034 static void
1035 add_new_entry_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1036 {
1037     char *title, *url, *to_free;
1038     int ret;
1039 
1040     /* Take current directory as default value for input fields */
1041     to_free = title = url = vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1042 
1043     ret = add_new_entry_input (_("New hotlist entry"), _("Directory label:"),
1044                                _("Directory path:"), "[Hotlist]", &title, &url);
1045     g_free (to_free);
1046 
1047     if (ret == 0)
1048         return;
1049     if (title == NULL || *title == '\0' || url == NULL || *url == '\0')
1050     {
1051         g_free (title);
1052         g_free (url);
1053         return;
1054     }
1055 
1056     if (ret == B_ENTER || ret == B_APPEND)
1057         add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AFTER);
1058     else
1059         add2hotlist (title, url, HL_TYPE_ENTRY, LISTBOX_APPEND_BEFORE);
1060 
1061     hotlist_state.modified = TRUE;
1062 }
1063 
1064 /* --------------------------------------------------------------------------------------------- */
1065 
1066 static int
1067 add_new_group_input (const char *header, const char *label, char **result)
     /* [previous][next][first][last][top][bottom][index][help]  */
1068 {
1069     quick_widget_t quick_widgets[] = {
1070         /* *INDENT-OFF* */
1071         QUICK_LABELED_INPUT (label, input_label_above, "", "input", result, NULL,
1072                              FALSE, FALSE, INPUT_COMPLETE_NONE),
1073         QUICK_START_BUTTONS (TRUE, TRUE),
1074             QUICK_BUTTON (N_("&Append"), B_APPEND, NULL, NULL),
1075             QUICK_BUTTON (N_("&Insert"), B_INSERT, NULL, NULL),
1076             QUICK_BUTTON (N_("&Cancel"), B_CANCEL, NULL, NULL),
1077         QUICK_END
1078         /* *INDENT-ON* */
1079     };
1080 
1081     quick_dialog_t qdlg = {
1082         -1, -1, 64,
1083         header, "[Hotlist]",
1084         quick_widgets, NULL, NULL
1085     };
1086 
1087     int ret;
1088 
1089     ret = quick_dialog (&qdlg);
1090 
1091     return (ret != B_CANCEL) ? ret : 0;
1092 }
1093 
1094 /* --------------------------------------------------------------------------------------------- */
1095 
1096 static void
1097 add_new_group_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1098 {
1099     char *label;
1100     int ret;
1101 
1102     ret = add_new_group_input (_("New hotlist group"), _("Name of new group:"), &label);
1103     if (ret == 0 || label == NULL || *label == '\0')
1104         return;
1105 
1106     if (ret == B_ENTER || ret == B_APPEND)
1107         add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_AFTER);
1108     else
1109         add2hotlist (label, 0, HL_TYPE_GROUP, LISTBOX_APPEND_BEFORE);
1110 
1111     hotlist_state.modified = TRUE;
1112 }
1113 
1114 /* --------------------------------------------------------------------------------------------- */
1115 
1116 static void
1117 remove_group (struct hotlist *grp)
     /* [previous][next][first][last][top][bottom][index][help]  */
1118 {
1119     struct hotlist *current = grp->head;
1120 
1121     while (current != NULL)
1122     {
1123         struct hotlist *next = current->next;
1124 
1125         if (current->type == HL_TYPE_GROUP)
1126             remove_group (current);
1127 
1128         g_free (current->label);
1129         g_free (current->directory);
1130         g_free (current);
1131 
1132         current = next;
1133     }
1134 }
1135 
1136 /* --------------------------------------------------------------------------------------------- */
1137 
1138 static void
1139 remove_from_hotlist (struct hotlist *entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
1140 {
1141     if (entry == NULL)
1142         return;
1143 
1144     if (entry->type == HL_TYPE_DOTDOT)
1145         return;
1146 
1147     if (confirm_directory_hotlist_delete)
1148     {
1149         char text[BUF_MEDIUM];
1150         int result;
1151 
1152         if (safe_delete)
1153             query_set_sel (1);
1154 
1155         g_snprintf (text, sizeof (text), _("Are you sure you want to remove entry \"%s\"?"),
1156                     str_trunc (entry->label, 30));
1157         result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1158                                _("&Yes"), _("&No"));
1159         if (result != 0)
1160             return;
1161     }
1162 
1163     if (entry->type == HL_TYPE_GROUP)
1164     {
1165         struct hotlist *head = entry->head;
1166 
1167         if (head != NULL && (head->type != HL_TYPE_DOTDOT || head->next != NULL))
1168         {
1169             char text[BUF_MEDIUM];
1170             int result;
1171 
1172             g_snprintf (text, sizeof (text), _("Group \"%s\" is not empty.\nRemove it?"),
1173                         str_trunc (entry->label, 30));
1174             result = query_dialog (Q_ ("DialogTitle|Delete"), text, D_ERROR | D_CENTER, 2,
1175                                    _("&Yes"), _("&No"));
1176             if (result != 0)
1177                 return;
1178         }
1179 
1180         remove_group (entry);
1181     }
1182 
1183     unlink_entry (entry);
1184 
1185     g_free (entry->label);
1186     g_free (entry->directory);
1187     g_free (entry);
1188     /* now remove list entry from screen */
1189     listbox_remove_current (l_hotlist);
1190     hotlist_state.modified = TRUE;
1191 }
1192 
1193 /* --------------------------------------------------------------------------------------------- */
1194 
1195 static void
1196 load_group (struct hotlist *grp)
     /* [previous][next][first][last][top][bottom][index][help]  */
1197 {
1198     gchar **profile_keys, **keys;
1199     char *group_section;
1200     struct hotlist *current = 0;
1201 
1202     group_section = find_group_section (grp);
1203 
1204     keys = mc_config_get_keys (mc_global.main_config, group_section, NULL);
1205 
1206     current_group = grp;
1207 
1208     for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1209         add2hotlist (mc_config_get_string (mc_global.main_config, group_section, *profile_keys, ""),
1210                      g_strdup (*profile_keys), HL_TYPE_GROUP, LISTBOX_APPEND_AT_END);
1211 
1212     g_free (group_section);
1213     g_strfreev (keys);
1214 
1215     keys = mc_config_get_keys (mc_global.main_config, grp->directory, NULL);
1216 
1217     for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1218         add2hotlist (mc_config_get_string (mc_global.main_config, group_section, *profile_keys, ""),
1219                      g_strdup (*profile_keys), HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1220 
1221     g_strfreev (keys);
1222 
1223     for (current = grp->head; current; current = current->next)
1224         load_group (current);
1225 }
1226 
1227 /* --------------------------------------------------------------------------------------------- */
1228 
1229 static int
1230 hot_skip_blanks (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1231 {
1232     int c;
1233 
1234     while ((c = getc (hotlist_file)) != EOF && c != '\n' && g_ascii_isspace (c))
1235         ;
1236     return c;
1237 }
1238 
1239 /* --------------------------------------------------------------------------------------------- */
1240 
1241 static int
1242 hot_next_token (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1243 {
1244     int c, ret = 0;
1245     size_t l;
1246 
1247     if (tkn_buf == NULL)
1248         tkn_buf = g_string_new ("");
1249     g_string_set_size (tkn_buf, 0);
1250 
1251   again:
1252     c = hot_skip_blanks ();
1253     switch (c)
1254     {
1255     case EOF:
1256         ret = TKN_EOF;
1257         break;
1258     case '\n':
1259         ret = TKN_EOL;
1260         break;
1261     case '#':
1262         while ((c = getc (hotlist_file)) != EOF && c != '\n')
1263             g_string_append_c (tkn_buf, c);
1264         ret = TKN_COMMENT;
1265         break;
1266     case '"':
1267         while ((c = getc (hotlist_file)) != EOF && c != '"')
1268         {
1269             if (c == '\\')
1270             {
1271                 c = getc (hotlist_file);
1272                 if (c == EOF)
1273                 {
1274                     g_string_free (tkn_buf, TRUE);
1275                     return TKN_EOF;
1276                 }
1277             }
1278             g_string_append_c (tkn_buf, c == '\n' ? ' ' : c);
1279         }
1280         ret = (c == EOF) ? TKN_EOF : TKN_STRING;
1281         break;
1282     case '\\':
1283         c = getc (hotlist_file);
1284         if (c == EOF)
1285         {
1286             g_string_free (tkn_buf, TRUE);
1287             return TKN_EOF;
1288         }
1289         if (c == '\n')
1290             goto again;
1291 
1292         MC_FALLTHROUGH;         /* it is taken as normal character */
1293 
1294     default:
1295         do
1296         {
1297             g_string_append_c (tkn_buf, g_ascii_toupper (c));
1298         }
1299         while ((c = fgetc (hotlist_file)) != EOF && (g_ascii_isalnum (c) || !isascii (c)));
1300         if (c != EOF)
1301             ungetc (c, hotlist_file);
1302         l = tkn_buf->len;
1303         if (strncmp (tkn_buf->str, "GROUP", l) == 0)
1304             ret = TKN_GROUP;
1305         else if (strncmp (tkn_buf->str, "ENTRY", l) == 0)
1306             ret = TKN_ENTRY;
1307         else if (strncmp (tkn_buf->str, "ENDGROUP", l) == 0)
1308             ret = TKN_ENDGROUP;
1309         else if (strncmp (tkn_buf->str, "URL", l) == 0)
1310             ret = TKN_URL;
1311         else
1312             ret = TKN_UNKNOWN;
1313         break;
1314     }
1315     return ret;
1316 }
1317 
1318 /* --------------------------------------------------------------------------------------------- */
1319 
1320 static void
1321 hot_load_group (struct hotlist *grp)
     /* [previous][next][first][last][top][bottom][index][help]  */
1322 {
1323     int tkn;
1324     struct hotlist *new_grp;
1325     char *label, *url;
1326 
1327     current_group = grp;
1328 
1329     while ((tkn = hot_next_token ()) != TKN_ENDGROUP)
1330         switch (tkn)
1331         {
1332         case TKN_GROUP:
1333             CHECK_TOKEN (TKN_STRING);
1334             new_grp =
1335                 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1336                              LISTBOX_APPEND_AT_END);
1337             SKIP_TO_EOL;
1338             hot_load_group (new_grp);
1339             current_group = grp;
1340             break;
1341         case TKN_ENTRY:
1342             {
1343                 CHECK_TOKEN (TKN_STRING);
1344                 label = g_strndup (tkn_buf->str, tkn_buf->len);
1345                 CHECK_TOKEN (TKN_URL);
1346                 CHECK_TOKEN (TKN_STRING);
1347                 url = tilde_expand (tkn_buf->str);
1348                 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1349                 SKIP_TO_EOL;
1350             }
1351             break;
1352         case TKN_COMMENT:
1353             label = g_strndup (tkn_buf->str, tkn_buf->len);
1354             add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1355             break;
1356         case TKN_EOF:
1357             hotlist_state.readonly = TRUE;
1358             hotlist_state.file_error = TRUE;
1359             return;
1360         case TKN_EOL:
1361             /* skip empty lines */
1362             break;
1363         default:
1364             hotlist_state.readonly = TRUE;
1365             hotlist_state.file_error = TRUE;
1366             SKIP_TO_EOL;
1367             break;
1368         }
1369     SKIP_TO_EOL;
1370 }
1371 
1372 /* --------------------------------------------------------------------------------------------- */
1373 
1374 static void
1375 hot_load_file (struct hotlist *grp)
     /* [previous][next][first][last][top][bottom][index][help]  */
1376 {
1377     int tkn;
1378     struct hotlist *new_grp;
1379     char *label, *url;
1380 
1381     current_group = grp;
1382 
1383     while ((tkn = hot_next_token ()) != TKN_EOF)
1384         switch (tkn)
1385         {
1386         case TKN_GROUP:
1387             CHECK_TOKEN (TKN_STRING);
1388             new_grp =
1389                 add2hotlist (g_strndup (tkn_buf->str, tkn_buf->len), 0, HL_TYPE_GROUP,
1390                              LISTBOX_APPEND_AT_END);
1391             SKIP_TO_EOL;
1392             hot_load_group (new_grp);
1393             current_group = grp;
1394             break;
1395         case TKN_ENTRY:
1396             {
1397                 CHECK_TOKEN (TKN_STRING);
1398                 label = g_strndup (tkn_buf->str, tkn_buf->len);
1399                 CHECK_TOKEN (TKN_URL);
1400                 CHECK_TOKEN (TKN_STRING);
1401                 url = tilde_expand (tkn_buf->str);
1402                 add2hotlist (label, url, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1403                 SKIP_TO_EOL;
1404             }
1405             break;
1406         case TKN_COMMENT:
1407             label = g_strndup (tkn_buf->str, tkn_buf->len);
1408             add2hotlist (label, 0, HL_TYPE_COMMENT, LISTBOX_APPEND_AT_END);
1409             break;
1410         case TKN_EOL:
1411             /* skip empty lines */
1412             break;
1413         default:
1414             hotlist_state.readonly = TRUE;
1415             hotlist_state.file_error = TRUE;
1416             SKIP_TO_EOL;
1417             break;
1418         }
1419 }
1420 
1421 /* --------------------------------------------------------------------------------------------- */
1422 
1423 static void
1424 clean_up_hotlist_groups (const char *section)
     /* [previous][next][first][last][top][bottom][index][help]  */
1425 {
1426     char *grp_section;
1427 
1428     grp_section = g_strconcat (section, ".Group", (char *) NULL);
1429     if (mc_config_has_group (mc_global.main_config, section))
1430         mc_config_del_group (mc_global.main_config, section);
1431 
1432     if (mc_config_has_group (mc_global.main_config, grp_section))
1433     {
1434         char **profile_keys, **keys;
1435 
1436         keys = mc_config_get_keys (mc_global.main_config, grp_section, NULL);
1437 
1438         for (profile_keys = keys; *profile_keys != NULL; profile_keys++)
1439             clean_up_hotlist_groups (*profile_keys);
1440 
1441         g_strfreev (keys);
1442         mc_config_del_group (mc_global.main_config, grp_section);
1443     }
1444     g_free (grp_section);
1445 }
1446 
1447 /* --------------------------------------------------------------------------------------------- */
1448 
1449 static void
1450 load_hotlist (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1451 {
1452     gboolean remove_old_list = FALSE;
1453     struct stat stat_buf;
1454 
1455     if (hotlist_state.loaded)
1456     {
1457         stat (hotlist_file_name, &stat_buf);
1458         if (hotlist_file_mtime < stat_buf.st_mtime)
1459             done_hotlist ();
1460         else
1461             return;
1462     }
1463 
1464     if (hotlist_file_name == NULL)
1465         hotlist_file_name = mc_config_get_full_path (MC_HOTLIST_FILE);
1466 
1467     hotlist = g_new0 (struct hotlist, 1);
1468     hotlist->type = HL_TYPE_GROUP;
1469     hotlist->label = g_strdup (_("Top level group"));
1470     hotlist->up = hotlist;
1471     /*
1472      * compatibility :-(
1473      */
1474     hotlist->directory = g_strdup ("Hotlist");
1475 
1476     hotlist_file = fopen (hotlist_file_name, "r");
1477     if (hotlist_file == NULL)
1478     {
1479         int result;
1480 
1481         load_group (hotlist);
1482         hotlist_state.loaded = TRUE;
1483         /*
1484          * just to be sure we got copy
1485          */
1486         hotlist_state.modified = TRUE;
1487         result = save_hotlist ();
1488         hotlist_state.modified = FALSE;
1489         if (result != 0)
1490             remove_old_list = TRUE;
1491         else
1492             message (D_ERROR, _("Hotlist Load"),
1493                      _
1494                      ("MC was unable to write %s file,\nyour old hotlist entries were not deleted"),
1495                      MC_USERCONF_DIR PATH_SEP_STR MC_HOTLIST_FILE);
1496     }
1497     else
1498     {
1499         hot_load_file (hotlist);
1500         fclose (hotlist_file);
1501         hotlist_state.loaded = TRUE;
1502     }
1503 
1504     if (remove_old_list)
1505     {
1506         GError *mcerror = NULL;
1507 
1508         clean_up_hotlist_groups ("Hotlist");
1509         if (!mc_config_save_file (mc_global.main_config, &mcerror))
1510             setup_save_config_show_error (mc_global.main_config->ini_path, &mcerror);
1511 
1512         mc_error_message (&mcerror, NULL);
1513     }
1514 
1515     stat (hotlist_file_name, &stat_buf);
1516     hotlist_file_mtime = stat_buf.st_mtime;
1517     current_group = hotlist;
1518 }
1519 
1520 /* --------------------------------------------------------------------------------------------- */
1521 
1522 static void
1523 hot_save_group (struct hotlist *grp)
     /* [previous][next][first][last][top][bottom][index][help]  */
1524 {
1525     struct hotlist *current;
1526     int i;
1527     char *s;
1528 
1529 #define INDENT(n) \
1530 do { \
1531     for (i = 0; i < n; i++) \
1532         putc (' ', hotlist_file); \
1533 } while (0)
1534 
1535     for (current = grp->head; current != NULL; current = current->next)
1536         switch (current->type)
1537         {
1538         case HL_TYPE_GROUP:
1539             INDENT (list_level);
1540             fputs ("GROUP \"", hotlist_file);
1541             for (s = current->label; *s != '\0'; s++)
1542             {
1543                 if (*s == '"' || *s == '\\')
1544                     putc ('\\', hotlist_file);
1545                 putc (*s, hotlist_file);
1546             }
1547             fputs ("\"\n", hotlist_file);
1548             list_level += 2;
1549             hot_save_group (current);
1550             list_level -= 2;
1551             INDENT (list_level);
1552             fputs ("ENDGROUP\n", hotlist_file);
1553             break;
1554         case HL_TYPE_ENTRY:
1555             INDENT (list_level);
1556             fputs ("ENTRY \"", hotlist_file);
1557             for (s = current->label; *s != '\0'; s++)
1558             {
1559                 if (*s == '"' || *s == '\\')
1560                     putc ('\\', hotlist_file);
1561                 putc (*s, hotlist_file);
1562             }
1563             fputs ("\" URL \"", hotlist_file);
1564             for (s = current->directory; *s != '\0'; s++)
1565             {
1566                 if (*s == '"' || *s == '\\')
1567                     putc ('\\', hotlist_file);
1568                 putc (*s, hotlist_file);
1569             }
1570             fputs ("\"\n", hotlist_file);
1571             break;
1572         case HL_TYPE_COMMENT:
1573             fprintf (hotlist_file, "#%s\n", current->label);
1574             break;
1575         case HL_TYPE_DOTDOT:
1576             /* do nothing */
1577             break;
1578         default:
1579             break;
1580         }
1581 }
1582 
1583 /* --------------------------------------------------------------------------------------------- */
1584 
1585 static void
1586 add_dotdot_to_list (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1587 {
1588     if (current_group != hotlist && hotlist_has_dot_dot)
1589         add2hotlist (g_strdup (".."), g_strdup (".."), HL_TYPE_DOTDOT, LISTBOX_APPEND_AT_END);
1590 }
1591 
1592 /* --------------------------------------------------------------------------------------------- */
1593 /*** public functions ****************************************************************************/
1594 /* --------------------------------------------------------------------------------------------- */
1595 
1596 void
1597 add2hotlist_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1598 {
1599     char *lc_prompt;
1600     const char *cp = N_("Label for \"%s\":");
1601     int l;
1602     char *label_string, *label;
1603 
1604 #ifdef ENABLE_NLS
1605     cp = _(cp);
1606 #endif
1607 
1608     l = str_term_width1 (cp);
1609     label_string = vfs_path_to_str_flags (current_panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
1610     lc_prompt = g_strdup_printf (cp, str_trunc (label_string, COLS - 2 * UX - (l + 8)));
1611     label =
1612         input_dialog (_("Add to hotlist"), lc_prompt, MC_HISTORY_HOTLIST_ADD, label_string,
1613                       INPUT_COMPLETE_NONE);
1614     g_free (lc_prompt);
1615 
1616     if (label == NULL || *label == '\0')
1617     {
1618         g_free (label_string);
1619         g_free (label);
1620     }
1621     else
1622     {
1623         add2hotlist (label, label_string, HL_TYPE_ENTRY, LISTBOX_APPEND_AT_END);
1624         hotlist_state.modified = TRUE;
1625     }
1626 }
1627 
1628 /* --------------------------------------------------------------------------------------------- */
1629 
1630 char *
1631 hotlist_show (hotlist_t list_type)
     /* [previous][next][first][last][top][bottom][index][help]  */
1632 {
1633     char *target = NULL;
1634     int res;
1635 
1636     hotlist_state.type = list_type;
1637     load_hotlist ();
1638 
1639     init_hotlist (list_type);
1640 
1641     /* display file info */
1642     tty_setcolor (SELECTED_COLOR);
1643 
1644     hotlist_state.running = TRUE;
1645     res = dlg_run (hotlist_dlg);
1646     hotlist_state.running = FALSE;
1647     save_hotlist ();
1648 
1649     if (res == B_ENTER)
1650     {
1651         char *text = NULL;
1652         struct hotlist *hlp = NULL;
1653 
1654         listbox_get_current (l_hotlist, &text, (void **) &hlp);
1655         target = g_strdup (hlp != NULL ? hlp->directory : text);
1656     }
1657 
1658     hotlist_done ();
1659     return target;
1660 }
1661 
1662 /* --------------------------------------------------------------------------------------------- */
1663 
1664 gboolean
1665 save_hotlist (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1666 {
1667     gboolean saved = FALSE;
1668     struct stat stat_buf;
1669 
1670     if (!hotlist_state.readonly && hotlist_state.modified && hotlist_file_name != NULL)
1671     {
1672         mc_util_make_backup_if_possible (hotlist_file_name, ".bak");
1673 
1674         hotlist_file = fopen (hotlist_file_name, "w");
1675         if (hotlist_file == NULL)
1676             mc_util_restore_from_backup_if_possible (hotlist_file_name, ".bak");
1677         else
1678         {
1679             hot_save_group (hotlist);
1680             fclose (hotlist_file);
1681             stat (hotlist_file_name, &stat_buf);
1682             hotlist_file_mtime = stat_buf.st_mtime;
1683             hotlist_state.modified = FALSE;
1684             saved = TRUE;
1685         }
1686     }
1687 
1688     return saved;
1689 }
1690 
1691 /* --------------------------------------------------------------------------------------------- */
1692 /**
1693  * Unload list from memory.
1694  * Don't confuse with hotlist_done() for GUI.
1695  */
1696 
1697 void
1698 done_hotlist (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1699 {
1700     if (hotlist != NULL)
1701     {
1702         remove_group (hotlist);
1703         g_free (hotlist->label);
1704         g_free (hotlist->directory);
1705         MC_PTR_FREE (hotlist);
1706     }
1707 
1708     hotlist_state.loaded = FALSE;
1709 
1710     MC_PTR_FREE (hotlist_file_name);
1711     l_hotlist = NULL;
1712     current_group = NULL;
1713 
1714     if (tkn_buf != NULL)
1715     {
1716         g_string_free (tkn_buf, TRUE);
1717         tkn_buf = NULL;
1718     }
1719 }
1720 
1721 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */