rofi/source/history.c

271 lines
7.9 KiB
C
Raw Normal View History

/**
* rofi
*
* MIT/X11 License
* Copyright 2013-2014 Qball Cow <qball@gmpclient.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include "rofi.h"
#include "history.h"
2014-05-22 07:33:32 +00:00
#define HISTORY_NAME_LENGTH 256
#define HISTORY_MAX_ENTRIES 25
typedef struct __element
{
long int index;
char name[HISTORY_NAME_LENGTH];
}_element;
static int __element_sort_func ( const void *ea, const void *eb )
{
_element *a = *(_element * *) ea;
_element *b = *(_element * *) eb;
return b->index - a->index;
}
2014-05-22 07:33:32 +00:00
static void __history_write_element_list ( FILE *fd, _element **list, unsigned int length )
{
2014-06-04 19:29:23 +00:00
if ( list == NULL ) {
return;
}
// Sort the list before writing out.
qsort ( list, length, sizeof ( _element* ), __element_sort_func );
2014-05-25 15:55:27 +00:00
// Get minimum index.
int min_value = list[length - 1]->index;
2014-05-25 15:55:27 +00:00
// Set the max length of the list.
2014-05-22 07:33:32 +00:00
length = ( length > HISTORY_MAX_ENTRIES ) ? HISTORY_MAX_ENTRIES : length;
// Write out entries.
2014-06-04 19:29:23 +00:00
for ( unsigned int iter = 0; iter < length; iter++ ) {
fprintf ( fd, "%ld %s\n", list[iter]->index - min_value, list[iter]->name );
2014-05-22 07:33:32 +00:00
}
}
static _element ** __history_get_element_list ( FILE *fd, unsigned int *length )
{
2014-05-22 07:33:32 +00:00
char buffer[HISTORY_NAME_LENGTH + 16];
_element **retv = NULL;
2014-06-04 19:29:23 +00:00
if ( length == NULL ) {
return NULL;
}
*length = 0;
2014-06-04 19:29:23 +00:00
if ( fd == NULL ) {
return NULL;
}
2014-06-04 19:29:23 +00:00
while ( fgets ( buffer, HISTORY_NAME_LENGTH + 16, fd ) != NULL ) {
char * start = NULL;
// Skip empty lines.
2014-06-04 19:29:23 +00:00
if ( strlen ( buffer ) == 0 ) {
continue;
}
// Resize and check.
retv = g_realloc ( retv, ( *length + 2 ) * sizeof ( _element* ) );
retv[( *length )] = g_malloc ( sizeof ( _element ) );
// remove trailing \n
buffer[strlen ( buffer ) - 1] = '\0';
// Parse the number of times.
2014-05-22 07:33:32 +00:00
retv[( *length )]->index = strtol ( buffer, &start, 10 );
strncpy ( retv[( *length )]->name, ( start + 1 ), HISTORY_NAME_LENGTH );
// Force trailing '\0'
2014-05-22 07:33:32 +00:00
retv[( *length )]->name[HISTORY_NAME_LENGTH - 1] = '\0';
retv[( *length ) + 1] = NULL;
2014-05-22 07:33:32 +00:00
( *length )++;
}
return retv;
}
void history_set ( const char *filename, const char *entry )
{
2014-06-05 19:55:47 +00:00
if ( config.disable_history ) {
return;
}
2014-05-22 07:33:32 +00:00
int found = 0;
unsigned int curr = 0;
unsigned int length = 0;
2014-05-22 07:33:32 +00:00
_element **list = NULL;
// Open file for reading and writing.
2014-05-22 07:33:32 +00:00
FILE *fd = fopen ( filename, "a+" );
2014-06-04 19:29:23 +00:00
if ( fd == NULL ) {
2014-05-22 07:33:32 +00:00
fprintf ( stderr, "Failed to open file: %s\n", strerror ( errno ) );
return;
}
// Get list.
2014-05-22 07:33:32 +00:00
list = __history_get_element_list ( fd, &length );
// Look if the entry exists.
2014-06-04 19:29:23 +00:00
for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
if ( strcmp ( list[iter]->name, entry ) == 0 ) {
2014-05-22 07:33:32 +00:00
curr = iter;
found = 1;
}
}
2014-06-04 19:29:23 +00:00
if ( found ) {
// If exists, increment list index number
list[curr]->index++;
2014-05-22 07:33:32 +00:00
}
2014-06-04 19:29:23 +00:00
else{
// If not exists, add it.
// Increase list by one
list = g_realloc ( list, ( length + 2 ) * sizeof ( _element* ) );
list[length] = g_malloc ( sizeof ( _element ) );
// Copy name
if ( list[length] != NULL ) {
g_strlcpy ( list[length]->name, entry, HISTORY_NAME_LENGTH );
list[length]->name[HISTORY_NAME_LENGTH - 1] = '\0';
// set # hits
list[length]->index = 1;
length++;
list[length] = NULL;
}
}
// Rewind.
2014-05-22 07:33:32 +00:00
fseek ( fd, 0L, SEEK_SET );
// Clear file.
2014-06-04 19:29:23 +00:00
if ( ftruncate ( fileno ( fd ), 0 ) == 0 ) {
2014-05-13 20:11:42 +00:00
// Write list.
2014-05-22 07:33:32 +00:00
__history_write_element_list ( fd, list, length );
}
2014-06-04 19:29:23 +00:00
else{
2014-05-22 07:33:32 +00:00
fprintf ( stderr, "Failed to truncate file: %s\n", strerror ( errno ) );
2014-05-13 20:11:42 +00:00
}
// Free the list.
2014-06-04 19:29:23 +00:00
for ( unsigned int iter = 0; iter < length; iter++ ) {
g_free ( list[iter] );
}
g_free ( list );
// Close file.
2014-05-22 07:33:32 +00:00
fclose ( fd );
}
void history_remove ( const char *filename, const char *entry )
{
2014-06-05 19:55:47 +00:00
if ( config.disable_history ) {
return;
}
2014-05-22 07:33:32 +00:00
_element ** list = NULL;
int found = 0;
unsigned int curr = 0;
unsigned int length = 0;
// Open file for reading and writing.
2014-05-22 07:33:32 +00:00
FILE *fd = fopen ( filename, "a+" );
2014-06-04 19:29:23 +00:00
if ( fd == NULL ) {
2014-05-22 07:33:32 +00:00
fprintf ( stderr, "Failed to open file: %s\n", strerror ( errno ) );
return;
}
// Get list.
2014-05-22 07:33:32 +00:00
list = __history_get_element_list ( fd, &length );
// Find entry.
2014-06-04 19:29:23 +00:00
for ( unsigned int iter = 0; !found && iter < length; iter++ ) {
if ( strcmp ( list[iter]->name, entry ) == 0 ) {
2014-05-22 07:33:32 +00:00
curr = iter;
found = 1;
}
}
// If found, remove it and write out new file.
2014-06-04 19:29:23 +00:00
if ( found ) {
// Remove the entry.
g_free ( list[curr] );
// Swap last to here (if list is size 1, we just swap empty sets).
2014-05-22 07:33:32 +00:00
list[curr] = list[length - 1];
// Empty last.
2014-05-22 07:33:32 +00:00
list[length - 1] = NULL;
length--;
// Rewind.
2014-05-22 07:33:32 +00:00
fseek ( fd, 0L, SEEK_SET );
// Clear list.
2014-06-04 19:29:23 +00:00
if ( ftruncate ( fileno ( fd ), 0 ) == 0 ) {
2014-05-13 20:11:42 +00:00
// Write list.
2014-05-22 07:33:32 +00:00
__history_write_element_list ( fd, list, length );
}
2014-06-04 19:29:23 +00:00
else{
2014-05-22 07:33:32 +00:00
fprintf ( stderr, "Failed to open file: %s\n", strerror ( errno ) );
2014-05-13 20:11:42 +00:00
}
}
// Free the list.
2014-06-04 19:29:23 +00:00
for ( unsigned int iter = 0; iter < length; iter++ ) {
g_free ( list[iter] );
2014-05-22 07:33:32 +00:00
}
2014-06-04 19:29:23 +00:00
if ( list != NULL ) {
g_free ( list );
}
// Close file.
2014-05-22 07:33:32 +00:00
fclose ( fd );
}
char ** history_get_list ( const char *filename, unsigned int *length )
{
2014-06-05 19:55:47 +00:00
if ( config.disable_history ) {
return NULL;
}
_element **list = NULL;
2014-05-22 07:33:32 +00:00
char **retv = NULL;
// Open file.
2014-05-22 07:33:32 +00:00
FILE *fd = fopen ( filename, "r" );
2014-06-04 19:29:23 +00:00
if ( fd == NULL ) {
2014-05-27 06:42:21 +00:00
// File that does not exists is not an error, so ignore it.
// Everything else? panic.
2014-06-04 19:29:23 +00:00
if ( errno != ENOENT ) {
2014-05-22 07:33:32 +00:00
fprintf ( stderr, "Failed to open file: %s\n", strerror ( errno ) );
}
return NULL;
}
// Get list.
2014-05-22 07:33:32 +00:00
list = __history_get_element_list ( fd, length );
2014-05-22 07:33:32 +00:00
// Copy list in right format.
2014-05-27 06:42:21 +00:00
// Lists are always short, so performance should not be an issue.
2014-06-04 19:29:23 +00:00
if ( ( *length ) > 0 ) {
retv = g_malloc ( ( ( *length ) + 1 ) * sizeof ( char * ) );
for ( unsigned int iter = 0; iter < ( *length ); iter++ ) {
retv[iter] = g_strdup ( list[iter]->name );
g_free ( list[iter] );
}
2014-05-22 07:33:32 +00:00
retv[( *length )] = NULL;
g_free ( list );
}
2014-05-22 07:33:32 +00:00
fclose ( fd );
return retv;
}