summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile_Ecore.am12
-rw-r--r--src/lib/ecore/Ecore_Eo.h1
-rw-r--r--src/lib/ecore/efl_core_command_line.c267
-rw-r--r--src/lib/ecore/efl_core_command_line.eo80
-rw-r--r--src/lib/ecore/meson.build4
-rw-r--r--src/tests/ecore/efl_app_suite.c1
-rw-r--r--src/tests/ecore/efl_app_suite.h1
-rw-r--r--src/tests/ecore/efl_app_test_cml.c85
-rw-r--r--src/tests/ecore/efl_app_test_cml.eo4
-rw-r--r--src/tests/ecore/meson.build22
10 files changed, 473 insertions, 4 deletions
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 = \
52 lib/ecore/efl_view_model.eo \ 52 lib/ecore/efl_view_model.eo \
53 lib/ecore/efl_core_env.eo \ 53 lib/ecore/efl_core_env.eo \
54 lib/ecore/efl_core_proc_env.eo \ 54 lib/ecore/efl_core_proc_env.eo \
55 lib/ecore/efl_core_command_line.eo
56
57ecore_test_eolian_files = \
58 tests/ecore/efl_app_test_cml.eo
55 59
56ecore_eolian_files = \ 60ecore_eolian_files = \
57 $(ecore_eolian_files_legacy) \ 61 $(ecore_eolian_files_legacy) \
@@ -60,10 +64,14 @@ ecore_eolian_files = \
60ecore_eolian_c = $(ecore_eolian_files:%.eo=%.eo.c) 64ecore_eolian_c = $(ecore_eolian_files:%.eo=%.eo.c)
61ecore_eolian_h = $(ecore_eolian_files:%.eo=%.eo.h) \ 65ecore_eolian_h = $(ecore_eolian_files:%.eo=%.eo.h) \
62 $(ecore_eolian_files_legacy:%.eo=%.eo.legacy.h) 66 $(ecore_eolian_files_legacy:%.eo=%.eo.legacy.h)
67ecore_test_c = $(ecore_test_eolian_files:%.eo=%.eo.c)
68ecore_test_h = $(ecore_test_eolian_files:%.eo=%.eo.h)
63 69
64BUILT_SOURCES += \ 70BUILT_SOURCES += \
65 $(ecore_eolian_c) \ 71 $(ecore_eolian_c) \
66 $(ecore_eolian_h) 72 $(ecore_eolian_h) \
73 $(ecore_test_c) \
74 $(ecore_test_h)
67 75
68ecoreeolianfilesdir = $(datadir)/eolian/include/ecore-@VMAJ@ 76ecoreeolianfilesdir = $(datadir)/eolian/include/ecore-@VMAJ@
69ecoreeolianfiles_DATA = $(ecore_eolian_files_public) lib/ecore/efl_loop_timer.eo 77ecoreeolianfiles_DATA = $(ecore_eolian_files_public) lib/ecore/efl_loop_timer.eo
@@ -100,6 +108,7 @@ lib/ecore/ecore_job.c \
100lib/ecore/ecore_main.c \ 108lib/ecore/ecore_main.c \
101lib/ecore/ecore_event_message.c \ 109lib/ecore/ecore_event_message.c \
102lib/ecore/ecore_event_message_handler.c \ 110lib/ecore/ecore_event_message_handler.c \
111lib/ecore/efl_core_command_line.c \
103lib/ecore/efl_core_env.c \ 112lib/ecore/efl_core_env.c \
104lib/ecore/efl_core_proc_env.c \ 113lib/ecore/efl_core_proc_env.c \
105lib/ecore/efl_app.c \ 114lib/ecore/efl_app.c \
@@ -339,6 +348,7 @@ tests/ecore/efl_app_test_loop.c \
339tests/ecore/efl_app_test_loop_fd.c \ 348tests/ecore/efl_app_test_loop_fd.c \
340tests/ecore/efl_app_test_loop_timer.c \ 349tests/ecore/efl_app_test_loop_timer.c \
341tests/ecore/efl_app_test_promise.c \ 350tests/ecore/efl_app_test_promise.c \
351tests/ecore/efl_app_test_cml.c \
342tests/ecore/efl_app_test_env.c \ 352tests/ecore/efl_app_test_env.c \
343tests/ecore/efl_app_suite.c \ 353tests/ecore/efl_app_suite.c \
344tests/ecore/efl_app_suite.h 354tests/ecore/efl_app_suite.h
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 @@
28 28
29#include "efl_core_env.eo.h" 29#include "efl_core_env.eo.h"
30#include "efl_core_proc_env.eo.h" 30#include "efl_core_proc_env.eo.h"
31#include "efl_core_command_line.eo.h"
31 32
32#include "efl_loop_message.eo.h" 33#include "efl_loop_message.eo.h"
33#include "efl_loop_message_handler.eo.h" 34#include "efl_loop_message_handler.eo.h"
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 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#define EFL_CORE_COMMAND_LINE_PROTECTED
6
7#include <Efl_Core.h>
8
9#define MY_CLASS EFL_CORE_COMMAND_LINE_MIXIN
10
11typedef struct {
12 Eina_Bool filled;
13 char *string_command;
14 Eina_Array *command;
15} Efl_Core_Command_Line_Data;
16
17static Eina_Array *
18_unescape(const char *s)
19{
20 Eina_Array *args;
21 const char *p;
22 char *tmp = NULL, *d = NULL;
23 if (!s) return NULL;
24
25 Eina_Bool in_quote_dbl = EINA_FALSE;
26 Eina_Bool in_quote = EINA_FALSE;
27
28 args = eina_array_new(16);
29 if (!args) return NULL;
30 for (p = s; *p; p++)
31 {
32 if (!tmp) tmp = d = strdup(p);
33 if (tmp)
34 {
35 if (in_quote_dbl)
36 {
37 switch (*p)
38 {
39 case '\"':
40 in_quote_dbl = EINA_FALSE;
41 *d = 0;
42 eina_array_push(args, eina_stringshare_add(tmp));
43 free(tmp);
44 tmp = d = NULL;
45 break;
46 case '\\':
47 p++;
48 EINA_FALLTHROUGH
49 default:
50 *d = *p;
51 d++;
52 break;
53 }
54 }
55 else if (in_quote)
56 {
57 switch (*p)
58 {
59 case '\'':
60 in_quote = EINA_FALSE;
61 *d = 0;
62 eina_array_push(args, eina_stringshare_add(tmp));
63 free(tmp);
64 tmp = d = NULL;
65 break;
66 case '\\':
67 p++;
68 EINA_FALLTHROUGH
69 default:
70 *d = *p;
71 d++;
72 break;
73 }
74 }
75 else
76 {
77 switch (*p)
78 {
79 case ' ':
80 case '\t':
81 case '\r':
82 case '\n':
83 *d = 0;
84 eina_array_push(args, eina_stringshare_add(tmp));
85 free(tmp);
86 tmp = d = NULL;
87 break;
88 case '\"':
89 in_quote_dbl = EINA_TRUE;
90 break;
91 case '\'':
92 in_quote = EINA_TRUE;
93 break;
94 case '\\':
95 p++;
96 EINA_FALLTHROUGH
97 default:
98 *d = *p;
99 d++;
100 break;
101 }
102 }
103 }
104 }
105 if (tmp)
106 {
107 *d = 0;
108 eina_array_push(args, eina_stringshare_add(tmp));
109 free(tmp);
110 }
111 return args;
112}
113
114static char *
115_escape(const char *s)
116{
117 Eina_Bool need_quote = EINA_FALSE;
118 const char *p;
119 char *s2 = malloc((strlen(s) * 2) + 1 + 2), *d;
120
121 if (!s2) return NULL;
122
123 for (p = s; *p; p++)
124 {
125 switch (*p)
126 {
127 case '\'':
128 case '\"':
129 case '$':
130 case '#':
131 case ';':
132 case '&':
133 case '`':
134 case '|':
135 case '(':
136 case ')':
137 case '[':
138 case ']':
139 case '{':
140 case '}':
141 case '>':
142 case '<':
143 case '\n':
144 case '\r':
145 case '\t':
146 case ' ':
147 need_quote = EINA_TRUE;
148 default:
149 break;
150 }
151 }
152
153 d = s2;
154 if (need_quote)
155 {
156 *d = '\"';
157 d++;
158 }
159 for (p = s; *p; p++, d++)
160 {
161 switch (*p)
162 {
163 case '\\':
164 case '\'':
165 case '\"':
166 *d = '\\';
167 d++;
168 EINA_FALLTHROUGH
169 default:
170 *d = *p;
171 break;
172 }
173 }
174 if (need_quote)
175 {
176 *d = '\"';
177 d++;
178 }
179 *d = 0;
180 return s2;
181}
182
183EOLIAN static const char*
184_efl_core_command_line_command_get(const Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd)
185{
186 return eina_strdup(pd->string_command);
187}
188
189EOLIAN static Eina_Accessor*
190_efl_core_command_line_command_access(Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd)
191{
192 return pd->command ? eina_array_accessor_new(pd->command) : NULL;
193}
194
195static void
196_remove_invalid_chars(char *command)
197{
198 for (unsigned int i = 0; i < strlen(command); ++i)
199 {
200 char c = command[i];
201 if (c < 0x20 || c == 0x7f)
202 command[i] = '\x12';
203 }
204}
205
206EOLIAN static Eina_Bool
207_efl_core_command_line_command_array_set(Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd, Eina_Array *array)
208{
209 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->filled, EINA_FALSE);
210 Eina_Strbuf *command = eina_strbuf_new();
211 unsigned int i = 0;
212
213 pd->command = eina_array_new(eina_array_count(array));
214 for (i = 0; i < (array ? eina_array_count(array) : 0); ++i)
215 {
216 char *content = eina_array_data_get(array, i);
217 char *param = calloc(1, strlen(content));
218
219 if (!param)
220 {
221 free(param);
222 while (eina_array_count(pd->command) > 0)
223 eina_stringshare_del(eina_array_pop(pd->command));
224 eina_array_free(pd->command);
225 pd->command = NULL;
226 eina_array_free(array);
227 return EINA_FALSE;
228 }
229
230 //build the command
231 if (i != 0)
232 eina_strbuf_append(command, " ");
233 eina_strbuf_append(command, _escape(content));
234 //convert string to stringshare
235 strcpy(param, content);
236 _remove_invalid_chars(param);
237 eina_array_push(pd->command, eina_stringshare_add(param));
238 free(param);
239 }
240 pd->string_command = eina_strbuf_release(command);
241 pd->filled = EINA_TRUE;
242 eina_array_free(array);
243
244 return EINA_TRUE;
245}
246
247EOLIAN static Eina_Bool
248_efl_core_command_line_command_string_set(Eo *obj EINA_UNUSED, Efl_Core_Command_Line_Data *pd, const char *str)
249{
250 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->filled, EINA_FALSE);
251
252 pd->string_command = eina_strdup(str);
253 _remove_invalid_chars(pd->string_command);
254 pd->command = _unescape(str);
255 if (!pd->command)
256 {
257 if (pd->string_command)
258 free(pd->string_command);
259 pd->string_command = NULL;
260 return EINA_FALSE;
261 }
262 pd->filled = EINA_TRUE;
263
264 return EINA_TRUE;
265}
266
267#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 @@
1mixin Efl.Core.Command_Line {
2 [[A mixin that implements standard functions for command lines.
3
4 This object parses the command line that gets passed, later the object can be accessed via accessor or the string directly.
5 ]]
6 methods {
7 @property command {
8 [[ A commandline that encodes arguments in a command string.
9 This command is unix shell-style, thus whitespace separates
10 arguments unless escaped. Also a semi-colon ';', ampersand
11 '&', pipe/bar '|', hash '#', bracket, square brace, brace
12 character ('(', ')', '[', ']', '{', '}'), exclamation
13 mark '!', backquote '`', greator or less than ('>' '<')
14 character unless escaped or in quotes would cause
15 args_count/value to not be generated properly, because
16 it would force complex shell interpretation which
17 will not be supported in evaluating the arg_count/value
18 information, but the final shell may interpret this if this
19 is executed via a command-line shell. To not be a complex
20 shell command, it should be simple with paths, options
21 and variable expansions, but nothing more complex involving
22 the above unescaped characters.
23
24 "cat -option /path/file"
25 "cat 'quoted argument'"
26 "cat ~/path/escaped\ argument"
27 "/bin/cat escaped\ argument $VARIABLE"
28 etc.
29
30 It should not try and use "complex shell features" if you
31 want the arg_count and arg_value set to be correct after
32 setting the command string. For example none of:
33
34 "VAR=x /bin/command && /bin/othercommand >& /dev/null"
35 "VAR=x /bin/command `/bin/othercommand` | /bin/cmd2 && cmd3 &"
36 etc.
37
38 If you set the command the arg_count/value property contents
39 can change and be completely re-evaluated by parsing the
40 command string into an argument array set along with
41 interpreting escapes back into individual argument strings.
42 ]]
43 get {
44
45 }
46 values {
47 commandline : string;
48 }
49 }
50 command_access {
51 [[ Get the accessor which enables access to each argument that got passed to this object. ]]
52 return : accessor<stringshare>;
53 }
54 @property command_array {
55 [[ Use an array to fill this object
56
57 Every element of a string is a argument.
58 ]]
59 set {
60 return : bool; [[On success $true, $false otherwise]]
61 }
62 values {
63 array : array<string> @owned; [[An array where every array field is an argument]]
64 }
65 }
66 @property command_string {
67 [[ Use a string to fill this object
68
69 The string will be split at every unescaped ' ', every resulting substring will be a new argument to the command line.
70 ]]
71 set {
72 return : bool; [[On success $true, $false otherwise]]
73 }
74 values {
75 str : string; [[A command in form of a string]]
76 }
77
78 }
79 }
80}
diff --git a/src/lib/ecore/meson.build b/src/lib/ecore/meson.build
index 98909cb618..375f745abd 100644
--- a/src/lib/ecore/meson.build
+++ b/src/lib/ecore/meson.build
@@ -76,7 +76,8 @@ pub_eo_files = [
76 'efl_composite_model.eo', 76 'efl_composite_model.eo',
77 'efl_view_model.eo', 77 'efl_view_model.eo',
78 'efl_core_env.eo', 78 'efl_core_env.eo',
79 'efl_core_proc_env.eo' 79 'efl_core_proc_env.eo',
80 'efl_core_command_line.eo',
80] 81]
81 82
82foreach eo_file : pub_eo_files 83foreach eo_file : pub_eo_files
@@ -184,6 +185,7 @@ ecore_src = [
184 'efl_appthread.c', 185 'efl_appthread.c',
185 'efl_core_env.c', 186 'efl_core_env.c',
186 'efl_core_proc_env.c', 187 'efl_core_proc_env.c',
188 'efl_core_command_line.c',
187] 189]
188 190
189if sys_windows == true 191if 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[] = {
53 { "Promise", efl_app_test_promise_3 }, 53 { "Promise", efl_app_test_promise_3 },
54 { "Promise", efl_app_test_promise_safety }, 54 { "Promise", efl_app_test_promise_safety },
55 { "Env", efl_test_efl_env }, 55 { "Env", efl_test_efl_env },
56 { "CML", efl_test_efl_cml },
56 { NULL, NULL } 57 { NULL, NULL }
57}; 58};
58 59
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);
12void efl_app_test_promise_3(TCase *tc); 12void efl_app_test_promise_3(TCase *tc);
13void efl_app_test_promise_safety(TCase *tc); 13void efl_app_test_promise_safety(TCase *tc);
14void efl_test_efl_env(TCase *tc); 14void efl_test_efl_env(TCase *tc);
15void efl_test_efl_cml(TCase *tc);
15 16
16#endif /* _EFL_APP_SUITE_H */ 17#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 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#define EFL_CORE_COMMAND_LINE_PROTECTED
6
7#include <stdio.h>
8#include <unistd.h>
9#define EFL_NOLEGACY_API_SUPPORT
10#include <Efl_Core.h>
11#include "efl_app_suite.h"
12#include "../efl_check.h"
13
14typedef struct {
15
16} Efl_App_Test_CML_Data;
17
18#include "efl_app_test_cml.eo.h"
19#include "efl_app_test_cml.eo.c"
20
21static Eina_Array*
22_construct_array(void)
23{
24 Eina_Array *array = eina_array_new(16);
25
26 eina_array_push(array, "/bin/sh");
27 eina_array_push(array, "-C");
28 eina_array_push(array, "foo");
29 eina_array_push(array, "--test");
30 eina_array_push(array, "--option=done");
31 eina_array_push(array, "--");
32 eina_array_push(array, "asdf --test");
33 return array;
34}
35
36static const char*
37_construct_string(void)
38{
39 return "/bin/sh -C foo --test --option=done -- \"asdf --test\"";
40}
41
42EFL_START_TEST(efl_core_cml_string)
43{
44 Efl_App_Test_CML *cml = efl_add_ref(EFL_APP_TEST_CML_CLASS, NULL);
45 Eina_Array *content = _construct_array();
46 Eina_Stringshare *str;
47 Eina_Bool b;
48 int i = 0;
49
50 b = efl_core_command_line_command_string_set(cml, _construct_string());
51 ck_assert_int_ne(b, 0);
52
53 EINA_ACCESSOR_FOREACH(efl_core_command_line_command_access(cml), i, str)
54 {
55 ck_assert_str_eq(eina_array_data_get(content, i), str);
56 }
57 ck_assert_str_eq(efl_core_command_line_command_get(cml), _construct_string());
58}
59EFL_END_TEST
60
61EFL_START_TEST(efl_core_cml_array)
62{
63 Efl_App_Test_CML *cml = efl_add_ref(EFL_APP_TEST_CML_CLASS, NULL);
64 Eina_Array *content1 = _construct_array();
65 Eina_Array *content2 = _construct_array();
66 Eina_Stringshare *str;
67 Eina_Bool b;
68 int i = 0;
69
70 b = efl_core_command_line_command_array_set(cml, content1);
71 ck_assert_int_ne(b, 0);
72
73 EINA_ACCESSOR_FOREACH(efl_core_command_line_command_access(cml), i, str)
74 {
75 ck_assert_str_eq(eina_array_data_get(content2, i), str);
76 }
77 ck_assert_str_eq(efl_core_command_line_command_get(cml), _construct_string());
78}
79EFL_END_TEST
80
81void efl_test_efl_cml(TCase *tc)
82{
83 tcase_add_test(tc, efl_core_cml_string);
84 tcase_add_test(tc, efl_core_cml_array);
85}
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 @@
1class Efl.App.Test.CML extends Efl.Object implements Efl.Core.Command_Line
2{
3
4}
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 = [
76 'efl_app_test_loop_fd.c', 76 'efl_app_test_loop_fd.c',
77 'efl_app_test_loop_timer.c', 77 'efl_app_test_loop_timer.c',
78 'efl_app_test_promise.c', 78 'efl_app_test_promise.c',
79 'efl_app_test_env.c' 79 'efl_app_test_env.c',
80 'efl_app_test_cml.c',
80] 81]
81 82
83priv_eo_files = [
84 'efl_app_test_cml.eo',
85]
86
87priv_eo_file_target = []
88foreach eo_file : priv_eo_files
89 priv_eo_file_target += custom_target('eolian_gen_' + eo_file,
90 input : eo_file,
91 output : [eo_file + '.h'],
92 depfile : eo_file + '.d',
93 command : eolian_gen + [ '-I', meson.current_source_dir(), eolian_include_directories,
94 '-o', 'h:' + join_paths(meson.current_build_dir(), eo_file + '.h'),
95 '-o', 'c:' + join_paths(meson.current_build_dir(), eo_file + '.c'),
96 '-o', 'd:' + join_paths(meson.current_build_dir(), eo_file + '.d'),
97 '-gchd', '@INPUT@'])
98endforeach
99
82efl_app_suite_deps = [m] 100efl_app_suite_deps = [m]
83efl_app_suite_deps += ecore 101efl_app_suite_deps += ecore
84 102
85efl_app_suite = executable('efl_app_suite', 103efl_app_suite = executable('efl_app_suite',
86 efl_app_suite_src, 104 efl_app_suite_src, priv_eo_file_target,
87 dependencies: [efl_app_suite_deps, check], 105 dependencies: [efl_app_suite_deps, check],
88 c_args : [ 106 c_args : [
89 '-DTESTS_BUILD_DIR="'+meson.current_build_dir()+'"', 107 '-DTESTS_BUILD_DIR="'+meson.current_build_dir()+'"',