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-2026
   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     char *file_name;
  89 
  90     file_name = str_escape (fhd->file_name, -1, "", TRUE);
  91     g_string_append (s, file_name);
  92     g_free (file_name);
  93 
  94     if (fhd->file_pos != NULL)
  95     {
  96         g_string_append_c (s, *SEPARATOR);
  97         g_string_append (s, fhd->file_pos);
  98     }
  99 }
 100 
 101 /* --------------------------------------------------------------------------------------------- */
 102 
 103 static GList *
 104 file_history_list_read (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 105 {
 106     char *fn;
 107     FILE *f;
 108     char buf[MC_MAXPATHLEN + 100];
 109     GList *file_list = NULL;
 110 
 111     // open file with positions
 112     fn = mc_config_get_full_path (MC_FILEPOS_FILE);
 113     if (fn == NULL)
 114         return NULL;
 115 
 116     f = fopen (fn, "r");
 117     g_free (fn);
 118     if (f == NULL)
 119         return NULL;
 120 
 121     while (fgets (buf, sizeof (buf), f) != NULL)
 122         file_history_parse_entry (buf, &file_list);
 123 
 124     fclose (f);
 125 
 126     return file_list;
 127 }
 128 
 129 /* --------------------------------------------------------------------------------------------- */
 130 
 131 static void
 132 file_history_list_write (const GList *file_list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 133 {
 134     char *fn;
 135     FILE *f;
 136     gboolean write_error = FALSE;
 137 
 138     fn = mc_config_get_full_path (MC_FILEPOS_FILE);
 139     if (fn == NULL)
 140         return;
 141 
 142     mc_util_make_backup_if_possible (fn, TMP_SUFFIX);
 143 
 144     f = fopen (fn, "w");
 145     if (f != NULL)
 146     {
 147         GString *s;
 148 
 149         s = g_string_sized_new (128);
 150 
 151         for (; file_list != NULL && !write_error; file_list = g_list_next (file_list))
 152         {
 153             file_history_write_entry (file_list->data, s);
 154             write_error = (fprintf (f, "%s\n", s->str) < 0);
 155             g_string_truncate (s, 0);
 156         }
 157 
 158         g_string_free (s, TRUE);
 159 
 160         fclose (f);
 161     }
 162 
 163     if (write_error)
 164         mc_util_restore_from_backup_if_possible (fn, TMP_SUFFIX);
 165     else
 166         mc_util_unlink_backup_if_possible (fn, TMP_SUFFIX);
 167 
 168     g_free (fn);
 169 }
 170 
 171 /* --------------------------------------------------------------------------------------------- */
 172 
 173 static void
 174 file_history_create_item (history_descriptor_t *hd, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 175 {
 176     file_history_data_t *fhd = (file_history_data_t *) data;
 177     size_t width;
 178 
 179     width = str_term_width1 (fhd->file_name);
 180     hd->max_width = MAX (width, hd->max_width);
 181 
 182     listbox_add_item (hd->listbox, LISTBOX_APPEND_AT_END, 0, fhd->file_name, fhd->file_pos, TRUE);
 183     // fhd->file_pos is not copied, NULLize it to prevent double free
 184     fhd->file_pos = NULL;
 185 }
 186 
 187 /* --------------------------------------------------------------------------------------------- */
 188 
 189 static void *
 190 file_history_release_item (history_descriptor_t *hd, WLEntry *le)
     /* [previous][next][first][last][top][bottom][index][help]  */
 191 {
 192     file_history_data_t *fhd;
 193 
 194     (void) hd;
 195 
 196     fhd = g_new (file_history_data_t, 1);
 197     fhd->file_name = le->text;
 198     le->text = NULL;
 199     fhd->file_pos = (char *) le->data;
 200     le->data = NULL;
 201 
 202     return fhd;
 203 }
 204 
 205 /* --------------------------------------------------------------------------------------------- */
 206 
 207 static void
 208 file_history_free_item (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 209 {
 210     file_history_data_t *fhd = (file_history_data_t *) data;
 211 
 212     g_free (fhd->file_name);
 213     g_free (fhd->file_pos);
 214     g_free (fhd);
 215 }
 216 
 217 /* --------------------------------------------------------------------------------------------- */
 218 /*** public functions ****************************************************************************/
 219 /* --------------------------------------------------------------------------------------------- */
 220 
 221 /**
 222  * Show file history and return the selected file
 223  *
 224  * @param w widget used for positioning of history window
 225  * @param action to do with file (edit, view, etc)
 226  *
 227  * @return name of selected file, A newly allocated string.
 228  */
 229 char *
 230 show_file_history (const Widget *w, int *action)
     /* [previous][next][first][last][top][bottom][index][help]  */
 231 {
 232     GList *file_list;
 233     size_t len;
 234     history_descriptor_t hd;
 235 
 236     file_list = file_history_list_read ();
 237     if (file_list == NULL)
 238         return NULL;
 239 
 240     len = g_list_length (file_list);
 241 
 242     file_list = g_list_last (file_list);
 243 
 244     history_descriptor_init (&hd, w->rect.y, w->rect.x, file_list, 0);
 245     // redefine list-specific functions
 246     hd.create = file_history_create_item;
 247     hd.release = file_history_release_item;
 248     hd.free = file_history_free_item;
 249 
 250     history_show (&hd);
 251 
 252     hd.list = g_list_first (hd.list);
 253 
 254     // Has history cleaned up or not?
 255     if (len != g_list_length (hd.list))
 256     {
 257         hd.list = g_list_reverse (hd.list);
 258         file_history_list_write (hd.list);
 259     }
 260 
 261     g_list_free_full (hd.list, (GDestroyNotify) file_history_free_item);
 262 
 263     *action = hd.action;
 264 
 265     return hd.text;
 266 }
 267 
 268 /* --------------------------------------------------------------------------------------------- */

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