1 /*
2 Configure module for the Midnight Commander
3
4 Copyright (C) 1994-2025
5 Free Software Foundation, Inc.
6
7 Authors:
8 Radek Doulik, 1994, 1995
9 Miguel de Icaza, 1994, 1995
10 Jakub Jelinek, 1995
11 Andrej Borsenkow, 1996
12 Norbert Warmuth, 1997
13 Andrew Borodin <aborodin@vmail.ru>, 2009-2023
14
15 This file is part of the Midnight Commander.
16
17 The Midnight Commander is free software: you can redistribute it
18 and/or modify it under the terms of the GNU General Public License as
19 published by the Free Software Foundation, either version 3 of the License,
20 or (at your option) any later version.
21
22 The Midnight Commander is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
31 /** \file history.c
32 * \brief Source: save and load history
33 */
34
35 #include <config.h>
36
37 #include <stdlib.h>
38 #include <sys/types.h>
39
40 #include "lib/global.h"
41
42 #include "lib/fileloc.h" // MC_HISTORY_FILE
43 #include "lib/strutil.h"
44 #include "lib/util.h" // list_append_unique
45
46 #include "lib/mcconfig.h"
47
48 /*** global variables ****************************************************************************/
49
50 /* how much history items are used */
51 int num_history_items_recorded = 60;
52
53 /*** file scope macro definitions ****************************************************************/
54
55 /*** file scope type declarations ****************************************************************/
56
57 /*** file scope variables ************************************************************************/
58
59 /* --------------------------------------------------------------------------------------------- */
60 /*** file scope functions ************************************************************************/
61 /* --------------------------------------------------------------------------------------------- */
62
63 /* --------------------------------------------------------------------------------------------- */
64 /*** public functions ****************************************************************************/
65 /* --------------------------------------------------------------------------------------------- */
66
67 /**
68 * Load the history from the ${XDG_DATA_HOME}/mc/history file.
69 * It is called with the widgets history name and returns the GList list.
70 */
71
72 GList *
73 mc_config_history_get (const char *name)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
74 {
75 GList *hist = NULL;
76 char *profile;
77 mc_config_t *cfg;
78
79 if (num_history_items_recorded == 0) // this is how to disable
80 return NULL;
81 if (name == NULL || *name == '\0')
82 return NULL;
83
84 profile = mc_config_get_full_path (MC_HISTORY_FILE);
85 cfg = mc_config_init (profile, TRUE);
86
87 hist = mc_config_history_load (cfg, name);
88
89 mc_config_deinit (cfg);
90 g_free (profile);
91
92 return hist;
93 }
94
95 /* --------------------------------------------------------------------------------------------- */
96
97 /**
98 * Get the recent item of a history from the ${XDG_DATA_HOME}/mc/history file.
99 *
100 * TODO: get rid of load the entire history to get the only top item.
101 */
102
103 char *
104 mc_config_history_get_recent_item (const char *name)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
105 {
106 GList *history;
107 char *item = NULL;
108
109 history = mc_config_history_get (name);
110 if (history != NULL)
111 {
112 // FIXME: can history->data be NULL?
113 item = (char *) history->data;
114 history->data = NULL;
115 history = g_list_first (history);
116 g_list_free_full (history, g_free);
117 }
118
119 return item;
120 }
121
122 /* --------------------------------------------------------------------------------------------- */
123
124 /**
125 * Load history from the mc_config
126 */
127 GList *
128 mc_config_history_load (mc_config_t *cfg, const char *name)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
129 {
130 size_t i;
131 GList *hist = NULL;
132 char **keys;
133 size_t keys_num = 0;
134 GIConv conv = INVALID_CONV;
135 GString *buffer;
136
137 if (name == NULL || *name == '\0')
138 return NULL;
139
140 // get number of keys
141 keys = mc_config_get_keys (cfg, name, &keys_num);
142 g_strfreev (keys);
143
144 /* create charset conversion handler to convert strings
145 from utf-8 to system codepage */
146 if (!mc_global.utf8_display)
147 conv = str_crt_conv_from ("UTF-8");
148
149 buffer = g_string_sized_new (64);
150
151 for (i = 0; i < keys_num; i++)
152 {
153 char key[BUF_TINY];
154 char *this_entry;
155
156 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
157 this_entry = mc_config_get_string_raw (cfg, name, key, "");
158
159 if (this_entry == NULL)
160 continue;
161
162 if (conv == INVALID_CONV)
163 hist = list_append_unique (hist, this_entry);
164 else
165 {
166 g_string_set_size (buffer, 0);
167 if (str_convert (conv, this_entry, buffer) == ESTR_FAILURE)
168 hist = list_append_unique (hist, this_entry);
169 else
170 {
171 hist = list_append_unique (hist, g_strndup (buffer->str, buffer->len));
172 g_free (this_entry);
173 }
174 }
175 }
176
177 g_string_free (buffer, TRUE);
178 if (conv != INVALID_CONV)
179 str_close_conv (conv);
180
181 // return pointer to the last entry in the list
182 return g_list_last (hist);
183 }
184
185 /* --------------------------------------------------------------------------------------------- */
186
187 /**
188 * Save history to the mc_config, but don't save config to file
189 *
190 * @param cfg mc_config_t object where history is stored in
191 * @param name history name
192 * @param h history. If NULL, then the @name group will be removed from @cfg
193 */
194 void
195 mc_config_history_save (mc_config_t *cfg, const char *name, GList *h)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
196 {
197 GIConv conv = INVALID_CONV;
198 GString *buffer;
199 int i;
200
201 if (name == NULL || *name == '\0')
202 return;
203
204 mc_config_del_group (cfg, name);
205
206 if (h == NULL)
207 return;
208
209 // go to end of list
210 h = g_list_last (h);
211
212 // go back 60 places
213 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
214 h = g_list_previous (h);
215
216 /* create charset conversion handler to convert strings
217 from system codepage to UTF-8 */
218 if (!mc_global.utf8_display)
219 conv = str_crt_conv_to ("UTF-8");
220
221 buffer = g_string_sized_new (64);
222
223 // dump history into profile
224 for (i = 0; h != NULL; h = g_list_next (h))
225 {
226 char key[BUF_TINY];
227 char *text = (char *) h->data;
228
229 // We shouldn't have null entries, but let's be sure
230 if (text == NULL)
231 continue;
232
233 g_snprintf (key, sizeof (key), "%d", i++);
234
235 if (conv == INVALID_CONV)
236 mc_config_set_string_raw (cfg, name, key, text);
237 else
238 {
239 g_string_set_size (buffer, 0);
240 if (str_convert (conv, text, buffer) == ESTR_FAILURE)
241 mc_config_set_string_raw (cfg, name, key, text);
242 else
243 mc_config_set_string_raw (cfg, name, key, buffer->str);
244 }
245 }
246
247 g_string_free (buffer, TRUE);
248 if (conv != INVALID_CONV)
249 str_close_conv (conv);
250 }
251
252 /* --------------------------------------------------------------------------------------------- */