rofi  1.5.4
history.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2017 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 <config.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <errno.h>
35 #include <glib.h>
36 #include <glib/gstdio.h>
37 #include "rofi.h"
38 #include "history.h"
39 #include "settings.h"
40 
44 typedef struct __element
45 {
47  long int index;
49  char *name;
51 
52 static int __element_sort_func ( const void *ea, const void *eb, void *data __attribute__( ( unused ) ) )
53 {
54  _element *a = *(_element * *) ea;
55  _element *b = *(_element * *) eb;
56  return b->index - a->index;
57 }
58 
59 static void __history_write_element_list ( FILE *fd, _element **list, unsigned int length )
60 {
61  if ( list == NULL || length == 0 ) {
62  return;
63  }
64  // Sort the list before writing out.
65  g_qsort_with_data ( list, length, sizeof ( _element* ), __element_sort_func, 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 = ( 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, unsigned int *length )
80 {
81  unsigned int real_length = 0;
82  char **retv = NULL;;
83  if ( length == NULL ) {
84  return NULL;
85  }
86  *length = 0;
87 
88  if ( fd == NULL ) {
89  return NULL;
90  }
91  char *buffer = NULL;
92  size_t buffer_length = 0;
93  ssize_t l = 0;
94  while ( ( l = getline ( &buffer, &buffer_length, fd ) ) > 0 ) {
95  // Jump to the first space.
96  const char *start = strchr ( buffer, ' ' );
97  // not found, skip.
98  if ( start == NULL ) {
99  continue;
100  }
101  start++;
102  // remove trailing \n
103  buffer[l - 1] = '\0';
104  if ( real_length < ( *length + 2 ) ) {
105  real_length += 15;
106  // Resize and check.
107  retv = g_realloc ( retv, ( real_length ) * sizeof ( char* ) );
108  }
109  // Parse the number of times.
110  retv[( *length )] = g_strndup ( start, l - 1 - ( start - buffer ) );
111  // Force trailing '\0'
112  retv[( *length ) + 1] = NULL;
113 
114  ( *length )++;
115  }
116  if ( buffer_length > 0 ) {
117  g_free ( buffer );
118  }
119  return retv;
120 }
121 
122 static _element ** __history_get_element_list ( FILE *fd, unsigned int *length )
123 {
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 {
180  if ( config.disable_history ) {
181  return;
182  }
183 
184  // Check if program should be ignored
185  for ( char *checked_prefix = strtok ( config.ignored_prefixes, ";" ); 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 ( checked_prefix ); // Some users will probably want "; " as their separator for aesthetics.
190  }
191 
192  if ( g_str_has_prefix ( entry, checked_prefix ) ) {
193  return;
194  }
195  }
196 
197  int found = 0;
198  unsigned int curr = 0;
199  unsigned int length = 0;
200  _element **list = NULL;
201  // Open file for reading and writing.
202  FILE *fd = g_fopen ( filename, "r" );
203  if ( fd != NULL ) {
204  // Get list.
205  list = __history_get_element_list ( fd, &length );
206  // Close file, if fails let user know on stderr.
207  if ( fclose ( fd ) != 0 ) {
208  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
209  }
210  }
211  // Look if the entry exists.
212  for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
213  if ( strcmp ( list[iter]->name, entry ) == 0 ) {
214  curr = iter;
215  found = 1;
216  }
217  }
218 
219  if ( found ) {
220  // If exists, increment list index number
221  list[curr]->index++;
222  }
223  else{
224  // If not exists, add it.
225  // Increase list by one
226  list = g_realloc ( list, ( length + 2 ) * sizeof ( _element* ) );
227  list[length] = g_malloc ( sizeof ( _element ) );
228  // Copy name
229  if ( list[length] != NULL ) {
230  list[length]->name = g_strdup ( entry );
231  // set # hits
232  list[length]->index = 1;
233 
234  length++;
235  list[length] = NULL;
236  }
237  }
238 
239  fd = fopen ( filename, "w" );
240  if ( fd == NULL ) {
241  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
242  }
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 {
261  if ( config.disable_history ) {
262  return;
263  }
264  _element ** list = NULL;
265  int found = 0;
266  unsigned int curr = 0;
267  unsigned int length = 0;
268  // Open file for reading and writing.
269  FILE *fd = g_fopen ( filename, "r" );
270  if ( fd == NULL ) {
271  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
272  return;
273  }
274  // Get list.
275  list = __history_get_element_list ( fd, &length );
276 
277  // Close file, if fails let user know on stderr.
278  if ( fclose ( fd ) != 0 ) {
279  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
280  }
281  // Find entry.
282  for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
283  if ( strcmp ( list[iter]->name, entry ) == 0 ) {
284  curr = iter;
285  found = 1;
286  }
287  }
288 
289  // If found, remove it and write out new file.
290  if ( found ) {
291  // Remove the entry.
292  g_free ( list[curr]->name );
293  g_free ( list[curr] );
294  // Swap last to here (if list is size 1, we just swap empty sets).
295  list[curr] = list[length - 1];
296  // Empty last.
297  list[length - 1] = NULL;
298  length--;
299 
300  fd = g_fopen ( filename, "w" );
301  // Clear list.
302  if ( fd != NULL ) {
303  // Write list.
304  __history_write_element_list ( fd, list, length );
305  // Close file, if fails let user know on stderr.
306  if ( fclose ( fd ) != 0 ) {
307  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
308  }
309  }
310  else{
311  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
312  }
313  }
314 
315  // Free the list.
316  for ( unsigned int iter = 0; iter < length; iter++ ) {
317  g_free ( list[iter]->name );
318  g_free ( list[iter] );
319  }
320  if ( list != NULL ) {
321  g_free ( list );
322  }
323 }
324 
325 char ** history_get_list ( const char *filename, unsigned int *length )
326 {
327  *length = 0;
328 
329  if ( config.disable_history ) {
330  return NULL;
331  }
332  char **retv = NULL;
333  // Open file.
334  FILE *fd = g_fopen ( filename, "r" );
335  if ( fd == NULL ) {
336  // File that does not exists is not an error, so ignore it.
337  // Everything else? panic.
338  if ( errno != ENOENT ) {
339  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
340  }
341  return NULL;
342  }
343  // Get list.
344  retv = __history_get_element_list_fields ( fd, length );
345 
346  // Close file, if fails let user know on stderr.
347  if ( fclose ( fd ) != 0 ) {
348  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
349  }
350  return retv;
351 }
history.h
settings.h
Settings::disable_history
unsigned int disable_history
Definition: settings.h:110
__element
Definition: history.c:45
Settings::max_history_size
unsigned int max_history_size
Definition: settings.h:180
_element
struct __element _element
__history_get_element_list_fields
static char ** __history_get_element_list_fields(FILE *fd, unsigned int *length)
Definition: history.c:79
rofi.h
__element::index
long int index
Definition: history.c:47
__element::name
char * name
Definition: history.c:49
__history_write_element_list
static void __history_write_element_list(FILE *fd, _element **list, unsigned int length)
Definition: history.c:59
history_set
void history_set(const char *filename, const char *entry)
Definition: history.c:178
history_get_list
char ** history_get_list(const char *filename, unsigned int *length)
Definition: history.c:325
__history_get_element_list
static _element ** __history_get_element_list(FILE *fd, unsigned int *length)
Definition: history.c:122
__element_sort_func
static int __element_sort_func(const void *ea, const void *eb, void *data __attribute__((unused)))
Definition: history.c:52
Settings::ignored_prefixes
char * ignored_prefixes
Definition: settings.h:112
config
Settings config
history_remove
void history_remove(const char *filename, const char *entry)
Definition: history.c:259