Rewrite of the configuration parsing, no longer duplicating options between Xrm and CMD.

* NEEDS TESTING.
This commit is contained in:
Dave Davenport 2015-02-17 10:31:59 +01:00
parent bda97e0849
commit a534a0f244
7 changed files with 205 additions and 183 deletions

View File

@ -14,6 +14,8 @@
- On broken UTF-8 show everything up to the broken character. (#121)
Others:
- Significant code refactoring.
- Rewriting of config parser, Commandline Parser uses structure from Xresource parser.
Avoiding duplication and making it easier to maintain.
Performance improvements:
- Fix blocking on grabbing keyboard.

View File

@ -128,6 +128,8 @@ helper_test_SOURCES=\
include/rofi.h\
source/helper.c\
include/helper.h\
source/xrmoptions.h\
source/xrmoptions.c\
test/helper-test.c
.PHONY: test

View File

@ -146,19 +146,12 @@ int execute_generator ( char * cmd ) __attribute__( ( nonnull ) );
*/
void create_pid_file ( const char *pidfile );
/**
* @param argc Number of arguments.
* @param argv Array of arguments.
*
* Parse commandline options.
*/
void config_parse_cmd_options ( int argc, char ** argv );
/**
* Do some input validation, especially the first few could break things.
* It is good to catch them beforehand.
*
* This functions exits the program with 1 when it finds an invalid configuration.
*/
void config_sanity_check ( void );
void config_sanity_check ( int argc, char **argv );
char helper_parse_char ( const char *arg );
#endif // __HELPER_H__

View File

@ -8,7 +8,8 @@ typedef enum
xrm_String = 0,
xrm_Number = 1,
xrm_SNumber = 2,
xrm_Boolean = 3
xrm_Boolean = 3,
xrm_Char = 4
} XrmOptionType;
/**
@ -19,6 +20,21 @@ typedef enum
*/
void config_parse_xresource_options ( Display *display );
/**
* @param argc Number of arguments.
* @param argv Array of arguments.
*
* Parse commandline options.
*/
void config_parse_cmd_options ( int argc, char ** argv );
/**
* @param argc Number of arguments.
* @param argv Array of arguments.
*
* Parse dynamic commandline options.
*/
void config_parse_cmd_options_dynamic ( int argc, char ** argv );
/**
* @param display Handler of the display to fetch the settings from.
*
@ -38,5 +54,8 @@ void config_xresource_free ( void );
*/
void xresource_dump ( void );
/**
* Add option to the dynamic option parser.
*/
void config_parser_add_option ( XrmOptionType type, const char *key, void **value );
#endif

View File

