Manual pages: mcmcdiffmceditmcview

root/src/file_history.c

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

DEFINITIONS

This source file includes following definitions.
  1. file_history_parse_entry
  2. file_history_write_entry
  3. file_history_list_read
  4. file_history_list_write
  5. file_history_create_item
  6. file_history_release_item
  7. file_history_free_item
  8. show_file_history

   1 /*
   2    Load and show history of edited and viewed files
   3 
   4    Copyright (C) 2020-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Andrew Borodin <aborodin@vmail.ru>, 2019-2022
   9 
  10    This file is part of the Midnight Commander.
  11 
  12    The Midnight Commander is free software: you can redistribute it
  13    and/or modify it under the terms of the GNU General Public License as
  14    published by the Free Software Foundation, either version 3 of the License,
  15    or (at your option) any later version.
  16 
  17    The Midnight Commander is distributed in the hope that it will be useful,
  18    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20    GNU General Public License for more details.
  21 
  22    You should have received a copy of the GNU General Public License
  23    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  24  */
  25 
  26 #include <config.h>
  27 
  28 #include <stdio.h>  // file functions
  29 
  30 #include "lib/global.h"
  31 
  32 #include "lib/fileloc.h"   // MC_FILEPOS_FILE
  33 #include "lib/mcconfig.h"  // mc_config_get_full_path()
  34 #include "lib/strutil.h"   // str_term_width1()
  35 #include "lib/util.h"      // backup functions
  36 
  37 #include "file_history.h"
  38 
  39 /*** global variables ****************************************************************************/
  40 
  41 /*** file scope macro definitions ****************************************************************/
  42 
  43 #define TMP_SUFFIX ".tmp"
  44 
  45 /*** file scope type declarations ****************************************************************/
  46 
  47 typedef struct file_history_data_t
  48 {
  49     char *file_name;
  50     char *file_pos;
  51 } file_history_data_t;
  52 
  53 /*** forward declarations (file scope functions) *************************************************/
  54 
  55 /*** file scope variables ************************************************************************/
  56 
  57 static const char SEPARATOR[] = " ";
  58 
  59 /* --------------------------------------------------------------------------------------------- */
  60 /*** file scope functions ************************************************************************/
  61 /* --------------------------------------------------------------------------------------------- */
  62 
  63 static void
  64 file_history_parse_entry (const char *buf, GList **file_list)
     /* [previous][next][first][last][top][bottom][index][help]  */
  65 {
  66     file_history_data_t *fhd;
  67     const char *s = strrchr (buf, *SEPARATOR);
  68 
  69     // Ignore entries without saved file position info in the filepos file
  70     if (s == NULL)
  71         return;
  72 
  73     fhd = g_new (file_history_data_t, 1);
  74     fhd->file_name = str_unescape (buf, s - buf, "", TRUE);
  75 
  76     const size_t len = strlen (s + 1);
  77 
  78     fhd->file_pos = g_strndup (s + 1, len - 1);  // ignore '\n'
  79 
  80     *file_list = g_list_prepend (*file_list, fhd);
  81 }
  82 
  83 /* --------------------------------------------------------------------------------------------- */
  84 
  85 static void
  86 file_history_write_entry (const file_history_data_t *fhd, GString *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
  87 {
  88     {
  89         char *file_name;
  90 
  91         file_name = str_escape (fhd->file_name, -1, "", TRUE);
  92         g_string_append (s, file_name);
  93         g_free (file_name);
  94     }
  95 
  96     if (fhd->file_pos != NULL)
  97     {
  98         g_string_append_c (s, *SEPARATOR);
  99         g_string_append (s, fhd->file_pos);
 100     }
 101 }
 102 
 103 /* --------------------------------------------------------------------------------------------- */
 104 
 105 static GList *
 106 file_history_list_read (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 107 {
 108     char *fn;
 109     FILE *f;
 110     char buf[MC_MAXPATHLEN + 100];
 111     GList *file_list = NULL;
 112 
 113     // open file with positions
 114     fn = mc_config_get_full_path (MC_FILEPOS_FILE);
 115     if (fn == NULL)
 116         return NULL;
 117 
 118     f = fopen (fn, "r");
 119     g_free (fn);
 120     if (f == NULL)
 121         return NULL;
 122 
 123     while (fgets (buf, sizeof (buf), f) != NULL)
 124         file_history_parse_entry (buf, &file_list);
 125 
 126     fclose (f);
 127 
 128     return file_list;
 129 }
 130 
 131 /* --------------------------------------------------------------------------------------------- */
 132 
 133 static void
 134 file_history_list_write (const GList *file_list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 135 {
 136     char *fn;
 137     FILE *f;
 138     gboolean write_error = FALSE;
 139 
 140     fn = mc_config_get_full_path (MC_FILEPOS_FILE);
 141     if (fn == NULL)
 142         return;
 143 
 144     mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
 145 
 146     f = fopen (fn, "w");
 147     if (f != NULL)
 148     {
 149         GString *s;
 150 
 151         s = g_string_sized_new (128);
 152 
 153         for (; file_list != NULL && !write_error; file_list = g_list_next (file_list))
 154         {
 155             file_history_write_entry (file_list->data, s);
 156             write_error = (fprintf (f, "%s\n", s->str) < 0);
 157             g_string_truncate (s, 0);
 158         }
 159 
 160         g_string_free (s, TRUE);
 161 
 162         fclose (f);
 163     }
 164 
 165     if (write_error)
 166         mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
 167     else
 168         mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
 169 
 170     g_free (fn);
 171 }
 172 
 173 /* --------------------------------------------------------------------------------------------- */
 174 
 175 static void
 176 file_history_create_item (history_descriptor_t *hd, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 177 {
 178     file_history_data_t *fhd = (file_history_data_t *) data;
 179     size_t width;
 180 
 181     width = str_term_width1 (fhd->file_name);
 182     hd->max_width = MAX (width, hd->max_width);
 183 
 184     listbox_add_item (hd->listbox, LISTBOX_APPEND_AT_END, 0, fhd->file_name, fhd->file_pos, TRUE);
 185     // fhd->file_pos is not copied, NULLize it to prevent double free
 186     fhd->file_pos = NULL;
 187 }
 188 
 189 /* --------------------------------------------------------------------------------------------- */
 190 
 191 static void *
 192 file_history_release_item (history_descriptor_t *hd, WLEntry *le)
     /* [previous][next][first][last][top][bottom][index][help]  */
 193 {
 194     file_history_data_t *fhd;
 195 
 196     (void) hd;
 197 
 198     fhd = g_new (file_history_data_t, 1);
 199     fhd->file_name = le->text;
 200     le->text = NULL;
 201     fhd->file_pos = (char *) le->data;
 202     le->data = NULL;
 203 
 204     return fhd;
 205 }
 206 
 207 /* --------------------------------------------------------------------------------------------- */
 208 
 209 static void
 210 file_history_free_item (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 211 {
 212     file_history_data_t *fhd = (file_history_data_t *) data;
 213 
 214     g_free (fhd->file_name);
 215     g_free (fhd->file_pos);
 216     g_free (fhd);
 217 }
 218 
 219 /* --------------------------------------------------------------------------------------------- */
 220 /*** public functions ****************************************************************************/
 221 /* --------------------------------------------------------------------------------------------- */
 222 
 223 /**
 224  * Show file history and return the selected file
 225  *
 226  * @param w widget used for positioning of history window
 227  * @param action to do with file (edit, view, etc)
 228  *
 229  * @return name of selected file, A newly allocated string.
 230  */
 231 char *
 232 show_file_history (const Widget *w, int *action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 233 {
 234     GList *file_list;
 235     size_t len;
 236     history_descriptor_t hd;
 237 
 238     file_list = file_history_list_read ();
 239     if (file_list == NULL)
 240         return NULL;
 241 
 242     len = g_list_length (file_list);
 243 
 244     file_list = g_list_last (file_list);
 245 
 246     history_descriptor_init (&hd, w->rect.y, w->rect.x, file_list, 0);
 247     // redefine list-specific functions
 248     hd.create = file_history_create_item;
 249     hd.release = file_history_release_item;
 250     hd.free = file_history_free_item;
 251 
 252     history_show (&hd);
 253 
 254     hd.list = g_list_first (hd.list);
 255 
 256     // Has history cleaned up or not?
 257     if (len != g_list_length (hd.list))
 258     {
 259         hd.list = g_list_reverse (hd.list);
 260         file_history_list_write (hd.list);
 261     }
 262 
 263     g_list_free_full (hd.list, (GDestroyNotify) file_history_free_item);
 264 
 265     *action = hd.action;
 266 
 267     return hd.text;
 268 }
 269 
 270 /* --------------------------------------------------------------------------------------------- */

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