1 /*--- command.c ----------------------------------------------------------------
2 Copyright (C) 2004, 2005 Sylvain Fourmanoit <syfou@users.sourceforge.net>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to
6 deal in the Software without restriction, including without limitation the
7 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 sell copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies of the Software and its documentation and acknowledgment shall be
13 given in the documentation and software packages that this Software was
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 ------------------------------------------------------------------------------*/
23 /* Management routines for adesklets command interpreter, based
27 /*----------------------------------------------------------------------------*/
28 #include "command.h" /* Adesklets command interpreter header */
30 /*----------------------------------------------------------------------------*/
31 char * command_generator(const char *, int);
32 char * on_off_generator (const char *, int);
33 extern char * image_generator (const char *, int);
34 extern char * font_generator (const char *, int);
35 extern char * font_generator_with_null (const char *, int);
36 extern char * base_font_generator (const char *, int);
37 extern char * base_font_path_generator(const char *, int);
38 extern char * menu_generator (const char *, int);
39 extern char * window_manager_generator (const char *, int);
40 char * xwindow_event_generator(const char *, int);
41 extern char * color_range_generator (const char *, int);
42 extern char * color_range_generator_with_null (const char *, int);
43 extern char * color_modifier_generator (const char *, int);
44 extern char * color_modifier_generator_with_null (const char *, int);
45 char * color_modifier_value_generator(const char *, int);
46 extern char * filter_generator (const char *, int);
47 extern char * filter_generator_with_null (const char *, int);
48 char * direction_generator(const char *, int);
49 char * operation_generator(const char *, int);
50 extern char * polygon_generator(const char *, int);
51 extern char * variable_generator(const char *,int);
53 /* WARNING: this array is requested to have all its 'doc' strings beginning
54 with a capital letter since this characteristic is used by
55 the `command_enum.sh' script as hint to perform filtering.
56 For similar reasons, command names are expected to be entirely lower case.
58 const COMMAND COMMANDS
[] = {
59 { "time_gate", NULL
, "Set a time gate" },
60 { "help", command_generator
, "Display short help about a command" },
61 { "ping", NULL
, "Ping the interpreter" },
62 { "pause", NULL
, "Freeze the interpreter for debugging" },
63 { "version", NULL
, "Get interpreter version" },
64 { "get_id", NULL
, "Get current applet identificator" },
65 { "history", rl_filename_completion_function
, "List/save commands history" },
66 { "set", variable_generator
, "Set or unset a textual variable" },
67 { "unset_all", NULL
, "Unset all defined textual variables" },
68 { "echo" , NULL
, "Echo a string" },
69 { "start_recording", NULL
, "Start recording a macro" },
70 { "stop_recording", NULL
, "Stop recording the macro" },
71 { "play_get_abort_on_events", NULL
,
72 "Get replays interuptable status" },
73 { "play_set_abort_on_events", on_off_generator
,
74 "Set replays to be interuptable" },
75 { "play", NULL
, "Play a given macro" },
76 { "context_get_dither", NULL
, "Get context dither" },
77 { "context_get_anti_alias", NULL
, "Get context anti alias" },
78 { "context_get_blend", NULL
, "Get context blending"},
79 { "context_get_operation", NULL
, "Get context operation"},
80 { "context_get_cliprect", NULL
, "Get clipping rectange"},
81 { "context_get_image", NULL
, "Get context image" },
82 { "context_get_font", NULL
, "Get context font" },
83 { "context_get_color_range", NULL
, "Get context color range" },
84 { "context_get_color_modifier", NULL
, "Get context color modifier" },
85 { "context_get_filter", NULL
, "Get context filter" },
86 { "context_get_color", NULL
, "Get context color" },
87 { "context_get_angle", NULL
, "Get context angle" },
88 { "context_get_direction", NULL
, "Get context direction" },
89 { "context_set_dither", on_off_generator
, "Set context dither" },
90 { "context_set_anti_alias", on_off_generator
, "Set context anti-alias" },
91 { "context_set_blend", on_off_generator
, "Set context blending" },
92 { "context_set_operation", operation_generator
, "Set context operation" },
93 { "context_set_cliprect", NULL
, "Set context clipping rectangle" },
94 { "context_set_image", image_generator
, "Set context image" },
95 { "context_set_font", font_generator_with_null
,
97 { "context_set_color_range", color_range_generator_with_null
,
98 "Set context color range" },
99 { "context_set_color_modifier", color_modifier_generator_with_null
,
100 "Set context color modifier" },
101 { "context_set_filter" , filter_generator_with_null
, "Set context filter" },
102 { "context_set_color", NULL
, "Set context RGBA color" },
103 { "context_set_angle", NULL
, "Set context angle" },
104 { "context_set_direction", direction_generator
, "Set context direction" },
105 { "add_color_to_color_range", NULL
, "Add a color to a color range" },
106 { "blend_image_onto_image_at_angle", image_generator
,
107 "Blend images together"},
108 { "blend_image_onto_image_skewed", image_generator
, "Blend images together"},
109 { "blend_image_onto_image", image_generator
, "Blend images together" },
110 { "apply_filter", NULL
, "Apply a dynamic filter" },
111 { "get_text_size", NULL
, "Get size of text" },
112 { "get_text_advance", NULL
, "Get advance of text"},
113 { "text_draw", NULL
, "Draw a text string" },
114 { "modify_color_modifier_gamma", NULL
, "Modify gamma correction" },
115 { "modify_color_modifier_brightness", NULL
, "Modify brightness" },
116 { "modify_color_modifier_contrast", NULL
, "Modify contrast" },
117 { "get_color_modifier_tables", NULL
, "Get tables for a color modifier" },
118 { "set_color_modifier_tables", NULL
, "Set tables for a color modifier" },
119 { "get_color_modifier_value", color_modifier_value_generator
,
120 "Get a value for a color modifier" },
121 { "set_color_modifier_value", color_modifier_value_generator
,
122 "Set a value for a color modifier" },
123 { "apply_color_modifier", NULL
, "Apply a color modifier" },
124 { "apply_color_modifier_to_rectangle", NULL
, "Apply a color modifier" },
125 { "load_image_without_cache", rl_filename_completion_function
,
126 "Load an image from disk bypassing the cache" },
127 { "load_image", rl_filename_completion_function
, "Load an image from disk" },
128 { "save_image", rl_filename_completion_function
, "Save an image to disk" },
129 { "create_image", NULL
, "Create an image" },
130 { "create_image_using_data", NULL
, "Create an image from data" },
131 { "clone_image", NULL
, "Create a copy of an image" },
132 { "free_image", image_generator
, "Free an image" },
133 { "load_font", base_font_generator
, "Load a font" },
134 { "free_font", font_generator
, "Free a font" },
135 { "list_fonts", NULL
, "List all fonts"},
136 { "list_font_path", NULL
, "List all fonts path"},
137 { "add_path_to_font_path", rl_filename_completion_function
, "Add a font path" },
138 { "remove_path_from_font_path", base_font_path_generator
,
139 "Remove a font path" },
140 { "create_color_range", NULL
, "Create a color range" },
141 { "free_color_range", color_range_generator
, "Free a color range" },
142 { "create_color_modifier", NULL
, "Create a color modifier" },
143 { "free_color_modifier", color_modifier_generator
, "Free a color modifier" },
144 { "create_filter", NULL
, "Create a filter"},
145 { "free_filter", filter_generator
, "Free a filter"},
146 { "polygon_new", NULL
, "Create a polygon" },
147 { "polygon_free", polygon_generator
, "Free a polygon"},
148 { "polygon_add_point", polygon_generator
, "Add point to a polygon" },
149 { "images_reset_all", NULL
, "Free all images and refresh foreground" },
150 { "images_info", NULL
, "Get information on all images"},
151 { "fonts_reset_all", NULL
, "Free all fonts" },
152 { "fonts_info", NULL
, "Get information on all fonts"},
153 { "color_ranges_reset_all", NULL
, "Free all color ranges" },
154 { "color_ranges_info", NULL
, "Get information on all color ranges" },
155 { "color_modifiers_reset_all", NULL
, "Free all color modifiers" },
156 { "color_modifiers_info", NULL
, "Get information on all color modifiers"},
157 { "filters_reset_all", NULL
, "Free all filters" },
158 { "filters_info", NULL
, "Get information on all filters" },
159 { "polygons_reset_all", NULL
, "Free all polygons" },
160 { "polygons_info", NULL
, "Get information on all polygons" },
161 { "image_has_alpha", NULL
, "Get alpha channel setting of an image" },
162 { "image_get_width", NULL
, "Get width of an image" },
163 { "image_get_height", NULL
, "Get height of an image" },
164 { "image_get_filename", NULL
, "Get filename of an image" },
165 { "image_get_data" , NULL
, "Get the data of an image" },
166 { "image_query_pixel", NULL
, "Query a pixel value" },
167 { "image_set_has_alpha", on_off_generator
, "Set alpha channel of an image"},
168 { "image_set_changes_on_disk", NULL
, "Set image load time behavior"},
169 { "image_set_format", NULL
, "Set image format" },
170 { "image_filter_recurse ", NULL
, "Apply filter again and again to image" },
171 { "image_draw_line", NULL
, "Draw a line" },
172 { "image_draw_rectangle", NULL
, "Draw a rectangle" },
173 { "image_fill_rectangle", NULL
, "Draw a filled rectangle" },
174 { "image_fill_color_range_rectangle", NULL
, "Draw a gradian filled rectange" },
175 { "image_draw_ellipse", NULL
, "Draw an ellipse" },
176 { "image_fill_ellipse", NULL
, "Fill an ellipse" },
177 { "image_copy_alpha_to_image", image_generator
, "Transfert alpha channel" },
178 { "image_copy_alpha_rectangle_to_image", image_generator
,
179 "Transfert alpha channel" },
180 { "image_draw_polygon", polygon_generator
, "Draw a polygon onto image" },
181 { "image_fill_polygon", polygon_generator
, "Fill a polygon onto image" },
182 { "image_flip_horizontal", NULL
, "Flip an image horizontally" },
183 { "image_flip_vertical", NULL
, "Flip an image vertically" },
184 { "image_flip_diagonal", NULL
, "Flip an image diagonally" },
185 { "image_orientate", NULL
, "Orientate an image" },
186 { "image_blur", NULL
, "Blur an image" },
187 { "image_sharpen", NULL
, "Sharpen an image" },
188 { "filter_set", NULL
, "Set filter" },
189 { "filter_set_red", NULL
, "Set filter red channel" },
190 { "filter_set_green", NULL
, "Set filter grean channel" },
191 { "filter_set_blue", NULL
, "Set filter blue channel" },
192 { "filter_set_alpha", NULL
, "Set filter alpha channel" },
193 { "filter_constants", NULL
, "Set filter constants" },
194 { "filter_divisors", NULL
, "Set filter divisors" },
195 { "menu_fire", menu_generator
, "Fire a given menu"},
196 { "menu_reset_all", NULL
, "Reset all menus to initial state" },
197 { "menu_add_menu", NULL
, "Add a new menu"},
198 { "menu_add_submenu", NULL
, "Add a submenu to current menu" },
199 { "menu_add_item", NULL
, "Add an item to current menu" },
200 { "menu_add_separator", NULL
, "Add a separator to current menu" },
201 { "menu_end_submenu", NULL
, "End a submenu construction" },
202 { "event_catch", xwindow_event_generator
, "Set an event to catch" },
203 { "event_uncatch", xwindow_event_generator
, "Unset an event to catch" },
204 { "events_reset_all", NULL
, "Unset catching of all events" },
205 { "events_info", NULL
, "Get all caught events" },
206 { "events_get_echo", NULL
, "Get events echo status" },
207 { "events_set_echo", on_off_generator
, "Set events echo status" },
208 { "events_get_send_sigusr1", NULL
,
209 "Get sending of SIGUSR1 to parent on event" },
210 { "events_set_send_sigusr1", on_off_generator
,
211 "Set sending of SIGUSR1 to parent on event" },
212 { "events_purge", NULL
, "Get all accumulated events" },
213 { "window_reset", window_manager_generator
, "Reset the window" },
214 { "window_show", NULL
, "Map the window on the screen" },
215 { "window_hide", NULL
, "Unmap the window from the screen" },
216 { "window_resize", NULL
, "Resize the window" },
217 { "window_get_transparency", NULL
, "Get automatic transparency" },
218 { "window_get_background_grab", NULL
, "Get automatic grab" },
219 { "window_get_background_image", NULL
, "Get background image"},
220 { "window_get_managed_status", NULL
, "Get managed status" },
221 { "window_set_transparency", on_off_generator
, "Set automatic transparency" },
222 { "window_set_background_grab", on_off_generator
, "Set automatic grab" },
223 { "window_set_background_image", image_generator
, "Set background image" },
224 { "screen_get_width", NULL
, "Get screen width" },
225 { "screen_get_height", NULL
, "Get screen height" },
226 { "screen_get_depth", NULL
, "Get screen depth" },
227 { "get_charset", NULL
, "Get input charset" },
228 { "set_charset", NULL
, "Set input charset" },
229 { "charset_status", NULL
, "Get charset capabilities" },
230 { "x_status", NULL
, "Status of connection to X Window server" },
231 { "quit", NULL
, "Quit the program" },
236 const char * DIRECTIONS
[] = { /* prefix TEXT */
245 const char * OPERATIONS
[] = { /* prefix OP */
253 const char * RGBA_TABLES
[] = { /* prefix CHANNEL */
261 const char * WINDOW_MANAGER
[] = { /* prefix WINDOW */
268 const char * LOAD_ERRORS
[] = {
270 "file does not exist",
271 "file is a directory",
272 "permission denied to read",
273 "no loader for file format",
275 "path component non existant",
276 "path component not directory",
277 "path points outside address space",
278 "too many symbolic links",
280 "out of file descriptors",
281 "permission denied to write",
285 #ifndef X_DISPLAY_MISSING
286 const X_WINDOW_EVENT X_WINDOW_EVENTS
[] = {
287 { "BackgroundGrab", BackgroundGrabMask
},
288 { "MenuFire", MenuFireMask
},
289 { "ButtonPress", ButtonPressMask
},
290 { "ButtonRelease", ButtonReleaseMask
},
291 { "MotionNotify", PointerMotionMask
},
292 { "EnterNotify", EnterWindowMask
},
293 { "LeaveNotify", LeaveWindowMask
},
298 /*----------------------------------------------------------------------------*/
299 t_command command
= { /* Main structure to pass command back
301 0, /* int interactive; */
302 0, /* int recording; */
303 0, /* int replay_pos; */
304 0, /* int replay_stop; */
305 0, /* int replay_abort; */
306 0, /* int replay_abort_on_events;*/
307 {0, 0}, /* struct timeval replay_time; */
310 0, /* int message_out; */
311 NULL
, /* char * line; */
312 NULL
/* char * message; */
314 , NULL
, /* char * from_page; */
315 NULL
/* iconv_t cd; */
319 /*----------------------------------------------------------------------------*/
320 /* History convenience function: it exists in GNU readline >= 5.0,
321 but this is a backport for older libraries
323 Please note there is no `char * timestamp' field in `_hist_entry' structures
324 for GNU readline prior to 5.0.
326 #ifdef HAVE_READLINE_HISTORY_H
327 #ifndef HAVE_FREE_HISTORY_ENTRY
329 free_history_entry (HIST_ENTRY
* hist
)
333 return ((histdata_t
) 0);
334 if (hist
->line
) free(hist
->line
);
342 /*----------------------------------------------------------------------------*/
343 /* NOTE: the four next functions are truly ugly, but that's a necessity.
344 Since they are called _really_ often (and are IO related), we need
345 to strip them as much as possible whenever debugging is not involved.
347 I know this goes against GNU guidelines (http://www.gnu.org/prep/standards/),
348 but what else could be done?
350 /*----------------------------------------------------------------------------*/
351 /* Command output formatting function */
353 command_ok(char * format
, ...)
359 const char PREFIX
[] = "command %d ok: ";
360 if(!command
.replay_pos
) {
361 printf(PREFIX
, command
.rank
);
363 if (!debug_on_stderr())
364 debug (PREFIX
, command
.rank
);
366 if(!command
.message_out
) {
369 if (!debug_on_stderr()) {
371 vdebug(format
,apcopy
);
379 else debug("Double message out processing!\n");
382 command
.message_out
=1;
384 if(command
.replay_pos
) {
386 debug(PREFIX
,command
.rank
);
394 /*----------------------------------------------------------------------------*/
395 /* Command output formating function */
397 command_error(char * format
, ...)
403 const char PREFIX
[] = "command %d error: ";
404 if(!command
.replay_pos
) {
405 printf(PREFIX
, command
.rank
);
407 if (!debug_on_stderr())
408 debug(PREFIX
, command
.rank
);
410 if(!command
.message_out
) {
413 if(!debug_on_stderr()) {
415 vdebug(format
,apcopy
);
423 else debug("Double message out processing!\n");
427 if (!debug_on_stderr()) {
429 debug(PREFIX
,command
.rank
);
432 vdebug(format
,apcopy
);
436 if ((command
.message
=malloc(sizeof(char)*100))) {
440 vsnprintf(command
.message
,99,format
,ap
);
442 command
.message
[99]=0;
444 command
.replay_abort
=1;
446 command
.message_out
=1;
449 /*----------------------------------------------------------------------------*/
450 /* stdout printf for command execution: make sure no input is generated
451 while replaying, and always send an output to debug if it is a log file. */
453 command_printf(const char * format
, ...)
460 if (!command
.replay_pos
462 || !debug_on_stderr()
468 vdebug(format
,apcopy
);
471 if (!command
.replay_pos
)
472 result
= vprintf(format
,ap
);
478 /*----------------------------------------------------------------------------*/
479 /* Duplicate a string.
480 Return dynamically allocated string copy.
483 dupstr(const char *s
)
487 (r
=malloc(strlen(s
)+1)))
492 /*----------------------------------------------------------------------------*/
493 /* Duplicate a string.
494 Return dynamically allocated string copy.
497 dupnstr(const char *s
, int n
)
501 (r
=malloc(sizeof(char)*n
+1))) {
508 /*----------------------------------------------------------------------------*/
509 /* Duplicate a string and convert it from local charset to utf8, but only if
510 a correct conversion descriptor has been set using CMD_SET_CHARSET. Always
511 return at least a newly allocated copy of initial string. */
513 dupstr_utf8(const char * s
)
519 char *i_buf
= NULL
, * o_buf
= NULL
;
520 size_t i_bytes
, o_bytes
, o_len
;
525 if (i_buf
) free(i_buf
);
526 if ((i_buf
=strdup(s
)) &&
527 (o
=(o_buf
=realloc(o_buf
,(o_len
=o_bytes
=sizeof(char)*
528 (i_bytes
=strlen(s
))*(m
++))+1))) &&
530 result
=(iconv(command
.cd
,
532 &o_buf
,&o_bytes
)!=(size_t)(-1));
535 /* In case of output buffer overflow, we resize things until it works */
536 } while (o_buf
&& errno
==E2BIG
);
545 debug("string '%s' conversion error: %s\n",
551 /* debug("Conversion %s -> %s\n",s,o); */
556 /*----------------------------------------------------------------------------*/
557 /* Return actual size of history list*/
558 #ifdef HAVE_READLINE_HISTORY_H
562 HISTORY_STATE
* state
;
563 return ((state
=history_get_history_state())?state
->length
:0);
567 /*----------------------------------------------------------------------------*/
568 /* Test if a given string is only filled with space characters.
569 Returns 1 if there is nothing significant (or if the reference is NULL),
573 blank_line(const char * s
)
584 /*----------------------------------------------------------------------------*/
586 command_gate_chronometer(void)
590 if (gettimeofday(&time
,0)==0)
591 result
=(time
.tv_sec
+((double)time
.tv_usec
)/1E6
)-
592 (command
.replay_time
.tv_sec
+((double)command
.replay_time
.tv_usec
)/1E6
);
596 /*----------------------------------------------------------------------------*/
597 /* Generic generator mecanism: used for different wrappers.*/
598 char * generic_generator(const char * text
, int state
,
599 char ** names
, int number
)
601 static int list_index
, len
;
609 while((number
==-1 || list_index
<number
) &&
610 (name
=names
[list_index
])) {
612 if(strncmp(name
,text
,len
)==0)
613 return(dupstr(name
));
619 /*----------------------------------------------------------------------------*/
621 generic_index_generator(const char * text
, int state
, int number
)
624 static char ** index
;
625 static uint list_index
;
629 number
=(number
>=0)?number
:0;
630 /* Initial construction */
631 index
=(char**)malloc(sizeof(char*)*(number
+1));
632 for(i
=0;i
<number
;++i
) {
633 index
[i
]=(char*)malloc(sizeof(char)*8);
634 snprintf(index
[i
],8,"%d",i
);
641 while(index
[list_index
]) {
643 if(strncmp(index
[list_index
-1],text
,len
)==0)
644 return index
[list_index
-1];
651 /*----------------------------------------------------------------------------*/
653 generic_index_generator_with_null(const char * text
, int state
, int number
)
656 static char ** index
;
657 static uint list_index
;
661 number
=(number
>0)?number
:0;
662 /* Initial construction */
663 index
=(char**)malloc(sizeof(char*)*(number
+2));
664 for(i
=0;i
<=number
;++i
) {
665 index
[i
]=(char*)malloc(sizeof(char)*8);
666 snprintf(index
[i
],8,"%d",i
-1);
673 while(index
[list_index
]) {
675 if(strncmp(index
[list_index
-1],text
,len
)==0)
676 return index
[list_index
-1];
679 /*debug("Freeing: %8x\n", index);*/
684 /*----------------------------------------------------------------------------*/
685 /* Our generator function */
687 command_generator(const char * text
, int state
)
689 static int list_index
, len
;
697 while((name
=COMMANDS
[list_index
].name
)) {
699 if(strncmp(name
,text
,len
)==0)
700 return(dupstr(name
));
706 /*----------------------------------------------------------------------------*/
708 xwindow_event_generator(const char * text
, int state
)
710 #ifndef X_DISPLAY_MISSING
711 static int list_index
, len
;
719 while((name
=X_WINDOW_EVENTS
[list_index
].name
)) {
721 if(strncmp(name
,text
,len
)==0)
722 return(dupstr(name
));
729 /*----------------------------------------------------------------------------*/
731 on_off_generator(const char * text
, int state
)
733 return generic_index_generator(text
, state
, 2);
736 /*----------------------------------------------------------------------------*/
738 operation_generator(const char * text
, int state
)
740 return generic_generator(text
, state
, (char**)OPERATIONS
, -1);
743 /*----------------------------------------------------------------------------*/
745 direction_generator(const char * text
, int state
)
747 return generic_generator(text
, state
, (char**)DIRECTIONS
, -1);
750 /*----------------------------------------------------------------------------*/
752 color_modifier_value_generator(const char * text
, int state
)
754 return generic_generator(text
, state
, (char**)RGBA_TABLES
, -1);
757 /*----------------------------------------------------------------------------*/
759 window_manager_generator(const char * text
, int state
)
761 return generic_generator(text
,state
,(char**)WINDOW_MANAGER
, -1);
764 /*----------------------------------------------------------------------------*/
765 /* Our completion function */
767 command_completion(const char * text
, int start
, int end
)
770 char **matches
= NULL
;
771 rl_attempted_completion_over
=1;
773 matches
=rl_completion_matches(text
,
774 (rl_compentry_func_t
*)command_generator
);
776 for(i
=0;COMMANDS
[i
].name
;++i
)
777 if (strstr(rl_line_buffer
,COMMANDS
[i
].name
)==rl_line_buffer
)
779 if (COMMANDS
[i
].name
&&
780 start
==strlen(COMMANDS
[i
].name
)+1 &&
781 COMMANDS
[i
].generator
)
782 matches
=rl_completion_matches(text
,COMMANDS
[i
].generator
);
787 /*----------------------------------------------------------------------------*/
788 /* Callback function: called by readline every time a line is finished reading
789 Comply to rl_vcpfunc_t* typedef, installed by rc_callback_handler_install:
790 see command_interpreter_reset() below.
792 In fact, it is a very simple placeholder function that puts its entry
793 back into the `command' structure and set the command.ready flag
794 for future use; the only processing here is remplacement of tabs with
797 If GNU history library support is enabled, it also records all
798 non-empty commands below the configured lenght (if applicable).
801 readline_process_line_callback(char* line
)
809 /* Replace all tabs with spaces */
811 if(line
[i
]=='\t') line
[i
]=' ';
812 /* And trim down comments */
820 #ifdef HAVE_READLINE_HISTORY_H
821 if(!blank_line(line
) &&
822 (command
.recording
||
823 ((command
.interactive
&&
824 (!HISTORY_MAX_COMMAND_LENGHT
||
825 strlen(line
)<=HISTORY_MAX_COMMAND_LENGHT
)))
830 if (command
.interactive
)
831 rl_callback_handler_remove();
834 /*----------------------------------------------------------------------------*/
836 command_interpreter_reset(void)
841 if(command
.interactive
&&
842 snprintf(prompt
,sizeof(prompt
),"%d >>> ",command
.rank
)==-1)
844 rl_attempted_completion_function
= command_completion
;
845 rl_callback_handler_install(((command
.interactive
)?
846 ((result
)?prompt
:">>> "):NULL
),
847 readline_process_line_callback
);
851 /*----------------------------------------------------------------------------*/
852 /* This is the indirect mode counterpart to `command_interpreter()'.
853 When called, if the interpreter is indeed in indirect mode
854 (the 'play' command has been successfully used), it fills
855 the command.line with the next command to replay and set to
856 true the command.ready flag. If there is no commands left to replay,
857 it switches back the interpreter to direct mode.
859 It returns 1 if a result is available, 0 otherwise.
862 command_replayer(void)
864 #ifdef HAVE_READLINE_HISTORY_H
866 static int replay_start
=0;
869 if(command
.replay_pos
) {
871 replay_start
=command
.replay_pos
;
874 if(command
.replay_pos
-1<=command
.replay_stop
&&
875 !command
.replay_abort
&&
876 !(command
.replay_abort_on_events
&& events
&& events
->pos
)) {
877 if((entry
=history_get(command
.replay_pos
))) {
878 command
.line
=strdup(entry
->line
);
880 ++command
.replay_pos
;
883 pos
=command
.replay_pos
-1;
884 // replay_start=(command.replay_abort)?command.replay_pos-1:replay_start;
885 command
.replay_pos
=command
.message_out
=0;
887 if (!command
.replay_abort
&&
888 !(command
.replay_abort_on_events
&& events
&& events
->pos
))
889 command_ok("play %d %d\n", replay_start
-1,command
.replay_stop
);
891 if (command
.replay_abort
) {
892 command_error("on command %d - %s", pos
-1,
893 (command
.message
)?command
.message
:"too long\n");
894 if (command
.message
) {
895 free(command
.message
);
896 command
.message
=NULL
;
898 command
.replay_abort
=0;
900 command_ok("play_aborded %d\n", pos
);
905 if (command
.interactive
)
906 command_interpreter_reset();
912 return command
.ready
;
918 /*----------------------------------------------------------------------------*/
920 command_interpreter(void)
924 struct timeval timeout
;
926 /* Character polling */
928 FD_SET(fileno(stdin
), &fds
);
929 timeout
.tv_sec
=0;timeout
.tv_usec
=X_POLLING_PERIOD
;
931 if((polling
=select(FD_SETSIZE
,&fds
,NULL
,NULL
,&timeout
))>=0) {
932 if(FD_ISSET(fileno(stdin
),&fds
)) {
934 /* There is at least one character: let us poll
935 until nothing is left in stdin, or that a line
938 rl_callback_read_char();
940 FD_SET(fileno(stdin
),&fds
);
942 while(!command
.ready
&&
943 (polling
=select(FD_SETSIZE
,&fds
,NULL
,NULL
,&timeout
))>0);
948 fprintf(stderr
,"Command interpreter: error with character polling\n");
953 /*----------------------------------------------------------------------------*/
955 command_subsplitter(char * text
, int index
)
959 /* Strip initial whitespace */
962 /* Find beginning of the word */
963 for(;text
[i
] && text
[i
]==' ';++i
);
965 /* Find end of the word */
966 for(;text
[i
] && text
[i
]!=' ';++i
);
970 /* Stripping terminal blank */
971 for(i
=strlen(text
)-1;i
>=0;--i
)
972 if(text
[i
]==' ')text
[i
]=0;else break;
982 /*----------------------------------------------------------------------------*/
984 command_splitter(const char * text
, command_enum
* command_type
) {
988 *command_type
=CMD_UNKNOWN
;
989 if((vec
=vector_init())) {
990 /* Strip initial whitespace */
993 /* Find beginning of the word */
994 for(;text
[i
] && text
[i
]==' ';++i
);
996 /* Find end of the word */
997 for(;text
[i
] && text
[i
]!=' ';++i
);
998 if(pos
!=i
) vector_push(vec
,dupnstr(text
+pos
,i
-pos
));
1002 COMMANDS
[i
].name
&& strcmp(vec
->content
[0],COMMANDS
[i
].name
);
1004 *command_type
=(command_enum
)i
;