home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / mc454src.zip / mc-4.5.4.src / mc-4.5.4 / src / hotlist.c < prev    next >
C/C++ Source or Header  |  1999-01-04  |  41KB  |  1,621 lines

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