rofi 2.1.0
history.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2013-2023 Qball Cow <qball@gmpclient.org>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 */
27#include "config.h"
28
29#include "history.h"
30#include "rofi.h"
31#include "settings.h"
32#include <errno.h>
33#include <glib.h>
34#include <glib/gstdio.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <sys/types.h>
39#include <unistd.h>
40
44typedef struct __element {
46 long int index;
48 char *name;
50
51static int __element_sort_func(const void *ea, const void *eb,
52 void *data __attribute__((unused))) {
53 _element *a = *(_element **)ea;
54 _element *b = *(_element **)eb;
55 return b->index - a->index;
56}
57
58static void __history_write_element_list(FILE *fd, _element **list,
59 unsigned int length) {
60 if (list == NULL || length == 0) {
61 return;
62 }
63 // Sort the list before writing out.
64 g_qsort_with_data(list, length, sizeof(_element *), __element_sort_func,
65 NULL);
66
67 // Get minimum index.
68 int min_value = list[length - 1]->index;
69
70 // Set the max length of the list.
71 length =
72 (length > config.max_history_size) ? config.max_history_size : length;
73
74 // Write out entries.
75 for (unsigned int iter = 0; iter < length; iter++) {
76 fprintf(fd, "%ld %s\n", list[iter]->index - min_value, list[iter]->name);
77 }
78}
79
80static char **__history_get_element_list_fields(FILE *fd,
81 unsigned int *length) {
82 unsigned int real_length = 0;
83 char **retv = NULL;
84 ;
85 if (length == NULL) {
86 return NULL;
87 }
88 *length = 0;
89
90 if (fd == NULL) {
91 return NULL;
92 }
93 char *buffer = NULL;
94 size_t buffer_length = 0;
95 ssize_t l = 0;
96 while ((l = getline(&buffer, &buffer_length, fd)) > 0) {
97 // Jump to the first space.
98 const char *start = strchr(buffer, ' ');
99 // not found, skip.
100 if (start == NULL) {
101 continue;
102 }
103 start++;
104 // remove trailing \n
105 buffer[l - 1] = '\0';
106 if (real_length < (*length + 2)) {
107 real_length += 15;
108 // Resize and check.
109 retv = g_realloc(retv, (real_length) * sizeof(char *));
110 }
111 // Parse the number of times.
112 retv[(*length)] = g_strndup(start, l - 1 - (start - buffer));
113 // Force trailing '\0'
114 retv[(*length) + 1] = NULL;
115
116 (*length)++;
117 }
118 // Don't use buffer_length to check, as static
119 // code analysis fails on this.
120 if (buffer != NULL) {
121 g_free(buffer);
122 }
123 return retv;
124}
125
126static _element **__history_get_element_list(FILE *fd, unsigned int *length) {
127 unsigned int real_length = 0;
128 _element **retv = NULL;
129
130 if (length == NULL) {
131 return NULL;
132 }
133 *length = 0;
134
135 if (fd == NULL) {
136 return NULL;
137 }
138 char *buffer = NULL;
139 size_t buffer_length = 0;
140 ssize_t l = 0;
141 while ((l = getline(&buffer, &buffer_length, fd)) > 0) {
142 char *start = NULL;
143 // Skip empty lines.
144 if (l <= 1) {
145 continue;
146 }
147
148 long int index = strtol(buffer, &start, 10);
149 if (start == buffer || *start == '\0') {
150 continue;
151 }
152 start++;
153 if ((l - (start - buffer)) < 2) {
154 continue;
155 }
156 if (real_length < (*length + 2)) {
157 real_length += 15;
158 // Resize and check.
159 retv = g_realloc(retv, (real_length) * sizeof(_element *));
160 }
161
162 retv[(*length)] = g_malloc(sizeof(_element));
163
164 // remove trailing \n
165 buffer[l - 1] = '\0';
166 // Parse the number of times.
167 retv[(*length)]->index = index;
168 retv[(*length)]->name = g_strndup(start, l - 1 - (start - buffer));
169 // Force trailing '\0'
170 retv[(*length) + 1] = NULL;
171
172 (*length)++;
173 }
174 if (buffer != NULL) {
175 free(buffer);
176 buffer = NULL;
177 }
178 return retv;
179}
180
181void history_set(const char *filename, const char *entry) {
182 if (config.disable_history) {
183 return;
184 }
185
186 // Check if program should be ignored
187 // TODO: This code does not! belong here.
188 for (char *checked_prefix = strtok(config.ignored_prefixes, ";");
189 checked_prefix != NULL; checked_prefix = strtok(NULL, ";")) {
190 // For each ignored prefix
191
192 while (g_unichar_isspace(g_utf8_get_char(checked_prefix))) {
193 checked_prefix = g_utf8_next_char(
194 checked_prefix); // Some users will probably want "; " as their
195 // separator for aesthetics.
196 }
197
198 if (g_str_has_prefix(entry, checked_prefix)) {
199 return;
200 }
201 }
202
203 int found = 0;
204 unsigned int curr = 0;
205 unsigned int length = 0;
206 _element **list = NULL;
207 // Open file for reading and writing.
208 FILE *fd = g_fopen(filename, "r");
209 if (fd != NULL) {
210 // Get list.
211 list = __history_get_element_list(fd, &length);
212 // Close file, if fails let user know on stderr.
213 if (fclose(fd) != 0) {
214 g_warning("Failed to close history file: %s", g_strerror(errno));
215 }
216 }
217 // Look if the entry exists.
218 for (unsigned int iter = 0; !found && iter < length; iter++) {
219 if (strcmp(list[iter]->name, entry) == 0) {
220 curr = iter;
221 found = 1;
222 }
223 }
224
225 if (found) {
226 // If exists, increment list index number
227 list[curr]->index++;
228 } else {
229 // If not exists, add it.
230 // Increase list by one
231 list = g_realloc(list, (length + 2) * sizeof(_element *));
232 list[length] = g_malloc(sizeof(_element));
233 // Copy name
234 if (list[length] != NULL) {
235 list[length]->name = g_strdup(entry);
236 // set # hits
237 list[length]->index = 1;
238
239 length++;
240 list[length] = NULL;
241 }
242 }
243
244 fd = fopen(filename, "w");
245 if (fd == NULL) {
246 g_warning("Failed to open file: %s", g_strerror(errno));
247 } else {
248 // Write list.
249 __history_write_element_list(fd, list, length);
250 // Close file, if fails let user know on stderr.
251 if (fclose(fd) != 0) {
252 g_warning("Failed to close history file: %s", g_strerror(errno));
253 }
254 }
255 // Free the list.
256 for (unsigned int iter = 0; iter < length; iter++) {
257 g_free(list[iter]->name);
258 g_free(list[iter]);
259 }
260 g_free(list);
261}
262
263void history_remove(const char *filename, const char *entry) {
264 if (config.disable_history) {
265 return;
266 }
267 _element **list = NULL;
268 int found = 0;
269 unsigned int curr = 0;
270 unsigned int length = 0;
271 // Open file for reading and writing.
272 FILE *fd = g_fopen(filename, "r");
273 if (fd == NULL) {
274 g_warning("Failed to open file: %s", g_strerror(errno));
275 return;
276 }
277 // Get list.
278 list = __history_get_element_list(fd, &length);
279
280 // Close file, if fails let user know on stderr.
281 if (fclose(fd) != 0) {
282 g_warning("Failed to close history file: %s", g_strerror(errno));
283 }
284 // Find entry.
285 for (unsigned int iter = 0; !found && iter < length; iter++) {
286 if (strcmp(list[iter]->name, entry) == 0) {
287 curr = iter;
288 found = 1;
289 }
290 }
291
292 // If found, remove it and write out new file.
293 if (found) {
294 // Remove the entry.
295 g_free(list[curr]->name);
296 g_free(list[curr]);
297 // Swap last to here (if list is size 1, we just swap empty sets).
298 list[curr] = list[length - 1];
299 // Empty last.
300 list[length - 1] = NULL;
301 length--;
302
303 fd = g_fopen(filename, "w");
304 // Clear list.
305 if (fd != NULL) {
306 // Write list.
307 __history_write_element_list(fd, list, length);
308 // Close file, if fails let user know on stderr.
309 if (fclose(fd) != 0) {
310 g_warning("Failed to close history file: %s", g_strerror(errno));
311 }
312 } else {
313 g_warning("Failed to open file: %s", g_strerror(errno));
314 }
315 }
316
317 // Free the list.
318 for (unsigned int iter = 0; iter < length; iter++) {
319 g_free(list[iter]->name);
320 g_free(list[iter]);
321 }
322 if (list != NULL) {
323 g_free(list);
324 }
325}
326
327char **history_get_list(const char *filename, unsigned int *length) {
328 *length = 0;
329
330 if (config.disable_history) {
331 return NULL;
332 }
333 char **retv = NULL;
334 // Open file.
335 FILE *fd = g_fopen(filename, "r");
336 if (fd == NULL) {
337 // File that does not exists is not an error, so ignore it.
338 // Everything else? panic.
339 if (errno != ENOENT) {
340 g_warning("Failed to open file: %s", g_strerror(errno));
341 }
342 return NULL;
343 }
344 // Get list.
345 retv = __history_get_element_list_fields(fd, length);
346
347 // Close file, if fails let user know on stderr.
348 if (fclose(fd) != 0) {
349 g_warning("Failed to close history file: %s", g_strerror(errno));
350 }
351 return retv;
352}
void history_set(const char *filename, const char *entry)
Definition history.c:181
void history_remove(const char *filename, const char *entry)
Definition history.c:263
char ** history_get_list(const char *filename, unsigned int *length)
Definition history.c:327
struct __element _element
static char ** __history_get_element_list_fields(FILE *fd, unsigned int *length)
Definition history.c:80
static void __history_write_element_list(FILE *fd, _element **list, unsigned int length)
Definition history.c:58
static _element ** __history_get_element_list(FILE *fd, unsigned int *length)
Definition history.c:126
static int __element_sort_func(const void *ea, const void *eb, void *data __attribute__((unused)))
Definition history.c:51
Settings config
char * name
Definition history.c:48
long int index
Definition history.c:46