rofi  1.5.2
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  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  int found = 0;
184  unsigned int curr = 0;
185  unsigned int length = 0;
186  _element **list = NULL;
187  // Open file for reading and writing.
188  FILE *fd = g_fopen ( filename, "r" );
189  if ( fd != NULL ) {
190  // Get list.
191  list = __history_get_element_list ( fd, &length );
192  // Close file, if fails let user know on stderr.
193  if ( fclose ( fd ) != 0 ) {
194  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
195  }
196  }
197  // Look if the entry exists.
198  for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
199  if ( strcmp ( list[iter]->name, entry ) == 0 ) {
200  curr = iter;
201  found = 1;
202  }
203  }
204 
205  if ( found ) {
206  // If exists, increment list index number
207  list[curr]->index++;
208  }
209  else{
210  // If not exists, add it.
211  // Increase list by one
212  list = g_realloc ( list, ( length + 2 ) * sizeof ( _element* ) );
213  list[length] = g_malloc ( sizeof ( _element ) );
214  // Copy name
215  if ( list[length] != NULL ) {
216  list[length]->name = g_strdup ( entry );
217  // set # hits
218  list[length]->index = 1;
219 
220  length++;
221  list[length] = NULL;
222  }
223  }
224 
225  fd = fopen ( filename, "w" );
226  if ( fd == NULL ) {
227  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
228  }
229  else {
230  // Write list.
231  __history_write_element_list ( fd, list, length );
232  // Close file, if fails let user know on stderr.
233  if ( fclose ( fd ) != 0 ) {
234  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
235  }
236  }
237  // Free the list.
238  for ( unsigned int iter = 0; iter < length; iter++ ) {
239  g_free ( list[iter]->name );
240  g_free ( list[iter] );
241  }
242  g_free ( list );
243 }
244 
245 void history_remove ( const char *filename, const char *entry )
246 {
247  if ( config.disable_history ) {
248  return;
249  }
250  _element ** list = NULL;
251  int found = 0;
252  unsigned int curr = 0;
253  unsigned int length = 0;
254  // Open file for reading and writing.
255  FILE *fd = g_fopen ( filename, "r" );
256  if ( fd == NULL ) {
257  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
258  return;
259  }
260  // Get list.
261  list = __history_get_element_list ( fd, &length );
262 
263  // Close file, if fails let user know on stderr.
264  if ( fclose ( fd ) != 0 ) {
265  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
266  }
267  // Find entry.
268  for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
269  if ( strcmp ( list[iter]->name, entry ) == 0 ) {
270  curr = iter;
271  found = 1;
272  }
273  }
274 
275  // If found, remove it and write out new file.
276  if ( found ) {
277  // Remove the entry.
278  g_free ( list[curr]->name );
279  g_free ( list[curr] );
280  // Swap last to here (if list is size 1, we just swap empty sets).
281  list[curr] = list[length - 1];
282  // Empty last.
283  list[length - 1] = NULL;
284  length--;
285 
286  fd = g_fopen ( filename, "w" );
287  // Clear list.
288  if ( fd != NULL ) {
289  // Write list.
290  __history_write_element_list ( fd, list, length );
291  // Close file, if fails let user know on stderr.
292  if ( fclose ( fd ) != 0 ) {
293  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
294  }
295  }
296  else{
297  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
298  }
299  }
300 
301  // Free the list.
302  for ( unsigned int iter = 0; iter < length; iter++ ) {
303  g_free ( list[iter]->name );
304  g_free ( list[iter] );
305  }
306  if ( list != NULL ) {
307  g_free ( list );
308  }
309 }
310 
311 char ** history_get_list ( const char *filename, unsigned int *length )
312 {
313  *length = 0;
314 
315  if ( config.disable_history ) {
316  return NULL;
317  }
318  char **retv = NULL;
319  // Open file.
320  FILE *fd = g_fopen ( filename, "r" );
321  if ( fd == NULL ) {
322  // File that does not exists is not an error, so ignore it.
323  // Everything else? panic.
324  if ( errno != ENOENT ) {
325  g_warning ( "Failed to open file: %s", g_strerror ( errno ) );
326  }
327  return NULL;
328  }
329  // Get list.
330  retv = __history_get_element_list_fields ( fd, length );
331 
332  // Close file, if fails let user know on stderr.
333  if ( fclose ( fd ) != 0 ) {
334  g_warning ( "Failed to close history file: %s", g_strerror ( errno ) );
335  }
336  return retv;
337 }
void history_set(const char *filename, const char *entry)
Definition: history.c:178
char ** history_get_list(const char *filename, unsigned int *length)
Definition: history.c:311
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:178
void history_remove(const char *filename, const char *entry)
Definition: history.c:245
static _element ** __history_get_element_list(FILE *fd, unsigned int *length)
Definition: history.c:122
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:110