@ -252,60 +252,65 @@ int find_arg_uint ( const int argc, char * const argv[], const char * const key,
return FALSE;
}
char helper_parse_char ( const char *arg )
{
char retv = -1;
int len = strlen ( arg );
// If the length is 1, it is not escaped.
if ( len == 1 ) {
retv = arg[0];
}
// If the length is 2 and the first character is '\', we unescape it.
else if ( len == 2 && arg[0] == '\\' ) {
// New line
if ( arg[1] == 'n' ) {
retv = '\n';
}
// Bell
else if ( arg[1] == 'a' ) {
retv = '\a';
}
// Backspace
else if ( arg[1] == 'b' ) {
retv = '\b';
}
// Tab
else if ( arg[1] == 't' ) {
retv = '\t';
}
// Vertical tab
else if ( arg[1] == 'v' ) {
retv = '\v';
}
// Form feed
else if ( arg[1] == 'f' ) {
retv = '\f';
}
// Carriage return
else if ( arg[1] == 'r' ) {
retv = '\r';
}
// Forward slash
else if ( arg[1] == '\\' ) {
retv = '\\';
}
}
else if ( len > 2 && arg[0] == '\\' && arg[1] == 'x' ) {
retv = (char) strtol ( &arg[2], NULL, 16 );
}
if ( retv < 0 ) {
fprintf ( stderr, "Failed to parse character string: \"%s\"\n", arg );
exit ( 1 );
}
return retv;
}
int find_arg_char ( const int argc, char * const argv[], const char * const key, char *val )
{
int i = find_arg ( argc, argv, key );
if ( val != NULL && i > 0 && i < ( argc - 1 ) ) {
int len = strlen ( argv[i + 1] );
// If the length is 1, it is not escaped.
if ( len == 1 ) {
*val = argv[i + 1][0];
}
// If the length is 2 and the first character is '\', we unescape it.
else if ( len == 2 && argv[i + 1][0] == '\\' ) {
// New line
if ( argv[i + 1][1] == 'n' ) {
*val = '\n';
}
// Bell
else if ( argv[i + 1][1] == 'a' ) {
*val = '\a';
}
// Backspace
else if ( argv[i + 1][1] == 'b' ) {
*val = '\b';
}
// Tab
else if ( argv[i + 1][1] == 't' ) {
*val = '\t';
}
// Vertical tab
else if ( argv[i + 1][1] == 'v' ) {
*val = '\v';
}
// Form feed
else if ( argv[i + 1][1] == 'f' ) {
*val = '\f';
}
// Carriage return
else if ( argv[i + 1][1] == 'r' ) {
*val = '\r';
}
// Forward slash
else if ( argv[i + 1][1] == '\\' ) {
*val = '\\';
}
// Otherwise it is not valid and throw error
else {
fprintf ( stderr, "Failed to parse command-line argument." );
exit ( 1 );
}
}
else{
fprintf ( stderr, "Failed to parse command-line argument." );
exit ( 1 );
}
*val = helper_parse_char ( argv[i + 1] );
return TRUE;
}
return FALSE;
@ -405,84 +410,25 @@ void create_pid_file ( const char *pidfile )
}
}
void config_parse_cmd_options ( int argc, char ** argv )
{
if ( find_arg ( argc, argv, "-rnow") >= 0 ||
find_arg ( argc, argv, "-snow") >= 0 ||
find_arg ( argc, argv, "-now") >= 0 ||
find_arg ( argc, argv, "-key") >= 0 ||
find_arg ( argc, argv, "-skey") >= 0 ||
find_arg ( argc, argv, "-rkey") >= 0 ) {
fprintf(stderr, "The -snow, -now, -rnow, -key, -rkey, -skey are deprecated "
"and have been removed.\n"
"Please see the manpage: %s -help for the correct syntax.", argv[0]);
exit(EXIT_FAILURE);
}
find_arg_str ( argc, argv, "-switchers", &( config.switchers ) );
// Parse commandline arguments about the looks.
find_arg_uint ( argc, argv, "-opacity", &( config.window_opacity ) );
find_arg_int ( argc, argv, "-width", &( config.menu_width ) );
find_arg_uint ( argc, argv, "-lines", &( config.menu_lines ) );
find_arg_uint ( argc, argv, "-columns", &( config.menu_columns ) );
find_arg_str ( argc, argv, "-font", &( config.menu_font ) );
find_arg_str ( argc, argv, "-fg", &( config.menu_fg ) );
find_arg_str ( argc, argv, "-bg", &( config.menu_bg ) );
find_arg_str ( argc, argv, "-bgalt", &( config.menu_bg_alt ) );
find_arg_str ( argc, argv, "-hlfg", &( config.menu_hlfg ) );
find_arg_str ( argc, argv, "-hlbg", &( config.menu_hlbg ) );
find_arg_str ( argc, argv, "-bc", &( config.menu_bc ) );
find_arg_uint ( argc, argv, "-bw", &( config.menu_bw ) );
// Parse commandline arguments about size and position
find_arg_uint ( argc, argv, "-location", &( config.location ) );
find_arg_uint ( argc, argv, "-padding", &( config.padding ) );
find_arg_int ( argc, argv, "-xoffset", &( config.x_offset ) );
find_arg_int ( argc, argv, "-yoffset", &( config.y_offset ) );
if ( find_arg ( argc, argv, "-fixed-num-lines" ) >= 0 ) {
config.fixed_num_lines = 1;
}
if ( find_arg ( argc, argv, "-disable-history" ) >= 0 ) {
config.disable_history = TRUE;
}
if ( find_arg ( argc, argv, "-levenshtein-sort" ) >= 0 ) {
config.levenshtein_sort = TRUE;
}
if ( find_arg ( argc, argv, "-case-sensitive" ) >= 0 ) {
config.case_sensitive = TRUE;
}
// Parse commandline arguments about behavior
find_arg_str ( argc, argv, "-terminal", &( config.terminal_emulator ) );
find_arg_str ( argc, argv, "-ssh-client", &( config.ssh_client ) );
find_arg_str ( argc, argv, "-ssh-command", &( config.ssh_command ) );
find_arg_str ( argc, argv, "-run-command", &( config.run_command ) );
find_arg_str ( argc, argv, "-run-list-command", &( config.run_list_command ) );
find_arg_str ( argc, argv, "-run-shell-command", &( config.run_shell_command ) );
find_arg_char ( argc, argv, "-sep", &( config.separator ) );
find_arg_int ( argc, argv, "-eh", &( config.element_height ) );
find_arg_uint ( argc, argv, "-lazy-filter-limit", &( config.lazy_filter_limit ) );
if ( find_arg ( argc, argv, "-sidebar-mode" ) >= 0 ) {
config.sidebar_mode = TRUE;
}
}
/**
* Do some input validation, especially the first few could break things.
* It is good to catch them beforehand.
*
* This functions exits the program with 1 when it finds an invalid configuration.
*/
void config_sanity_check ( void )
void config_sanity_check ( int argc, char **argv )
{
if ( find_arg ( argc, argv, "-rnow" ) >= 0 ||
find_arg ( argc, argv, "-snow" ) >= 0 ||
find_arg ( argc, argv, "-now" ) >= 0 ||
find_arg ( argc, argv, "-key" ) >= 0 ||
find_arg ( argc, argv, "-skey" ) >= 0 ||
find_arg ( argc, argv, "-rkey" ) >= 0 ) {
fprintf ( stderr, "The -snow, -now, -rnow, -key, -rkey, -skey are deprecated "
"and have been removed.\n"
"Please see the manpage: %s -help for the correct syntax.", argv[0] );
exit ( EXIT_FAILURE );
}
if ( config.element_height < 1 ) {
fprintf ( stderr, "config.element_height is invalid. It needs to be atleast 1 line high.\n" );
exit ( 1 );

View File

@ -553,9 +553,10 @@ inline static void menu_nav_down ( MenuState *state )
* specified by key and modstate. Returns -1 if none was found
*/
extern unsigned int NumlockMask;
static int locate_switcher( KeySym key, unsigned int modstate ) {
static int locate_switcher ( KeySym key, unsigned int modstate )
{
// ignore annoying modifiers
unsigned int modstate_filtered = modstate & ~(LockMask | NumlockMask);
unsigned int modstate_filtered = modstate & ~( LockMask | NumlockMask );
for ( unsigned int i = 0; i < num_switchers; i++ ) {
if ( switchers[i].keystr != NULL ) {
if ( ( modstate_filtered == switchers[i].modmask ) &&
@ -577,7 +578,7 @@ static void menu_keyboard_navigation ( MenuState *state, KeySym key, unsigned in
{
// pressing one of the global key bindings closes the switcher. this allows fast closing of the
// menu if an item is not selected
if ( locate_switcher( key, modstate ) != -1 || key == XK_Escape ) {
if ( locate_switcher ( key, modstate ) != -1 || key == XK_Escape ) {
state->retv = MENU_CANCEL;
state->quit = TRUE;
}
@ -1438,9 +1439,9 @@ static void run_switcher ( int do_fork, SwitcherMode mode )
static void handle_keypress ( XEvent *ev )
{
int index;
KeySym key = XkbKeycodeToKeysym ( display, ev->xkey.keycode, 0, 0 );
index = locate_switcher( key, ev->xkey.state );
if( index >= 0 ) {
KeySym key = XkbKeycodeToKeysym ( display, ev->xkey.keycode, 0, 0 );
index = locate_switcher ( key, ev->xkey.state );
if ( index >= 0 ) {
run_switcher ( TRUE, index );
}
else {
@ -1476,6 +1477,10 @@ static void cleanup ()
XCloseDisplay ( display );
}
}
// Cleanup pid file.
if ( pidfile ) {
unlink ( pidfile );
}
// Cleaning up memory allocated by the Xresources file.
config_xresource_free ();
@ -1484,22 +1489,13 @@ static void cleanup ()
if ( switchers[i].cb_data_free != NULL ) {
switchers[i].cb_data_free ( switchers[i].cb_data );
}
if ( switchers[i].keystr != NULL ) {
g_free ( switchers[i].keystr );
switchers[i].keystr = NULL;
}
// Switcher keystr is free'ed when needed by config system.
if ( switchers[i].keycfg != NULL ) {
g_free ( switchers[i].keycfg );
switchers[i].keycfg = NULL;
}
}
g_free ( switchers );
// Cleanup pid file.
if ( pidfile ) {
unlink ( pidfile );
g_free ( pidfile );
}
}
/**
@ -1570,8 +1566,7 @@ static void setup_switchers ( void )
// We cannot do this in main loop, as we create pointer to string,
// and re-alloc moves that pointer.
for ( unsigned int i = 0; i < num_switchers; i++ ) {
switchers[i].keycfg = g_strdup_printf ( "key-%s",
switchers[i].name );
switchers[i].keycfg = g_strdup_printf ( "key-%s", switchers[i].name );
config_parser_add_option ( xrm_String,
switchers[i].keycfg,
(void * *) &( switchers[i].keystr ) );
@ -1601,16 +1596,10 @@ static inline void load_configuration_dynamic ( Display *display )
{
// Load in config from X resources.
config_parse_xresource_options_dynamic ( display );
config_parse_cmd_options_dynamic ( stored_argc, stored_argv );
for ( unsigned int i = 0; i < num_switchers; i++ ) {
if ( switchers[i].keycfg != NULL ) {
char *flag = g_strdup_printf ( "-%s", switchers[i].keycfg );
find_arg_str_alloc ( stored_argc, stored_argv, flag, &( switchers[i].keystr ) );
g_free ( flag );
}
}
// Sanity check
config_sanity_check ();
config_sanity_check ( stored_argc, stored_argv );
}
@ -1655,6 +1644,7 @@ static void show_error_message ( const char *msg )
int main ( int argc, char *argv[] )
{
int dmenu_mode = FALSE;
stored_argc = argc;
stored_argv = argv;
@ -1669,16 +1659,30 @@ int main ( int argc, char *argv[] )
exit ( EXIT_SUCCESS );
}
// Detect if we are in dmenu mode.
// This has two possible causes.
// 1 the user specifies it on the command-line.
if ( find_arg ( argc, argv, "-dmenu" ) >= 0 ) {
dmenu_mode = TRUE;
}
// 2 the binary that executed is called dmenu (e.g. symlink to rofi)
else{
// Get the base name of the executable called.
char *base_name = g_path_get_basename ( argv[0] );
dmenu_mode = ( strcmp ( base_name, "dmenu" ) == 0 );
// Free the basename for dmenu detection.
g_free ( base_name );
}
// Get the path to the cache dir.
cache_dir = g_get_user_cache_dir ();
// Create pid file path.
if ( find_arg_str_alloc ( argc, argv, "-pid", &( pidfile ) ) == FALSE ) {
const char *path = g_get_user_runtime_dir ();
if ( path ) {
pidfile = g_build_filename ( path, "rofi.pid", NULL );
}
const char *path = g_get_user_runtime_dir ();
if ( path ) {
pidfile = g_build_filename ( path, "rofi.pid", NULL );
}
config_parser_add_option ( xrm_String, "pid", (void * *) &pidfile );
// Register cleanup function.
atexit ( cleanup );
@ -1692,10 +1696,15 @@ int main ( int argc, char *argv[] )
return EXIT_FAILURE;
}
load_configuration ( display );
// setup_switchers
setup_switchers ();
if ( !dmenu_mode ) {
// setup_switchers
setup_switchers ();
}
else {
// Add dmenu options.
config_parser_add_option ( xrm_Char, "sep", (void * *) &( config.separator ) );
}
// Reload for dynamic part.
load_configuration_dynamic ( display );
@ -1716,21 +1725,6 @@ int main ( int argc, char *argv[] )
exit ( EXIT_SUCCESS );
}
// Detect if we are in dmenu mode.
// This has two possible causes.
int dmenu_mode = FALSE;
// 1 the user specifies it on the command-line.
if ( find_arg ( argc, argv, "-dmenu" ) >= 0 ) {
dmenu_mode = TRUE;
}
// 2 the binary that executed is called dmenu (e.g. symlink to rofi)
else{
// Get the base name of the executable called.
char *base_name = g_path_get_basename ( argv[0] );
dmenu_mode = ( strcmp ( base_name, "dmenu" ) == 0 );
// Free the basename for dmenu detection.
g_free ( base_name );
}
// Dmenu mode.
if ( dmenu_mode == TRUE ) {

View File

@ -32,6 +32,7 @@
#include <X11/Xresource.h>
#include "rofi.h"
#include "xrmoptions.h"
#include "helper.h"
typedef struct
{
@ -43,12 +44,13 @@ typedef struct
int * snum;
char ** str;
void *pointer;
char * charc;
};
char *mem;
} XrmOption;
/**
* Map X resource settings to internal options
* Currently supports string and number.
* Map X resource and commandline options to internal options
* Currently supports string, boolean and number (signed and unsigned).
*/
static XrmOption xrmOptions[] = {
{ xrm_String, "switchers", { .str = &config.switchers }, NULL },
@ -98,9 +100,9 @@ static XrmOption xrmOptions[] = {
{ xrm_Boolean, "disable-history", { .num = &config.disable_history }, NULL },
{ xrm_Boolean, "levenshtein-sort", { .num = &config.levenshtein_sort }, NULL },
{ xrm_Boolean, "case-sensitive", { .num = &config.case_sensitive }, NULL },
/* Key bindings */
{ xrm_Boolean, "sidebar-mode", { .num = &config.sidebar_mode }, NULL },
{ xrm_Number, "lazy-filter-limit", { .num = &config.lazy_filter_limit }, NULL }
{ xrm_Number, "lazy-filter-limit", { .num = &config.lazy_filter_limit }, NULL },
{ xrm_SNumber, "eh", { .snum = &config.element_height }, NULL }
};
// Dynamic options.
@ -150,6 +152,9 @@ static void config_parser_set ( XrmOption *option, XrmValue *xrmValue )
*( option->num ) = FALSE;
}
}
else if ( option->type == xrm_Char ) {
*( option->charc ) = helper_parse_char ( xrmValue->addr );
}
}
void config_parse_xresource_options ( Display *display )
@ -185,6 +190,59 @@ void config_parse_xresource_options ( Display *display )
XrmDestroyDatabase ( xDB );
}
/**
* Parse an option from the commandline vector.
*/
static void config_parse_cmd_option ( XrmOption *option, int argc, char **argv )
{
// Prepend a - to the option name.
char *key = g_strdup_printf ( "-%s", option->name );
switch ( option->type )
{
case xrm_Number:
find_arg_uint ( argc, argv, key, option->num );
break;
case xrm_SNumber:
find_arg_int ( argc, argv, key, option->snum );
break;
case xrm_String:
if ( find_arg_str ( argc, argv, key, option->str ) == TRUE ) {
if ( option->mem != NULL ) {
g_free ( option->mem );
option->mem = NULL;
}
}
break;
case xrm_Boolean:
if ( find_arg ( argc, argv, key ) >= 0 ) {
*( option->num ) = TRUE;
}
break;
case xrm_Char:
find_arg_char ( argc, argv, key, option->charc );
break;
default:
break;
}
g_free ( key );
}
void config_parse_cmd_options ( int argc, char ** argv )
{
for ( unsigned int i = 0; i < sizeof ( xrmOptions ) / sizeof ( XrmOption ); ++i ) {
XrmOption *op = &( xrmOptions[i] );
config_parse_cmd_option ( op, argc, argv );
}
}
void config_parse_cmd_options_dynamic ( int argc, char ** argv )
{
for ( unsigned int i = 0; i < num_extra_options; ++i ) {
XrmOption *op = &( extra_options[i] );
config_parse_cmd_option ( op, argc, argv );
}
}
void config_parse_xresource_options_dynamic ( Display *display )
{
char *xRMS;
@ -256,6 +314,14 @@ void xresource_dump_entry ( const char *namePrefix, XrmOption *option )
case xrm_Boolean:
printf ( "%s", ( *( option->num ) == TRUE ) ? "true" : "false" );
break;
case xrm_Char:
if ( *( option->charc ) > 32 && *( option->charc ) < 127 ) {
printf ( "%c", *( option->charc ) );
}
else {
printf ( "\\x%02X", *( option->charc ) );
}
break;
default:
break;
}