rofi  1.7.0
history.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2021 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 
28 #include "history.h"
29 #include "rofi.h"
30 #include "settings.h"
31 #include <errno.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39 
43 typedef struct __element {
45  long int index;
47  char *name;
49 
50 static int __element_sort_func(const void *ea, const void *eb,
51  void *data __attribute__((unused))) {
52  _element *a = *(_element **)ea;
53  _element *b = *(_element **)eb;
54  return b->index - a->index;
55 }
56 
57 static void __history_write_element_list(FILE *fd, _element **list,
58  unsigned int length) {
59  if (list == NULL || length == 0) {
60  return;
61  }
62  // Sort the list before writing out.
63  g_qsort_with_data(list, length, sizeof(_element *), __element_sort_func,
64  NULL);
65 
66  // Get minimum index.
67  int min_value = list[length - 1]->index;
68 
69  // Set the max length of the list.
70  length =
71  (length > config.max_history_size) ? config.max_history_size : length;
72 
73  // Write out entries.
74  for (unsigned int iter = 0; iter < length; iter++) {
75  fprintf(fd, "%ld %s\n", list[iter]->index - min_value, list[iter]->name);
76  }
77 }
78 
79 static char **__history_get_element_list_fields(FILE *fd,
80  unsigned int *length) {
81  unsigned int real_length = 0;
82  char **retv = NULL;
83  ;
84  if (length == NULL) {
85  return NULL;
86  }
87  *length = 0;
88 
89  if (fd == NULL) {
90  return NULL;
91  }
92  char *buffer = NULL;
93  size_t buffer_length = 0;
94  ssize_t l = 0;
95  while ((l = getline(&buffer, &buffer_length, fd)) > 0) {
96  // Jump to the first space.
97  const char *start = strchr(buffer, ' ');
98  // not found, skip.
99  if (start == NULL) {
100  continue;
101  }
102  start++;
103  // remove trailing \n
104  buffer[l - 1] = '\0';
105  if (real_length < (*length + 2)) {
106  real_length += 15;
107  // Resize and check.
108  retv = g_realloc(retv, (real_length) * sizeof(char *));
109  }
110  // Parse the number of times.
111  retv[(*length)] = g_strndup(start, l - 1 - (start - buffer));
112  // Force trailing '\0'
113  retv[(*length) + 1] = NULL;
114 
115  (*length)++;
116  }
117  if (buffer_length > 0) {
118  g_free(buffer);
119  }
120  return retv;
121 }
122 
123 static _element **__history_get_element_list(FILE *fd, unsigned int *length) {
124  unsigned int real_length = 0;
125  _element **retv = NULL;
126 
127  if (length == NULL) {
128  return NULL;
129  }
130  *length = 0;
131 
132  if (fd == NULL) {
133  return NULL;
134  }
135  char *buffer = NULL;
136  size_t buffer_length = 0;
137  ssize_t l = 0;
138  while ((l = getline(&buffer, &buffer_length, fd)) > 0) {
139  char *start = NULL;
140  // Skip empty lines.
141  if (l <= 1) {
142  continue;
143  }
144 
145  long int index = strtol(buffer, &start, 10);
146  if (start == buffer || *start == '\0') {
147  continue;
148  }
149  start++;
150  if ((l - (start - buffer)) < 2) {
151  continue;
152  }
153  if (real_length < (*length + 2)) {
154  real_length += 15;
155  // Resize and check.
156  retv = g_realloc(retv, (real_length) * sizeof(_element *));
157  }
158 
159  retv[(*length)] = g_malloc(sizeof(_element));
160 
161  // remove trailing \n
162  buffer[l - 1] = '\0';
163  // Parse the number of times.
164  retv[(*length)]->index = index;
165  retv[(*length)]->name = g_strndup(start, l - 1 - (start - buffer));
166  // Force trailing '\0'
167  retv[(*length) + 1] = NULL;
168 
169  (*length)++;
170  }
171  if (buffer != NULL) {
172  free(buffer);
173  buffer = NULL;
174  }
175  return retv;
176 }
177 
178 void history_set(const char *filename, const char *entry) {
179  if (config.disable_history) {
180  return;
181  }
182 
183  // Check if program should be ignored
184  for (char *checked_prefix = strtok(config.ignored_prefixes, ";");
185  checked_prefix != NULL; checked_prefix = strtok(NULL, ";")) {
186  // For each ignored prefix
187 
188  while (g_unichar_isspace(g_utf8_get_char(checked_prefix))) {
189  checked_prefix = g_utf8_next_char(
190  checked_prefix); // Some users will probably want "; " as their
191  // separator for aesthetics.
192  }
193 
194  if (g_str_has_prefix(entry, checked_prefix)) {
195  return;
196  }
197  }
198 
199  int found = 0;
200  unsigned int curr = 0;
201  unsigned int length = 0;
202  _element **list = NULL;
203  // Open file for reading and writing.
204  FILE *fd = g_fopen(filename, "r");
205  if (fd != NULL) {
206  // Get list.
207  list = __history_get_element_list(fd, &length);
208  // Close file, if fails let user know on stderr.
209  if (fclose(fd) != 0) {
210  g_warning("Failed to close history file: %s", g_strerror(errno));
211  }
212  }
213  // Look if the entry exists.
214  for (unsigned int iter = 0; !found && iter < length; iter++) {
215  if (strcmp(list[iter]->name, entry) == 0) {
216  curr = iter;
217  found = 1;
218  }
219  }
220 
221  if (found) {
222  // If exists, increment list index number
223  list[curr]->index++;
224  } else {
225  // If not exists, add it.
226  // Increase list by one
227  list = g_realloc(list, (length + 2) * sizeof(_element *));
228  list[length] = g_malloc(sizeof(_element));
229  // Copy name
230  if (list[length] != NULL) {
231  list[length]->name = g_strdup(entry);
232  // set # hits
233  list[length]->index = 1;
234 
235  length++;
236  list[length] = NULL;
237  }
238  }
239 
240  fd = fopen(filename, "w");
241  if (fd == NULL) {
242  g_warning("Failed to open file: %s", g_strerror(errno));
243  } else {
244  // Write list.
245  __history_write_element_list(fd, list, length);
246  // Close file, if fails let user know on stderr.
247  if (fclose(fd) != 0) {
248  g_warning("Failed to close history file: %s", g_strerror(errno));
249  }
250  }
251  // Free the list.
252  for (unsigned int iter = 0; iter < length; iter++) {
253  g_free(list[iter]->name);
254  g_free(list[iter]);
255  }
256  g_free(list);
257 }
258 
259 void history_remove(const char *filename, const char *entry) {
260  if (config.disable_history) {
261  return;
262  }
263  _element **list = NULL;
264  int found = 0;
265  unsigned int curr = 0;
266  unsigned int length = 0;
267  // Open file for reading and writing.
268  FILE *fd = g_fopen(filename, "r");
269  if (fd == NULL) {
270  g_warning("Failed to open file: %s", g_strerror(errno));
271  return;
272  }
273  // Get list.
274  list = __history_get_element_list(fd, &length);
275 
276  // Close file, if fails let user know on stderr.
277  if (fclose(fd) != 0) {
278  g_warning("Failed to close history file: %s", g_strerror(errno));
279  }
280  // Find entry.
281  for (unsigned int iter = 0; !found && iter < length; iter++) {
282  if (strcmp(list[iter]->name, entry) == 0) {
283  curr = iter;
284  found = 1;
285  }
286  }
287 
288  // If found, remove it and write out new file.
289  if (found) {
290  // Remove the entry.
291  g_free(list[curr]->name);
292  g_free(list[curr]);
293  // Swap last to here (if list is size 1, we just swap empty sets).
294  list[curr] = list[length - 1];
295  // Empty last.
296  list[length - 1] = NULL;
297  length--;
298 
299  fd = g_fopen(filename, "w");
300  // Clear list.
301  if (fd != NULL) {
302  // Write list.
303  __history_write_element_list(fd, list, length);
304  // Close file, if fails let user know on stderr.
305  if (fclose(fd) != 0) {
306  g_warning("Failed to close history file: %s", g_strerror(errno));
307  }
308  } else {
309  g_warning("Failed to open file: %s", g_strerror(errno));
310  }
311  }
312 
313  // Free the list.
314  for (unsigned int iter = 0; iter < length; iter++) {
315  g_free(list[iter]->name);
316  g_free(list[iter]);
317  }
318  if (list != NULL) {
319  g_free(list);
320  }
321 }
322 
323 char **history_get_list(const char *filename, unsigned int *length) {
324  *length = 0;
325 
326  if (config.disable_history) {
327  return NULL;
328  }
329  char **retv = NULL;
330  // Open file.
331  FILE *fd = g_fopen(filename, "r");
332  if (fd == NULL) {
333  // File that does not exists is not an error, so ignore it.
334  // Everything else? panic.
335  if (errno != ENOENT) {
336  g_warning("Failed to open file: %s", g_strerror(errno));
337  }
338  return NULL;
339  }
340  // Get list.
341  retv = __history_get_element_list_fields(fd, length);
342 
343  // Close file, if fails let user know on stderr.
344  if (fclose(fd) != 0) {
345  g_warning("Failed to close history file: %s", g_strerror(errno));
346  }
347  return retv;
348 }
void history_set(const char *filename, const char *entry)
Definition: history.c:178
void history_remove(const char *filename, const char *entry)
Definition: history.c:259
char ** history_get_list(const char *filename, unsigned int *length)
Definition: history.c:323
struct __element _element
static void __history_write_element_list(FILE *fd, _element **list, unsigned int length)
Definition: history.c:57
static _element ** __history_get_element_list(FILE *fd, unsigned int *length)
Definition: history.c:123
static char ** __history_get_element_list_fields(FILE *fd, unsigned int *length)
Definition: history.c:79
static int __element_sort_func(const void *ea, const void *eb, void *data __attribute__((unused)))
Definition: history.c:50
Settings config
unsigned int disable_history
Definition: settings.h:92
char * ignored_prefixes
Definition: settings.h:94
unsigned int max_history_size
Definition: settings.h:155
char * name
Definition: history.c:47
long int index
Definition: history.c:45