#define ERROR_STATE -1
#define SAFE_BUFFER_SIZE 255
+#define CHECK_OUTPUT_WIDTH 15
+#define MINIMAL_MODULE_WIDTH 3
+#define ATTR_WIDTH 50
+#define NUMBER_WIDTH 6
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define COLUMN_NO 3
+#define FLAG_NO 3
+#define DUMP 0
+#define CHECK 1
+#define FIRST_MATCH 2
static unsigned indent_width = 2;
+static int module_name_limit = -1;
static const char* atspi_state_names[] = {
[ATSPI_STATE_INVALID] = "INVALID",
[ATSPI_STATE_LAST_DEFINED] = "LAST_DEFINED"
};
-typedef struct _StringAccumulator {
- char *string;
- int length;
-} StringAccumulator;
+typedef struct _Box_Size {
+ int height;
+ int width;
+} Box_Size;
-char *strdup(const char *c) {
+char *_strdup(const char *c)
+{
char *result = (char *)calloc(1, strlen(c) + 1);
return result ? strcpy(result, c) : result;
}
-static char *multiply_string(char c, unsigned number) {
+static char *_multiply_string(char c, unsigned number)
+{
char *result = (char *)calloc(1, number + 1);
if (result == NULL)
return result;
}
-static void _print_atspi_states_legend() {
+static void _print_module_legend()
+{
+ printf("\n[[node role name],[attributes list],[width,height],[node name],[states list]]\n\n");
+}
+
+static void _print_atspi_states_legend()
+{
+ _print_module_legend();
+
int array_len = ARRAY_SIZE(atspi_state_names);
int line_count = (array_len + COLUMN_NO - 1)/COLUMN_NO;
for (int line_idx = 0; line_idx < line_count; line_idx++) {
- if ((line_idx < line_count - 1) || (array_len % 3 == 0)) {
- printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3], (line_idx * 3) + 1, atspi_state_names[(line_idx * 3) + 1], (line_idx * 3) + 2, atspi_state_names[(line_idx * 3) + 2]);
- } else if (array_len % 3 == 2) {
- printf("[%d]\t%-24s[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3], (line_idx * 3) + 1, atspi_state_names[(line_idx * 3) + 1]);
+ if ((line_idx < line_count - 1) || (array_len % COLUMN_NO == 0)) {
+ printf("[%d]\t%-24s[%d]\t%-24s[%d]\t%s\n",
+ line_idx * COLUMN_NO,
+ atspi_state_names[line_idx * COLUMN_NO],
+ (line_idx * COLUMN_NO) + 1,
+ atspi_state_names[(line_idx * COLUMN_NO) + 1],
+ (line_idx * COLUMN_NO) + 2,
+ atspi_state_names[(line_idx * COLUMN_NO) + 2]);
+ } else if (array_len % COLUMN_NO == 2) {
+ printf("[%d]\t%-24s[%d]\t%s\n",
+ line_idx * COLUMN_NO,
+ atspi_state_names[line_idx * COLUMN_NO],
+ (line_idx * COLUMN_NO) + 1,
+ atspi_state_names[(line_idx * COLUMN_NO) + 1]);
} else {
- printf("[%d]\t%s\n", line_idx * 3, atspi_state_names[line_idx * 3]);
+ printf("[%d]\t%s\n", line_idx * COLUMN_NO, atspi_state_names[line_idx * COLUMN_NO]);
}
}
}
-static void set_indent(int indent_size)
+static void _set_indent(int indent_size)
{
if (indent_size < 0) {
fprintf(stderr, "WARNING: Invalid indent size. Default value retained.\n");
indent_width = indent_size;
}
+static void _set_module_length_limit(int limit)
+{
+ if (limit < MINIMAL_MODULE_WIDTH) {
+ fprintf(stderr, "WARNING: Invalid maximum field size. Default value retained.\n");
+ return;
+ }
+
+ module_name_limit = limit;
+}
+
static int _int_sort_function(gconstpointer a, gconstpointer b)
{
int int_a = GPOINTER_TO_INT (a);
return int_a - int_b;
}
-static int _accumulate_string(const char *string, StringAccumulator *accumulator) {
- if (!accumulator)
- return FALSE;
-
- if (string) {
- int length = accumulator->length + strlen(string);
- char *rebuffer = realloc(accumulator->string, length + 1);
- if (rebuffer) {
- strcpy(rebuffer + accumulator->length, string);
- accumulator->string = rebuffer;
- accumulator->length = length;
- } else {
- return FALSE;
- }
+static void _truncate_string(char *string, int length_limit)
+{
+ if (length_limit < MINIMAL_MODULE_WIDTH || !string)
+ return;
+
+ int len = strlen(string);
+
+ if (len > length_limit)
+ memcpy(string + length_limit - 3, "...", 4);
+}
+
+static size_t _combine_strings(char **destination, const char *source)
+{
+ if (!source || !*source || !destination)
+ return 0;
+
+ size_t old_len = *destination ? strlen(*destination) : 0;
+ size_t source_len = strlen(source);
+ size_t len = old_len + source_len;
+ char *buf = realloc(*destination, (len + 1) * sizeof(char));
+
+ if (!buf) {
+ fprintf(stderr, "_combine_strings: allocation failed");
+ return old_len;
}
- return TRUE;
+
+ memcpy(buf + old_len, source, source_len + 1);
+
+ *destination = buf;
+
+ return len;
}
-static char *get_info(AtspiAccessible *node) {
- if (!node)
+static Box_Size *_get_box_size(AtspiAccessible *node)
+{
+ Box_Size *box_size = (Box_Size *)malloc(sizeof(Box_Size));
+ if (box_size == NULL)
return NULL;
- int node_width = -1;
- int node_height = -1;
- char *node_name = atspi_accessible_get_name(node, NULL);
- char *node_role_name = atspi_accessible_get_role_name(node, NULL);
+ box_size->width = -1;
+ box_size->height = -1;
+
AtspiComponent *component = atspi_accessible_get_component_iface(node);
- AtspiRect *extent = NULL;
- if (component != NULL){
- extent = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
+ if (component == NULL)
+ return box_size;
- if (extent != NULL) {
- node_width = extent->width;
- node_height = extent->height;
- }
- }
+ AtspiRect *extent = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
+ if (extent == NULL)
+ return box_size;
+
+ box_size->width = extent->width;
+ box_size->height = extent->height;
+
+ g_object_unref(component);
+ g_free(extent);
+ return box_size;
+}
+
+static char *_get_states(AtspiAccessible *node)
+{
AtspiStateSet *node_state_set = atspi_accessible_get_state_set(node);
GArray *states = atspi_state_set_get_states(node_state_set);
g_array_sort(states, _int_sort_function);
- AtspiStateType state;
- StringAccumulator state_accumulator = {};
+ AtspiStateType state_type;
+ char *state_string = NULL;
+
for (int i = 0; states && (i < states->len); i++) {
- state = g_array_index(states, AtspiStateType, i);
+ state_type = g_array_index(states, AtspiStateType, i);
+
char node_state_str[8];
- sprintf(node_state_str, "[%d]", state);
- _accumulate_string(node_state_str, &state_accumulator);
+ sprintf(node_state_str, "(%d)", state_type);
+ _combine_strings(&state_string, node_state_str);
}
+
g_array_free(states, 0);
+ g_object_unref(node_state_set);
+
+ return state_string;
+}
- StringAccumulator attribute_accumulator = {};
+static char *_get_attributes(AtspiAccessible *node)
+{
GHashTable *attributes = NULL;
attributes = atspi_accessible_get_attributes(node, NULL);
- if (attributes) {
- GHashTableIter attributes_iter;
- gpointer attr_key, attr_value;
- g_hash_table_iter_init (&attributes_iter, attributes);
- while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
- _accumulate_string("[", &attribute_accumulator);
- _accumulate_string((char*)attr_key, &attribute_accumulator);
- _accumulate_string("=", &attribute_accumulator);
- _accumulate_string((char*)attr_value, &attribute_accumulator);
- _accumulate_string("]", &attribute_accumulator);
- }
- g_hash_table_unref(attributes);
- }
- char result[SAFE_BUFFER_SIZE + 1];
- snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%d,%d],[%s],[%s]]\n", node_role_name, attribute_accumulator.string, node_width, node_height, node_name, state_accumulator.string);
+ if (!attributes)
+ return NULL;
- free(node_name);
- free(node_role_name);
+ GHashTableIter attributes_iter;
+ gpointer attr_key;
+ gpointer attr_value;
+ g_hash_table_iter_init (&attributes_iter, attributes);
+ char *result = NULL;
+ while (g_hash_table_iter_next (&attributes_iter, &attr_key, &attr_value)) {
+ char attributes_string[ATTR_WIDTH];
+ int ret = snprintf(attributes_string, ATTR_WIDTH, "(%s=%s)", (char*)attr_key, (char*)attr_value);
+ if (ret >= ATTR_WIDTH) {
+ fprintf(stderr, "_get_attributes: generated string is too long. Buffer overflow");
+ abort();
+ }
+ _combine_strings(&result, attributes_string);
+ }
+ g_hash_table_unref(attributes);
- if (state_accumulator.string)
- g_free(state_accumulator.string);
+ return result;
+}
- if (attribute_accumulator.string)
- g_free(attribute_accumulator.string);
+static char *_get_info(AtspiAccessible *node, int length_limit)
+{
+ if (!node)
+ return NULL;
- if (component)
- g_object_unref(component);
+ char *node_name = atspi_accessible_get_name(node, NULL);
+ char *node_role_name = atspi_accessible_get_role_name(node, NULL);
- if (extent)
- g_free(extent);
+ char *attributes = _get_attributes(node);
+ _truncate_string(attributes, length_limit);
+ Box_Size *box_size = _get_box_size(node);
+ char *states = _get_states(node);
+ _truncate_string(states, length_limit);
+
+ char result[SAFE_BUFFER_SIZE];
+ int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s],[%s],[%d,%d],[%s],[%s]]",
+ node_role_name,
+ attributes,
+ box_size->width,
+ box_size->height,
+ node_name,
+ states);
+
+ if (ret >= SAFE_BUFFER_SIZE) {
+ fprintf(stderr, "_get_info: generated string is too long. Buffer overflow");
+ abort();
+ }
- if (node_state_set)
- g_object_unref(node_state_set);
+ free(node_name);
+ free(node_role_name);
+ free(attributes);
+ free(box_size);
+ free(states);
- return strdup(result);
+ return _strdup(result);
}
-static void test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index)
+static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessible *parent_candidate, int parent_candidate_index)
{
int parent_index = atspi_accessible_get_index_in_parent(obj, NULL);
AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
+
+ char output[CHECK_OUTPUT_WIDTH];
if (parent_index != parent_candidate_index || parent_candidate != parent) {
- printf("[FAIL<%d,", parent_candidate_index);
+ char parent_status[NUMBER_WIDTH];
if (parent == NULL)
- printf("_");
+ strcpy(parent_status, "_");
else
- printf("%d", parent_index);
+ snprintf(parent_status, NUMBER_WIDTH, "%d", parent_index);
- printf(">]\t");
+ snprintf(output, CHECK_OUTPUT_WIDTH, "[FAIL<%d,%s>]", parent_candidate_index, parent_status);
} else {
- printf("[OK]\t");
+ snprintf(output, CHECK_OUTPUT_WIDTH, "[OK]");
}
+
+ printf("%-*s\t", CHECK_OUTPUT_WIDTH, output);
}
-static int expand_r(int indent_number, AtspiAccessible *object, bool check_integrity)
+static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit)
{
- char *indent = multiply_string(' ', indent_number*indent_width);
+ char *indent = _multiply_string(' ', indent_number*indent_width);
if (indent != NULL) {
printf("%s", indent);
free(indent);
}
- char *node_info = get_info(object);
+ char *node_info = _get_info(object, length_limit);
if (node_info != NULL) {
- printf("%s", node_info);
+ printf("%s\n", node_info);
free(node_info);
}
AtspiAccessible *child = atspi_accessible_get_child_at_index(object, i, NULL);
if (child) {
if (check_integrity)
- test_atspi_parent_child_relation(child, object, i);
+ _test_atspi_parent_child_relation(child, object, i);
- expand_r(indent_number + 1, child, check_integrity);
+ _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit);
}
}
return 0;
}
-void print_help()
+void _print_help()
{
printf("AT-SPI2-CORE-UTIL\n\n");
printf("USAGE: at_spi2_tool [OPTION] [PARAMETER] ...\n\n");
printf("-l, --list-apps\t\tlist all applications of desktop\n");
printf("-d, --tree-dump\t\tdump tree for selected application\n");
printf("-c, --tree-check\tcheck tree for selected application\n");
- printf("-i, --indent\t\tset indentation size\n");
+ printf("-f, --first-match\tperform dump or check only for first matching application\n");
+ printf("-i, --indent\t\tset indentation size, default value stands at 2\n");
+ printf("-t, --truncate\t\tset maximum single field size, default value stands at 30\n");
printf("\nEXAMPLE:\n");
printf("\tat_spi2_tool -i3 -d quickpanel\n");
printf("\t show AT-SPI tree for node \"quickpanel\" using three-space indentation\n");
printf("\t show AT-SPI tree with integrity test for node \"starter\" using one-space indentation\n");
}
-static void atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, bool dump, bool check)
+static void _atspi_tree_traverse(AtspiAccessible *desktop, const char *app_name, bool dump, bool check, bool first_match, int length_limit)
{
int count = atspi_accessible_get_child_count(desktop, NULL);
+ bool app_name_matched = false;
for (int i = 0; i < count; i++) {
AtspiAccessible *child = atspi_accessible_get_child_at_index(desktop, i, NULL);
if (child) {
char *name = atspi_accessible_get_name(child, NULL);
- if (!dump && !check)
+ if (!dump && !check) {
printf("%s\n", name);
-
- if (dump && name && !strcmp(name, app_name)) {
- expand_r(0, child, false);
- break;
}
- if (check && name && !strcmp(name, app_name)) {
- test_atspi_parent_child_relation(child, desktop, i);
- expand_r(0, child, true);
- break;
+ if ((check || dump) && name && !strcmp(name, app_name)) {
+ app_name_matched = true;
+
+ _print_module_legend();
+
+ if (check)
+ _test_atspi_parent_child_relation(child, desktop, i);
+
+ _print_atspi_tree_verify_maybe_r(0, child, check, length_limit);
+
+ if (first_match) {
+ free(name);
+ break;
+ } else
+ printf("\n");
}
+ free(name);
}
}
+
+ if (!app_name_matched && (dump || check))
+ fprintf(stderr, "There is no application with name: %s. Try again.\n", app_name);
}
-static void run_command(int argc, char *argv[], AtspiAccessible *desktop)
+static void _run_command(int argc, char *argv[], AtspiAccessible *desktop)
{
struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"list-apps", no_argument, 0, 'l'},
{"tree-dump", required_argument, 0, 'd'},
{"tree-check", required_argument, 0, 'c'},
+ {"first-match", no_argument, 0, 'f'},
{"indent", required_argument, 0, 'i'},
+ {"truncate", required_argument, 0, 't'},
};
int command = 0;
int option_index = 0;
+ bool traverse_flags[FLAG_NO] = {false};
+ char *app_name = NULL;
while (TRUE) {
- command = getopt_long(argc, argv, "hgld:c:i:", long_options, &option_index);
+ command = getopt_long(argc, argv, "hgld:c:ft:i:", long_options, &option_index);
if (command == ERROR_STATE)
break;
switch (command) {
case 'h':
- print_help();
+ _print_help();
break;
case 'g':
break;
case 'l':
- atspi_tree_traverse(desktop, NULL, false, false);
+ _atspi_tree_traverse(desktop, NULL, false, false, false, module_name_limit);
break;
case 'd':
- atspi_tree_traverse(desktop, optarg, true, false);
+ traverse_flags[DUMP] = true;
+ app_name = optarg;
break;
case 'c':
- atspi_tree_traverse(desktop, optarg, false, true);
+ traverse_flags[CHECK] = true;
+ app_name = optarg;
+ break;
+
+ case 'f':
+ traverse_flags[FIRST_MATCH] = true;
break;
case 'i':
- set_indent(atoi(optarg));
+ _set_indent(atoi(optarg));
+ break;
+
+ case 't':
+ _set_module_length_limit(atoi(optarg));
break;
case '?':
+ fprintf(stderr, "Invalid parameter. Use: \"--help\"\n");
break;
default:
abort();
}
}
+
+ if (traverse_flags[DUMP] || traverse_flags[CHECK])
+ _atspi_tree_traverse(desktop, app_name, traverse_flags[DUMP], traverse_flags[CHECK], traverse_flags[FIRST_MATCH], module_name_limit);
}
int main(int argc, char *argv[])
return -1;
}
- AtspiAccessible *desktop = atspi_get_desktop(0);
+ int result = atspi_init();
+ if (result != 0)
+ {
+ fprintf(stderr, "Unable to initilize AT-SPI infrastructure, atspi_init failed\n");
+ return -1;
+ }
+ AtspiAccessible *desktop = atspi_get_desktop(0);
if (!desktop) {
fprintf(stderr, "atspi_get_desktop failed\n");
- return 1;
+ return -1;
}
- atspi_init();
-
- run_command(argc, argv, desktop);
+ _run_command(argc, argv, desktop);
return 0;
}