*** handle configuration and history with C code ***

This is a major change that may lead to a complete C application.

Deleted files:
- prefs.cc
- prefs.h
- history.cc
- history.h

New files:
- config_prefs.c
- config_prefs.h
- history.c
- history.h

gmrun was mostly broken, I'm not sure when the app became so broken
but it's better now.

The code is more readable and more stable.

Various bugs were fixed, it would take many lines to explain the changes,
they were just so dramatic
This commit is contained in:
wdlkmpx 2020-09-13 00:16:42 +08:00
parent 1836e4a58f
commit 05511520d9
13 changed files with 1060 additions and 879 deletions

View File

@ -24,10 +24,7 @@ Features
* CTRL-Enter runs the command in a terminal. CTRL-Enter without any
command starts a new terminal.
* You can use CTRL-R / CTRL-S to search through history, much like in bash
or Emacs. Also, pressing "!" as the first character enters some special
search mode, finding those history entries that begin with the entered
text.
* You can use CTRL-R / CTRL-S to search through history.
* URL handlers allowing you to enter lines like "http://www.google.com"
to start your favorite browser on www.google.com.
@ -39,8 +36,6 @@ Features
it in the configuration file. The default handler for ".txt" files is,
of course, Emacs. But you can easily change that, you... you VIM user!
* UTF-8 support (added in 0.9.2)
Requirements
-------------

View File

@ -4,11 +4,10 @@ AM_CPPFLAGS = @GTK_CFLAGS@
bin_PROGRAMS = gmrun
gmrun_SOURCES = \
gmrun_SOURCES = config_prefs.c \
history.c \
gtkcompletionline.cc gtkcompletionline.h \
history.cc history.h \
main.cc \
prefs.cc prefs.h ci_string.h
main.cc
# gmrun_LDFLAGS = -s

View File

@ -1,41 +0,0 @@
// $Id: ci_string.h,v 1.2 2001/05/16 14:39:31 mishoo Exp $
// This is a very nice class, probably because it's not coded by me ;=]~
// Provide a case-insensitive std::string-like class.
#ifndef __CI_STRING_H__
#define __CI_STRING_H__
#include <string.h>
#include <ctype.h>
struct ci_char_traits : public std::char_traits<char>
{
static bool eq( char c1, char c2 ) {
return ::tolower(c1) == ::tolower(c2);
}
static bool ne( char c1, char c2 ) {
return ::tolower(c1) != ::tolower(c2);
}
static bool lt( char c1, char c2 ) {
return ::tolower(c1) < ::tolower(c2);
}
static int compare( const char* s1,
const char* s2,
size_t n ) {
return strncasecmp( s1, s2, n );
}
static const char*
find( const char* s, int n, char a ) {
while ( n-- > 0 && ::tolower(*s) != ::tolower(a) ) ++s;
return s;
}
};
typedef std::basic_string<char, ci_char_traits> ci_string;
#endif // __CI_STRING_H__

356
src/config_prefs.c Normal file
View File

@ -0,0 +1,356 @@
/*
* This is free and unencumbered software released into the public domain.
*
* For more information, please refer to <https://unlicense.org>
*/
/*
* Read config file formatted like this:
*
* key1 = value1
* key2=value2
*
* It's possible to open several files with the same config
* The keys will be updated with thew new values.
* 'key' is not case sensitive
*/
#include "config_prefs.h"
#define APP_CONFIG_FILE "gmrunrc"
// ============================================================
// PRIVATE
// ============================================================
static GList * PrefsGList = NULL;
static GList * ExtensionGList = NULL;
struct _pref_item
{
char * key;
char * value;
};
typedef struct _pref_item pref_item;
static void pref_item_free (pref_item * item)
{
if (item) {
if (item->key) g_free (item->key);
if (item->value) g_free (item->value);
g_free (item);
}
}
static void pref_item_free_cb (gpointer data)
{
pref_item_free ((pref_item *) data);
}
static GList * config_find_key (GList * list, const char * key)
{
GList *i;
pref_item * item;
if (!key || !*key) { /* ignore empty keys (strings) */
return (NULL);
}
for (i = list; i; i = i->next)
{
item = (pref_item *) (i->data);
if (strcasecmp (key, item->key) == 0) {
return (i);
}
}
return (NULL); /* key not found */
}
static void config_replace_key (GList ** list, pref_item * item)
{
GList * found = config_find_key (*list, item->key);
if (found) {
/* only update found item */
pref_item * found_item = (pref_item *) (found->data);
if (strcmp (found_item->value, item->value) == 0) {
pref_item_free (item);
return; /* values are equal, nothing to update */
}
g_free (found_item->value);
found_item->value = g_strdup (item->value);
pref_item_free (item);
} else {
/* append item */
*list = g_list_append (*list, (gpointer) item);
}
}
/** get value, it's always a string **/
static char * config_get_item_value (GList * list, const char * key)
{
GList * ret;
pref_item * item;
ret = config_find_key (list, key);
if (ret) {
item = (pref_item *) (ret->data);
return (item->value);
}
return (NULL); /* key not found */
}
static void config_load_from_file (const char * filename, GList ** out_list)
{
FILE *fp;
char buf[1024];
char * stripped;
char ** keyvalue;
pref_item * item;
fp = fopen (filename, "r");
if (!fp) {
return;
}
/* Read file line by line */
while (fgets (buf, sizeof (buf), fp))
{
stripped = buf;
while (*stripped && *stripped <= 0x20) { // 32 = space
stripped++;
}
if (strlen (stripped) < 3 || *stripped == '#') {
continue;
}
if (!strchr (stripped, '=')) {
continue;
}
item = (pref_item *) g_malloc0 (sizeof (pref_item));
keyvalue = g_strsplit (stripped, "=", 2);
item->key = g_strstrip (keyvalue[0]);
item->value = g_strstrip (keyvalue[1]);
if (!*item->key || !*item->value) {
g_strfreev (keyvalue);
g_free (item);
continue;
}
/// fprintf (stderr, "### %s = %s\n", key, value);
/* Insert or replace item */
config_replace_key (out_list, item);
}
fclose (fp);
return;
}
static void create_extension_handler_list (void)
{
GList * i;
pref_item * item, * item_out;
char ** str_vector;
for (i = PrefsGList; i; i = i->next)
{
item = (pref_item *) (i->data);
if (strncasecmp (item->key, "EXT:", 4) == 0)
{
int w;
str_vector = g_strsplit (item->key + 4, ",", 0);
for (w = 0; str_vector[w]; w++)
{
item_out = (pref_item *) g_malloc0 (sizeof (pref_item));
item_out->key = g_strdup (str_vector[w]);
item_out->value = g_strdup (item->value);
config_replace_key (&ExtensionGList, item_out);
}
g_strfreev (str_vector);
}
}
}
static char * replace_variable (char * txt) /* config_get_string_expanded() */
{
// pre${variable}post : ${Terminal} -e ...
char * pre = NULL, * post = NULL;
char * variable = NULL;
char * variable_value = NULL;
char * new_text = NULL;
char * p, * p2;
if (strlen (txt) < 5) { // at least ${xx}
return (NULL);
}
p = strstr (txt, "${");
if (!p) {
return (NULL); // syntax error
}
if (!strchr (p + 3, '}')) {
return (NULL); // syntax error
}
if (txt[0] != '$' && txt[1] != '$') {
pre = g_strdup (txt);
p2 = strchr (pre, '$');
if (p2) p2 = 0;
}
variable = g_strdup (p + 2); // variable start
p2 = strchr (variable, '}'); // variable end
*p2 = 0; // `Terminal`
post = p2 + 1; // ` -e ...`
variable_value = config_get_item_value (PrefsGList, variable); // xterm
if (variable_value) {
if (pre) {
// pre xterm -e ...
new_text = g_strconcat (pre, variable_value, post, NULL);
} else {
// xterm -e ...
new_text = g_strconcat (variable_value, post, NULL);
}
}
if (pre) g_free (pre);
if (variable) g_free (variable);
return (new_text);
}
// ============================================================
// PUBLIC
// ============================================================
void config_init ()
{
if (PrefsGList) {
return;
}
char config_file[512];
char * HOME;
snprintf (config_file, sizeof (config_file), "/etc/%s", APP_CONFIG_FILE);
config_load_from_file (config_file, &PrefsGList);
HOME = getenv ("HOME");
if (HOME) {
snprintf (config_file, sizeof (config_file), "%s/.%s", HOME, APP_CONFIG_FILE);
config_load_from_file (config_file, &PrefsGList);
}
create_extension_handler_list ();
}
void config_destroy ()
{
if (PrefsGList) {
g_list_free_full (PrefsGList, pref_item_free_cb);
g_list_free_full (ExtensionGList, pref_item_free_cb);
PrefsGList = NULL;
ExtensionGList = NULL;
}
}
void config_reload ()
{
config_destroy ();
config_init ();
}
void config_print ()
{
GList * i;
pref_item * item;
for (i = PrefsGList; i; i = i->next)
{
item = (pref_item *) (i->data);
printf ("%s = %s\n", item->key, item->value);
}
for (i = ExtensionGList; i; i = i->next)
{
item = (pref_item *) (i->data);
printf ("%s = %s\n", item->key, item->value);
}
}
gboolean config_get_int (const char * key, int * out_int)
{
char * value;
value = config_get_item_value (PrefsGList, key);
if (value) {
*out_int = (int) strtoll (value, NULL, 0);
return TRUE;
} else {
*out_int = -1;
return FALSE;
}
}
// returns a string that must be freed with g_free
gboolean config_get_string_expanded (const char * key, char ** out_str)
{
char * value1, * value2, * value = NULL;
value1 = config_get_item_value (PrefsGList, key);
if (value1 && strstr (value1, "${")) {
value2 = replace_variable (value1);
value = value2;
// expand variable up to 2 times
if (value2 && strstr (value2, "${")) {
value = replace_variable (value2);
g_free (value2);
}
} else {
value = g_strdup (value1);
}
if (value) {
*out_str = value;
return TRUE;
} else {
*out_str = NULL;
return FALSE;
}
}
gboolean config_get_string (const char * key, char ** out_str)
{
char * value;
value = config_get_item_value (PrefsGList, key);
if (value) {
*out_str = value;
return TRUE;
} else {
*out_str = NULL;
return FALSE;
}
}
char * config_get_handler_for_extension (const char * extension)
{
char * handler;
if (extension && *extension == '.') {
extension++; // .html -> html
}
handler = config_get_item_value (ExtensionGList, extension);
return (handler);
}

