test : enhance accessible state information
[platform/upstream/at-spi2-core.git] / test / at_spi2_tool.c
1 #include "atspi/atspi.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <getopt.h>
6 #include <stdbool.h>
7 #include <gio/gio.h>
8 #include <assert.h>
9
10 #define ERROR_STATE -1
11 #define SAFE_BUFFER_SIZE 2048
12 #define CHECK_OUTPUT_WIDTH 42
13 #define MINIMAL_MODULE_WIDTH 3
14 #define ATTR_WIDTH 50
15 #define NUMBER_WIDTH 6
16 #define ARRAY_SIZE(x)  (sizeof(x) / sizeof((x)[0]))
17 #define COLUMN_NO 3
18 #define FLAG_NO 3
19 #define DUMP 0
20 #define CHECK 1
21 #define FIRST_MATCH 2
22 #define RELATION_TABLE_COLUMN_COUNT 7
23 #define RELATION_COLUMN_WIDTH 20
24 #define VERSION "1.1"
25
26 static unsigned indent_width = 2;
27 static int module_name_limit = -1;
28
29 static const char* atspi_state_names[] = {
30         [ATSPI_STATE_INVALID] = "INVALID",
31         [ATSPI_STATE_ACTIVE] = "ACTIVE",
32         [ATSPI_STATE_ARMED] = "ARMED",
33         [ATSPI_STATE_BUSY] =  "BUSY",
34         [ATSPI_STATE_CHECKED] = "CHECKED",
35         [ATSPI_STATE_COLLAPSED] = "COLLAPSED",
36         [ATSPI_STATE_DEFUNCT] = "DEFUNCT",
37         [ATSPI_STATE_EDITABLE] = "EDITABLE",
38         [ATSPI_STATE_ENABLED] = "ENABLED",
39         [ATSPI_STATE_EXPANDABLE] = "EXPANDABLE",
40         [ATSPI_STATE_EXPANDED] = "EXPANDED",
41         [ATSPI_STATE_FOCUSABLE] = "FOCUSABLE",
42         [ATSPI_STATE_FOCUSED] = "FOCUSED",
43         [ATSPI_STATE_HAS_TOOLTIP] = "HAS_TOOLTIP",
44         [ATSPI_STATE_HORIZONTAL] = "HORIZONTAL",
45         [ATSPI_STATE_ICONIFIED] = "ICONIFIED",
46         [ATSPI_STATE_MULTI_LINE] = "MULTI_LINE",
47         [ATSPI_STATE_MULTISELECTABLE] = "MULTISELECTABLE",
48         [ATSPI_STATE_OPAQUE] = "OPAQUE",
49         [ATSPI_STATE_PRESSED] = "PRESSED",
50         [ATSPI_STATE_RESIZABLE] = "RESIZABLE",
51         [ATSPI_STATE_SELECTABLE] = "SELECTABLE",
52         [ATSPI_STATE_SELECTED] = "SELECTED",
53         [ATSPI_STATE_SENSITIVE] = "SENSITIVE",
54         [ATSPI_STATE_SHOWING] = "SHOWING",
55         [ATSPI_STATE_SINGLE_LINE] = "SINGLE_LINE",
56         [ATSPI_STATE_STALE] = "STALE",
57         [ATSPI_STATE_TRANSIENT] = "TRANSIENT",
58         [ATSPI_STATE_VERTICAL] = "VERTICAL",
59         [ATSPI_STATE_VISIBLE] = "VISIBLE",
60         [ATSPI_STATE_MANAGES_DESCENDANTS] = "MANAGES_DESCENDANTS",
61         [ATSPI_STATE_INDETERMINATE] = "INDETERMINATE",
62         [ATSPI_STATE_REQUIRED] = "REQUIRED",
63         [ATSPI_STATE_TRUNCATED] = "TRUNCATED",
64         [ATSPI_STATE_ANIMATED] = "ANIMATED",
65         [ATSPI_STATE_INVALID_ENTRY] = "INVALID_ENTRY",
66         [ATSPI_STATE_SUPPORTS_AUTOCOMPLETION] = "SUPPORTS_AUTOCOMPLETION",
67         [ATSPI_STATE_SELECTABLE_TEXT] = "SELECTABLE_TEXT",
68         [ATSPI_STATE_IS_DEFAULT] = "IS_DEFAULT",
69         [ATSPI_STATE_VISITED] = "VISITED",
70         [ATSPI_STATE_CHECKABLE] = "CHECKABLE",
71         [ATSPI_STATE_MODAL] = "MODAL",
72         [ATSPI_STATE_HIGHLIGHTED] = "HIGHLIGHTED",
73         [ATSPI_STATE_HIGHLIGHTABLE] = "HIGHLIGHTABLE",
74         [ATSPI_STATE_HAS_POPUP] = "HAS_POPUP",
75         [ATSPI_STATE_READ_ONLY] = "READ_ONLY",
76         [ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED"
77 };
78
79 typedef struct _Box_Size {
80         char *x;
81         char *y;
82         char *height;
83         char *width;
84 } Box_Size;
85
86 static char *_multiply_string(char c, unsigned number)
87 {
88         char *result = (char *)calloc(1, number + 1);
89
90         if (result == NULL)
91                 return result;
92
93         memset(result, c, number);
94
95         return result;
96 }
97
98 static void _print_module_legend()
99 {
100         printf("\n[[node],[node role name],[attributes list],[x,y,width,height],[node name],[states list][relations]\n\n");
101 }
102
103 static void _print_atspi_states_legend()
104 {
105         _print_module_legend();
106
107         int array_len = ARRAY_SIZE(atspi_state_names);
108         int line_count = (array_len + COLUMN_NO - 1)/COLUMN_NO;
109         for (int line_idx = 0; line_idx < line_count; line_idx++) {
110                 if ((line_idx < line_count - 1) || (array_len % COLUMN_NO == 0)) {
111                         printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n",
112                                         line_idx * COLUMN_NO,
113                                         atspi_state_names[line_idx * COLUMN_NO],
114                                         (line_idx * COLUMN_NO) + 1,
115                                         atspi_state_names[(line_idx * COLUMN_NO) + 1],
116                                         (line_idx * COLUMN_NO) + 2,
117                                         atspi_state_names[(line_idx * COLUMN_NO) + 2]);
118                 } else if (array_len % COLUMN_NO == 2) {
119                         printf("[%d]\t%-24s[%d]\t%s\n",
120                                         line_idx * COLUMN_NO,
121                                         atspi_state_names[line_idx * COLUMN_NO],
122                                         (line_idx * COLUMN_NO) + 1,
123                                         atspi_state_names[(line_idx * COLUMN_NO) + 1]);
124                 } else {
125                         printf("[%d]\t%s\n", line_idx * COLUMN_NO, atspi_state_names[line_idx * COLUMN_NO]);
126                 }
127         }
128 }
129
130 static void _set_indent(int indent_size)
131 {
132         if (indent_size < 0) {
133                 fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n");
134                 return;
135         }
136
137         indent_width = indent_size;
138 }
139
140 static void _set_module_length_limit(int limit)
141 {
142         if (limit < MINIMAL_MODULE_WIDTH) {
143                 fprintf(stderr, "WARNING: Invalid maximum field size. Default value retained.\n");
144                 return;
145         }
146
147         module_name_limit = limit;
148 }
149
150 static int _int_sort_function(gconstpointer a, gconstpointer b)
151 {
152         int int_a = GPOINTER_TO_INT (a);
153         int int_b = GPOINTER_TO_INT (b);
154
155         return int_a - int_b;
156 }
157
158 static void _truncate_string(char *string, int length_limit)
159 {
160         if (length_limit < MINIMAL_MODULE_WIDTH || !string)
161                 return;
162
163         int len = strlen(string);
164
165         if (len > length_limit)
166                 memcpy(string + length_limit - 3, "...", 4);
167 }
168
169 static size_t _combine_strings(char **destination, const char *source)
170 {
171         if (!source || !*source || !destination)
172                 return 0;
173
174         size_t old_len = *destination ? strlen(*destination) : 0;
175         size_t source_len = strlen(source);
176         size_t len = old_len + source_len;
177         char *buf = realloc(*destination, (len + 1) * sizeof(char));
178
179         if (!buf) {
180                 fprintf(stderr, "_combine_strings: allocation failed");
181                 return old_len;
182         }
183
184         memcpy(buf + old_len, source, source_len + 1);
185
186         *destination = buf;
187
188         return len;
189 }
190
191 static Box_Size *_get_box_size(AtspiAccessible *node)
192 {
193         Box_Size *box_size = (Box_Size *)malloc(sizeof(Box_Size));
194         if (box_size == NULL)
195                 return NULL;
196
197         char *x = NULL;
198         char *y = NULL;
199         char *width = NULL;
200         char *height = NULL;
201
202         AtspiComponent *component = atspi_accessible_get_component_iface(node);
203         AtspiRect *extent = component ? atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL) : NULL;
204
205         if (extent == NULL) {
206                 _combine_strings(&x, "_");
207                 _combine_strings(&y, "_");
208                 _combine_strings(&width, "_");
209                 _combine_strings(&height, "_");
210         } else {
211                 char temp_buff[NUMBER_WIDTH];
212
213                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->x);
214                 _combine_strings(&x, temp_buff);
215
216                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->y);
217                 _combine_strings(&y, temp_buff);
218
219                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->width);
220                 _combine_strings(&width, temp_buff);
221
222                 snprintf(temp_buff, NUMBER_WIDTH, "%d", extent->height);
223                 _combine_strings(&height, temp_buff);
224
225                 g_object_unref(component);
226                 g_free(extent);
227         }
228
229         box_size->x = x;
230         box_size->y = y;
231         box_size->width = width;
232         box_size->height = height;
233
234         return box_size;
235 }
236
237 static char *_get_states(AtspiAccessible *node, int length_limit)
238 {
239         AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node);
240         GArray *states = atspi_state_set_get_states(node_state_set);
241         if (!states) return NULL;
242         g_array_sort(states, _int_sort_function);
243
244         AtspiStateType state_type;
245         char *state_string = NULL;
246
247         for (int i = 0; i < states->len; i++) {
248                 state_type = g_array_index(states, AtspiStateType, i);
249
250                 char node_state_str[27] = "";
251                 strncat(node_state_str, "(", sizeof(node_state_str) - strlen(node_state_str) - 1);
252                 strncat(node_state_str, atspi_state_names[state_type], sizeof(node_state_str) - strlen(node_state_str) - 1);
253                 strncat(node_state_str, ")", sizeof(node_state_str) - strlen(node_state_str) - 1);
254
255                 _combine_strings(&state_string, node_state_str);
256         }
257
258         g_array_free(states, 0);
259         g_object_unref(node_state_set);
260
261         _truncate_string(state_string, length_limit);
262
263         return state_string;
264 }
265
266 static char *_get_attributes(AtspiAccessible *node, int length_limit)
267 {
268         GHashTable *attributes = atspi_accessible_get_attributes(node, NULL);
269
270         if (!attributes)
271                 return NULL;
272
273         GHashTableIter attributes_iter;
274         gpointer attr_key;
275         gpointer attr_value;
276         g_hash_table_iter_init (&attributes_iter, attributes);
277         char *result = NULL;
278         while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
279                 char attributes_string[ATTR_WIDTH];
280                 int ret = snprintf(attributes_string, ATTR_WIDTH, "(%s=%s)", (char *)attr_key, (char *)attr_value);
281                 if (ret >= ATTR_WIDTH)
282                         fprintf(stderr, "\n_get_attributes: generated string is too long. Buffer overflow\n");
283
284                 _combine_strings(&result, attributes_string);
285         }
286         g_hash_table_unref(attributes);
287
288         _truncate_string(result, length_limit);
289         return result;
290 }
291
292 static char *_get_info(AtspiAccessible *node, int length_limit)
293 {
294         if (!node)
295                 return NULL;
296
297         char *node_name = atspi_accessible_get_name(node, NULL);
298         char *node_role_name = atspi_accessible_get_role_name(node, NULL);
299         char *unique_id = atspi_accessible_get_unique_id(node, NULL);
300
301         char *attributes = _get_attributes(node, length_limit);
302         Box_Size *box_size = _get_box_size(node);
303         char *states = _get_states(node, length_limit);
304
305         GArray *relations = atspi_accessible_get_relation_set(node, NULL);
306
307         char result[SAFE_BUFFER_SIZE];
308         int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]",
309                                                 unique_id,
310                                                 node_role_name,
311                                                 attributes,
312                                                 box_size->x,
313                                                 box_size->y,
314                                                 box_size->width,
315                                                 box_size->height,
316                                                 node_name,
317                                                 states,
318                                                 (relations && relations->len) ? "*" : "");
319
320         if (ret >= SAFE_BUFFER_SIZE)
321                 fprintf(stderr, "\n%s, %s %d: generated string is too long. Buffer overflow\n", __FILE__, __FUNCTION__, __LINE__);
322
323         free(node_name);
324         free(node_role_name);
325         free(unique_id);
326         free(attributes);
327         if (box_size) {
328                 free(box_size->width);
329                 free(box_size->height);
330                 free(box_size);
331         }
332         free(states);
333         g_array_free(relations, TRUE);
334
335         return g_strdup(result);
336 }
337
338 static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index)
339 {
340         int parent_index = atspi_accessible_get_index_in_parent(obj, NULL);
341         AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
342
343         char output[CHECK_OUTPUT_WIDTH];
344         if (parent_index != parent_candidate_index || parent_candidate != parent) {
345                 char parent_status[NUMBER_WIDTH];
346
347                 if (parent == NULL)
348                         strncpy(parent_status, "_", NUMBER_WIDTH);
349                 else
350                         snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index);
351
352                 char *parent_unique_id = atspi_accessible_get_unique_id(parent, NULL);
353                 char *parent_candidate_unique_id = atspi_accessible_get_unique_id(parent_candidate, NULL);
354                 snprintf(output, CHECK_OUTPUT_WIDTH, "[FAIL<%d,%s><%s,%s>]", parent_candidate_index, parent_status,
355                                 parent_candidate_unique_id, parent_unique_id);
356                 free(parent_unique_id);
357                 free(parent_candidate_unique_id);
358
359         } else {
360                 snprintf(output, CHECK_OUTPUT_WIDTH, "[OK]");
361         }
362
363         printf("%-*s\t", CHECK_OUTPUT_WIDTH, output);
364 }
365
366 static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit)
367 {
368         char *indent = _multiply_string(' ', indent_number*indent_width);
369         if (indent != NULL) {
370                 printf("%s", indent);
371                 free(indent);
372         }
373
374         char *node_info = _get_info(object, length_limit);
375         if (node_info != NULL) {
376                 printf("%s\n", node_info);
377                 free(node_info);
378         }
379
380         int count = atspi_accessible_get_child_count(object, NULL);
381         for (int i = 0; i < count; i++) {
382                 AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL);
383                 if (child) {
384                         if (check_integrity)
385                                 _test_atspi_parent_child_relation(child, object, i);
386
387                         _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit);
388                 }
389         }
390         return 0;
391 }
392
393 void _print_help()
394 {
395         printf("AT-SPI2-CORE-UTIL\n\n");
396         printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n");
397         printf("OPTION LIST:\n");
398         printf("-h, --help\t\tshow this message\n");
399         printf("-v, --version\t\tshow actual version of tool\n");
400         printf("-g, --show-legend\tprint AT-SPI state legend\n");
401         printf("-l, --list-apps\t\tlist all applications of desktop\n");
402         printf("-a, --at-spi-client <true|false>\tenable/disable org.a11y.Status.IsEnabled property\n");
403         printf("-s, --sleep <N>\tsleep N seconds\n");
404         printf("-d, --tree-dump\t\tdump tree for selected application\n");
405         printf("-c, --tree-check\tcheck tree for selected application\n");
406         printf("-f, --first-match\tperform dump or check only for first matching application\n");
407         printf("-i, --indent\t\tset indentation size, default value stands at 2\n");
408         printf("-t, --truncate\t\tset maximum single field size, default value stands at 30\n");
409         printf("\nEXAMPLE:\n");
410         printf("\tat_spi2_tool -i3 -d quickpanel\n");
411         printf("\t  show AT-SPI tree for node \"quickpanel\" using three-space indentation\n");
412         printf("\tat_spi2_tool -i1 -c starter\n");
413         printf("\t  show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n");
414 }
415
416 void _print_version()
417 {
418         printf("AT-SPI2-CORE-UTIL v%s\n", VERSION);
419 }
420
421 static void _print_horizontal_line_in_relation_table() {
422         for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
423                 for (int j = 0; j < RELATION_COLUMN_WIDTH; j++)
424                         printf("-");
425                 printf("+");
426         }
427         printf("\n");
428 }
429
430 static void _print_legend_for_relation_table() {
431         char *table[] = {"OBJECT", "CONTROLLER_FOR", "CONTROLLED_BY", "FLOWS_TO", "FLOWS_FROM", "DESCRIBED_BY", "OTHER RELATION [ID]"};
432         assert(ARRAY_SIZE(table) == RELATION_TABLE_COLUMN_COUNT);
433         for(int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++)
434                 printf("%*s|", RELATION_COLUMN_WIDTH , table[i]);
435         printf("\n");
436         _print_horizontal_line_in_relation_table();
437 }
438
439 static char *_get_unique_id_of_object_in_relation(AtspiRelation *relation) {
440         if (!relation)
441                 return NULL;
442         gint last_index = atspi_relation_get_n_targets(relation) - 1;
443         AtspiAccessible *target = atspi_relation_get_target(relation, last_index);
444         return atspi_accessible_get_unique_id(target, NULL);
445 }
446
447 static void _print_relations_for_object(AtspiAccessible *node) {
448         GArray *relations = atspi_accessible_get_relation_set(node, NULL);
449         if (!relations)
450                 return;
451
452         if (!relations->len) {
453                 g_array_free(relations, FALSE);
454                 return;
455         }
456
457         char **table = calloc(RELATION_TABLE_COLUMN_COUNT, sizeof(char *));
458         table[0] = atspi_accessible_get_unique_id(node, NULL);
459         for (int i = 0; i < relations->len; i++) {
460                 AtspiRelation *relation = g_array_index(relations, AtspiRelation *, i);
461                 AtspiRelationType type = atspi_relation_get_relation_type(relation);
462                 int idx;
463                 switch (type) {
464                         case ATSPI_RELATION_CONTROLLER_FOR:     idx = 1; break;
465                         case ATSPI_RELATION_CONTROLLED_BY:      idx = 2; break;
466                         case ATSPI_RELATION_FLOWS_TO:           idx = 3; break;
467                         case ATSPI_RELATION_FLOWS_FROM:         idx = 4; break;
468                         case ATSPI_RELATION_DESCRIBED_BY:       idx = 5; break;
469                         default: idx = 0;
470                 }
471
472                 if (idx > 0)
473                         table[idx] = _get_unique_id_of_object_in_relation(relation);
474                 else {
475                         char buf[16];
476                         snprintf(buf, sizeof(buf), " [%d]", type);
477                         _combine_strings(&table[RELATION_TABLE_COLUMN_COUNT - 1], buf);
478                 }
479         }
480
481         for (int i = 0; i < RELATION_TABLE_COLUMN_COUNT; i++) {
482                 printf("%*s|", RELATION_COLUMN_WIDTH, table[i] ? table[i] : "");
483                 free(table[i]);
484         }
485         free(table);
486
487         printf("\n");
488         _print_horizontal_line_in_relation_table();
489         g_array_free(relations, TRUE);
490 }
491
492 static void _print_relations_for_objects_in_tree(AtspiAccessible *node) {
493         _print_relations_for_object(node);
494
495         int count = atspi_accessible_get_child_count(node, NULL);
496         for (int i = 0; i < count; i++) {
497                 AtspiAccessible *child = atspi_accessible_get_child_at_index(node, i, NULL);
498                 if (child)
499                         _print_relations_for_objects_in_tree(child);
500         }
501 }
502
503 static void _print_relations_table(AtspiAccessible *node) {
504         _print_legend_for_relation_table();
505         _print_relations_for_objects_in_tree(node);
506 }
507
508 static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bool first_match, int length_limit)
509 {
510
511         AtspiAccessible *desktop = atspi_get_desktop(0);
512         if (!desktop) {
513                 fprintf(stderr, "atspi_get_desktop failed\n");
514                 return;
515         }
516
517         int count = atspi_accessible_get_child_count(desktop, NULL);
518         bool app_name_matched = false;
519         for (int i = 0; i < count; i++) {
520                 AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL);
521                 if (child == NULL) {
522                         fprintf(stderr, "\n%s, %s %d: Null child occured. Results may be misleading.\n", __FILE__, __FUNCTION__, __LINE__);
523                         continue;
524                 }
525
526                 char *name = atspi_accessible_get_name(child, NULL);
527
528                 if (!dump && !check)
529                         printf("%s\n", name);
530
531                 if ((check || dump) && name && !strcmp(name, app_name)) {
532                         app_name_matched = true;
533
534                         _print_module_legend();
535
536                         if (check)
537                                 _test_atspi_parent_child_relation(child, desktop, i);
538
539                         _print_atspi_tree_verify_maybe_r(0, child, check, length_limit);
540
541                         if (first_match) {
542                                 free(name);
543                                 break;
544                         } else {
545                                 printf("\n");
546                                 _print_relations_table(child);
547                         }
548
549                         free(name);
550                 }
551         }
552
553         if (!app_name_matched && (dump || check))
554                 fprintf(stderr, "There is no application with name: %s. Try again.\n", app_name);
555
556         g_object_unref(desktop);
557 }
558
559 static void _at_spi_client_enable(gboolean enabled)
560 {
561         static GDBusProxy *proxy = NULL; //we keep proxy (dbus connection) until program exits
562         GVariant *result;
563         GError *error = NULL;
564         GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_NONE;
565
566
567         if (!proxy) {
568                 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
569                                         flags,
570                                         NULL, /* GDBusInterfaceInfo */
571                                         "org.a11y.Bus",
572                                         "/org/a11y/bus",
573                                         "org.freedesktop.DBus.Properties",
574                                         NULL, /* GCancellable */
575                                         &error);
576                 if (error) {
577                         fprintf(stderr, "Failed to create proxy object for '/org/a11y/bus': %s\n", error->message);
578                         g_error_free(error);
579                         return;
580                 }
581         }
582
583         result = g_dbus_proxy_call_sync(proxy,
584                                         "Set",
585                                         g_variant_new ("(ssv)",  "org.a11y.Status", "IsEnabled", g_variant_new_boolean(enabled)),
586                                         G_DBUS_CALL_FLAGS_NONE,
587                                         -1,
588                                         NULL,
589                                         &error);
590         if (result)
591                 g_variant_unref(result);
592
593         if (error) {
594                 fprintf(stderr, "Fail to call org.freedesktop.DBus.Properties.Set: %s\n", error->message);
595                 g_error_free(error);
596         }
597 }
598
599 static void _run_command(int argc, char *argv[])
600 {
601         struct option long_options[] = {
602                 {"help", no_argument, 0, 'h'},
603                 {"version", no_argument, 0, 'v'},
604                 {"show-legend", no_argument, 0, 'g'},
605                 {"list-apps", no_argument, 0, 'l'},
606                 {"at-spi-client", no_argument, 0, 'a'},
607                 {"sleep", required_argument, 0, 's'},
608                 {"tree-dump", required_argument, 0, 'd'},
609                 {"tree-check", required_argument, 0, 'c'},
610                 {"first-match", no_argument, 0, 'f'},
611                 {"indent", required_argument, 0, 'i'},
612                 {"truncate", required_argument, 0, 't'},
613         };
614
615         int command = 0;
616         int option_index = 0;
617         bool traverse_flags[FLAG_NO] = {false};
618         char *app_name = NULL;
619         gboolean enable_at_spi_client;
620
621         while (TRUE) {
622                 command = getopt_long(argc, argv, "hvgla:s:d:c:ft:i:", long_options, &option_index);
623
624                 if (command == ERROR_STATE)
625                         break;
626
627                 switch (command) {
628                 case 'h':
629                         _print_help();
630                         break;
631
632                 case 'v':
633                         _print_version();
634                         break;
635
636                 case 'g':
637                         _print_atspi_states_legend();
638                         break;
639
640                 case 'l':
641                         _atspi_tree_traverse(NULL, false, false, false, module_name_limit);
642                         break;
643
644                 case 'a':
645                         enable_at_spi_client = TRUE;
646                         if(optarg[0] == 'f' || optarg[0] == '0')
647                                 enable_at_spi_client = FALSE;
648
649                         _at_spi_client_enable(enable_at_spi_client);
650                         break;
651
652                 case 's':
653                         sleep(atoi(optarg));
654                         break;
655
656                 case 'd':
657                         traverse_flags[DUMP] = true;
658                         app_name = optarg;
659                         break;
660
661                 case 'c':
662                         traverse_flags[CHECK] = true;
663                         app_name = optarg;
664                         break;
665
666                 case 'f':
667                         traverse_flags[FIRST_MATCH] = true;
668                         break;
669
670                 case 'i':
671                         _set_indent(atoi(optarg));
672                         break;
673
674                 case 't':
675                         _set_module_length_limit(atoi(optarg));
676                         break;
677
678                 case '?':
679                         fprintf(stderr, "Invalid parameter. Use: \"--help\"\n");
680                         break;
681
682                 default:
683                         abort();
684                 }
685         }
686
687         if (traverse_flags[DUMP] || traverse_flags[CHECK])
688                 _atspi_tree_traverse(app_name, traverse_flags[DUMP], traverse_flags[CHECK], traverse_flags[FIRST_MATCH], module_name_limit);
689 }
690
691 int main(int argc, char *argv[])
692 {
693         if (argc < 2) {
694                 printf("Arguments required. Type %s --help.\n", argv[0]);
695                 return -1;
696         }
697
698         int result = atspi_init();
699         if (result != 0)
700         {
701                 fprintf(stderr, "Unable to initilize AT-SPI infrastructure, atspi_init failed\n");
702                 return -1;
703         }
704
705         _run_command(argc, argv);
706
707         return 0;
708 }