rofi  1.5.1
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;
50 }_element;
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  return retv;
117 }
118 
119 static _element ** __history_get_element_list ( FILE *fd, unsigned int *length )
120 {
121  unsigned int real_length = 0;
122  _element **retv = NULL;
123 
124  if ( length == NULL ) {
125  return NULL;
126  }
127  *length = 0;
128 
129  if ( fd == NULL ) {
130  return NULL;
131  }
132  char *buffer = NULL;
133  size_t buffer_length = 0;
134  ssize_t l = 0;
135  while ( ( l = getline ( &buffer, &buffer_length, fd ) ) > 0 ) {
136  char * start = NULL;
137  // Skip empty lines.
138  if ( l <= 1 ) {
139  continue;
140  }
141 
142  long int index = strtol ( buffer, &start, 10 );
143  if ( start == buffer || *start == '\0' ) {
144  continue;
145  }
146  start++;
147  if ( ( l - ( start - buffer ) ) < 2 ) {
148  continue;
149  }
150  if ( real_length < ( *length + 2 ) ) {
151  real_length += 15;
152  // Resize and check.
153  retv = g_realloc ( retv, ( real_length ) * sizeof ( _element* ) );
154  }
155 
156  retv[( *length )] = g_malloc ( sizeof ( _element ) );
157 
158  // remove trailing \n
159  buffer[l - 1] = '\0';
160  // Parse the number of times.
161  retv[( *length )]->index = index;
162  retv[( *length )]->name = g_strndup ( start, l - 1 - ( start - buffer ) );
163  // Force trailing '\0'
164  retv[( *length ) + 1] = NULL;
165 
166  ( *length )++;
167  }
168  if ( buffer != NULL ) {
169  free ( buffer );
170  buffer = NULL;
171  }
172  return retv;
173 }
174 
175 void history_set ( const char *filename, const char *entry )
176 {
177  if ( config.disable_history ) {
178  return;
179  }
180  int found = 0;
181  unsigned int curr = 0;
182  unsigned int length = 0;
183  _element **list = NULL;
184  // Open file for reading and writing.
185  FILE *fd = g_fopen ( filename, "r" );
186  if ( fd != NULL ) {
187  // Get list.
188  list = __history_get_element_list ( fd, &length );
189  // Close file, if fails let user know on stderr.
190  if ( fclose ( fd ) != 0 ) {
191  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
192  }
193  }
194  // Look if the entry exists.
195  for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
196  if ( strcmp ( list[iter]->name, entry ) == 0 ) {
197  curr = iter;
198  found = 1;
199  }
200  }
201 
202  if ( found ) {
203  // If exists, increment list index number
204  list[curr]->index++;
205  }
206  else{
207  // If not exists, add it.
208  // Increase list by one
209  list = g_realloc ( list, ( length + 2 ) * sizeof ( _element* ) );
210  list[length] = g_malloc ( sizeof ( _element ) );
211  // Copy name
212  if ( list[length] != NULL ) {
213  list[length]->name = g_strdup ( entry );
214  // set # hits
215  list[length]->index = 1;
216 
217  length++;
218  list[length] = NULL;
219  }
220  }
221 
222  fd = fopen ( filename, "w" );
223  if ( fd == NULL ) {
224  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
225  }
226  else {
227  // Write list.
228  __history_write_element_list ( fd, list, length );
229  // Close file, if fails let user know on stderr.
230  if ( fclose ( fd ) != 0 ) {
231  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
232  }
233  }
234  // Free the list.
235  for ( unsigned int iter = 0; iter < length; iter++ ) {
236  g_free ( list[iter]->name );
237  g_free ( list[iter] );
238  }
239  g_free ( list );
240 }
241 
242 void history_remove ( const char *filename, const char *entry )
243 {
244  if ( config.disable_history ) {
245  return;
246  }
247  _element ** list = NULL;
248  int found = 0;
249  unsigned int curr = 0;
250  unsigned int length = 0;
251  // Open file for reading and writing.
252  FILE *fd = g_fopen ( filename, "r" );
253  if ( fd == NULL ) {
254  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
255  return;
256  }
257  // Get list.
258  list = __history_get_element_list ( fd, &length );
259 
260  // Close file, if fails let user know on stderr.
261  if ( fclose ( fd ) != 0 ) {
262  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
263  }
264  // Find entry.
265  for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
266  if ( strcmp ( list[iter]->name, entry ) == 0 ) {
267  curr = iter;
268  found = 1;
269  }
270  }
271 
272  // If found, remove it and write out new file.
273  if ( found ) {
274  // Remove the entry.
275  g_free ( list[curr]->name );
276  g_free ( list[curr] );
277  // Swap last to here (if list is size 1, we just swap empty sets).
278  list[curr] = list[length - 1];
279  // Empty last.
280  list[length - 1] = NULL;
281  length--;
282 
283  fd = g_fopen ( filename, "w" );
284  // Clear list.
285  if ( fd != NULL ) {
286  // Write list.
287  __history_write_element_list ( fd, list, length );
288  // Close file, if fails let user know on stderr.
289  if ( fclose ( fd ) != 0 ) {
290  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
291  }
292  }
293  else{
294  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
295  }
296  }
297 
298  // Free the list.
299  for ( unsigned int iter = 0; iter < length; iter++ ) {
300  g_free ( list[iter]->name );
301  g_free ( list[iter] );
302  }
303  if ( list != NULL ) {
304  g_free ( list );
305  }
306 }
307 
308 char ** history_get_list ( const char *filename, unsigned int *length )
309 {
310  *length = 0;
311 
312  if ( config.disable_history ) {
313  return NULL;
314  }
315  char **retv = NULL;
316  // Open file.
317  FILE *fd = g_fopen ( filename, "r" );
318  if ( fd == NULL ) {
319  // File that does not exists is not an error, so ignore it.
320  // Everything else? panic.
321  if ( errno != ENOENT ) {
322  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
323  }
324  return NULL;
325  }
326  // Get list.
327  retv = __history_get_element_list_fields ( fd, length );
328 
329  // Close file, if fails let user know on stderr.
330  if ( fclose ( fd ) != 0 ) {
331  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
332  }
333  return retv;
334 }
void history_set(const char *filename, const char *entry)
Definition: history.c:175
char ** history_get_list(const char *filename, unsigned int *length)
Definition: history.c:308
static char ** __history_get_element_list_fields(FILE *fd, unsigned int *length)
Definition: history.c:79
struct __element _element
unsigned int max_history_size
Definition: settings.h:166
void history_remove(const char *filename, const char *entry)
Definition: history.c:242
static _element ** __history_get_element_list(FILE *fd, unsigned int *length)
Definition: history.c:119
static int __element_sort_func(const void *ea, const void *eb, void *data __attribute__((unused)))
Definition: history.c:52
char * name
Definition: history.c:49
long int index
Definition: history.c:47
static void __history_write_element_list(FILE *fd, _element **list, unsigned int length)
Definition: history.c:59
Settings config
unsigned int disable_history
Definition: settings.h:104