41
src/config_prefs.h Normal file
View File

@ -0,0 +1,41 @@
/*
* This is free and unencumbered software released into the public domain.
*
* For more information, please refer to <https://unlicense.org>
*/
#ifndef __CONFIG_PREFS_H
#define __CONFIG_PREFS_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "gtkcompat.h" // glib-compat.h
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void config_init ();
void config_destroy ();
void config_reload ();
void config_print ();
gboolean config_get_int (const char * key, int * out_int);
/// changes string pointer (must not be freed)
gboolean config_get_string (const char * key, char ** out_str);
/// allocates a string that must be freed with g_free
gboolean config_get_string_expanded (const char * key, char ** out_str);
/// returns a constant string (must not be freed)
char * config_get_handler_for_extension (const char * extension);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -31,8 +31,11 @@
using namespace std;
#include "config_prefs.h"
#include "gtkcompletionline.h"
#define HISTORY_FILE ".gmrun_history"
static int on_cursor_changed_handler = 0;
static int on_key_press_handler = 0;
static guint timeout_id = 0;
@ -60,7 +63,6 @@ enum {
static guint gtk_completion_line_signals[LAST_SIGNAL];
typedef set<string> StrSet;
typedef vector<string> StrList;
static StrSet path;
static StrSet execs;
@ -80,15 +82,17 @@ G_DEFINE_TYPE_EXTENDED (GtkCompletionLine, /* type name */
GTK_TYPE_ENTRY, /* GType of the parent type */
(GTypeFlags)0,
NULL);
static void gtk_completion_line_finalize (GObject *object);
// see also https://developer.gnome.org/gobject/stable/GTypeModule.html#G-DEFINE-DYNAMIC-TYPE:CAPS
/* class_init */
static void gtk_completion_line_class_init (GtkCompletionLineClass *klass)
{
GtkWidgetClass *object_class;
GtkWidgetClass * object_class = GTK_WIDGET_CLASS (klass);
guint s;
object_class = (GtkWidgetClass*)klass;
GObjectClass * basic_class = G_OBJECT_CLASS (klass);
basic_class->finalize = gtk_completion_line_finalize;
s = g_signal_new ("unique",
G_TYPE_FROM_CLASS (object_class),
@ -182,7 +186,7 @@ static void gtk_completion_line_class_init (GtkCompletionLineClass *klass)
s = g_signal_new ("cancel",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GtkCompletionLineClass, ext_handler),
G_STRUCT_OFFSET (GtkCompletionLineClass, cancel),
NULL,
NULL,
g_cclosure_marshal_VOID__POINTER,
@ -212,7 +216,7 @@ static void gtk_completion_line_init (GtkCompletionLine *self)
self->list_compl = NULL;
self->sort_list_compl = NULL;
self->tree_compl = NULL;
self->hist_search_mode = GCL_SEARCH_OFF;
self->hist_search_mode = FALSE;
self->hist_word = new string;
self->tabtimeout = 0;
self->show_dot_files = 0;
@ -226,18 +230,41 @@ static void gtk_completion_line_init (GtkCompletionLine *self)
g_signal_connect(G_OBJECT(self), "scroll-event",
G_CALLBACK(on_scroll), NULL);
self->hist = new HistoryFile();
char * HOME = getenv ("HOME");
char history_file[512] = "";
if (HOME) {
snprintf (history_file, sizeof (history_file), "%s/%s", HOME, HISTORY_FILE);
}
int HIST_MAX_SIZE;
if (!config_get_int ("History", &HIST_MAX_SIZE))
HIST_MAX_SIZE = 20;
self->hist = history_new (history_file, HIST_MAX_SIZE);
// hacks for prev/next will be applied
history_unset_current (self->hist);
self->first_key = 1;
}
void gtk_completion_line_last_history_item(GtkCompletionLine* object) {
const char *last_item = object->hist->last_item();
static void gtk_completion_line_finalize (GObject *object)
{
GtkCompletionLine * self = GTK_COMPLETION_LINE (object);
if (self->hist) {
history_save (self->hist);
//history_print (self->hist); //debug
history_destroy (self->hist);
self->hist = NULL;
}
G_OBJECT_CLASS (gtk_completion_line_parent_class)->finalize (object);
}
// ====================================================================
void gtk_completion_line_last_history_item (GtkCompletionLine* object) {
const char *last_item = history_last (object->hist);
if (last_item) {
object->hist->set_default("");
const char* txt = object->hist->prev();
gtk_entry_set_text(GTK_ENTRY(object),
g_locale_to_utf8 (txt, -1, NULL, NULL, NULL));
gtk_entry_set_text (GTK_ENTRY(object), last_item);
}
}
@ -319,12 +346,9 @@ int set_words(GtkCompletionLine *object, const vector<string>& words, int pos =
g_regex_unref (regex);
if (words.size() == 1) {
const string& s = words.back();
size_t pos = s.rfind('.');
if (pos != string::npos)
g_signal_emit_by_name(G_OBJECT(object), "ext_handler", s.substr(pos + 1).c_str());
else
g_signal_emit_by_name(G_OBJECT(object), "ext_handler", NULL);
const char * dot = NULL;
dot = strrchr (words.back().c_str(), '.');
g_signal_emit_by_name (G_OBJECT(object), "ext_handler", dot);
}
gtk_entry_set_text(GTK_ENTRY(object),
@ -772,135 +796,91 @@ gtk_completion_line_new()
static void
up_history(GtkCompletionLine* cl)
{
cl->hist->set_default(gtk_entry_get_text(GTK_ENTRY(cl)));
gtk_entry_set_text(GTK_ENTRY(cl),
g_locale_to_utf8 (cl->hist->prev(), -1, NULL, NULL, NULL));
static int pause = 0;
const char * text_up;
if (pause == 1) {
text_up = history_last (cl->hist);
pause = 0;
} else {
text_up = history_prev (cl->hist);
if (!text_up) { // empty, set a flag, next time we'll get something
pause = 1;
text_up = "";
}
}
if (text_up) {
gtk_entry_set_text (GTK_ENTRY(cl), text_up);
}
}
static void
down_history(GtkCompletionLine* cl)
{
cl->hist->set_default(gtk_entry_get_text(GTK_ENTRY(cl)));
gtk_entry_set_text(GTK_ENTRY(cl),
g_locale_to_utf8 (cl->hist->next(), -1, NULL, NULL, NULL));
static int pause = 0;
const char * text_down;
if (pause == 1) {
text_down = history_first (cl->hist);
pause = 0;
} else {
text_down = history_next (cl->hist);
if (!text_down) { // empty, set a flag, next time we'll get something
pause = 1;
text_down = "";
}
}
if (text_down) {
gtk_entry_set_text (GTK_ENTRY(cl), text_down);
}
}
static void search_off (GtkCompletionLine* cl)
{
int pos = gtk_editable_get_position (GTK_EDITABLE(cl));
cl->hist_search_mode = FALSE;
g_signal_emit_by_name (G_OBJECT(cl), "search_mode");
history_unset_current (cl->hist);
}
static int
search_back_history(GtkCompletionLine* cl, bool avance, bool begin)
{
if (!cl->hist_word->empty()) {
const char * histext;
if (avance) {
histext = cl->hist->prev_to_first();
if (histext == NULL) {
g_signal_emit_by_name(G_OBJECT(cl), "search_not_found");
return 0;
}
} else {
histext = gtk_entry_get_text(GTK_ENTRY(cl));
}
while (true) {
string s = histext;
string::size_type i;
i = s.find(*cl->hist_word);
if (i != string::npos && !(begin && i != 0)) {
const char *tmp = gtk_entry_get_text(GTK_ENTRY(cl));
if (!(avance && strcmp(tmp, histext) == 0)) {
gtk_entry_set_text(GTK_ENTRY(cl),
g_locale_to_utf8 (histext, -1, NULL, NULL, NULL));
g_signal_emit_by_name(G_OBJECT(cl), "search_letter");
search_history (GtkCompletionLine* cl)
{ // must only be called if cl->hist_search_mode = TRUE
/* a key is pressed and added to cl->hist_word */
if (!cl->hist_word->empty())
{
const char * history_current_item;
const char * search_str;
int search_str_len;
history_current_item = history_first (cl->hist);
search_str = cl->hist_word->c_str();
search_str_len = strlen (search_str);
while (true)
{
const char * s;
s = strstr (history_current_item, search_str);
if (s) {
if (strncmp (history_current_item, search_str, search_str_len) == 0) {
gtk_entry_set_text (GTK_ENTRY(cl), history_current_item);
g_signal_emit_by_name (G_OBJECT(cl), "search_letter");
return 1;
}
}
histext = cl->hist->prev_to_first();
if (histext == NULL) {
g_signal_emit_by_name(G_OBJECT(cl), "search_not_found");
history_current_item = history_next (cl->hist);
if (history_current_item == NULL) {
g_signal_emit_by_name (G_OBJECT(cl), "search_not_found");
break;
}
}
} else {
g_signal_emit_by_name(G_OBJECT(cl), "search_letter");
}
g_signal_emit_by_name (G_OBJECT(cl), "search_letter");
return 0;
return 1;
}
static int search_forward_history(GtkCompletionLine* cl, bool avance, bool begin)
{
if (!cl->hist_word->empty()) {
const char * histext;
if (avance) {
histext = cl->hist->next_to_last();
if (histext == NULL) {
g_signal_emit_by_name(G_OBJECT(cl), "search_not_found");
return 0;
}
} else {
histext = gtk_entry_get_text(GTK_ENTRY(cl));
}
while (true) {
string s = histext;
string::size_type i;
i = s.find(*cl->hist_word);
if (i != string::npos && !(begin && i != 0)) {
const char *tmp = gtk_entry_get_text(GTK_ENTRY(cl));
if (!(avance && strcmp(tmp, histext) == 0)) {
gtk_entry_set_text(GTK_ENTRY(cl),
g_locale_to_utf8 (histext, -1, NULL, NULL, NULL));
g_signal_emit_by_name(G_OBJECT(cl), "search_letter");
return 1;
}
}
histext = cl->hist->next_to_last();
if (histext == NULL) {
g_signal_emit_by_name(G_OBJECT(cl), "search_not_found");
break;
}
}
} else {
g_signal_emit_by_name(G_OBJECT(cl), "search_letter");
}
return 0;
}
static int search_history(GtkCompletionLine* cl, bool avance, bool begin)
{
switch (cl->hist_search_mode) {
case GCL_SEARCH_REW:
case GCL_SEARCH_BEG:
return search_back_history(cl, avance, begin);
case GCL_SEARCH_FWD:
return search_forward_history(cl, avance, begin);
default:
return -1;
}
}
static void search_off(GtkCompletionLine* cl)
{
int pos = gtk_editable_get_position(GTK_EDITABLE(cl));
cl->hist_search_mode = GCL_SEARCH_OFF;
g_signal_emit_by_name(G_OBJECT(cl), "search_mode");
cl->hist->reset_position();
}
#define STOP_PRESS \
(g_signal_stop_emission_by_name(G_OBJECT(cl), "key_press_event"))
#define MODE_BEG \
(cl->hist_search_mode == GCL_SEARCH_BEG)
#define MODE_REW \
(cl->hist_search_mode == GCL_SEARCH_REW)
#define MODE_FWD \
(cl->hist_search_mode == GCL_SEARCH_FWD)
#define MODE_SRC \
(cl->hist_search_mode != GCL_SEARCH_OFF)
static guint tab_pressed(GtkCompletionLine* cl)
{
if (MODE_SRC)
if (cl->hist_search_mode == TRUE)
search_off(cl);
complete_line(cl);
timeout_id = 0;
@ -945,7 +925,7 @@ on_scroll(GtkCompletionLine *cl, GdkEventScroll *event, gpointer data)
} else {
up_history(cl);
}
if (MODE_SRC) {
if (cl->hist_search_mode == TRUE) {
search_off(cl);
}
return TRUE;
@ -959,7 +939,7 @@ on_scroll(GtkCompletionLine *cl, GdkEventScroll *event, gpointer data)
} else {
down_history(cl);
}
if (MODE_SRC) {
if (cl->hist_search_mode == TRUE) {
search_off(cl);
}
return TRUE;
@ -969,7 +949,7 @@ on_scroll(GtkCompletionLine *cl, GdkEventScroll *event, gpointer data)
static gboolean
on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
{
{ // https://developer.gnome.org/gtk2/stable/GtkWidget.html#GtkWidget-key-press-event
if (event->type == GDK_KEY_PRESS)
{
switch (event->keyval) {
@ -987,8 +967,7 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
timeout_id = 0;
}
tab_pressed(cl);
STOP_PRESS;
return TRUE;
return TRUE; /* stop signal emission */
case GDK_KEY_Up:
if (cl->win_compl != NULL) {
@ -1011,16 +990,15 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
} else {
up_history(cl);
}
if (MODE_SRC) {
if (cl->hist_search_mode == TRUE) {
search_off(cl);
}
STOP_PRESS;
return TRUE;
return TRUE; /* stop signal emission */
case GDK_KEY_space:
{
cl->first_key = 0;
bool search = MODE_SRC;
bool search = cl->hist_search_mode;
if (search)
search_off(cl);
if (cl->win_compl != NULL) {
@ -1043,11 +1021,10 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
} else {
down_history(cl);
}
if (MODE_SRC) {
if (cl->hist_search_mode == TRUE) {
search_off(cl);
}
STOP_PRESS;
return TRUE;
return TRUE; /* stop signal emission */
case GDK_KEY_Return:
if (cl->win_compl != NULL) {
@ -1059,61 +1036,30 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
} else {
g_signal_emit_by_name(G_OBJECT(cl), "activate");
}
STOP_PRESS;
return TRUE;
case GDK_KEY_exclam:
if (!MODE_BEG) {
if (!MODE_SRC)
gtk_editable_delete_selection(GTK_EDITABLE(cl));
const char *tmp = gtk_entry_get_text(GTK_ENTRY(cl));
if (!(*tmp == '\0' || cl->first_key))
goto ordinary;
cl->hist_search_mode = GCL_SEARCH_BEG;
cl->hist_word->clear();
g_signal_emit_by_name(G_OBJECT(cl), "search_mode");
STOP_PRESS;
return true;
} else goto ordinary;
case GDK_KEY_R:
case GDK_KEY_r:
if (event->state & GDK_CONTROL_MASK) {
if (MODE_SRC) {
search_back_history(cl, true, MODE_BEG);
} else {
cl->hist_search_mode = GCL_SEARCH_REW;
cl->hist_word->clear();
cl->hist->reset_position();
g_signal_emit_by_name(G_OBJECT(cl), "search_mode");
}
STOP_PRESS;
return TRUE;
} else goto ordinary;
return TRUE; /* stop signal emission */
case GDK_KEY_S:
case GDK_KEY_s:
case GDK_KEY_R:
case GDK_KEY_r:
if (event->state & GDK_CONTROL_MASK) {
if (MODE_SRC) {
search_forward_history(cl, true, MODE_BEG);
} else {
cl->hist_search_mode = GCL_SEARCH_FWD;
if (cl->hist_search_mode == FALSE) {
history_first (cl->hist);
cl->hist_search_mode = TRUE;
cl->hist_word->clear();
cl->hist->reset_position();
g_signal_emit_by_name(G_OBJECT(cl), "search_mode");
}
STOP_PRESS;
return TRUE;
return TRUE; /* stop signal emission */
} else goto ordinary;
case GDK_KEY_BackSpace:
if (MODE_SRC) {
if (cl->hist_search_mode == TRUE) {
if (!cl->hist_word->empty()) {
cl->hist_word->erase(cl->hist_word->length() - 1);
search_history(cl);
g_signal_emit_by_name(G_OBJECT(cl), "search_letter");
}
STOP_PRESS;
return TRUE;
return TRUE; /* stop signal emission */
}
return FALSE;
@ -1123,7 +1069,7 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
goto ordinary;
case GDK_KEY_Escape:
if (MODE_SRC) {
if (cl->hist_search_mode == TRUE) {
search_off(cl);
} else if (cl->win_compl != NULL) {
gtk_widget_destroy(cl->win_compl);
@ -1132,23 +1078,20 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
// user cancelled
g_signal_emit_by_name(G_OBJECT(cl), "cancel");
}
STOP_PRESS;
return TRUE;
return TRUE; /* stop signal emission */
case GDK_KEY_G:
case GDK_KEY_g:
if (event->state & GDK_CONTROL_MASK) {
search_off(cl);
if (MODE_SRC)
STOP_PRESS;
return TRUE;
return TRUE; /* stop signal emission */
} else goto ordinary;
case GDK_KEY_E:
case GDK_KEY_e:
if (event->state & GDK_CONTROL_MASK) {
search_off(cl);
if (MODE_SRC)
if (cl->hist_search_mode == TRUE)
clear_selection(cl);
}
goto ordinary;
@ -1161,13 +1104,11 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
cl->win_compl = NULL;
}
cl->where = NULL;
if (MODE_SRC) {
if (cl->hist_search_mode == TRUE) {
if (event->length > 0) {
*cl->hist_word += event->string;
if (search_history(cl, false, MODE_BEG) <= 0)
cl->hist_word->erase(cl->hist_word->length() - 1);
STOP_PRESS;
return TRUE;
search_history(cl);
return TRUE; /* stop signal emission */
} else
search_off(cl);
}
@ -1187,12 +1128,6 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data)
return FALSE;
}
#undef STOP_PRESS
#undef MODE_BEG
#undef MODE_REW
#undef MODE_FWD
#undef MODE_SRC
// Local Variables: ***
// mode: c++ ***
// c-basic-offset: 2 ***

View File

@ -31,14 +31,6 @@ extern "C++" {
typedef struct _GtkCompletionLine GtkCompletionLine;
typedef struct _GtkCompletionLineClass GtkCompletionLineClass;
enum GCL_SEARCH_MODE
{
GCL_SEARCH_OFF = 0,
GCL_SEARCH_REW = 1,
GCL_SEARCH_FWD = 2,
GCL_SEARCH_BEG = 3
};
struct _GtkCompletionLine
{
GtkEntry parent;
@ -53,7 +45,7 @@ extern "C++" {
GList *where; /* current row pointer ??? */
HistoryFile *hist;
GCL_SEARCH_MODE hist_search_mode;
gboolean hist_search_mode;
std::string *hist_word;
int first_key;

308
src/history.c Normal file
View File

@ -0,0 +1,308 @@
/*
* This is free and unencumbered software released into the public domain.
*
* For more information, please refer to <https://unlicense.org>
*/
/*
* Generic implementation of HistoryFile
* HOWTO: see example at the end of the file
*/
#include "history.h"
// ============================================================
// PRIVATE
// ============================================================
struct _Whistory
{
long int index;
unsigned int count;
unsigned int max;
char * filename;
GList * list;
GList * current;
};
static void _history_clear (HistoryFile * history)
{
// keep history->filename (destroyed in _history_free())
if (history->list) {
GList * i ;
for (i = history->list; i; i = i->next) {
if (i->data) g_free (i->data);
}
g_list_free (history->list);
history->list = NULL;
}
history->index = 0;
history->count = 0;
}
static void _history_free (HistoryFile * history)
{
_history_clear (history);
if (history->filename) {
g_free (history->filename);
history->filename = NULL;
}
g_free (history);
}
static void _history_load_from_file (HistoryFile * history, const char * filename)
{
FILE *fp;
char buf[1024];
char * p, * item;
size_t len;
unsigned int count = 0;
unsigned int max = history->max;
GList * out_list = NULL;
fp = fopen (filename, "r");
if (!fp) {
return;
}
/* Read file line by line */
while (fgets (buf, sizeof (buf), fp))
{
p = buf;
while (*p && *p <= 0x20) { // 32 = space [ignore spaces]
p++;
}
if (!*p) {
continue;
}
item = g_strdup (p);
len = strlen (buf);
item[len-1] = 0;
if (max > 0 && count >= max) {
break;
}
count++;
out_list = g_list_prepend (out_list, (gpointer) item);
}
if (out_list) {
out_list = g_list_reverse (out_list);
history->index = 1;
}
history->count = count;
history->list = out_list;
history->current = history->list; // current = 1st item
fclose (fp);
return;
}
void _history_write_to_file (HistoryFile * history, const char * filename)
{
FILE *fp;
fp = fopen (filename, "w");
if (!fp) {
return;
}
unsigned int count = 0;
unsigned int max = history->max;
GList * i;
for (i = history->list; i; i = i->next)
{
if (max > 0 && count >= max) {
break;
}
count++;
fprintf (fp, "%s\n", (char *) (i->data));
}
fclose (fp);
return;
}
// ============================================================
// PUBLIC
// ============================================================
HistoryFile * history_new (char * filename, unsigned int maxcount)
{
HistoryFile * history = g_malloc0 (sizeof (HistoryFile));
history->max = maxcount;
if (filename || *filename) {
history->filename = g_strdup (filename);
_history_load_from_file (history, filename);
}
return (history);
}
void history_save (HistoryFile * history)
{
if (history && history->filename) {
_history_write_to_file (history, history->filename);
} else {
fprintf (stderr, "history_save(): history or filename is NULL\n");
}
}
void history_destroy (HistoryFile * history)
{
if (history) {
_history_free (history);
}
}
void history_reload (HistoryFile * history)
{
if (history) {
_history_clear (history);
if (history->filename) {
_history_load_from_file (history, history->filename);
}
}
}
void history_print (HistoryFile * history)
{
if (history) {
unsigned int count = 0;
GList * i;
for (i = history->list; i; i = i->next) {
count++;
printf ("[%d] %s\n", count, (char *) (i->data));
}
printf ("-- list internal count: [%d]\n", history->count);
}
}
// some apps might want to handle prev/next in a special way
void history_unset_current (HistoryFile * history)
{
if (history) {
history->current = NULL;
history->index = -1;
}
}
const char * history_get_current (HistoryFile * history)
{
if (history) return ((char*) (history->current->data));
else return (NULL);
}
int history_get_current_index (HistoryFile * history)
{
if (history) return (history->index);
else return (-1);
}
const char * history_next (HistoryFile * history)
{
if (history->current && history->current->next) {
history->current = history->current->next;
history->index++;
return ((char *) (history->current->data));
}
return (NULL);
}
const char * history_prev (HistoryFile * history)
{
if (history->current && history->current->prev) {
history->current = history->current->prev;
history->index--;
return ((char *) (history->current->data));
}
return (NULL);
}
const char * history_first (HistoryFile * history)
{
if (history->list) {
history->current = history->list;
history->index = 1;
return ((char *) (history->current->data));
}
return (NULL);
}
const char * history_last (HistoryFile * history)
{
if (history->list) {
history->current = g_list_last (history->list);
return ((char *) (history->current->data));
history->index = history->count;
}
return (NULL);
}
void history_append (HistoryFile * history, const char * text)
{
if (!text || !*text) {
return;
}
unsigned int remove_first = 0;
if (history->max > 0
&& history->count > 1
&& history->count >= history->max) {
remove_first = 1;
}
char * new_item = g_strdup (text);
history->list = g_list_append (history->list, (gpointer) new_item);
if (history->count < history->max) {
history->count++;
}
if (remove_first && history->list->next) {
gpointer data = history->list->data;
history->list = g_list_remove (history->list, data);
}
}
// ============================================================
// example
// ============================================================
/*
// gcc -o history history.c -Wall -g -O2 `pkg-config --cflags glib-2.0` `pkg-config --libs glib-2.0`
int main (int argc, char ** argv)
{
#define HISTORY_FILE ".gmrun_history"
char * HOME = getenv ("HOME");
char history_file[512] = "";
if (HOME) {
snprintf (history_file, sizeof (history_file), "%s/%s", HOME, HISTORY_FILE);
}
HistoryFile * history;
history = history_new (history_file, 10);
history_print (history);
history_append (history, "new line");
history_print (history);
history_append (history, "soldier dream");
history_print (history);
history_destroy (history);
history = NULL;
return (0);
}
*/

View File

@ -1,158 +0,0 @@
/*****************************************************************************
* $Id: history.cc,v 1.10 2002/08/17 13:19:31 mishoo Exp $
* Copyright (C) 2000, Mishoo
* Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro
*
* Distributed under the terms of the GNU General Public License. You are
* free to use/modify/distribute this program as long as you comply to the
* terms of the GNU General Public License, version 2 or above, at your
* option, and provided that this copyright notice remains intact.
*****************************************************************************/
#include <glib.h>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
#include "history.h"
#include "prefs.h"
HistoryFile::HistoryFile()
{
m_file_entries = 0;
m_filename = g_get_home_dir();
m_filename += "/.gmrun_history";
m_current = 0;
m_default_set = false;
read_the_file();
}
HistoryFile::~HistoryFile()
{}
void HistoryFile::read_the_file()
{
const char *filename = m_filename.c_str();
ifstream f(filename);
if (!f) return;
string line_text;
while (!f.eof()) {
string line_str;
getline(f,line_text);
line_str = line_text;
history.push_back(line_str);
}
m_file_entries = history.size();
m_current = m_file_entries;
}
void HistoryFile::sync_the_file()
{
const char *filename = m_filename.c_str();
int HIST_MAX_SIZE;
if (!configuration.get_int("History", HIST_MAX_SIZE))
HIST_MAX_SIZE = 20;
ofstream f(filename, ios::out);
int start = 0;
if (history.size() > (size_t)HIST_MAX_SIZE)
start = history.size() - HIST_MAX_SIZE;
for (size_t i = start; i < history.size(); i++)
if (history[i].length() != 0)
f << history[i] << endl;
f.flush();
}
void HistoryFile::append(const char *entry)
{
std::string ent = std::string(entry);
if (!history.empty()) {
StrArray::reverse_iterator i;
#ifdef DEBUG
for_each(history.begin(), history.end(), DumpString(cerr));
#endif
i = find(history.rbegin(), history.rend(), ent);
if (i != history.rend()) {
#ifdef DEBUG
cerr << "erasing "<< ent << endl;
#endif
history.erase(remove(history.begin(), history.end(), ent));
}
}
history.push_back(ent);
}
void HistoryFile::set_default(const char *defstr)
{
if (!m_default_set) {
m_default = defstr;
m_default_set = true;
}
}
const char * HistoryFile::operator [] (size_t index)
{
if (index >= history.size()) {
return m_default.c_str();
}
return history[index].c_str();
}
const char *
HistoryFile::prev()
{
const char *ret = (*this)[--m_current];
if (m_current < 0) m_current = history.size();
return ret;
}
const char *
HistoryFile::next()
{
const char *ret = (*this)[++m_current];
if ((guint)m_current >= history.size()) m_current = -1;
return ret;
}
const char *
HistoryFile::prev_to_first()
{
if (m_current > 0) {
return (*this)[--m_current];
} else {
return NULL;
}
}
const char * HistoryFile::next_to_last()
{
if ((guint)m_current < history.size()) {
return (*this)[++m_current];
} else {
return NULL;
}
}
void HistoryFile::clear_default()
{
m_default_set = false;
}
void HistoryFile::reset_position()
{
m_current = m_file_entries;
}

View File

@ -1,71 +1,77 @@
/*****************************************************************************
* $Id: history.h,v 1.11 2002/08/17 13:19:31 mishoo Exp $
* Copyright (C) 2000, Mishoo
* Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro
/*
* This is free and unencumbered software released into the public domain.
*
* Distributed under the terms of the GNU General Public License. You are
* free to use/modify/distribute this program as long as you comply to the
* terms of the GNU General Public License, version 2 or above, at your
* option, and provided that this copyright notice remains intact.
*****************************************************************************/
* For more information, please refer to <https://unlicense.org>
*/
/*
* Generic implementation of HistoryFile
*/
#ifndef __HISTORY_H__
#define __HISTORY_H__
#ifndef __HISTORY_H
#define __HISTORY_H
#include <vector>
#include <string>
using namespace std;
class HistoryFile
#ifdef __cplusplus
extern "C"
{
protected:
int m_file_entries;
string m_filename;
string m_default;
bool m_default_set;
int m_current;
#endif
typedef vector<string> StrArray;
StrArray history;
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
public:
HistoryFile();
~HistoryFile();
typedef struct _Whistory HistoryFile;
void append(const char *entry);
void set_default(const char *defstr);
void clear_default();
/// create a new HistoryFile
/// the history is initialized using filename and the filename is stored
/// maxcount > 0 sets a maximun item count
HistoryFile * history_new (char * filename, unsigned int maxcount);
void reset_position();
/// history is saved to file
void history_save (HistoryFile * history);
const char * operator [] (size_t index);
/// history is destroyed, you must variable to NULL
void history_destroy (HistoryFile * history);
const char * prev();
const char * next();
/// clear history and reload from file
void history_reload (HistoryFile * history);
const char * prev_to_first();
const char * next_to_last();
/// print history, good for debugging
void history_print (HistoryFile * history);
void sync_the_file();
/// some apps might want to handle prev/next in a special way
void history_unset_current (HistoryFile * history);
inline const char* last_item() {
return history.empty() ? 0 : history.back().c_str();
}
/// returns string with the current history item
const char * history_get_current (HistoryFile * history);
inline const char* first_item() {
return history.empty() ? 0 : history.front().c_str();
}
/// returns current index: valid index > 0
/// assume unser current item if index <= 0
int history_get_current_index (HistoryFile * history);
protected:
void read_the_file();
private:
struct DumpString {
DumpString(std::ostream& o) : _out(o) {}
void operator()(std::string& str) { _out << str << endl; }
private:
std::ostream& _out;
};
};
/// moves position to the next entry and returns text
/// if there is no next entry, the position is not moved and returns NULL
const char * history_next (HistoryFile * history);
#endif // __HISTORY_H__
/// moves position to the previous entry and returns text
/// if there is no next entry, the position is not moved and returns NULL
const char * history_prev (HistoryFile * history);
/// moves position to the first entry en returns text
/// if there are no entries, it returns NULL
const char * history_first (HistoryFile * history);
/// moves position to the last entry en returns text
/// if there are no entries, it returns NULL
const char * history_last (HistoryFile * history);
/// append item to history
void history_append (HistoryFile * history, const char * text);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -15,15 +15,6 @@
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
#ifdef MTRACE
#include <mcheck.h>
#endif
@ -32,7 +23,7 @@ using namespace std;
#include <errno.h>
#include "gtkcompletionline.h"
#include "prefs.h"
#include "config_prefs.h"
enum
{
@ -44,10 +35,7 @@ enum
static void gmrun_exit (void);
GtkAllocation window_geom = { -1, -1, -1, -1 };
// defined in gtkcompletionline.cc
int get_words(GtkCompletionLine *object, vector<string>& words);
string quote_string(const string& str);
GtkWidget * compline;
struct gigi
{
@ -98,9 +86,11 @@ static void set_info_text_color (GtkWidget *w, const char *text, int spec)
}
static void
run_the_command(const std::string& command, struct gigi* g)
run_the_command(const char * cmd, struct gigi* g)
{
const char * cmd = command.c_str();
#if DEBUG
fprintf (stderr, "command: %s\n", cmd);
#endif
GError * error = NULL;
gboolean success;
int argc;
@ -130,57 +120,44 @@ run_the_command(const std::string& command, struct gigi* g)
static void
on_ext_handler(GtkCompletionLine *cl, const char* ext, struct gigi* g)
{
string cmd;
if (ext && configuration.get_ext_handler(ext, cmd)) {
string str("Handler: ");
size_t pos = cmd.find_first_of(" \t");
if (pos == string::npos)
str += cmd;
else
str += cmd.substr(0, pos);
gtk_label_set_text(GTK_LABEL(g->w2), str.c_str());
gtk_widget_show(g->w2);
// gtk_timeout_add(1000, GSourceFunc(search_off_timeout), g);
} else {
search_off_timeout(g);
if (!ext) {
search_off_timeout (g);
return;
}
const char * handler = config_get_handler_for_extension (ext);
if (handler) {
char * tmp = g_strconcat ("Handler: ", handler, NULL);
gtk_label_set_text (GTK_LABEL(g->w2), tmp);
gtk_widget_show (g->w2);
g_free (tmp);
}
}
static void on_compline_runwithterm(GtkCompletionLine *cl, struct gigi* g)
static void on_compline_runwithterm (GtkCompletionLine *cl, struct gigi* g)
{
string command(g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)),
-1,
NULL,
NULL,
NULL));
string tmp;
string term;
char cmd[512];
char * term;
char * entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY(cl)));
g_strstrip (entry_text);
string::size_type i;
i = command.find_first_not_of(" \t");
if (i != string::npos) {
if (!configuration.get_string("TermExec", term)) {
term = "xterm -e";
}
tmp = term;
tmp += " ";
tmp += command;
} else {
if (!configuration.get_string("Terminal", term)) {
tmp = "xterm";
if (*entry_text) {
if (config_get_string_expanded ("TermExec", &term)) {
snprintf (cmd, sizeof (cmd), "%s %s", term, entry_text);
g_free (term);
} else {
tmp = term;
snprintf (cmd, sizeof (cmd), "xterm -e %s", entry_text);
}
} else {
if (config_get_string ("Terminal", &term)) {
strncpy (cmd, term, sizeof (cmd));
} else {
strncpy (cmd, "xterm", sizeof (cmd));
}
}
#ifdef DEBUG
cerr << tmp << endl;
#endif
cl->hist->append(command.c_str());
cl->hist->sync_the_file();
run_the_command(tmp.c_str(), g);
history_append (cl->hist, cmd);
run_the_command (cmd, g);
g_free (entry_text);
}
static gint search_off_timeout(struct gigi *g)
@ -215,7 +192,7 @@ on_compline_incomplete(GtkCompletionLine *cl, struct gigi *g)
static void
on_search_mode(GtkCompletionLine *cl, struct gigi *g)
{
if (cl->hist_search_mode != GCL_SEARCH_OFF) {
if (cl->hist_search_mode == TRUE) {
gtk_widget_show(g->w2);
gtk_label_set_text(GTK_LABEL(g->w1), "Search:");
gtk_label_set_text(GTK_LABEL(g->w2), cl->hist_word->c_str());
@ -236,6 +213,7 @@ static gint
search_fail_timeout(struct gigi *g)
{
set_info_text_color(g->w1, "Search:", W_TEXT_STYLE_NOTUNIQUE);
g_search_off_timeout_id = 0;
return FALSE;
}
@ -246,144 +224,134 @@ on_search_not_found(GtkCompletionLine *cl, struct gigi *g)
add_search_off_timeout(1000, g, GSourceFunc(search_fail_timeout));
}
static bool url_check(GtkCompletionLine *cl, struct gigi *g)
static bool url_check(GtkCompletionLine *cl, struct gigi *g, char * entry_text)
{
string text(g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)),
-1,
NULL,
NULL,
NULL));
// <url_type> <delim> <url>
// http : //www.fsf.org
// <f u l l u r l>
// config: URL_<url_type>
// handler %s (format 1) = run handler with <url>
// handler %u (format 2) = run handler with <full url>
char * cmd;
char * tmp, * delim, * p;
char * url, * url_type, * full_url, * chosen_url;
char * url_handler;
char * config_key;
string::size_type i;
string::size_type sp;
delim = strchr (entry_text, ':');
if (!delim || !*(delim+1)) {
return FALSE;
}
tmp = g_strdup (entry_text);
delim = strchr (tmp, ':');
*delim = 0;
url_type = tmp; // http
url = delim + 1; // //www.fsf.org
full_url = entry_text;
sp = text.find_first_not_of(" \t");
if (sp == string::npos) return true;
text = text.substr(sp);
sp = text.find_first_of(" \t");
i = text.find(":");
if (i != string::npos && i < sp) {
// URL entered...
string url(text.substr(i + 1));
string url_type(text.substr(0, i));
string url_handler;
if (configuration.get_string(string("URL_") + url_type, url_handler)) {
string::size_type j = 0;
do {
j = url_handler.find("%s", j);
if (j != string::npos) {
url_handler.replace(j, 2, url);
}
} while (j != string::npos);
j = 0;
do {
j = url_handler.find("%u", j);
if (j != string::npos) {
url_handler.replace(j, 2, text);
}
} while (j != string::npos);
cl->hist->append(text.c_str());
cl->hist->sync_the_file();
run_the_command(url_handler.c_str(), g);
return true;
config_key = g_strconcat ("URL_", url_type);
if (config_get_string_expanded (config_key, &url_handler))
{
chosen_url = url;
p = strchr (url_handler, '%');
if (p) { // handler %s
p++;
if (*p == 'u') { // handler %u
*p = 's'; // convert %u to %s (for printf)
chosen_url = full_url;
}
cmd = g_strdup_printf (url_handler, chosen_url);
} else {
set_info_text_color(g->w1,
(string("No URL handler for [") + url_type + "]").c_str(),
W_TEXT_STYLE_NOTFOUND);
add_search_off_timeout(1000, g);
return true;
cmd = g_strconcat (url_handler, " ", url, NULL);
}
g_free (url_handler);
}
return false;
if (cmd) {
history_append (cl->hist, cmd);
run_the_command (cmd, g);
g_free (cmd);
} else {
g_free (tmp);
tmp = g_strconcat ("No URL handler for [", config_key, "]", NULL);
set_info_text_color (g->w1, tmp, W_TEXT_STYLE_NOTFOUND);
add_search_off_timeout (1000, g);
}
g_free (entry_text);
g_free (config_key);
g_free (tmp);
return TRUE;
}
static bool ext_check(GtkCompletionLine *cl, struct gigi *g)
static bool ext_check (GtkCompletionLine *cl, struct gigi *g, char * entry_text)
{
vector<string> words;
get_words(cl, words);
vector<string>::const_iterator
i = words.begin(),
i_end = words.end();
GRegex *regex = g_regex_new (" ", G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY, NULL);
while (i != i_end) {
gchar *quoted = g_regex_replace_literal (
regex, (*i++).c_str(), -1, 0, "\\ ", G_REGEX_MATCH_NOTEMPTY, NULL);
const string w = quoted;
if (w[0] == '/') {
// absolute path, check for extension
size_t pos = w.rfind('.');
if (pos != string::npos) {
// we have extension
string ext = w.substr(pos + 1);
string ext_handler;
if (configuration.get_ext_handler(ext, ext_handler)) {
// we have the handler
pos = ext_handler.find("%s");
if (pos != string::npos)
ext_handler.replace(pos, 2, w);
cl->hist->append(w.c_str());
cl->hist->sync_the_file();
run_the_command(ext_handler.c_str(), g);
return true;
}
}
}
g_free(quoted);
// FIXME: for now we check only one entry
break;
// example: file.html -> `xdg-open %s` -> `xdg-open file.html`
char * cmd;
char * ext = strrchr (entry_text, '.');
char * handler_format = NULL;
if (ext) {
handler_format = config_get_handler_for_extension (ext);
}
g_regex_unref(regex);
return false;
if (handler_format) {
if (strchr (handler_format, '%')) { // xdg-open %s
cmd = g_strdup_printf (handler_format, entry_text);
} else { // xdg-open
cmd = g_strconcat (handler_format, " ", entry_text, NULL);
}
history_append (cl->hist, cmd);
run_the_command (cmd, g);
g_free (entry_text);
g_free (cmd);
return TRUE;
}
return FALSE;
}
static void on_compline_activated(GtkCompletionLine *cl, struct gigi *g)
static void on_compline_activated (GtkCompletionLine *cl, struct gigi *g)
{
if (url_check(cl, g))
char * entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY(cl)));
g_strstrip (entry_text);
if (url_check(cl, g, entry_text))
return;
if (ext_check(cl, g))
if (ext_check(cl, g, entry_text))
return;
string command = g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)),
-1,
NULL,
NULL,
NULL);
char cmd[512];
char * AlwaysInTerm = NULL;
char ** term_progs = NULL;
char * selected_term_prog = NULL;
string::size_type i;
i = command.find_first_not_of(" \t");
if (i != string::npos) {
string::size_type j = command.find_first_of(" \t", i);
string progname = command.substr(i, j - i);
list<string> term_progs;
if (configuration.get_string_list("AlwaysInTerm", term_progs)) {
#ifdef DEBUG
cerr << "---" << std::endl;
std::copy(term_progs.begin(), term_progs.end(),
std::ostream_iterator<string>(cerr, "\n"));
cerr << "---" << std::endl;
#endif
list<string>::const_iterator w =
std::find(term_progs.begin(), term_progs.end(), progname);
if (w != term_progs.end()) {
on_compline_runwithterm(cl, g);
return;
if (config_get_string ("AlwaysInTerm", &AlwaysInTerm))
{
term_progs = g_strsplit (AlwaysInTerm, " ", 0);
int i;
for (i = 0; term_progs[i]; i++) {
if (strcmp (term_progs[i], entry_text) == 0) {
selected_term_prog = g_strdup (term_progs[i]);
break;
}
}
cl->hist->append(command.c_str());
cl->hist->sync_the_file();
run_the_command(command, g);
g_strfreev (term_progs);
}
if (selected_term_prog) {
char * TermExec;
config_get_string_expanded ("TermExec", &TermExec);
snprintf (cmd, sizeof (cmd), "%s %s", TermExec, selected_term_prog);
g_free (selected_term_prog);
g_free (TermExec);
} else {
strncpy (cmd, entry_text, sizeof (cmd));
}
g_free (entry_text);
history_append (cl->hist, cmd);
run_the_command (cmd, g);
}
// =============================================================
@ -391,7 +359,7 @@ static void on_compline_activated(GtkCompletionLine *cl, struct gigi *g)
static void gmrun_activate(void)
{
GtkWidget *dialog, * main_vbox;
GtkWidget *compline;
GtkWidget *label_search;
struct gigi g;
@ -428,11 +396,11 @@ static void gmrun_activate(void)
gtk_box_pack_start (GTK_BOX (main_vbox), compline, TRUE, TRUE, 0);
// don't show files starting with "." by default
if (!configuration.get_int("ShowDotFiles", GTK_COMPLETION_LINE(compline)->show_dot_files))
if (config_get_int ("ShowDotFiles", &(GTK_COMPLETION_LINE(compline)->show_dot_files))) {
GTK_COMPLETION_LINE(compline)->show_dot_files = 0;
{
int tmp;
if (configuration.get_int("TabTimeout", tmp))
}
int tmp;
if (config_get_int ("TabTimeout", &tmp)) {
((GtkCompletionLine*)compline)->tabtimeout = tmp;
}
@ -464,7 +432,7 @@ static void gmrun_activate(void)
G_CALLBACK(on_ext_handler), &g);
int shows_last_history_item;
if (!configuration.get_int("ShowLast", shows_last_history_item)) {
if (!config_get_int ("ShowLast", &shows_last_history_item)) {
shows_last_history_item = 0;
}
if (shows_last_history_item) {
@ -484,8 +452,8 @@ static void gmrun_activate(void)
gtk_window_set_default_size (GTK_WINDOW (dialog), window_geom.width,
window_geom.height);
} else {
/* default width = 400 */
gtk_window_set_default_size (GTK_WINDOW (dialog), 400, -1);
/* default width = 450 */
gtk_window_set_default_size (GTK_WINDOW (dialog), 450, -1);
}
// window icon
@ -537,9 +505,9 @@ static void parse_command_line (int argc, char ** argv)
if (!geometry_str)
{
// --geometry was not specified, see config file
std::string geomstr;
if (configuration.get_string("Geometry", geomstr)) {
geometry_str = g_strdup (geomstr.c_str());
char * geomstr;
if (config_get_string ("Geometry", &geomstr)) {
geometry_str = g_strdup (geomstr);
}
}
@ -591,6 +559,8 @@ static void parse_command_line (int argc, char ** argv)
void gmrun_exit(void)
{
gtk_widget_destroy (compline);
config_destroy ();
gtk_main_quit ();
}
@ -603,6 +573,7 @@ int main(int argc, char **argv)
gtk_init(&argc, &argv);
config_init ();
parse_command_line (argc, argv);
gmrun_activate ();

View File

@ -1,178 +0,0 @@
/*****************************************************************************
* $Id: prefs.cc,v 1.9 2002/08/16 10:48:22 mishoo Exp $
* Copyright (C) 2000, Mishoo
* Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro
*
* Distributed under the terms of the GNU General Public License. You are
* free to use/modify/distribute this program as long as you comply to the
* terms of the GNU General Public License, version 2 or above, at your
* option, and provided that this copyright notice remains intact.
*****************************************************************************/
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#define GMRUNRC "gmrunrc"
using std::ifstream;
using std::getline;
using std::string;
using std::list;
#include "prefs.h"
#include "config.h"
Prefs configuration;
Prefs::Prefs()
{
string file_name = "/etc/";
file_name += GMRUNRC;
init(file_name);
file_name = getenv("HOME");
if (!file_name.empty()) {
string::iterator i = file_name.end() - 1;
if (*i == '/') file_name.erase(i);
file_name += "/.";
file_name += GMRUNRC;
init(file_name);
}
}
Prefs::~Prefs()
{}
bool Prefs::get_string(const string& key, string& val) const
{
CONFIG::const_iterator i;
i = vals_.find(ci_string(key.c_str()));
if (i != vals_.end()) {
val = process(i->second);
return true;
} else {
return false;
}
}
bool Prefs::get_ext_handler(const std::string& ext, std::string& val) const
{
CONFIG::const_iterator i;
i = exts_.find(ci_string(ext.c_str()));
if (i != exts_.end()) {
val = process(i->second);
return true;
} else {
return false;
}
}
bool Prefs::get_int(const std::string& key, int& val) const
{
string sval;
if (get_string(key, sval)) {
int ret;
if (sscanf(sval.c_str(), "%d", &ret) == 1) {
val = ret;
return true;
} else {
return false;
}
} else {
return false;
}
}
bool Prefs::init(const string& file_name)
{
ifstream f(file_name.c_str());
if (!f.good() || f.eof()) return false;
while (f.good() && !f.eof()) {
string line;
char key[0x100];
char val[0x100];
getline(f, line);
if (f.eof()) break;
string::size_type i = line.find_first_not_of(" \t");
if (i == string::npos) continue;
if (line[i] == '#') continue;
sscanf(line.c_str(), "%255[a-zA-Z_0-9:,;] = %255[^\n]", key, val);
if (strncmp(key, "EXT:", 4) == 0) {
string k(key + 4);
size_t i = 0;
while (i != string::npos) {
string ext;
size_t j = k.find(',', i);
if (j != string::npos) {
ext = k.substr(i, j - i);
i = j + 1;
} else {
ext = k.substr(i);
i = string::npos;
}
if (ext.length() > 0) {
#ifdef DEBUG
std::cerr << "Extension: " << ext << " - " << val << std::endl;
#endif
exts_[ext.c_str()] = val;
}
}
} else {
vals_[key] = val;
}
#ifdef DEBUG
std::cerr << "Key: " << key << ", val: " << vals_[key] << std::endl;
#endif
}
return true;
}
string Prefs::process(const std::string& cmd) const
{
string::size_type i = cmd.find("${");
string::size_type j = cmd.find("}", i);
if (i == string::npos || j == string::npos) {
return cmd;
}
string val;
if (!get_string(cmd.substr(i + 2, j - i - 2), val)) return cmd;
string ret(cmd);
ret.replace(i, j - i + 1, val);
return process(ret);
}
bool Prefs::get_string_list(const string& key, list<string>& val) const
{
string sval;
if (get_string(key, sval)) {
const char *q, *p = sval.c_str();
for (q = sval.c_str(); *q; ++q) {
if (::isspace(*q)) {
string s(p, q - p);
val.push_back(s);
while (*q && ::isspace(*q)) ++q;
p = q;
}
}
if (p != q) {
string s(p, q - p);
val.push_back(s);
}
return (val.size() > 0);
}
return false;
}

View File

@ -1,45 +0,0 @@
/*****************************************************************************
* $Id: prefs.h,v 1.6 2002/08/16 10:30:18 mishoo Exp $
* Copyright (C) 2000, Mishoo
* Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro
*
* Distributed under the terms of the GNU General Public License. You are
* free to use/modify/distribute this program as long as you comply to the
* terms of the GNU General Public License, version 2 or above, at your
* option, and provided that this copyright notice remains intact.
*****************************************************************************/
#ifndef __PREFS_H__
#define __PREFS_H__
#include "ci_string.h"
#include <map>
#include <list>
class Prefs
{
public:
typedef std::map<ci_string, std::string> CONFIG;
typedef std::pair<ci_string, std::string> PAIR;
private:
CONFIG vals_;
CONFIG exts_;
bool init(const std::string& file_name);
string process(const std::string& cmd) const;
public:
Prefs();
~Prefs();
bool get_string(const std::string& key, std::string& val) const;
bool get_int(const std::string& key, int& val) const;
bool get_string_list(const std::string& ket, std::list<string>& val) const;
bool get_ext_handler(const std::string& ext, std::string& val) const;
};
extern Prefs configuration;
#endif /* __PREFS_H__ */