1 #ifdef COPYRIGHT_INFORMATION
2 #include "gplv3.h"
3 #endif
4 /*
5  * Copyright (C) 2002-2012 Edscott Wilson Garcia
6  * EMail: edscott@users.sf.net
7  *
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program;
21  */
22 #ifdef HAVE_READLINE_HISTORY_H
23 # include <readline/history.h>
24 #endif
25 
place_cursor(GtkTextView * status,gint pos)26 static void place_cursor(GtkTextView *status, gint pos){
27     GtkTextIter iter;
28     GtkTextBuffer *buffer = gtk_text_view_get_buffer (status);
29     gtk_text_buffer_get_iter_at_offset (buffer, &iter, 2+pos);
30     gtk_text_buffer_place_cursor (buffer, &iter);
31 }
32 
place_command(GtkWidget * textview,widgets_t * widgets_p,const gchar * p)33 static void place_command(GtkWidget *textview, widgets_t *widgets_p, const gchar *p){
34     rfm_status (widgets_p, "xffm/emblem_terminal", (char *) p, NULL);
35     g_object_set_data (G_OBJECT (textview), "clean", NULL);
36     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW  (widgets_p->status), TRUE);
37     gtk_widget_grab_focus (widgets_p->status);
38 }
39 
40 static void
print_suggestion(widgets_t * widgets_p,const char * data,int i,gboolean cr)41 print_suggestion (
42     widgets_t * widgets_p,
43     const char *data,
44     int i,
45     gboolean cr
46 ) {
47     gchar *element = g_strdup_printf ("%d", i);
48     rfm_diagnostics (widgets_p, NULL, "[", NULL);
49     rfm_diagnostics (widgets_p, "xffm_tag/green", element, NULL);
50     rfm_diagnostics (widgets_p, NULL, "] ", NULL);
51     rfm_diagnostics (widgets_p, "xffm_tag/blue", data, NULL);
52     if(cr)
53         rfm_diagnostics (widgets_p, NULL, "\n", NULL);
54 
55     g_free (element);
56 }
57 
58 static gboolean
internal_cd(widgets_t * widgets_p,gchar ** argvp)59 internal_cd ( widgets_t * widgets_p, gchar ** argvp) {
60     view_t *view_p = widgets_p->view_p;
61     gchar *gg=NULL;
62 
63     if(argvp[1]) {
64 	if (*argvp[1] == '~'){
65 	    if (strcmp(argvp[1], "~")==0 ||
66 		    strncmp(argvp[1], "~/", strlen("~/"))==0){
67 		gg = g_strdup_printf("%s%s", g_get_home_dir (), argvp[1]+1);
68 	    } else {
69 		//gchar *tilde_dir = rfm_get_tilde_dir(argvp[1]);
70 		gchar *tilde_dir =
71 		    rfm_natural(RFM_MODULE_DIR, "completion",
72 			    argvp[1], "rfm_get_tilde_dir");
73 		if (tilde_dir) gg = g_strconcat(tilde_dir,
74 			strchr(argvp[1], '/')+1, NULL);
75 		else gg = g_strdup(argvp[1]);
76 		g_free(tilde_dir);
77 	    }
78 	} else {
79 	    gg = g_strdup(argvp[1]);
80 	}
81 
82     } else {
83         gg = g_strdup(g_get_home_dir ());
84     }
85     rfm_show_text (widgets_p);
86     NOOP ("CD: gg=%s argv[1]=%s\n", gg, argvp[1]);
87 
88     // must allow relative paths too.
89     if (!g_path_is_absolute(gg)){
90 	if(!view_p->en || ! view_p->en->path ||
91 	    !rfm_g_file_test_with_wait (view_p->en->path, G_FILE_TEST_IS_DIR))
92 	{
93 	    rfm_diagnostics (widgets_p, "xffm/stock_dialog-error", NULL);
94 	    rfm_diagnostics (widgets_p, "xffm_tag/stderr", "* ", gg, ": ", strerror (ENOENT), "\n", NULL);
95 	    g_free (gg);
96 	    return TRUE;
97         }
98 	gchar *fullpath = g_strconcat(view_p->en->path, "/", gg, NULL);
99 	g_free(gg);
100 	gg = fullpath;
101 	NOOP("CD: fullpath=%s\n", fullpath);
102     }
103 
104     if (gg[strlen(gg)-1]=='/' || strstr(gg, "/..")){
105 	gchar *rpath = realpath(gg, NULL);
106 	g_free(gg);
107 	gg=rpath;
108     }
109     rfm_diagnostics (widgets_p, "xffm_tag/command", "cd", " ", NULL);
110     rfm_diagnostics (widgets_p, "xffm_tag/green", gg, "\n", NULL);
111 
112     gchar *rpath = realpath(gg, NULL);
113     if (!rpath){
114 	rfm_diagnostics (widgets_p, "xffm/stock_dialog-error", NULL);
115 	rfm_diagnostics (widgets_p, "xffm_tag/stderr", gg, ": ", strerror (ENOENT), "\n", NULL);
116 	g_free (gg);
117 	return TRUE;
118     }
119     g_free (rpath);
120 
121     gtk_widget_grab_focus (widgets_p->paper);
122 
123     rodent_push_view_go_history ();
124     record_entry_t *new_en = rfm_stat_entry (gg, 0);
125     view_p->module = NULL;
126     if (!rodent_refresh (widgets_p, new_en)){
127 	rfm_destroy_entry(new_en);
128     } else {
129 	rfm_save_to_go_history ((gchar *) gg);
130 	gchar *command = g_strdup_printf ("cd %s", gg);
131 	rfm_save_sh_command_history (view_p, command);
132     }
133     g_free (gg);
134     return TRUE;
135 }
136 
137 static void
print_history(widgets_t * widgets_p)138 print_history ( widgets_t * widgets_p) {
139     int i;
140     view_t *view_p = widgets_p->view_p;
141     GList *p;
142     rfm_diagnostics (widgets_p, "xffm_tag/command", "history:", "\n", NULL);
143 
144     for(i = 1, p = g_list_first (view_p->sh_command); p && p->data; p = p->next, i++) {
145         print_suggestion (widgets_p, (char *) p->data, i, TRUE);
146     }
147 
148 }
149 
150 static void
print_tab(widgets_t * widgets_p,gchar * text,gchar * text2)151 print_tab ( widgets_t * widgets_p, gchar * text, gchar *text2) {
152     int tab_len = 18;
153     rfm_diagnostics (widgets_p, "xffm_tag/red", text, text2, NULL);
154     gint string_length = (text)?strlen(text):0 + (text2)?strlen(text2):0;
155     for(tab_len = tab_len - string_length; tab_len > 0; tab_len--)
156         rfm_diagnostics (widgets_p, NULL, " ", NULL);
157 }
158 
159 static void
print_history_help(widgets_t * widgets_p)160 print_history_help ( widgets_t * widgets_p) {
161     rfm_diagnostics (widgets_p, "xffm_tag/command",
162 	    _("History"), " (", _("Get help..."), "):\n", NULL);
163     print_tab (widgets_p, "?",NULL);
164     rfm_diagnostics (widgets_p, "xffm_tag/green",
165 	    _("Show help about options"), "\n", NULL);
166 
167     print_tab (widgets_p, "history",NULL);
168     rfm_diagnostics (widgets_p, "xffm_tag/green",
169 	    _("Show History"), "\n", NULL);
170 
171     print_tab (widgets_p, "!","n");
172     rfm_diagnostics (widgets_p, "xffm_tag/green",
173 	    _("Line Number"),  " (", _("Command Line"), ") ", NULL);
174     rfm_diagnostics (widgets_p, "xffm_tag/red",
175 	    "n", NULL);
176     rfm_diagnostics (widgets_p, "xffm_tag/green",
177 	    ".", "\n", NULL);
178 
179     print_tab (widgets_p, "!",_("STRING"));
180     rfm_diagnostics (widgets_p, "xffm_tag/green",
181 	    _("Complete Match"), NULL);
182     rfm_diagnostics (widgets_p, "xffm_tag/red",
183 	    " ", _("STRING"), NULL);
184     rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
185 
186     print_tab (widgets_p, "!?",_("STRING"));
187     rfm_diagnostics (widgets_p, "xffm_tag/green",
188 	    _("Anywhere"),  NULL);
189     rfm_diagnostics (widgets_p, "xffm_tag/red", " ",
190 	    _("STRING"), NULL);
191     rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
192 
193     print_tab (widgets_p, _("STRING"),"<CTRL+TAB>");
194     rfm_diagnostics (widgets_p, "xffm_tag/green",
195 	    _("Completion mode:")," ", _("Command Line"), NULL);
196     //rfm_diagnostics (widgets_p, "xffm_tag/red",  _("STRING"), NULL);
197     rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
198 
199     print_tab (widgets_p, _("STRING"),"<TAB>");
200     rfm_diagnostics (widgets_p, "xffm_tag/green",
201 	    _("Completion mode:")," ", "bash", NULL);
202     //rfm_diagnostics (widgets_p, "xffm_tag/red",  _("STRING"), NULL);
203     rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
204     rfm_diagnostics (widgets_p, "xffm/stock_dialog-info",NULL);
205     rfm_diagnostics (widgets_p, "xffm_tag/blue", _("Full readline library history commands available"), "\n", NULL);
206 
207 #if 0
208     I have not used these nonconventional options, ever...
209     print_tab (widgets_p, "!!", NULL);
210     rfm_diagnostics (widgets_p, "xffm_tag/green",
211 	    _("Clear History"), " (", _("Current"), ")", "\n", NULL);
212 
213     print_tab (widgets_p, "!!!", NULL);
214     rfm_diagnostics (widgets_p, "xffm_tag/green",
215 	    _("Clear History"), " (", _("Disk"), ")", "\n", NULL);
216 #endif
217 }
218 
219 static void
suggest_command(widgets_t * widgets_p,const char * complete,gboolean anywhere)220 suggest_command (
221     widgets_t * widgets_p,
222     const char *complete,
223     gboolean anywhere
224 ) {
225     view_t *view_p = widgets_p->view_p;
226     GList *p;
227     char *suggest = NULL;
228     for(p = g_list_last (view_p->sh_command); p && p->data; p = p->prev) {
229         char *data = (char *) p->data;
230         if((anywhere && strstr (data, complete)) || (!anywhere && strncmp (complete, data, strlen (complete)) == 0)) {
231             suggest = g_strdup (data);
232             break;
233         }
234         NOOP ("COMPLETE: ?? %s\n", data);
235     }
236     if(suggest) {
237         rfm_status (widgets_p, "xffm/emblem_terminal", suggest, NULL);
238         g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
239         g_free (suggest);
240     }
241 }
242 
243 #ifndef HAVE_READLINE_HISTORY_H
244 
245 static gboolean
internal_history(widgets_t * widgets_p,gchar * cmd)246 internal_history ( widgets_t * widgets_p, gchar *cmd) {
247     view_t *view_p = widgets_p->view_p;
248     // internal fall back for when compiled without history library.
249 
250     //
251     const char *b = cmd + 1;
252     //   errno = 0;
253     long n = strtol (b, NULL, 10);
254     NOOP ("COMPLETE: n=%ld\n", n);
255 /*    if (errno)  {
256 	      rfm_diagnostics (widgets_p, "xffm/stock_dialog-warning", b, ": ",
257 				 strerror (errno), NULL);
258 	      return TRUE;
259     }*/
260     if(n > 1 && n <= g_list_length (view_p->sh_command)) {
261         GList *p = g_list_nth (view_p->sh_command, n - 1);
262         if(p && p->data) {
263             rfm_status (widgets_p, "xffm/emblem_terminal", (char *) p->data, NULL);
264             g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
265         }
266     } else {
267         if(*b != '?') {
268             suggest_command (widgets_p, b, FALSE);
269         } else {
270             suggest_command (widgets_p, b + 1, TRUE);
271         }
272     }
273     return TRUE;
274 
275 }
276 #endif
277 
278 #ifdef HAVE_READLINE_HISTORY_H
readline_history(widgets_t * widgets_p,gchar * cmd)279 static gchar *readline_history(widgets_t *widgets_p, gchar *cmd){
280     TRACE("readline_history: \"%s\"\n", cmd);
281     char *expansion;
282     static gchar *history = NULL;
283     if (!history) history = g_build_filename (LP_TERMINAL_HISTORY, NULL);
284 
285     read_history(history);
286     using_history();
287 
288 
289     int result = history_expand (cmd, &expansion);
290     TRACE ("result=%d expansion=%s\n", result, expansion);
291     if (result < 0){
292         rfm_diagnostics(widgets_p, "xffm/stock_dialog-warning", expansion, "\n", NULL);
293     }
294     if (result > 0){
295         place_command(widgets_p->status, widgets_p, expansion);
296         place_cursor(GTK_TEXT_VIEW(widgets_p->status), strlen(expansion));
297     }
298     if (!expansion || !strlen(expansion) || result <= 0){
299        g_free(expansion);
300        expansion = NULL;
301     }
302     clear_history();
303     return expansion;
304 }
305 #endif
306 
307 static gboolean
process_internal_commands(widgets_t * widgets_p,gchar ** command)308 process_internal_commands ( widgets_t * widgets_p, gchar ** command) {
309     TRACE("process_internal: %s\n", *command);
310 
311     if (*command == NULL) return TRUE;
312     gchar *cmd = *command;
313 #ifdef HAVE_READLINE_HISTORY_H
314     // readline history?
315     if ((*cmd == '!' && cmd[1] != ' ' && cmd[1] != '=') ||
316             (*cmd =='^' && strchr(cmd+1, '^'))){
317         gchar *expansion;
318         if ((expansion = readline_history(widgets_p, *command)) != NULL){
319             TRACE("readline expansion=%s\n", expansion);
320             g_free(expansion);
321             return TRUE;
322         }
323     }
324 #else
325     if (*cmd == '!' && cmd[1] != ' ' && cmd[1] != '='){
326             internal_history(widgets_p, cmd);
327             return TRUE;
328     }
329 
330 
331 #endif
332     gchar *command2=NULL;
333     if (strchr(*command, ';')){
334 	// split command in two.
335 	command2 = g_strdup(strchr(*command, ';') + 1);
336 	*strchr(*command, ';')=0;
337     }
338     gint argcp;
339     gchar **argvp;
340     GError *error = NULL;
341     if(!g_shell_parse_argv (*command, &argcp, &argvp, &error)) {
342         rfm_diagnostics (widgets_p, "xffm/stock_dialog-error", error->message, "\n", NULL);
343         g_error_free (error);
344 	if (command2){
345 	    g_free(*command);
346 	    *command=command2;
347 	    return FALSE;
348 	}
349         return TRUE;
350     } else {
351         // shortcircuit chdir and history commands
352         gboolean is_internal = FALSE;
353         if(strcmp (argvp[0], "cd") == 0) {
354             internal_cd (widgets_p, argvp);
355             NOOP ("CD: command=%s argv[1]=%s\n", *command, argvp[1]);
356             is_internal = TRUE;
357         } else if(strncmp (argvp[0], "history", strlen ("history")) == 0) {
358             rfm_show_text(widgets_p);
359             print_history (widgets_p);
360             rfm_status (widgets_p, "xffm/emblem_terminal", "", NULL);
361             g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
362             is_internal = TRUE;
363         } else if(strncmp (argvp[0], "?", strlen ("?")) == 0) {
364             print_history_help (widgets_p);
365             rfm_status (widgets_p, "xffm/emblem_terminal", "", NULL);
366             g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
367             is_internal = TRUE;
368         }
369 
370 	if (command2){
371 	    g_free(*command);
372 	    *command=command2;
373 	    return FALSE;
374 	}
375         g_strfreev (argvp);
376         return is_internal;
377     }
378 }
379 
380 static
sudo_fix(gchar * command)381 gchar *sudo_fix(gchar *command){
382     if (! command || !strstr(command, "sudo ")) return command;
383     if (strncmp(strstr(command, "sudo "), "sudo -A ", strlen("sudo -A "))!=0)
384     {
385         gchar *original_head=g_strdup(command);
386         gchar *pos = strstr(original_head, "sudo ");
387         if (pos){
388 	    *pos = 0;
389 	    gchar *tail=g_strdup(strstr(command, "sudo ")+strlen("sudo "));
390 	    tail=sudo_fix(tail);
391 	    gchar *new_command = g_strconcat(original_head, "sudo -A ", tail, NULL);
392 	    g_free(tail);
393 	    g_free(command);
394 	    command = new_command;
395         }
396 	g_free(original_head);
397     }
398     return command;
399 }
400 
401 static gint
on_motion_event(GtkWidget * textview,GdkEventMotion * event,gpointer data)402 on_motion_event (
403     GtkWidget * textview,
404     GdkEventMotion* event,
405     gpointer data
406 ) {
407     //widgets_t *widgets_p = (widgets_t *) data;
408     //view_t *view_p = widgets_p->view_p;
409     NOOP(stderr,"GdkEventMotion...\n");
410     if (event->x < TINY_ICON_SIZE) return TRUE;
411     return FALSE;
412 }
413 static gint
on_button_press(GtkWidget * textview,GdkEventButton * event,gpointer data)414 on_button_press (
415     GtkWidget * textview,
416      GdkEventButton * event,
417     gpointer data
418 ) {
419     widgets_t *widgets_p = (widgets_t *) data;
420     //view_t *view_p = widgets_p->view_p;
421     NOOP(stderr,"on_button_press...%lf\n", event->x);
422     if (lp_get_active(widgets_p)){
423 	if (event->x < TINY_ICON_SIZE)event->x = TINY_ICON_SIZE;
424     }
425     return FALSE;
426 }
427 static gint
on_button_release(GtkWidget * textview,GdkEventButton * event,gpointer data)428 on_button_release (
429     GtkWidget * textview,
430      GdkEventButton * event,
431     gpointer data
432 ) {
433     widgets_t *widgets_p = (widgets_t *) data;
434     view_t *view_p = widgets_p->view_p;
435     NOOP("on_button_release...%lf\n", event->x);
436     if (lp_get_active(widgets_p)){
437 	if (event->x < TINY_ICON_SIZE)event->x = TINY_ICON_SIZE;
438 	return FALSE;
439     }
440 
441 
442     if(!rfm_population_try_read_lock (view_p, "on_button_release")) return FALSE;
443     rodent_unselect_all_pixbuf (view_p);
444     rodent_unsaturate_icon (view_p);
445     status_grab_focus (widgets_p->view_p, 0);
446     if (!view_p->lp_command) view_p->lp_command = g_strdup("");
447     rfm_status (widgets_p, "xffm/emblem_terminal", view_p->lp_command, NULL);
448     g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
449     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW  (widgets_p->status), TRUE);
450     lp_set_active(widgets_p, TRUE);
451 
452     rfm_population_read_unlock (view_p, "on_button_release");
453 
454     return FALSE;
455 }
456 
457 static gboolean
csh_completion(GtkWidget * textview,widgets_t * widgets_p,gint direction)458 csh_completion(GtkWidget * textview, widgets_t *widgets_p, gint direction){
459     view_t *view_p = widgets_p->view_p;
460     gchar *command = get_current_text ((GtkTextView *) widgets_p->status);
461     if (!command || !strlen(command)) {
462         g_object_set_data(G_OBJECT(widgets_p->status), "csh_cmd_len", NULL);
463         g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", NULL);
464         return FALSE;
465     }
466     gint csh_cmd_len =
467         GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widgets_p->status), "csh_cmd_len"));
468     if (!csh_cmd_len) {
469         g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", NULL);
470         return FALSE;
471     }
472     gint nth = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widgets_p->status), "csh_nth"));
473 
474     if (!nth) nth = g_list_length(view_p->sh_command);
475     gchar *p=NULL;
476     if (direction > 0){
477         if (nth > 1){
478             nth--;
479 	    GList *list = g_list_nth(view_p->sh_command, nth);
480 	    for (;list && list->data;list=list->prev, nth--){
481 		p = list->data;
482 		if (p && strncmp(command, p, csh_cmd_len)==0) break;
483 		p=NULL;
484 	    }
485 	}
486     } else {
487 	nth++;
488 	if (nth <= g_list_length(view_p->sh_command)){
489             GList *list = g_list_nth(view_p->sh_command, nth);
490             for (;list && list->data;list=list->next, nth++){
491                 p = list->data;
492                 if (p && strncmp(command, p, csh_cmd_len)==0) break;
493                 p=NULL;
494             }
495         }
496     }
497     if (p){
498             TRACE("gotcha (%d): %s\n", nth, p);
499             g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", GINT_TO_POINTER(nth));
500 	    place_command(textview, widgets_p, p);
501             place_cursor(GTK_TEXT_VIEW(widgets_p->status), csh_cmd_len);
502     }
503     return TRUE;
504 }
505 
506 static gboolean
offset_history(GtkWidget * textview,widgets_t * widgets_p,gint offset)507 offset_history(GtkWidget *textview, widgets_t *widgets_p, gint offset){
508     view_t *view_p = widgets_p->view_p;
509     void *p = g_list_nth_data (view_p->sh_command, view_p->sh_command_counter + offset);
510     NOOP ("get nth sh_command_counter=%d\n", view_p->sh_command_counter + offset);
511     if(p) {
512             view_p->sh_command_counter += offset;
513 	    place_command(textview, widgets_p, p);
514     }
515     return TRUE;
516 }
517 
518 static gint
on_status_key_press(GtkWidget * textview,GdkEventKey * event,gpointer data)519 on_status_key_press (
520     GtkWidget * textview,
521     GdkEventKey * event,
522     gpointer data
523 ) {
524     widgets_t *widgets_p = (widgets_t *) data;
525     view_t *view_p = widgets_p->view_p;
526     if(!event) {
527         DBG ("on_status_key_press(): returning on event==0\n");
528         //status_grab_focus (widgets_p->view_p, 0);
529         return TRUE;
530     }
531 
532     // backspace must not erase icon. Place_cursor is offset by 2 for this purpose.
533     TRACE ("on_status_key_press(): * on_status_key_press: got key= 0x%x\n", event->keyval);
534     if(event->keyval == GDK_KEY_Home) {
535         place_cursor(GTK_TEXT_VIEW(widgets_p->status), 0);
536         return TRUE;
537     }
538     if(event->keyval == GDK_KEY_BackSpace || event->keyval == GDK_KEY_Left) {
539         gint intval;
540         GtkTextBuffer *buffer = gtk_text_view_get_buffer ((GtkTextView *) widgets_p->status);
541         g_object_get (G_OBJECT (buffer), "cursor-position", &intval, NULL);
542         NOOP ("on_status_key_press(): cursor-position =%d\n", intval);
543         if(intval > 2) {
544             // offset==1 is the icon and offset==2 is the space after icon
545             return FALSE;
546         }
547         return TRUE;
548     }
549 
550     // CTRL-TAB for command history completion
551     if(event->keyval == GDK_KEY_Tab && event->state & GDK_CONTROL_MASK) {
552         // do history completion ...
553         gchar *complete = get_current_text ((GtkTextView *) widgets_p->status);
554 	if (strncmp(complete, "sudo", strlen("sudo"))==0 &&
555 	    strncmp(complete, "sudo -A", strlen("sudo -A"))){
556 	    gchar *o = complete;
557 	    gchar *oo = o+strlen("sudo");
558 	    while (*oo == ' ') oo++;
559 	    complete = g_strconcat("sudo -A ", oo, NULL);
560 	    g_free(o);
561 	}
562 #if 0
563 	// too nerdy?
564 	// "&&" --> " && "
565 	// "||" --> " || "
566 	// ";" --> "; "
567 	// "|" --> " | "
568 	if (command && strlen(command)){
569 	    command = subst(complete, "&&", " && ");
570 	    command = subst(complete, "||", " || ");
571 	    command = subst(complete, ">>", " >> ");
572 	    command = subst(complete, ";", "; ");
573 	    while (strchr(complete, "  ")) command = subst(complete, "  ", " ");
574 	}
575 #endif
576         gchar *suggest = rfm_rational(RFM_MODULE_DIR,
577 		"completion", widgets_p, complete,
578 		"rfm_history_completion");
579         g_free (complete);
580         if(suggest) {
581             rfm_status (widgets_p, "xffm/emblem_terminal", suggest, NULL);
582             g_free (suggest);
583         }
584         g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
585 	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW  (widgets_p->status), TRUE);
586         return TRUE;
587     }
588     // tab for bash completion
589     if(event->keyval == GDK_KEY_Tab) {
590 	gchar *head=get_text_to_cursor(GTK_TEXT_VIEW(widgets_p->status));
591 	gint head_len = strlen(head);
592         gchar *token = get_current_text (GTK_TEXT_VIEW(widgets_p->status));
593 	gint token_len = strlen(token);
594 
595 	//gchar *tail=get_text_from_cursor(GTK_TEXT_VIEW(widgets_p->status));
596         g_free (head);
597 	//gchar *suggest = rfm_bash_complete(widgets_p, token, head_len);
598 	gchar *suggest = rfm_complex(RFM_MODULE_DIR, "completion",
599 		widgets_p, token, GINT_TO_POINTER(head_len),
600 		"rfm_bash_complete");
601         g_free (token);
602 
603 	if (suggest) {
604 	    gint suggest_len = strlen(suggest);
605 	    GtkTextIter end;
606 	    GtkTextBuffer *buffer =
607 		gtk_text_view_get_buffer (GTK_TEXT_VIEW(widgets_p->status));
608 	    gint offset = -1;
609 	    // +2 is icon and space...
610 	    offset = head_len + (suggest_len - token_len) + 2;
611             rfm_status (widgets_p, "xffm/emblem_terminal", suggest, NULL);
612 	    gtk_text_buffer_get_iter_at_offset (buffer, &end, offset);
613 	    gtk_text_buffer_place_cursor(buffer, &end);
614 	}
615 	g_free(suggest);
616 
617         g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
618 	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW  (widgets_p->status), TRUE);
619         return TRUE;
620     }
621     if ((event->state & GDK_SHIFT_MASK)
622 	    &&
623 	    ((event->keyval == GDK_KEY_Page_Up) ||
624 	    (event->keyval == GDK_KEY_Page_Down) ||
625 	    (event->keyval == GDK_KEY_Up) ||
626 	    (event->keyval == GDK_KEY_Down)) )
627     {
628 	// Show text.
629         rfm_show_text (widgets_p);
630 	// Get diagnostics area height.
631 	// Scroll up or down.
632 	gboolean page = (event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Page_Down);
633 	gboolean up = (event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Up);
634 	NOOP(stderr, "scroll \n");
635 	rfm_scroll(widgets_p, up, page);
636 
637         return TRUE;
638     }
639     if(event->keyval == GDK_KEY_Up) {
640         // csh command completion
641         if (csh_completion(textview, widgets_p, 1)) return TRUE;
642 	// push/pop history
643 	offset_history(textview, widgets_p, -1);
644 	return TRUE;
645     }
646 
647 
648     if(event->keyval == GDK_KEY_Down) {
649         // csh command completion
650         if (csh_completion(textview, widgets_p, -1)) return TRUE;
651 	// push/pop history
652 	offset_history(textview, widgets_p, 1);
653         return TRUE;
654     }
655     if(event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
656         if(g_object_get_data (G_OBJECT (textview), "clean") == NULL) {
657             g_object_set_data(G_OBJECT(widgets_p->status), "csh_cmd_len", NULL);
658             g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", NULL);
659             // get the command
660             gchar *command = get_current_text ((GtkTextView *) widgets_p->status);
661             if(command && strlen (command)) {
662                 // show the lp output area
663                 rfm_show_text (widgets_p);
664 
665                 // first process internal commands for cd and history
666 		// Internal commands will return focus to paper
667 		// (cd is a threaded reload which will overwrite
668 		//  the status line when done)
669                 if(process_internal_commands (widgets_p, &command)){
670 		    // probably not the best choice to send focus to paper...
671                     // lp_set_active(widgets_p, FALSE);
672 		    // rfm_update_status_line (widgets_p->view_p);
673                     return TRUE;
674 		}
675 
676                 // command is now external for /bin/sh
677                 // printstatus with the run icon run.png
678                 // this is already done in run.c
679                 // rfm_diagnostics(widgets_p, "run.png",command,"\n",NULL);
680 
681                 // run the command (in a shell)
682 		// XXX This will block if located at a remote directory with
683 		// a broken network connection.
684                 if(widgets_p->workdir) {
685                     g_free (widgets_p->workdir);
686                 }
687                 view_t *view_p = widgets_p->view_p;
688                 if (!view_p->en || !view_p->en->path || ! rfm_g_file_test(view_p->en->path, G_FILE_TEST_IS_DIR)){
689                     widgets_p->workdir = g_strdup (g_get_home_dir());
690 ;
691                 } else {
692                     widgets_p->workdir = g_strdup (view_p->en->path);
693                 }
694 		// Fix any sudo commands to use the -A option
695 		command = sudo_fix(command);
696                 RFM_THREAD_RUN (widgets_p, command, FALSE);
697                 g_free (command);
698             }
699         }
700         rfm_status (widgets_p, "xffm/emblem_terminal", NULL);
701 	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW  (widgets_p->status), TRUE);
702         g_object_set_data (G_OBJECT (textview), "clean", NULL);
703         return TRUE;
704     }
705 
706     if(g_object_get_data (G_OBJECT (textview), "clean")) {
707         rfm_status (widgets_p, "xffm/emblem_terminal", NULL);
708     }
709     // set unclean status...
710     g_object_set_data (G_OBJECT (textview), "clean", NULL);
711     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW  (widgets_p->status), TRUE);
712     // Allow default handler for text buffer
713     if (event->keyval >= GDK_KEY_space && event->keyval <= GDK_KEY_asciitilde){
714         gchar *command = get_current_text ((GtkTextView *) widgets_p->status);
715         gint csh_cmd_len = strlen(command)+1;
716         g_object_set_data(G_OBJECT(widgets_p->status), "csh_cmd_len", GINT_TO_POINTER(csh_cmd_len));
717     }
718 
719     return FALSE;
720 }
721 
722 
723 static gboolean
lp_get_active(widgets_t * widgets_p)724 lp_get_active(widgets_t *widgets_p){
725     if(g_object_get_data (G_OBJECT (widgets_p->status), "active")) return TRUE;
726     return FALSE;
727 }
728 
lp_set_active(widgets_t * widgets_p,gboolean state)729 static void lp_set_active(widgets_t *widgets_p, gboolean state){
730     if (state) g_object_set_data (G_OBJECT (widgets_p->status), "active", GINT_TO_POINTER(1));
731     else g_object_set_data (G_OBJECT (widgets_p->status), "active", NULL);
732     return;
733 }
734 
735 // Shared keybindings with either iconview or callback
736 typedef struct lpkey_t{
737     guint key;
738     guint mask;
739 } lpkey_t;
740 /*
741    Ctrl-Left               Word left
742    Ctrl-Right              Word right
743    Ctrl-Y                  Delete line
744    Ctrl-K                  Delete to end of line
745    Ctrl-BS                 Delete word left
746    Ctrl-Del        	   Delete word right
747    Ctrl-A                  Select all text
748    Ctrl-U                  Deselect block
749    Ctrl-V       	   Paste block from clipboard
750    Ctrl-X                  Cut block
751    Ctrl-C                  Copy block to clipboard
752    */
753 
754 #if 10
755 static gboolean
lp_is_key(GdkEventKey * event)756 lp_is_key(GdkEventKey * event){
757     // No mask, send to lp
758     if (event->state == 0) return TRUE;
759     // Shift page up/down, send to lp
760     if ((event->state & GDK_SHIFT_MASK)
761 	&&
762 	((event->keyval == GDK_KEY_Page_Up) ||
763 	 (event->keyval == GDK_KEY_Page_Down))
764        ){
765 	return TRUE;
766     }
767     // Control mask exceptions
768     gint keys[] = {
769 	GDK_KEY_Tab,
770 	GDK_KEY_Right,
771 	GDK_KEY_Left,
772 	GDK_KEY_y,
773 	GDK_KEY_Y,
774 	GDK_KEY_k,
775 	GDK_KEY_K,
776 	GDK_KEY_Delete,
777 	GDK_KEY_BackSpace,
778 	GDK_KEY_a,
779 	GDK_KEY_A,
780 	GDK_KEY_u,
781 	GDK_KEY_U,
782 	GDK_KEY_v,
783 	GDK_KEY_V,
784 	GDK_KEY_x,
785 	GDK_KEY_X,
786 	GDK_KEY_c,
787 	GDK_KEY_C,
788 	-1
789     };
790     gint *key=keys;
791     for (;key && *key>0; key++){
792 	if (event->keyval == *key) {
793 	    return TRUE;
794 	}
795     }
796     return FALSE;
797 }
798 #endif
799 
800