diff --git a/src/Makefile_Ecore.am b/src/Makefile_Ecore.am index 5f10ea7f2e..fd6074dec3 100644 --- a/src/Makefile_Ecore.am +++ b/src/Makefile_Ecore.am @@ -52,6 +52,10 @@ ecore_eolian_files_public = \ lib/ecore/efl_view_model.eo \ lib/ecore/efl_core_env.eo \ lib/ecore/efl_core_proc_env.eo \ + lib/ecore/efl_core_command_line.eo + +ecore_test_eolian_files = \ + tests/ecore/efl_app_test_cml.eo ecore_eolian_files = \ $(ecore_eolian_files_legacy) \ @@ -60,10 +64,14 @@ ecore_eolian_files = \ ecore_eolian_c = $(ecore_eolian_files:%.eo=%.eo.c) ecore_eolian_h = $(ecore_eolian_files:%.eo=%.eo.h) \ $(ecore_eolian_files_legacy:%.eo=%.eo.legacy.h) +ecore_test_c = $(ecore_test_eolian_files:%.eo=%.eo.c) +ecore_test_h = $(ecore_test_eolian_files:%.eo=%.eo.h) BUILT_SOURCES += \ $(ecore_eolian_c) \ - $(ecore_eolian_h) + $(ecore_eolian_h) \ + $(ecore_test_c) \ + $(ecore_test_h) ecoreeolianfilesdir = $(datadir)/eolian/include/ecore-@VMAJ@ ecoreeolianfiles_DATA = $(ecore_eolian_files_public) lib/ecore/efl_loop_timer.eo @@ -100,6 +108,7 @@ lib/ecore/ecore_job.c \ lib/ecore/ecore_main.c \ lib/ecore/ecore_event_message.c \ lib/ecore/ecore_event_message_handler.c \ +lib/ecore/efl_core_command_line.c \ lib/ecore/efl_core_env.c \ lib/ecore/efl_core_proc_env.c \ lib/ecore/efl_app.c \ @@ -339,6 +348,7 @@ tests/ecore/efl_app_test_loop.c \ tests/ecore/efl_app_test_loop_fd.c \ tests/ecore/efl_app_test_loop_timer.c \ tests/ecore/efl_app_test_promise.c \ +tests/ecore/efl_app_test_cml.c \ tests/ecore/efl_app_test_env.c \ tests/ecore/efl_app_suite.c \ tests/ecore/efl_app_suite.h diff --git a/src/bindings/mono/efl_mono/efl_csharp_application.cs b/src/bindings/mono/efl_mono/efl_csharp_application.cs index 2b2c55c75e..84732d3993 100644 --- a/src/bindings/mono/efl_mono/efl_csharp_application.cs +++ b/src/bindings/mono/efl_mono/efl_csharp_application.cs @@ -104,8 +104,9 @@ namespace Efl { public void Launch(Efl.Csharp.Components components=Components.Ui) { Init(components); Efl.App app = Efl.App.AppMain; - foreach (var arg in Environment.GetCommandLineArgs()) - app.AppendArg(arg); + Eina.Array command_line = new Eina.Array(); + command_line.Append(Environment.GetCommandLineArgs()); + app.SetCommandArray(command_line); app.ArgumentsEvt += (object sender, LoopArgumentsEvt_Args evt) => { if (evt.arg.Initialization) { OnInitialize(evt.arg.Argv); diff --git a/src/lib/ecore/Ecore_Eo.h b/src/lib/ecore/Ecore_Eo.h index 348b0f5b6d..3615219c38 100644 --- a/src/lib/ecore/Ecore_Eo.h +++ b/src/lib/ecore/Ecore_Eo.h @@ -28,6 +28,7 @@ #include "efl_core_env.eo.h" #include "efl_core_proc_env.eo.h" +#include "efl_core_command_line.eo.h" #include "efl_loop_message.eo.h" #include "efl_loop_message_handler.eo.h" diff --git a/src/lib/ecore/efl_app.eo b/src/lib/ecore/efl_app.eo index f90324b39d..e046428626 100644 --- a/src/lib/ecore/efl_app.eo +++ b/src/lib/ecore/efl_app.eo @@ -1,6 +1,6 @@ import efl_types; -class Efl.App extends Efl.Loop +class Efl.App extends Efl.Loop implements Efl.Core.Command_Line { [[ ]] data: null; diff --git a/src/lib/ecore/efl_appthread.eo b/src/lib/ecore/efl_appthread.eo index c60308f468..82a9d77ace 100644 --- a/src/lib/ecore/efl_appthread.eo +++ b/src/lib/ecore/efl_appthread.eo @@ -1,4 +1,4 @@ -class Efl.Appthread extends Efl.Loop implements Efl.ThreadIO, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer +class Efl.Appthread extends Efl.Loop implements Efl.ThreadIO, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer, Efl.Core.Command_Line { [[ ]] methods { diff --git a/src/lib/ecore/efl_core_command_line.c b/src/lib/ecore/efl_core_command_line.c new file mode 100644 index 0000000000..74ae690c26 --- /dev/null +++ b/src/lib/ecore/efl_core_command_line.c @@ -0,0 +1,267 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#define EFL_CORE_COMMAND_LINE_PROTECTED + +#include + +#define MY_CLASS EFL_CORE_COMMAND_LINE_MIXIN + +typedef struct { + Eina_Bool filled; + char *string_command; + Eina_Array *command; +} Efl_Core_Command_Line_Data; + +static Eina_Array * +_unescape(const char *s) +{ + Eina_Array *args; + const char *p; + char *tmp = NULL, *d = NULL; + if (!s) return NULL; + + Eina_Bool in_quote_dbl = EINA_FALSE; + Eina_Bool in_quote = EINA_FALSE; + + args = eina_array_new(16); + if (!args) return NULL; + for (p = s; *p; p++) + { + if (!tmp) tmp = d = strdup(p); + if (tmp) + { + if (in_quote_dbl) + { + switch (*p) + { + case '\"': + in_quote_dbl = EINA_FALSE; + *d = 0; + eina_array_push(args, eina_stringshare_add(tmp)); + free(tmp); + tmp = d = NULL; + break; + case '\\': + p++; + EINA_FALLTHROUGH + default: + *d = *p; + d++; + break; + } + } + else if (in_quote) + { + switch (*p) + { + case '\'': + in_quote = EINA_FALSE; + *d = 0; + eina_array_push(args, eina_stringshare_add(tmp)); + free(tmp); + tmp = d = NULL; + break; + case '\\': + p++; + EINA_FALLTHROUGH + default: + *d = *p; + d++; + break; + } + } + else + { + switch (*p) + { + case ' ': + case '\t': + case '\r': + case '\n': + *d = 0; + eina_array_push(args, eina_stringshare_add(tmp)); + free(tmp); + tmp = d = NULL; + break; + case '\"': + in_quote_dbl = EINA_TRUE; + break; + case '\'': + in_quote = EINA_TRUE; + break; + case '\\': + p++; + EINA_FALLTHROUGH + default: + *d = *p; + d++; + break; + } + } + } + } + if (tmp) + { + *d = 0; + eina_array_push(args, eina_stringshare_add(tmp)); + free(tmp); + } + return args; +} + +static char * +_escape(const char *s) +{ + Eina_Bool need_quote = EINA_FALSE; + const char *p; + char *s2 = malloc((strlen(s) * 2) + 1 + 2), *d; + + if (!s2) return NULL; + + for (p = s; *p; p++) + { + switch (*p) + { + case '\'': + case '\"': + case '$': + case '#': + case ';': + case '&': + case '`': + case '|': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '>': + case '<': + case '\n': + case '\r': + case '\t': + case ' ': + need_quote = EINA_TRUE; + default: + break; + } + } + + d = s2; + if (need_quote) + { + *d = '\"'; + d++; + } + for (p = s; *p; p++, d++) + { + switch (*p) + { + case '\\': + case '\'': + case '\"': + *d = '\\'; + d++; + EINA_FALLTHROUGH + default: + *d = *p; + break; + } + } + if (need_quote) + { + *d = '\"'; + d++; + } + *d = 0; + return s2; +} + +EOLIAN static const char* +_efl_core_command_line_command_get(const Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd) +{ + return eina_strdup(pd->string_command); +} + +EOLIAN static Eina_Accessor* +_efl_core_command_line_command_access(Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd) +{ + return pd->command ? eina_array_accessor_new(pd->command) : NULL; +} + +static void +_remove_invalid_chars(char *command) +{ + for (unsigned int i = 0; i < strlen(command); ++i) + { + char c = command[i]; + if (c < 0x20 || c == 0x7f) + command[i] = '\x12'; + } +} + +EOLIAN static Eina_Bool +_efl_core_command_line_command_array_set(Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd, Eina_Array *array) +{ + EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->filled, EINA_FALSE); + Eina_Strbuf *command = eina_strbuf_new(); + unsigned int i = 0; + + pd->command = eina_array_new(eina_array_count(array)); + for (i = 0; i < (array ? eina_array_count(array) : 0); ++i) + { + char *content = eina_array_data_get(array, i); + char *param = calloc(1, strlen(content)); + + if (!param) + { + free(param); + while (eina_array_count(pd->command) > 0) + eina_stringshare_del(eina_array_pop(pd->command)); + eina_array_free(pd->command); + pd->command = NULL; + eina_array_free(array); + return EINA_FALSE; + } + + //build the command + if (i != 0) + eina_strbuf_append(command, " "); + eina_strbuf_append(command, _escape(content)); + //convert string to stringshare + strcpy(param, content); + _remove_invalid_chars(param); + eina_array_push(pd->command, eina_stringshare_add(param)); + free(param); + } + pd->string_command = eina_strbuf_release(command); + pd->filled = EINA_TRUE; + eina_array_free(array); + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_core_command_line_command_string_set(Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd, const char *str) +{ + EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->filled, EINA_FALSE); + + pd->string_command = eina_strdup(str); + _remove_invalid_chars(pd->string_command); + pd->command = _unescape(str); + if (!pd->command) + { + if (pd->string_command) + free(pd->string_command); + pd->string_command = NULL; + return EINA_FALSE; + } + pd->filled = EINA_TRUE; + + return EINA_TRUE; +} + +#include "efl_core_command_line.eo.c" diff --git a/src/lib/ecore/efl_core_command_line.eo b/src/lib/ecore/efl_core_command_line.eo new file mode 100644 index 0000000000..1cbb020856 --- /dev/null +++ b/src/lib/ecore/efl_core_command_line.eo @@ -0,0 +1,80 @@ +mixin Efl.Core.Command_Line { + [[A mixin that implements standard functions for command lines. + + This object parses the command line that gets passed, later the object can be accessed via accessor or the string directly. + ]] + methods { + @property command { + [[ A commandline that encodes arguments in a command string. + This command is unix shell-style, thus whitespace separates + arguments unless escaped. Also a semi-colon ';', ampersand + '&', pipe/bar '|', hash '#', bracket, square brace, brace + character ('(', ')', '[', ']', '{', '}'), exclamation + mark '!', backquote '`', greator or less than ('>' '<') + character unless escaped or in quotes would cause + args_count/value to not be generated properly, because + it would force complex shell interpretation which + will not be supported in evaluating the arg_count/value + information, but the final shell may interpret this if this + is executed via a command-line shell. To not be a complex + shell command, it should be simple with paths, options + and variable expansions, but nothing more complex involving + the above unescaped characters. + + "cat -option /path/file" + "cat 'quoted argument'" + "cat ~/path/escaped\ argument" + "/bin/cat escaped\ argument $VARIABLE" + etc. + + It should not try and use "complex shell features" if you + want the arg_count and arg_value set to be correct after + setting the command string. For example none of: + + "VAR=x /bin/command && /bin/othercommand >& /dev/null" + "VAR=x /bin/command `/bin/othercommand` | /bin/cmd2 && cmd3 &" + etc. + + If you set the command the arg_count/value property contents + can change and be completely re-evaluated by parsing the + command string into an argument array set along with + interpreting escapes back into individual argument strings. + ]] + get { + + } + values { + commandline : string; + } + } + command_access { + [[ Get the accessor which enables access to each argument that got passed to this object. ]] + return : accessor; + } + @property command_array { + [[ Use an array to fill this object + + Every element of a string is a argument. + ]] + set { + return : bool; [[On success $true, $false otherwise]] + } + values { + array : array @owned; [[An array where every array field is an argument]] + } + } + @property command_string { + [[ Use a string to fill this object + + The string will be split at every unescaped ' ', every resulting substring will be a new argument to the command line. + ]] + set { + return : bool; [[On success $true, $false otherwise]] + } + values { + str : string; [[A command in form of a string]] + } + + } + } +} diff --git a/src/lib/ecore/efl_exe.c b/src/lib/ecore/efl_exe.c index 4b3bc658d6..61ff4ba798 100644 --- a/src/lib/ecore/efl_exe.c +++ b/src/lib/ecore/efl_exe.c @@ -395,7 +395,7 @@ _efl_exe_efl_task_priority_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd) } EOLIAN static Eina_Future * -_efl_exe_efl_task_run(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd) +_efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd) { #ifdef _WIN32 return EINA_FALSE; @@ -414,7 +414,7 @@ _efl_exe_efl_task_run(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd) if (!td) return NULL; // get a cmdline to run - cmd = efl_task_command_get(obj); + cmd = efl_core_command_line_command_get(obj); if (!cmd) return NULL; ret = pipe(pipe_exited); diff --git a/src/lib/ecore/efl_exe.eo b/src/lib/ecore/efl_exe.eo index 111814af21..b7f97da7dc 100644 --- a/src/lib/ecore/efl_exe.eo +++ b/src/lib/ecore/efl_exe.eo @@ -19,7 +19,7 @@ enum Efl.Exe_Flags { hide_io = 4 } -class Efl.Exe extends Efl.Task implements Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer +class Efl.Exe extends Efl.Task implements Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer,Efl.Core.Command_Line { [[ ]] methods { diff --git a/src/lib/ecore/efl_loop.c b/src/lib/ecore/efl_loop.c index 68f9573b76..cf8600c998 100644 --- a/src/lib/ecore/efl_loop.c +++ b/src/lib/ecore/efl_loop.c @@ -383,17 +383,19 @@ _efl_loop_arguments_cleanup(Eo *o EINA_UNUSED, void *data, const Eina_Future *de EAPI void ecore_loop_arguments_send(int argc, const char **argv) { - Eina_Array *arga; + Eina_Array *arga, *cml; int i = 0; - efl_task_arg_reset(efl_main_loop_get()); arga = eina_array_new(argc); + cml = eina_array_new(argc); for (i = 0; i < argc; i++) { - eina_array_push(arga, eina_stringshare_add(argv[i])); - efl_task_arg_append(efl_main_loop_get(), argv[i]); + Eina_Stringshare *arg = eina_stringshare_add(argv[i]); + eina_array_push(arga, arg); + eina_array_push(cml, arg); } + efl_core_command_line_command_array_set(efl_app_main_get(EFL_APP_CLASS), cml); efl_future_then(efl_main_loop_get(), efl_loop_job(efl_main_loop_get()), .success = _efl_loop_arguments_send, .free = _efl_loop_arguments_cleanup, diff --git a/src/lib/ecore/efl_task.c b/src/lib/ecore/efl_task.c index 311de0506a..d610fcbf8b 100644 --- a/src/lib/ecore/efl_task.c +++ b/src/lib/ecore/efl_task.c @@ -12,303 +12,6 @@ ////////////////////////////////////////////////////////////////////////// -static void -_clear_args(Efl_Task_Data *pd) -{ - unsigned int count, i; - - if (!pd->args) return; - count = eina_array_count(pd->args); - for (i = 0; i < count; i++) - eina_stringshare_del(eina_array_data_get(pd->args, i)); - eina_array_free(pd->args); - pd->args = NULL; -} - -static Eina_Array * -_unescape(const char *s) -{ - Eina_Array *args; - const char *p; - char *tmp = NULL, *d = NULL; - if (!s) return NULL; - - Eina_Bool in_quote_dbl = EINA_FALSE; - Eina_Bool in_quote = EINA_FALSE; - - args = eina_array_new(16); - if (!args) return NULL; - for (p = s; *p; p++) - { - if (!tmp) tmp = d = strdup(p); - if (tmp) - { - if (in_quote_dbl) - { - switch (*p) - { - case '\"': - in_quote_dbl = EINA_FALSE; - *d = 0; - eina_array_push(args, eina_stringshare_add(tmp)); - free(tmp); - tmp = d = NULL; - break; - case '\\': - p++; - EINA_FALLTHROUGH - default: - *d = *p; - d++; - break; - } - } - else if (in_quote) - { - switch (*p) - { - case '\'': - in_quote = EINA_FALSE; - *d = 0; - eina_array_push(args, eina_stringshare_add(tmp)); - free(tmp); - tmp = d = NULL; - break; - case '\\': - p++; - EINA_FALLTHROUGH - default: - *d = *p; - d++; - break; - } - } - else - { - switch (*p) - { - case ' ': - case '\t': - case '\r': - case '\n': - *d = 0; - eina_array_push(args, eina_stringshare_add(tmp)); - free(tmp); - tmp = d = NULL; - break; - case '\"': - in_quote_dbl = EINA_TRUE; - break; - case '\'': - in_quote = EINA_TRUE; - break; - case '\\': - p++; - EINA_FALLTHROUGH - default: - *d = *p; - d++; - break; - } - } - } - } - if (tmp) - { - *d = 0; - eina_array_push(args, eina_stringshare_add(tmp)); - free(tmp); - } - return args; -} - -static char * -_escape(const char *s) -{ - Eina_Bool need_quote = EINA_FALSE; - const char *p; - char *s2 = malloc((strlen(s) * 2) + 1 + 2), *d; - - if (!s2) return NULL; - - for (p = s; *p; p++) - { - switch (*p) - { - case '\'': - case '\"': - case '$': - case '#': - case ';': - case '&': - case '`': - case '|': - case '(': - case ')': - case '[': - case ']': - case '{': - case '}': - case '>': - case '<': - case '\n': - case '\r': - case '\t': - need_quote = EINA_TRUE; - default: - break; - } - } - - d = s2; - if (need_quote) - { - *d = '\"'; - d++; - } - for (p = s; *p; p++, d++) - { - switch (*p) - { - case ' ': - case '\\': - case '\'': - case '\"': - *d = '\\'; - d++; - EINA_FALLTHROUGH - default: - *d = *p; - break; - } - } - if (need_quote) - { - *d = '\"'; - d++; - } - *d = 0; - return s2; -} - -static void -_rebuild_command(Efl_Task_Data *pd) -{ - unsigned int count, i; - Eina_Strbuf *sb; - const char *arg, *cmd; - Eina_Bool have_args = EINA_FALSE; - - if (!pd->command_dirty) return; - pd->command_dirty = EINA_FALSE; - eina_stringshare_del(pd->command); - pd->command = NULL; - if (!pd->args) return; - sb = eina_strbuf_new(); - if (!sb) return; - count = eina_array_count(pd->args); - for (i = 0; i < count; i++) - { - arg = eina_array_data_get(pd->args, i); - if (arg) - { - char *str = _escape(arg); - if (str) - { - if (have_args) eina_strbuf_append(sb, " "); - eina_strbuf_append(sb, str); - free(str); - have_args = EINA_TRUE; - } - } - } - cmd = eina_strbuf_string_get(sb); - if (cmd) pd->command = eina_stringshare_add(cmd); - eina_strbuf_free(sb); -} - -////////////////////////////////////////////////////////////////////////// - -EOLIAN static void -_efl_task_command_set(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, const char *command) -{ - eina_stringshare_replace(&pd->command, command); - _clear_args(pd); - pd->args = _unescape(pd->command); -} - -EOLIAN static const char * -_efl_task_command_get(const Eo *obj EINA_UNUSED, Efl_Task_Data *pd) -{ - _rebuild_command(pd); - return pd->command; -} - -EOLIAN static unsigned int -_efl_task_arg_count_get(const Eo *obj EINA_UNUSED, Efl_Task_Data *pd) -{ - if (!pd->args) return 0; - return eina_array_count(pd->args); -} - -EOLIAN static void -_efl_task_arg_value_set(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, unsigned int num, const char *arg) -{ - const char *parg = NULL; - unsigned int count; - - if (!pd->args) pd->args = eina_array_new(16); - count = eina_array_count(pd->args); - if ((count > 0) && (count > num)) - parg = eina_array_data_get(pd->args, num); - else - { - unsigned int i; - - for (i = count; i <= num; i++) - { - eina_array_push(pd->args, ""); - eina_array_data_set(pd->args, i, NULL); - } - } - - if (arg) - eina_array_data_set(pd->args, num, eina_stringshare_add(arg)); - else - eina_array_data_set(pd->args, num, NULL); - if (parg) eina_stringshare_del(parg); - pd->command_dirty = EINA_TRUE; -} - -EOLIAN static const char * -_efl_task_arg_value_get(const Eo *obj EINA_UNUSED, Efl_Task_Data *pd, unsigned int num) -{ - unsigned int count; - - if (!pd->args) return NULL; - count = eina_array_count(pd->args); - if (num >= count) return NULL; - return eina_array_data_get(pd->args, num); -} - -EOLIAN static void -_efl_task_arg_append(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, const char *arg) -{ - if (!pd->args) pd->args = eina_array_new(16); - if (arg) - eina_array_push(pd->args, eina_stringshare_add(arg)); - else - eina_array_push(pd->args, NULL); - pd->command_dirty = EINA_TRUE; -} - -EOLIAN static void -_efl_task_arg_reset(Eo *obj EINA_UNUSED, Efl_Task_Data *pd) -{ - _clear_args(pd); - pd->command_dirty = EINA_TRUE; -} - EOLIAN static void _efl_task_priority_set(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, Efl_Task_Priority priority) { @@ -344,7 +47,6 @@ _efl_task_efl_object_destructor(Eo *obj EINA_UNUSED, Efl_Task_Data *pd) { eina_stringshare_del(pd->command); pd->command = NULL; - _clear_args(pd); efl_destructor(efl_super(obj, MY_CLASS)); } diff --git a/src/lib/ecore/efl_task.eo b/src/lib/ecore/efl_task.eo index 526746ff60..ddab5e6826 100644 --- a/src/lib/ecore/efl_task.eo +++ b/src/lib/ecore/efl_task.eo @@ -19,83 +19,6 @@ abstract Efl.Task extends Efl.Object { [[ ]] methods { - @property command { - [[ A commandline that encodes arguments in a command string. - This command is unix shell-style, thus whitespace separates - arguments unless escaped. Also a semi-colon ';', ampersand - '&', pipe/bar '|', hash '#', bracket, square brace, brace - character ('(', ')', '[', ']', '{', '}'), exclamation - mark '!', backquote '`', greator or less than ('>' '<') - character unless escaped or in quotes would cause - args_count/value to not be generated properly, because - it would force complex shell interpretation which - will not be supported in evaluating the arg_count/value - information, but the final shell may interpret this if this - is executed via a command-line shell. To not be a complex - shell command, it should be simple with paths, options - and variable expansions, but nothing more complex involving - the above unescaped characters. - - "cat -option /path/file" - "cat 'quoted argument'" - "cat ~/path/escaped\ argument" - "/bin/cat escaped\ argument $VARIABLE" - etc. - - It should not try and use "complex shell features" if you - want the arg_count and arg_value set to be correct after - setting the command string. For example none of: - - "VAR=x /bin/command && /bin/othercommand >& /dev/null" - "VAR=x /bin/command `/bin/othercommand` | /bin/cmd2 && cmd3 &" - etc. - - If you set the command the arg_count/value property contents - can change and be completely re-evaluated by parsing the - command string into an argument array set along with - interpreting escapes back into individual argument strings. ]] - get { } - set { } - values { - command: string; [[ The command string as described ]] - } - } - @property arg_count { - [[ Number of arguments passed in or arguments that are to be - passed as sepcified by arg_value ]] - get { } - values { - args: uint; [[ ]] - } - } - @property arg_value { - [[ Argument number by index. If the index does not exist when - set, it is allocated and created. Getting an argument that - Has not been set yet will return $NULL. Empty arguments will - Be ignored. Setting an argument will result in the command - porperty being re-evaluated and escaped into a single - command string if needed. ]] - set { } - get { } - keys { - num: uint; [[ ]] - } - values { - arg: string; [[ ]] - } - } - arg_append { - [[ Append a new string argument at the end of the arg set. - This functions like setting an arg_value at the end of the - current set so the set increases by 1 in size. ]] - params { - arg: string; [[ ]] - } - } - arg_reset { - [[ Clear all arguments in arg_value/count set. Will result in the - command property also being cleared. ]] - } @property priority { [[ The priority of this task. ]] get { } diff --git a/src/lib/ecore/efl_thread.c b/src/lib/ecore/efl_thread.c index 4d48296093..a324af4f58 100644 --- a/src/lib/ecore/efl_thread.c +++ b/src/lib/ecore/efl_thread.c @@ -25,10 +25,7 @@ typedef struct int in, out; Eo *in_handler, *out_handler; } fd, ctrl; - struct { - unsigned int argc; - const char **argv; - } args; + Eina_Array *argv; Efl_Callback_Array_Item_Full *event_cb; void *indata, *outdata; } Thread_Data; @@ -151,16 +148,16 @@ _efl_loop_arguments_send(Eo *obj, void *data EINA_UNUSED, const Eina_Value v) Efl_Loop_Arguments arge; Eina_Array *arga; Eina_Stringshare *s; - unsigned int argc = efl_task_arg_count_get(obj); - unsigned int i; + Eina_Accessor *accessor; + const char *argv; + int i = 0; - arga = eina_array_new(argc); + accessor = efl_core_command_line_command_access(obj); + arga = eina_array_new(10); - for (i = 0; i < argc; i++) + EINA_ACCESSOR_FOREACH(accessor, i, argv) { - const char *argv = efl_task_arg_value_get(obj, i); - if (argv) - eina_array_push(arga, eina_stringshare_add(argv)); + eina_array_push(arga, eina_stringshare_add(argv)); } arge.argv = arga; arge.initialization = EINA_TRUE; @@ -229,7 +226,6 @@ _efl_thread_main(void *data, Eina_Thread t) Eo *obj; Eina_Value *ret; Control_Data cmd; - unsigned int i; int real; Efl_Callback_Array_Item_Full *it; @@ -280,16 +276,13 @@ _efl_thread_main(void *data, Eina_Thread t) efl_event_callback_priority_add(obj, it->desc, it->priority, it->func, it->user_data); } - for (i = 0; i < thdat->args.argc; i++) - efl_task_arg_append(obj, thdat->args.argv[i]); + efl_core_command_line_command_array_set(obj, thdat->argv); efl_future_then(obj, efl_loop_job(obj), .success = _efl_loop_arguments_send); - for (i = 0; i < thdat->args.argc; i++) - eina_stringshare_del(thdat->args.argv[i]); - free(thdat->args.argv); + while (thdat->argv && eina_array_count(thdat->argv)) free(eina_array_pop(thdat->argv)); + eina_array_free(thdat->argv); free(thdat->event_cb); - thdat->args.argv = NULL; thdat->event_cb = NULL; ret = efl_loop_begin(obj); @@ -575,7 +568,7 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd) const char *name; int pipe_to_thread[2]; int pipe_from_thread[2]; - unsigned int argc, i, num; + unsigned int num; Efl_Callback_Array_Item_Full *it; Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS); @@ -729,24 +722,23 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd) name = efl_name_get(obj); if (name) thdat->name = eina_stringshare_add(name); - argc = efl_task_arg_count_get(obj); - if (argc > 0) - { - thdat->args.argc = argc; - thdat->args.argv = malloc(argc * sizeof(char *)); - if (thdat->args.argv) - { - for (i = 0; i < argc; i++) - { - const char *argv = efl_task_arg_value_get(obj, i); - if (argv) - thdat->args.argv[i] = eina_stringshare_add(argv); - else - thdat->args.argv[i] = NULL; - } - } - // XXX: if malloc fails? - } + { + Eina_Accessor *acc; + int i = 0; + const char *argv; + + acc = efl_core_command_line_command_access(obj); + if (acc) + { + thdat->argv = eina_array_new(0); + EINA_ACCESSOR_FOREACH(acc, i, argv) + { + eina_array_push(thdat->argv, eina_stringshare_add(argv)); + } + } + + } + if (pd->event_cb) { num = 0; @@ -762,9 +754,8 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd) if (!eina_thread_create(&(pd->thread), pri, -1, _efl_thread_main, thdat)) { - for (i = 0; i < thdat->args.argc; i++) - eina_stringshare_del(thdat->args.argv[i]); - free(thdat->args.argv); + while (eina_array_count(thdat->argv)) eina_stringshare_del(eina_array_pop(thdat->argv)); + eina_array_free(thdat->argv); efl_del(pd->fd.in_handler); efl_del(pd->fd.out_handler); efl_del(pd->ctrl.in_handler); diff --git a/src/lib/ecore/efl_thread.eo b/src/lib/ecore/efl_thread.eo index 7837c7bfdb..02bf2f15df 100644 --- a/src/lib/ecore/efl_thread.eo +++ b/src/lib/ecore/efl_thread.eo @@ -1,4 +1,4 @@ -class Efl.Thread extends Efl.Task implements Efl.ThreadIO, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer +class Efl.Thread extends Efl.Task implements Efl.ThreadIO, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer, Efl.Core.Command_Line { methods { } diff --git a/src/lib/ecore/meson.build b/src/lib/ecore/meson.build index 98909cb618..2e44804481 100644 --- a/src/lib/ecore/meson.build +++ b/src/lib/ecore/meson.build @@ -76,7 +76,8 @@ pub_eo_files = [ 'efl_composite_model.eo', 'efl_view_model.eo', 'efl_core_env.eo', - 'efl_core_proc_env.eo' + 'efl_core_proc_env.eo', + 'efl_core_command_line.eo', ] foreach eo_file : pub_eo_files @@ -180,10 +181,12 @@ ecore_src = [ 'ecore_main_common.h', 'efl_exe.c', 'efl_thread.c', + 'efl_appthread.c', 'efl_threadio.c', 'efl_appthread.c', 'efl_core_env.c', 'efl_core_proc_env.c', + 'efl_core_command_line.c', ] if sys_windows == true diff --git a/src/tests/ecore/efl_app_suite.c b/src/tests/ecore/efl_app_suite.c index cd26e2d95e..2cab632622 100644 --- a/src/tests/ecore/efl_app_suite.c +++ b/src/tests/ecore/efl_app_suite.c @@ -53,6 +53,7 @@ static const Efl_Test_Case etc[] = { { "Promise", efl_app_test_promise_3 }, { "Promise", efl_app_test_promise_safety }, { "Env", efl_test_efl_env }, + { "CML", efl_test_efl_cml }, { NULL, NULL } }; diff --git a/src/tests/ecore/efl_app_suite.h b/src/tests/ecore/efl_app_suite.h index 3a66dcdfcf..874d2bb503 100644 --- a/src/tests/ecore/efl_app_suite.h +++ b/src/tests/ecore/efl_app_suite.h @@ -12,5 +12,6 @@ void efl_app_test_promise_2(TCase *tc); void efl_app_test_promise_3(TCase *tc); void efl_app_test_promise_safety(TCase *tc); void efl_test_efl_env(TCase *tc); +void efl_test_efl_cml(TCase *tc); #endif /* _EFL_APP_SUITE_H */ diff --git a/src/tests/ecore/efl_app_test_cml.c b/src/tests/ecore/efl_app_test_cml.c new file mode 100644 index 0000000000..1b7cebf552 --- /dev/null +++ b/src/tests/ecore/efl_app_test_cml.c @@ -0,0 +1,85 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#define EFL_CORE_COMMAND_LINE_PROTECTED + +#include +#include +#define EFL_NOLEGACY_API_SUPPORT +#include +#include "efl_app_suite.h" +#include "../efl_check.h" + +typedef struct { + +} Efl_App_Test_CML_Data; + +#include "efl_app_test_cml.eo.h" +#include "efl_app_test_cml.eo.c" + +static Eina_Array* +_construct_array(void) +{ + Eina_Array *array = eina_array_new(16); + + eina_array_push(array, "/bin/sh"); + eina_array_push(array, "-C"); + eina_array_push(array, "foo"); + eina_array_push(array, "--test"); + eina_array_push(array, "--option=done"); + eina_array_push(array, "--"); + eina_array_push(array, "asdf --test"); + return array; +} + +static const char* +_construct_string(void) +{ + return "/bin/sh -C foo --test --option=done -- \"asdf --test\""; +} + +EFL_START_TEST(efl_core_cml_string) +{ + Efl_App_Test_CML *cml = efl_add_ref(EFL_APP_TEST_CML_CLASS, NULL); + Eina_Array *content = _construct_array(); + Eina_Stringshare *str; + Eina_Bool b; + int i = 0; + + b = efl_core_command_line_command_string_set(cml, _construct_string()); + ck_assert_int_ne(b, 0); + + EINA_ACCESSOR_FOREACH(efl_core_command_line_command_access(cml), i, str) + { + ck_assert_str_eq(eina_array_data_get(content, i), str); + } + ck_assert_str_eq(efl_core_command_line_command_get(cml), _construct_string()); +} +EFL_END_TEST + +EFL_START_TEST(efl_core_cml_array) +{ + Efl_App_Test_CML *cml = efl_add_ref(EFL_APP_TEST_CML_CLASS, NULL); + Eina_Array *content1 = _construct_array(); + Eina_Array *content2 = _construct_array(); + Eina_Stringshare *str; + Eina_Bool b; + int i = 0; + + b = efl_core_command_line_command_array_set(cml, content1); + ck_assert_int_ne(b, 0); + + EINA_ACCESSOR_FOREACH(efl_core_command_line_command_access(cml), i, str) + { + ck_assert_str_eq(eina_array_data_get(content2, i), str); + } + ck_assert_str_eq(efl_core_command_line_command_get(cml), _construct_string()); +} +EFL_END_TEST + +void efl_test_efl_cml(TCase *tc) +{ + tcase_add_test(tc, efl_core_cml_string); + tcase_add_test(tc, efl_core_cml_array); +} diff --git a/src/tests/ecore/efl_app_test_cml.eo b/src/tests/ecore/efl_app_test_cml.eo new file mode 100644 index 0000000000..b0877e0cf7 --- /dev/null +++ b/src/tests/ecore/efl_app_test_cml.eo @@ -0,0 +1,4 @@ +class Efl.App.Test.CML extends Efl.Object implements Efl.Core.Command_Line +{ + +} diff --git a/src/tests/ecore/meson.build b/src/tests/ecore/meson.build index e3b4f6c851..c49d941355 100644 --- a/src/tests/ecore/meson.build +++ b/src/tests/ecore/meson.build @@ -76,14 +76,32 @@ efl_app_suite_src = [ 'efl_app_test_loop_fd.c', 'efl_app_test_loop_timer.c', 'efl_app_test_promise.c', - 'efl_app_test_env.c' + 'efl_app_test_env.c', + 'efl_app_test_cml.c', ] +priv_eo_files = [ + 'efl_app_test_cml.eo', +] + +priv_eo_file_target = [] +foreach eo_file : priv_eo_files + priv_eo_file_target += custom_target('eolian_gen_' + eo_file, + input : eo_file, + output : [eo_file + '.h'], + depfile : eo_file + '.d', + command : eolian_gen + [ '-I', meson.current_source_dir(), eolian_include_directories, + '-o', 'h:' + join_paths(meson.current_build_dir(), eo_file + '.h'), + '-o', 'c:' + join_paths(meson.current_build_dir(), eo_file + '.c'), + '-o', 'd:' + join_paths(meson.current_build_dir(), eo_file + '.d'), + '-gchd', '@INPUT@']) +endforeach + efl_app_suite_deps = [m] efl_app_suite_deps += ecore efl_app_suite = executable('efl_app_suite', - efl_app_suite_src, + efl_app_suite_src, priv_eo_file_target, dependencies: [efl_app_suite_deps, check], c_args : [ '-DTESTS_BUILD_DIR="'+meson.current_build_dir()+'"',