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-2020
   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 reflect run time state
 129      */
 130 
 131     gboolean loaded;            /* hotlist is loaded */
 132     gboolean readonly;          /* hotlist readonly */
 133     gboolean file_error;        /* parse error while reading file */
 134     gboolean running;           /* we are running dlg (and have to
 135                                    update listbox */
 136     gboolean moving;            /* we are in moving hotlist currently */
 137     gboolean modified;          /* hotlist was modified */
 138     hotlist_t type;             /* LIST_HOTLIST || LIST_VFSLIST */
 139 } hotlist_state;
 140 
 141 /* Directory hotlist */
 142 struct hotlist
 143 {
 144     enum HotListType type;
 145     char *directory;
 146     char *label;
 147     struct hotlist *head;
 148     struct hotlist *up;
 149     struct hotlist *next;
 150 };
 151 
 152 /*** file scope variables ************************************************************************/
 153 
 154 static gboolean hotlist_has_dot_dot = TRUE;
 155 
 156 static WDialog *hotlist_dlg, *movelist_dlg;
 157 static WGroupbox *hotlist_group, *movelist_group;
 158 static WListbox *l_hotlist, *l_movelist;
 159 static WLabel *pname;
 160 
 161 static struct
 162 {
 163     int ret_cmd, flags, y, x, len;
 164     const char *text;
 165     int type;
 166     widget_pos_flags_t pos_flags;
 167 } hotlist_but[] =
 168 {
 169     /* *INDENT-OFF* */
 170     { B_ENTER, DEFPUSH_BUTTON, 0, 0, 0, N_("Change &to"),
 171             LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 172 #ifdef ENABLE_VFS
 173     { B_FREE_ALL_VFS, NORMAL_BUTTON, 0, 20, 0, N_("&Free VFSs now"),
 174             LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 175     { B_REFRESH_VFS, NORMAL_BUTTON, 0, 43, 0, N_("&Refresh"),
 176             LIST_VFSLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 177 #endif
 178     { B_ADD_CURRENT, NORMAL_BUTTON, 0, 20, 0, N_("&Add current"),
 179             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 180     { B_UP_GROUP, NORMAL_BUTTON, 0, 42, 0, N_("&Up"),
 181             LIST_HOTLIST | LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 182     { B_CANCEL, NORMAL_BUTTON, 0, 53, 0, N_("&Cancel"),
 183             LIST_HOTLIST | LIST_VFSLIST | LIST_MOVELIST, WPOS_KEEP_RIGHT | WPOS_KEEP_BOTTOM },
 184     { B_NEW_GROUP, NORMAL_BUTTON, 1, 0, 0, N_("New &group"),
 185             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 186     { B_NEW_ENTRY, NORMAL_BUTTON, 1, 15, 0, N_("New &entry"),
 187             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 188     { B_INSERT, NORMAL_BUTTON, 1, 0, 0, N_("&Insert"),
 189             LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 190     { B_APPEND, NORMAL_BUTTON, 1, 15, 0, N_("A&ppend"),
 191             LIST_MOVELIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 192     { B_REMOVE, NORMAL_BUTTON, 1, 30, 0, N_("&Remove"),
 193             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM },
 194     { B_MOVE, NORMAL_BUTTON, 1, 42, 0, N_("&Move"),
 195             LIST_HOTLIST, WPOS_KEEP_LEFT | WPOS_KEEP_BOTTOM }
 196     /* *INDENT-ON* */
 197 };
 198 
 199 static const size_t hotlist_but_num = G_N_ELEMENTS (hotlist_but);
 200 
 201 static struct hotlist *hotlist = NULL;
 202 
 203 static struct hotlist *current_group;
 204 
 205 static GString *tkn_buf = NULL;
 206 
 207 static char *hotlist_file_name;
 208 static FILE *hotlist_file;
 209 static time_t hotlist_file_mtime;
 210 
 211 static int list_level = 0;
 212 
 213 /* --------------------------------------------------------------------------------------------- */
 214 /*** file scope functions ************************************************************************/
 215 /* --------------------------------------------------------------------------------------------- */
 216 
 217 static void init_movelist (struct hotlist *item);
 218 static void add_new_group_cmd (void);
 219 static void add_new_entry_cmd (void);
 220 static void remove_from_hotlist (struct hotlist *entry);
 221 static void load_hotlist (void);
 222 static void add_dotdot_to_list (void);
 223 
 224 /* --------------------------------------------------------------------------------------------- */
 225 /** If current->data is 0, then we are dealing with a VFS pathname */
 226 
 227 static void
 228 update_path_name (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 229 {
 230     const char *text = "";
 231     char *p;
 232     WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
 233     Widget *w = WIDGET (list);
 234 
 235     if (!listbox_is_empty (list))
 236     {
 237         char *ctext = NULL;
 238         void *cdata = NULL;
 239 
 240         listbox_get_current (list, &ctext, &cdata);
 241         if (cdata == NULL)
 242             text = ctext;
 243         else
 244         {
 245             struct hotlist *hlp = (struct hotlist *) cdata;
 246 
 247             if (hlp->type == HL_TYPE_ENTRY || hlp->type == HL_TYPE_DOTDOT)
 248                 text = hlp->directory;
 249             else if (hlp->type == HL_TYPE_GROUP)
 250                 text = _("Subgroup - press ENTER to see list");
 251         }
 252     }
 253 
 254     p = g_strconcat (" ", current_group->label, " ", (char *) NULL);
 255     if (hotlist_state.moving)
 256         groupbox_set_title (movelist_group, str_trunc (p, w->cols - 2));
 257     else
 258     {
 259         groupbox_set_title (hotlist_group, str_trunc (p, w->cols - 2));
 260         label_set_text (pname, str_trunc (text, w->cols));
 261     }
 262     g_free (p);
 263 }
 264 
 265 /* --------------------------------------------------------------------------------------------- */
 266 
 267 static void
 268 fill_listbox (WListbox * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 269 {
 270     struct hotlist *current;
 271     GString *buff;
 272 
 273     buff = g_string_new ("");
 274 
 275     for (current = current_group->head; current != NULL; current = current->next)
 276         switch (current->type)
 277         {
 278         case HL_TYPE_GROUP:
 279             {
 280                 /* buff clean up */
 281                 g_string_truncate (buff, 0);
 282                 g_string_append (buff, "->");
 283                 g_string_append (buff, current->label);
 284                 listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, buff->str, current, FALSE);
 285             }
 286             break;
 287         case HL_TYPE_DOTDOT:
 288         case HL_TYPE_ENTRY:
 289             listbox_add_item (list, LISTBOX_APPEND_AT_END, 0, current->label, current, FALSE);
 290         default:
 291             break;
 292         }
 293 
 294     g_string_free (buff, TRUE);
 295 }
 296 
 297 /* --------------------------------------------------------------------------------------------- */
 298 
 299 static void
 300 unlink_entry (struct hotlist *entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
 301 {
 302     struct hotlist *current = current_group->head;
 303 
 304     if (current == entry)
 305         current_group->head = entry->next;
 306     else
 307     {
 308         while (current != NULL && current->next != entry)
 309             current = current->next;
 310         if (current != NULL)
 311             current->next = entry->next;
 312     }
 313     entry->next = entry->up = NULL;
 314 }
 315 
 316 /* --------------------------------------------------------------------------------------------- */
 317 
 318 #ifdef ENABLE_VFS
 319 static void
 320 add_name_to_list (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 321 {
 322     listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, path, NULL, FALSE);
 323 }
 324 #endif /* !ENABLE_VFS */
 325 
 326 /* --------------------------------------------------------------------------------------------- */
 327 
 328 static int
 329 hotlist_run_cmd (int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 330 {
 331     switch (action)
 332     {
 333     case B_MOVE:
 334         {
 335             struct hotlist *saved = current_group;
 336             struct hotlist *item = NULL;
 337             struct hotlist *moveto_item = NULL;
 338             struct hotlist *moveto_group = NULL;
 339             int ret;
 340 
 341             if (listbox_is_empty (l_hotlist))
 342                 return 0;       /* empty group - nothing to do */
 343 
 344             listbox_get_current (l_hotlist, NULL, (void **) &item);
 345             init_movelist (item);
 346             hotlist_state.moving = TRUE;
 347             ret = dlg_run (movelist_dlg);
 348             hotlist_state.moving = FALSE;
 349             listbox_get_current (l_movelist, NULL, (void **) &moveto_item);
 350             moveto_group = current_group;
 351             dlg_destroy (movelist_dlg);
 352             current_group = saved;
 353             if (ret == B_CANCEL)
 354                 return 0;
 355             if (moveto_item == item)
 356                 return 0;       /* If we insert/append a before/after a
 357                                    it hardly changes anything ;) */
 358             unlink_entry (item);
 359             listbox_remove_current (l_hotlist);
 360             item->up = moveto_group;
 361             if (moveto_group->head == NULL)
 362                 moveto_group->head = item;
 363             else if (moveto_item == NULL)
 364             {                   /* we have group with just comments */
 365                 struct hotlist *p = moveto_group->head;
 366 
 367                 /* skip comments */
 368                 while (p->next != NULL)
 369                     p = p->next;
 370                 p->next = item;
 371             }
 372             else if (ret == B_ENTER || ret == B_APPEND)
 373             {
 374                 if (moveto_item->next == NULL)
 375                     moveto_item->next = item;
 376                 else
 377                 {
 378                     item->next = moveto_item->next;
 379                     moveto_item->next = item;
 380                 }
 381             }
 382             else if (moveto_group->head == moveto_item)
 383             {
 384                 moveto_group->head = item;
 385                 item->next = moveto_item;
 386             }
 387             else
 388             {
 389                 struct hotlist *p = moveto_group->head;
 390 
 391                 while (p->next != moveto_item)
 392                     p = p->next;
 393                 item->next = p->next;
 394                 p->next = item;
 395             }
 396             listbox_remove_list (l_hotlist);
 397             fill_listbox (l_hotlist);
 398             repaint_screen ();
 399             hotlist_state.modified = TRUE;
 400             return 0;
 401         }
 402     case B_REMOVE:
 403         {
 404             struct hotlist *entry = NULL;
 405 
 406             listbox_get_current (l_hotlist, NULL, (void **) &entry);
 407             remove_from_hotlist (entry);
 408         }
 409         return 0;
 410 
 411     case B_NEW_GROUP:
 412         add_new_group_cmd ();
 413         return 0;
 414 
 415     case B_ADD_CURRENT:
 416         add2hotlist_cmd ();
 417         return 0;
 418 
 419     case B_NEW_ENTRY:
 420         add_new_entry_cmd ();
 421         return 0;
 422 
 423     case B_ENTER:
 424     case B_ENTER_GROUP:
 425         {
 426             WListbox *list;
 427             void *data;
 428             struct hotlist *hlp;
 429 
 430             list = hotlist_state.moving ? l_movelist : l_hotlist;
 431             listbox_get_current (list, NULL, &data);
 432 
 433             if (data == NULL)
 434                 return 1;
 435 
 436             hlp = (struct hotlist *) data;
 437 
 438             if (hlp->type == HL_TYPE_ENTRY)
 439                 return (action == B_ENTER ? 1 : 0);
 440             if (hlp->type != HL_TYPE_DOTDOT)
 441             {
 442                 listbox_remove_list (list);
 443                 current_group = hlp;
 444                 fill_listbox (list);
 445                 return 0;
 446             }
 447             MC_FALLTHROUGH;     /* go up */
 448         }
 449         MC_FALLTHROUGH;         /* if list empty - just go up */
 450 
 451     case B_UP_GROUP:
 452         {
 453             WListbox *list = hotlist_state.moving ? l_movelist : l_hotlist;
 454 
 455             listbox_remove_list (list);
 456             current_group = current_group->up;
 457             fill_listbox (list);
 458             return 0;
 459         }
 460 
 461 #ifdef ENABLE_VFS
 462     case B_FREE_ALL_VFS:
 463         vfs_expire (TRUE);
 464         MC_FALLTHROUGH;
 465 
 466     case B_REFRESH_VFS:
 467         listbox_remove_list (l_hotlist);
 468         listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
 469                           FALSE);
 470         vfs_fill_names (add_name_to_list);
 471         return 0;
 472 #endif /* ENABLE_VFS */
 473 
 474     default:
 475         return 1;
 476     }
 477 }
 478 
 479 /* --------------------------------------------------------------------------------------------- */
 480 
 481 static int
 482 hotlist_button_callback (WButton * button, int action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 483 {
 484     int ret;
 485 
 486     (void) button;
 487     ret = hotlist_run_cmd (action);
 488     update_path_name ();
 489     return ret;
 490 }
 491 
 492 /* --------------------------------------------------------------------------------------------- */
 493 
 494 static inline cb_ret_t
 495 hotlist_handle_key (WDialog * h, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 496 {
 497     switch (key)
 498     {
 499     case KEY_M_CTRL | '\n':
 500         goto l1;
 501 
 502     case '\n':
 503     case KEY_ENTER:
 504         if (hotlist_button_callback (NULL, B_ENTER) != 0)
 505         {
 506             h->ret_value = B_ENTER;
 507             dlg_stop (h);
 508         }
 509         return MSG_HANDLED;
 510 
 511     case KEY_RIGHT:
 512         /* enter to the group */
 513         if (hotlist_state.type == LIST_VFSLIST)
 514             return MSG_NOT_HANDLED;
 515         return hotlist_button_callback (NULL, B_ENTER_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
 516 
 517     case KEY_LEFT:
 518         /* leave the group */
 519         if (hotlist_state.type == LIST_VFSLIST)
 520             return MSG_NOT_HANDLED;
 521         return hotlist_button_callback (NULL, B_UP_GROUP) == 0 ? MSG_HANDLED : MSG_NOT_HANDLED;
 522 
 523     case KEY_DC:
 524         if (hotlist_state.moving)
 525             return MSG_NOT_HANDLED;
 526         hotlist_button_callback (NULL, B_REMOVE);
 527         return MSG_HANDLED;
 528 
 529       l1:
 530     case ALT ('\n'):
 531     case ALT ('\r'):
 532         if (!hotlist_state.moving)
 533         {
 534             void *ldata = NULL;
 535 
 536             listbox_get_current (l_hotlist, NULL, &ldata);
 537 
 538             if (ldata != NULL)
 539             {
 540                 struct hotlist *hlp = (struct hotlist *) ldata;
 541 
 542                 if (hlp->type == HL_TYPE_ENTRY)
 543                 {
 544                     char *tmp;
 545 
 546                     tmp = g_strconcat ("cd ", hlp->directory, (char *) NULL);
 547                     input_insert (cmdline, tmp, FALSE);
 548                     g_free (tmp);
 549                     h->ret_value = B_CANCEL;
 550                     dlg_stop (h);
 551                 }
 552             }
 553         }
 554         return MSG_HANDLED;     /* ignore key */
 555 
 556     default:
 557         return MSG_NOT_HANDLED;
 558     }
 559 }
 560 
 561 /* --------------------------------------------------------------------------------------------- */
 562 
 563 static cb_ret_t
 564 hotlist_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 565 {
 566     WDialog *h = DIALOG (w);
 567 
 568     switch (msg)
 569     {
 570     case MSG_INIT:
 571     case MSG_NOTIFY:           /* MSG_NOTIFY is fired by the listbox to tell us the item has changed. */
 572         update_path_name ();
 573         return MSG_HANDLED;
 574 
 575     case MSG_UNHANDLED_KEY:
 576         return hotlist_handle_key (h, parm);
 577 
 578     case MSG_POST_KEY:
 579         /*
 580          * The code here has two purposes:
 581          *
 582          * (1) Always stay on the hotlist.
 583          *
 584          * Activating a button using its hotkey (and even pressing ENTER, as
 585          * there's a "default button") moves the focus to the button. But we
 586          * want to stay on the hotlist, to be able to use the usual keys (up,
 587          * down, etc.). So we do `widget_select (lst)`.
 588          *
 589          * (2) Refresh the hotlist.
 590          *
 591          * We may have run a command that changed the contents of the list.
 592          * We therefore need to refresh it. So we do `widget_draw (lst)`.
 593          */
 594         {
 595             Widget *lst;
 596 
 597             lst = WIDGET (h == hotlist_dlg ? l_hotlist : l_movelist);
 598 
 599             /* widget_select() already redraws the widget, but since it's a
 600              * no-op if the widget is already selected ("focused"), we have
 601              * to call widget_draw() separately. */
 602             if (!widget_get_state (lst, WST_FOCUSED))
 603                 widget_select (lst);
 604             else
 605                 widget_draw (lst);
 606         }
 607         return MSG_HANDLED;
 608 
 609     case MSG_RESIZE:
 610         {
 611             WRect r;
 612 
 613             rect_init (&r, w->y, w->x, LINES - (h == hotlist_dlg ? 2 : 6), COLS - 6);
 614 
 615             return dlg_default_callback (w, NULL, MSG_RESIZE, 0, &r);
 616         }
 617 
 618     default:
 619         return dlg_default_callback (w, sender, msg, parm, data);
 620     }
 621 }
 622 
 623 /* --------------------------------------------------------------------------------------------- */
 624 
 625 static lcback_ret_t
 626 hotlist_listbox_callback (WListbox * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 627 {
 628     WDialog *dlg = DIALOG (WIDGET (list)->owner);
 629 
 630     if (!listbox_is_empty (list))
 631     {
 632         void *data = NULL;
 633 
 634         listbox_get_current (list, NULL, &data);
 635 
 636         if (data != NULL)
 637         {
 638             struct hotlist *hlp = (struct hotlist *) data;
 639 
 640             if (hlp->type == HL_TYPE_ENTRY)
 641             {
 642                 dlg->ret_value = B_ENTER;
 643                 dlg_stop (dlg);
 644                 return LISTBOX_DONE;
 645             }
 646             else
 647             {
 648                 hotlist_button_callback (NULL, B_ENTER);
 649                 send_message (dlg, NULL, MSG_POST_KEY, '\n', NULL);
 650                 return LISTBOX_CONT;
 651             }
 652         }
 653         else
 654         {
 655             dlg->ret_value = B_ENTER;
 656             dlg_stop (dlg);
 657             return LISTBOX_DONE;
 658         }
 659     }
 660 
 661     hotlist_button_callback (NULL, B_UP_GROUP);
 662     send_message (dlg, NULL, MSG_POST_KEY, 'u', NULL);
 663     return LISTBOX_CONT;
 664 }
 665 
 666 /* --------------------------------------------------------------------------------------------- */
 667 /**
 668  * Expands all button names (once) and recalculates button positions.
 669  * returns number of columns in the dialog box, which is 10 chars longer
 670  * then buttonbar.
 671  *
 672  * If common width of the window (i.e. in xterm) is less than returned
 673  * width - sorry :)  (anyway this did not handled in previous version too)
 674  */
 675 
 676 static int
 677 init_i18n_stuff (int list_type, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 678 {
 679     size_t i;
 680 
 681     static gboolean i18n_flag = FALSE;
 682 
 683     if (!i18n_flag)
 684     {
 685         for (i = 0; i < hotlist_but_num; i++)
 686         {
 687 #ifdef ENABLE_NLS
 688             hotlist_but[i].text = _(hotlist_but[i].text);
 689 #endif /* ENABLE_NLS */
 690             hotlist_but[i].len = str_term_width1 (hotlist_but[i].text) + 3;
 691             if (hotlist_but[i].flags == DEFPUSH_BUTTON)
 692                 hotlist_but[i].len += 2;
 693         }
 694 
 695         i18n_flag = TRUE;
 696     }
 697 
 698     /* Dynamic resizing of buttonbars */
 699     {
 700         int len[2], count[2];   /* at most two lines of buttons */
 701         int cur_x[2];
 702 
 703         len[0] = len[1] = 0;
 704         count[0] = count[1] = 0;
 705         cur_x[0] = cur_x[1] = 0;
 706 
 707         /* Count len of buttonbars, assuming 1 extra space between buttons */
 708         for (i = 0; i < hotlist_but_num; i++)
 709             if ((hotlist_but[i].type & list_type) != 0)
 710             {
 711                 int row;
 712 
 713                 row = hotlist_but[i].y;
 714                 ++count[row];
 715                 len[row] += hotlist_but[i].len + 1;
 716             }
 717 
 718         (len[0])--;
 719         (len[1])--;
 720 
 721         cols = MAX (cols, MAX (len[0], len[1]));
 722 
 723         /* arrange buttons */
 724         for (i = 0; i < hotlist_but_num; i++)
 725             if ((hotlist_but[i].type & list_type) != 0)
 726             {
 727                 int row;
 728 
 729                 row = hotlist_but[i].y;
 730 
 731                 if (hotlist_but[i].x != 0)
 732                 {
 733                     /* not first int the row */
 734                     if (hotlist_but[i].ret_cmd == B_CANCEL)
 735                         hotlist_but[i].x = cols - hotlist_but[i].len - 6;
 736                     else
 737                         hotlist_but[i].x = cur_x[row];
 738                 }
 739 
 740                 cur_x[row] += hotlist_but[i].len + 1;
 741             }
 742     }
 743 
 744     return cols;
 745 }
 746 
 747 /* --------------------------------------------------------------------------------------------- */
 748 
 749 static void
 750 init_hotlist (hotlist_t list_type)
     /* [previous][next][first][last][top][bottom][index][help]  */
 751 {
 752     size_t i;
 753     const char *title, *help_node;
 754     int lines, cols;
 755     int y;
 756     int dh = 0;
 757     WGroup *g;
 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 #ifdef ENABLE_VFS
 767     if (list_type == LIST_VFSLIST)
 768     {
 769         title = _("Active VFS directories");
 770         help_node = "[vfshot]"; /* FIXME - no such node */
 771         dh = 1;
 772     }
 773     else
 774 #endif /* !ENABLE_VFS */
 775     {
 776         title = _("Directory hotlist");
 777         help_node = "[Hotlist]";
 778     }
 779 
 780     hotlist_dlg =
 781         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
 782                     NULL, help_node, title);
 783     g = GROUP (hotlist_dlg);
 784 
 785     y = UY;
 786     hotlist_group = groupbox_new (y, UX, lines - 10 + dh, cols - 2 * UX, _("Top level group"));
 787     hotlist_widget = WIDGET (hotlist_group);
 788     group_add_widget_autopos (g, hotlist_widget, WPOS_KEEP_ALL, NULL);
 789 
 790     l_hotlist =
 791         listbox_new (y + 1, UX + 1, hotlist_widget->lines - 2, hotlist_widget->cols - 2, FALSE,
 792                      hotlist_listbox_callback);
 793 
 794     /* Fill the hotlist with the active VFS or the hotlist */
 795 #ifdef ENABLE_VFS
 796     if (list_type == LIST_VFSLIST)
 797     {
 798         listbox_add_item (l_hotlist, LISTBOX_APPEND_AT_END, 0, mc_config_get_home_dir (), NULL,
 799                           FALSE);
 800         vfs_fill_names (add_name_to_list);
 801     }
 802     else
 803 #endif /* !ENABLE_VFS */
 804         fill_listbox (l_hotlist);
 805 
 806     /* insert before groupbox to view scrollbar */
 807     group_add_widget_autopos (g, l_hotlist, WPOS_KEEP_ALL, NULL);
 808 
 809     y += hotlist_widget->lines;
 810 
 811     path_box = groupbox_new (y, UX, 3, hotlist_widget->cols, _("Directory path"));
 812     group_add_widget_autopos (g, path_box, WPOS_KEEP_BOTTOM | WPOS_KEEP_HORZ, NULL);
 813 
 814     pname = label_new (y + 1, UX + 2, "");
 815     group_add_widget_autopos (g, pname, WPOS_KEEP_BOTTOM | WPOS_KEEP_LEFT, NULL);
 816     y += WIDGET (path_box)->lines;
 817 
 818     group_add_widget_autopos (g, hline_new (y++, -1, -1), WPOS_KEEP_BOTTOM, NULL);
 819 
 820     for (i = 0; i < hotlist_but_num; i++)
 821         if ((hotlist_but[i].type & list_type) != 0)
 822             group_add_widget_autopos (g,
 823                                       button_new (y + hotlist_but[i].y, UX + hotlist_but[i].x,
 824                                                   hotlist_but[i].ret_cmd, hotlist_but[i].flags,
 825                                                   hotlist_but[i].text, hotlist_button_callback),
 826                                       hotlist_but[i].pos_flags, NULL);
 827 
 828     widget_select (WIDGET (l_hotlist));
 829 }
 830 
 831 /* --------------------------------------------------------------------------------------------- */
 832 
 833 static void
 834 init_movelist (struct hotlist *item)
     /* [previous][next][first][last][top][bottom][index][help]  */
 835 {
 836     size_t i;
 837     char *hdr;
 838     int lines, cols;
 839     int y;
 840     WGroup *g;
 841     Widget *movelist_widget;
 842 
 843     do_refresh ();
 844 
 845     lines = LINES - 6;
 846     cols = init_i18n_stuff (LIST_MOVELIST, COLS - 6);
 847 
 848     hdr = g_strdup_printf (_("Moving %s"), item->label);
 849 
 850     movelist_dlg =
 851         dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, hotlist_callback,
 852                     NULL, "[Hotlist]", hdr);
 853     g = GROUP (movelist_dlg);
 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     group_add_widget_autopos (g, 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     group_add_widget_autopos (g, l_movelist, WPOS_KEEP_ALL, NULL);
 868 
 869     y += movelist_widget->lines;
 870 
 871     group_add_widget_autopos (g, 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             group_add_widget_autopos (g,
 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]  */