Support for password authentification in adesklets_submit
[adesklets.git] / src / command.c
blob4d546701f1f75144ff56178228e89319f491baed
1 /*--- command.c ----------------------------------------------------------------
2 Copyright (C) 2004, 2005, 2006 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
14 used.
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
24 on GNU readline.
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,
96 "Set context font" },
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" },
232 { NULL, NULL, NULL }
235 /* begin enums */
236 const char * DIRECTIONS[] = { /* prefix TEXT */
237 "text_to_right",
238 "text_to_left",
239 "text_to_down",
240 "text_to_up",
241 "text_to_angle",
242 NULL
245 const char * OPERATIONS[] = { /* prefix OP */
246 "op_copy",
247 "op_add",
248 "op_substract",
249 "op_reshade",
250 NULL
253 const char * RGBA_TABLES[] = { /* prefix CHANNEL */
254 "red",
255 "green",
256 "blue",
257 "alpha",
258 NULL
261 const char * WINDOW_MANAGER[] = { /* prefix WINDOW */
262 "unmanaged",
263 "managed",
264 NULL
266 /* end enums */
268 const char * LOAD_ERRORS[] = {
269 "none",
270 "file does not exist",
271 "file is a directory",
272 "permission denied to read",
273 "no loader for file format",
274 "path too long",
275 "path component non existant",
276 "path component not directory",
277 "path points outside address space",
278 "too many symbolic links",
279 "out of memory",
280 "out of file descriptors",
281 "permission denied to write",
282 "out of disk space"
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 },
294 { NULL, 0L }
296 #endif
298 /*----------------------------------------------------------------------------*/
299 t_command command = { /* Main structure to pass command back
300 to the event loop */
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; */
308 0, /* int rank; */
309 0, /* int ready; */
310 0, /* int message_out; */
311 NULL, /* char * line; */
312 NULL /* char * message; */
313 #ifdef HAVE_ICONV_H
314 , NULL, /* char * from_page; */
315 NULL /* iconv_t cd; */
316 #endif
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
328 histdata_t
329 free_history_entry (HIST_ENTRY* hist)
331 histdata_t x;
332 if (hist == 0)
333 return ((histdata_t) 0);
334 if (hist->line) free(hist->line);
335 x = hist->data;
336 free(hist);
337 return (x);
339 #endif
340 #endif
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 */
352 void
353 command_ok(char * format, ...)
355 va_list ap;
356 #ifdef DEBUG
357 va_list apcopy;
358 #endif
359 const char PREFIX[] = "command %d ok: ";
360 if(!command.replay_pos) {
361 printf(PREFIX, command.rank);
362 #ifdef DEBUG
363 if (!debug_on_stderr())
364 debug (PREFIX, command.rank);
365 #endif
366 if(!command.message_out) {
367 va_start(ap,format);
368 #ifdef DEBUG
369 if (!debug_on_stderr()) {
370 va_copy(apcopy,ap);
371 vdebug(format,apcopy);
372 va_end(apcopy);
374 #endif
375 vprintf(format,ap);
376 va_end(ap);
378 #ifdef DEBUG
379 else debug("Double message out processing!\n");
380 #endif
382 command.message_out=1;
383 #ifdef DEBUG
384 if(command.replay_pos) {
385 debug("(replay) ");
386 debug(PREFIX,command.rank);
387 va_start(ap,format);
388 vdebug(format,ap);
389 va_end(ap);
391 #endif
394 /*----------------------------------------------------------------------------*/
395 /* Command output formating function */
396 void
397 command_error(char * format, ...)
399 va_list ap;
400 #ifdef DEBUG
401 va_list apcopy;
402 #endif
403 const char PREFIX[] = "command %d error: ";
404 if(!command.replay_pos) {
405 printf(PREFIX, command.rank);
406 #ifdef DEBUG
407 if (!debug_on_stderr())
408 debug(PREFIX, command.rank);
409 #endif
410 if(!command.message_out) {
411 va_start(ap,format);
412 #ifdef DEBUG
413 if(!debug_on_stderr()) {
414 va_copy(apcopy,ap);
415 vdebug(format,apcopy);
416 va_end(apcopy);
418 #endif
419 vprintf(format,ap);
420 va_end(ap);
422 #ifdef DEBUG
423 else debug("Double message out processing!\n");
424 #endif
425 } else {
426 #ifdef DEBUG
427 if (!debug_on_stderr()) {
428 debug("(replay) ");
429 debug(PREFIX,command.rank);
430 va_start(ap,format);
431 va_copy(apcopy,ap);
432 vdebug(format,apcopy);
433 va_end(apcopy);
435 #endif
436 if ((command.message=malloc(sizeof(char)*100))) {
437 #ifndef DEBUG
438 va_start(ap,format);
439 #endif
440 vsnprintf(command.message,99,format,ap);
441 va_end(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. */
452 int
453 command_printf(const char * format, ...)
455 int result = 0;
456 va_list ap;
457 #ifdef DEBUG
458 va_list apcopy;
459 #endif
460 if (!command.replay_pos
461 #ifdef DEBUG
462 || !debug_on_stderr()
463 #endif
465 va_start(ap,format);
466 #ifdef DEBUG
467 va_copy(apcopy,ap);
468 vdebug(format,apcopy);
469 va_end(apcopy);
470 #endif
471 if (!command.replay_pos)
472 result = vprintf(format,ap);
473 va_end(ap);
475 return result;
478 /*----------------------------------------------------------------------------*/
479 /* Duplicate a string.
480 Return dynamically allocated string copy.
482 char *
483 dupstr(const char *s)
485 char *r=NULL;
486 if (s &&
487 (r=malloc(strlen(s)+1)))
488 strcpy (r, s);
489 return r;
492 /*----------------------------------------------------------------------------*/
493 /* Duplicate a string.
494 Return dynamically allocated string copy.
496 char *
497 dupnstr(const char *s, int n)
499 char *r=NULL;
500 if (s && n>=0 &&
501 (r=malloc(sizeof(char)*n+1))) {
502 strncpy (r, s, n);
503 r[n]=0;
505 return r;
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. */
512 char *
513 dupstr_utf8(const char * s)
515 int result=0;
516 char *o = NULL;
517 #ifdef HAVE_ICONV_H
518 int m = 2;
519 char *i_buf = NULL, * o_buf = NULL;
520 size_t i_bytes, o_bytes, o_len;
522 errno=0;
523 if (command.cd) {
524 do {
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))) &&
529 errno!=ENOMEM) {
530 result=(iconv(command.cd,
531 &i_buf,&i_bytes,
532 &o_buf,&o_bytes)!=(size_t)(-1));
533 o[o_len-o_bytes]=0;
535 /* In case of output buffer overflow, we resize things until it works */
536 } while (o_buf && errno==E2BIG);
538 #endif
539 if (!result) {
540 if (o) free(o);
541 o=strdup(s);
542 #ifdef HAVE_ICONV_H
543 #ifdef DEBUG
544 if (errno)
545 debug("string '%s' conversion error: %s\n",
546 s, strerror(errno));
547 #endif
548 #endif
551 /* debug("Conversion %s -> %s\n",s,o); */
553 return o;
556 /*----------------------------------------------------------------------------*/
557 /* Return actual size of history list*/
558 #ifdef HAVE_READLINE_HISTORY_H
559 int
560 history_size(void)
562 HISTORY_STATE * state;
563 return ((state=history_get_history_state())?state->length:0);
565 #endif
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),
570 0 otherwise.
572 int
573 blank_line(const char * s)
575 int i=0;
576 if (s)
577 for(i=0;
578 s[i] && s[i]==' ';
579 ++i);
580 return !(s && s[i]);
584 /*----------------------------------------------------------------------------*/
585 double
586 command_gate_chronometer(void)
588 double result=1E9;
589 struct timeval time;
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);
593 return result;
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;
602 char * name;
604 if(!state) {
605 list_index=0;
606 len=strlen(text);
609 while((number==-1 || list_index<number) &&
610 (name=names[list_index])) {
611 ++list_index;
612 if(strncmp(name,text,len)==0)
613 return(dupstr(name));
616 return NULL;
619 /*----------------------------------------------------------------------------*/
620 char *
621 generic_index_generator(const char * text, int state, int number)
623 int i;
624 static char ** index;
625 static uint list_index;
626 static int len;
628 if(!state) {
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);
636 index[i]=NULL;
637 len=strlen(text);
638 list_index=0;
641 while(index[list_index]) {
642 ++list_index;
643 if(strncmp(index[list_index-1],text,len)==0)
644 return index[list_index-1];
647 free(index);
648 return NULL;
651 /*----------------------------------------------------------------------------*/
652 char *
653 generic_index_generator_with_null(const char * text, int state, int number)
655 int i;
656 static char ** index;
657 static uint list_index;
658 static int len;
660 if(!state) {
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);
668 index[i]=NULL;
669 len=strlen(text);
670 list_index=0;
673 while(index[list_index]) {
674 ++list_index;
675 if(strncmp(index[list_index-1],text,len)==0)
676 return index[list_index-1];
679 /*debug("Freeing: %8x\n", index);*/
680 free(index);
681 return NULL;
684 /*----------------------------------------------------------------------------*/
685 /* Our generator function */
686 char *
687 command_generator(const char * text, int state)
689 static int list_index, len;
690 char * name;
692 if(!state) {
693 list_index=0;
694 len=strlen(text);
697 while((name=COMMANDS[list_index].name)) {
698 ++list_index;
699 if(strncmp(name,text,len)==0)
700 return(dupstr(name));
703 return NULL;
706 /*----------------------------------------------------------------------------*/
707 char *
708 xwindow_event_generator(const char * text, int state)
710 #ifndef X_DISPLAY_MISSING
711 static int list_index, len;
712 char * name;
714 if(!state) {
715 list_index=0;
716 len=strlen(text);
719 while((name=X_WINDOW_EVENTS[list_index].name)) {
720 ++list_index;
721 if(strncmp(name,text,len)==0)
722 return(dupstr(name));
725 #endif
726 return NULL;
729 /*----------------------------------------------------------------------------*/
730 char *
731 on_off_generator(const char * text, int state)
733 return generic_index_generator(text, state, 2);
736 /*----------------------------------------------------------------------------*/
737 char *
738 operation_generator(const char * text, int state)
740 return generic_generator(text, state, (char**)OPERATIONS, -1);
743 /*----------------------------------------------------------------------------*/
744 char *
745 direction_generator(const char * text, int state)
747 return generic_generator(text, state, (char**)DIRECTIONS, -1);
750 /*----------------------------------------------------------------------------*/
751 char *
752 color_modifier_value_generator(const char * text, int state)
754 return generic_generator(text, state, (char**)RGBA_TABLES, -1);
757 /*----------------------------------------------------------------------------*/
758 char *
759 window_manager_generator(const char * text, int state)
761 return generic_generator(text,state,(char**)WINDOW_MANAGER, -1);
764 /*----------------------------------------------------------------------------*/
765 /* Our completion function */
766 char **
767 command_completion(const char * text, int start, int end)
769 int i;
770 char **matches = NULL;
771 rl_attempted_completion_over=1;
772 if (start==0)
773 matches=rl_completion_matches(text,
774 (rl_compentry_func_t*)command_generator);
775 else {
776 for(i=0;COMMANDS[i].name;++i)
777 if (strstr(rl_line_buffer,COMMANDS[i].name)==rl_line_buffer)
778 break;
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);
784 return matches;
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
795 spaces.
797 If GNU history library support is enabled, it also records all
798 non-empty commands below the configured lenght (if applicable).
800 void
801 readline_process_line_callback(char* line)
803 int i;
805 command.line=line;
806 command.ready=1;
808 if (line) {
809 /* Replace all tabs with spaces */
810 for(i=0;line[i];++i)
811 if(line[i]=='\t') line[i]=' ';
812 /* And trim down comments */
813 for(i=0;line[i];++i)
814 if(line[i]=='#') {
815 line[i]=0;
816 break;
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)))
828 add_history(line);
829 #endif
830 if (command.interactive)
831 rl_callback_handler_remove();
834 /*----------------------------------------------------------------------------*/
836 command_interpreter_reset(void)
838 int result=1;
839 char prompt[20];
841 if(command.interactive &&
842 snprintf(prompt,sizeof(prompt),"%d >>> ",command.rank)==-1)
843 result=0;
844 rl_attempted_completion_function = command_completion;
845 rl_callback_handler_install(((command.interactive)?
846 ((result)?prompt:">>> "):NULL),
847 readline_process_line_callback);
848 return result;
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
865 int pos;
866 static int replay_start=0;
867 HIST_ENTRY * entry;
869 if(command.replay_pos) {
870 if (!replay_start)
871 replay_start=command.replay_pos;
872 --command.rank;
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);
879 command.ready=1;
880 ++command.replay_pos;
882 } else {
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);
890 else {
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;
899 } else {
900 command_ok("play_aborded %d\n", pos);
903 ++command.rank;
904 replay_start=0;
905 if (command.interactive)
906 command_interpreter_reset();
907 events_delay=0;
908 if (events_echo)
909 events_purge();
912 return command.ready;
913 #else
914 return 0;
915 #endif
918 /*----------------------------------------------------------------------------*/
919 int
920 command_interpreter(void)
922 int polling;
923 fd_set fds;
924 struct timeval timeout;
926 /* Character polling */
927 FD_ZERO(&fds);
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)) {
933 timeout.tv_usec=0;
934 /* There is at least one character: let us poll
935 until nothing is left in stdin, or that a line
936 is completed. */
937 do {
938 rl_callback_read_char();
939 FD_ZERO(&fds);
940 FD_SET(fileno(stdin),&fds);
942 while(!command.ready &&
943 (polling=select(FD_SETSIZE,&fds,NULL,NULL,&timeout))>0);
947 if (polling<0)
948 fprintf(stderr,"Command interpreter: error with character polling\n");
950 return (polling>=0);
953 /*----------------------------------------------------------------------------*/
954 char *
955 command_subsplitter(char * text, int index)
957 int i,j,pos;
959 /* Strip initial whitespace */
960 pos=i=j=0;
961 while(text[i]) {
962 /* Find beginning of the word */
963 for(;text[i] && text[i]==' ';++i);
964 pos=i;
965 /* Find end of the word */
966 for(;text[i] && text[i]!=' ';++i);
967 if(pos!=i) {
968 ++j;
969 if (j==index+1) {
970 /* Stripping terminal blank */
971 for(i=strlen(text)-1;i>=0;--i)
972 if(text[i]==' ')text[i]=0;else break;
973 /* Returns result */
974 return text+pos;
979 return NULL;
982 /*----------------------------------------------------------------------------*/
983 vector *
984 command_splitter(const char * text, command_enum * command_type) {
985 int i,pos;
986 vector * vec;
988 *command_type=CMD_UNKNOWN;
989 if((vec=vector_init())) {
990 /* Strip initial whitespace */
991 pos=i=0;
992 while(text[i]) {
993 /* Find beginning of the word */
994 for(;text[i] && text[i]==' ';++i);
995 pos=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));
1000 if (vec->pos) {
1001 for(i=0;
1002 COMMANDS[i].name && strcmp(vec->content[0],COMMANDS[i].name);
1003 ++i);
1004 *command_type=(command_enum)i;
1007 return vec;