Blame android/client/tabcompletion.c

Packit 34410b
/*
Packit 34410b
 * Copyright (C) 2013 Intel Corporation
Packit 34410b
 *
Packit 34410b
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 34410b
 * you may not use this file except in compliance with the License.
Packit 34410b
 * You may obtain a copy of the License at
Packit 34410b
 *
Packit 34410b
 *      http://www.apache.org/licenses/LICENSE-2.0
Packit 34410b
 *
Packit 34410b
 * Unless required by applicable law or agreed to in writing, software
Packit 34410b
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 34410b
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 34410b
 * See the License for the specific language governing permissions and
Packit 34410b
 * limitations under the License.
Packit 34410b
 *
Packit 34410b
 */
Packit 34410b
Packit 34410b
#define _GNU_SOURCE
Packit 34410b
#include <stdio.h>
Packit 34410b
#include <ctype.h>
Packit 34410b
#include <string.h>
Packit 34410b
#include "if-main.h"
Packit 34410b
#include "terminal.h"
Packit 34410b
Packit 34410b
/* how many times tab was hit */
Packit 34410b
static int tab_hit_count;
Packit 34410b
Packit 34410b
typedef struct split_arg {
Packit 34410b
	struct split_arg *next; /* next argument in buffer */
Packit 34410b
	const char *origin; /* pointer to original argument */
Packit 34410b
	char ntcopy[1]; /* null terminated copy of argument */
Packit 34410b
} split_arg_t;
Packit 34410b
Packit 34410b
/* function returns method of given name or NULL if not found */
Packit 34410b
const struct method *get_interface_method(const char *iname,
Packit 34410b
							const char *mname)
Packit 34410b
{
Packit 34410b
	const struct interface *iface = get_interface(iname);
Packit 34410b
Packit 34410b
	if (iface == NULL)
Packit 34410b
		return NULL;
Packit 34410b
Packit 34410b
	return get_method(iface->methods, mname);
Packit 34410b
}
Packit 34410b
Packit 34410b
/* prints matching elements */
Packit 34410b
static void print_matches(enum_func f, void *user, const char *prefix, int len)
Packit 34410b
{
Packit 34410b
	int i;
Packit 34410b
	const char *enum_name;
Packit 34410b
Packit 34410b
	putchar('\n');
Packit 34410b
	for (i = 0; NULL != (enum_name = f(user, i)); ++i) {
Packit 34410b
		if (strncmp(enum_name, prefix, len) == 0)
Packit 34410b
			printf("%s\t", enum_name);
Packit 34410b
	}
Packit 34410b
	putchar('\n');
Packit 34410b
	terminal_draw_command_line();
Packit 34410b
}
Packit 34410b
Packit 34410b
/*
Packit 34410b
 * This function splits command line into linked list of arguments.
Packit 34410b
 * line_buffer - pointer to input command line
Packit 34410b
 * size - size of command line to parse
Packit 34410b
 * buf - output buffer to keep split arguments list
Packit 34410b
 * buf_size_in_bytes - size of buf
Packit 34410b
 */
Packit 34410b
static int split_command(const char *line_buffer, int size, split_arg_t *buf,
Packit 34410b
							int buf_size_in_bytes)
Packit 34410b
{
Packit 34410b
	split_arg_t *prev = NULL;
Packit 34410b
	split_arg_t *arg = buf;
Packit 34410b
	int argc = 0;
Packit 34410b
	const char *p = line_buffer;
Packit 34410b
	const char *e = p + (size > 0 ? size : (int) strlen(p));
Packit 34410b
	int len;
Packit 34410b
Packit 34410b
	do {
Packit 34410b
		while (p < e && isspace(*p))
Packit 34410b
			p++;
Packit 34410b
		arg->origin = p;
Packit 34410b
		arg->next = NULL;
Packit 34410b
		while (p < e && !isspace(*p))
Packit 34410b
			p++;
Packit 34410b
		len = p - arg->origin;
Packit 34410b
		if (&arg->ntcopy[0] + len + 1 >
Packit 34410b
			(const char *) buf + buf_size_in_bytes)
Packit 34410b
			break;
Packit 34410b
		strncpy(arg->ntcopy, arg->origin, len);
Packit 34410b
		arg->ntcopy[len] = 0;
Packit 34410b
		if (prev != NULL)
Packit 34410b
			prev->next = arg;
Packit 34410b
		prev = arg;
Packit 34410b
		arg += (2 * sizeof(*arg) + len) / sizeof(*arg);
Packit 34410b
		argc++;
Packit 34410b
	} while (p < e);
Packit 34410b
Packit 34410b
	return argc;
Packit 34410b
}
Packit 34410b
Packit 34410b
/* Function to enumerate method names */
Packit 34410b
static const char *methods_name(void *v, int i)
Packit 34410b
{
Packit 34410b
	const struct interface *iface = v;
Packit 34410b
Packit 34410b
	return iface->methods[i].name[0] ? iface->methods[i].name : NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
struct command_completion_args;
Packit 34410b
typedef void (*short_help)(struct command_completion_args *args);
Packit 34410b
Packit 34410b
struct command_completion_args {
Packit 34410b
	const split_arg_t *arg; /* list of arguments */
Packit 34410b
	const char *typed; /* last typed element */
Packit 34410b
	enum_func func; /* enumerating function */
Packit 34410b
	void *user; /* argument to enumerating function */
Packit 34410b
	short_help help; /* help function */
Packit 34410b
	const char *user_help; /* additional data (used by short_help) */
Packit 34410b
};
Packit 34410b
Packit 34410b
/* complete command line */
Packit 34410b
static void tab_completion(struct command_completion_args *args)
Packit 34410b
{
Packit 34410b
	const char *name = args->typed;
Packit 34410b
	const int len = strlen(name);
Packit 34410b
	int i;
Packit 34410b
	int j;
Packit 34410b
	char prefix[128] = {0};
Packit 34410b
	int prefix_len = 0;
Packit 34410b
	int count = 0;
Packit 34410b
	const char *enum_name;
Packit 34410b
Packit 34410b
	for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) {
Packit 34410b
		/* prefix does not match */
Packit 34410b
		if (strncmp(enum_name, name, len) != 0)
Packit 34410b
			continue;
Packit 34410b
Packit 34410b
		/* prefix matches first time */
Packit 34410b
		if (count++ == 0) {
Packit 34410b
			strcpy(prefix, enum_name);
Packit 34410b
			prefix_len = strlen(prefix);
Packit 34410b
			continue;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		/* Prefix matches next time reduce prefix to common part */
Packit 34410b
		for (j = 0; prefix[j] != 0
Packit 34410b
			&& prefix[j] == enum_name[j];)
Packit 34410b
			++j;
Packit 34410b
		prefix_len = j;
Packit 34410b
		prefix[j] = 0;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (count == 0) {
Packit 34410b
		/* no matches */
Packit 34410b
		if (args->help != NULL)
Packit 34410b
			args->help(args);
Packit 34410b
		tab_hit_count = 0;
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* len == prefix_len => nothing new was added */
Packit 34410b
	if (len == prefix_len) {
Packit 34410b
		if (count != 1) {
Packit 34410b
			if (tab_hit_count == 1) {
Packit 34410b
				putchar('\a');
Packit 34410b
			} else if (tab_hit_count == 2 ||
Packit 34410b
					args->help == NULL) {
Packit 34410b
				print_matches(args->func,
Packit 34410b
						args->user, name, len);
Packit 34410b
			} else {
Packit 34410b
				args->help(args);
Packit 34410b
				tab_hit_count = 1;
Packit 34410b
			}
Packit 34410b
		} else if (count == 1) {
Packit 34410b
			/* nothing to add, exact match add space */
Packit 34410b
			terminal_insert_into_command_line(" ");
Packit 34410b
		}
Packit 34410b
	} else {
Packit 34410b
		/* new chars can be added from some interface name(s) */
Packit 34410b
		if (count == 1) {
Packit 34410b
			/* exact match, add space */
Packit 34410b
			prefix[prefix_len++] = ' ';
Packit 34410b
			prefix[prefix_len] = '\0';
Packit 34410b
		}
Packit 34410b
Packit 34410b
		terminal_insert_into_command_line(prefix + len);
Packit 34410b
		tab_hit_count = 0;
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
/* interface completion */
Packit 34410b
static void command_completion(split_arg_t *arg)
Packit 34410b
{
Packit 34410b
	struct command_completion_args args = {
Packit 34410b
		.arg = arg,
Packit 34410b
		.typed = arg->ntcopy,
Packit 34410b
		.func = command_name
Packit 34410b
	};
Packit 34410b
Packit 34410b
	tab_completion(&args);
Packit 34410b
}
Packit 34410b
Packit 34410b
/* method completion */
Packit 34410b
static void method_completion(const struct interface *iface, split_arg_t *arg)
Packit 34410b
{
Packit 34410b
	struct command_completion_args args = {
Packit 34410b
		.arg = arg,
Packit 34410b
		.typed = arg->next->ntcopy,
Packit 34410b
		.func = methods_name,
Packit 34410b
		.user = (void *) iface
Packit 34410b
	};
Packit 34410b
Packit 34410b
	if (iface == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	tab_completion(&args);
Packit 34410b
}
Packit 34410b
Packit 34410b
static const char *bold = "\x1b[1m";
Packit 34410b
static const char *normal = "\x1b[0m";
Packit 34410b
Packit 34410b
static bool find_nth_argument(const char *str, int n, const char **s,
Packit 34410b
								const char **e)
Packit 34410b
{
Packit 34410b
	const char *p = str;
Packit 34410b
	int argc = 0;
Packit 34410b
	*e = NULL;
Packit 34410b
Packit 34410b
	while (p != NULL && *p != 0) {
Packit 34410b
Packit 34410b
		while (isspace(*p))
Packit 34410b
			++p;
Packit 34410b
Packit 34410b
		if (n == argc)
Packit 34410b
			*s = p;
Packit 34410b
Packit 34410b
		if (*p == '[') {
Packit 34410b
			p = strchr(p, ']');
Packit 34410b
			if (p != NULL)
Packit 34410b
				*e = ++p;
Packit 34410b
		} else if (*p == '<') {
Packit 34410b
			p = strchr(p, '>');
Packit 34410b
			if (p != NULL)
Packit 34410b
				*e = ++p;
Packit 34410b
		} else {
Packit 34410b
			*e = strchr(p, ' ');
Packit 34410b
			if (*e == NULL)
Packit 34410b
				*e = p + strlen(p);
Packit 34410b
			p = *e;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (n == argc)
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		argc++;
Packit 34410b
		*e = NULL;
Packit 34410b
	}
Packit 34410b
	return *e != NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
/* prints short help on method for interface */
Packit 34410b
static void method_help(struct command_completion_args *args)
Packit 34410b
{
Packit 34410b
	int argc;
Packit 34410b
	const split_arg_t *arg = args->arg;
Packit 34410b
	const char *sb = NULL;
Packit 34410b
	const char *eb = NULL;
Packit 34410b
	const char *arg1 = "";
Packit 34410b
	int arg1_size = 0; /* size of method field (for methods > 0) */
Packit 34410b
Packit 34410b
	if (args->user_help == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	for (argc = 0; arg != NULL; argc++)
Packit 34410b
		arg = arg->next;
Packit 34410b
Packit 34410b
	/* Check if this is method from interface */
Packit 34410b
	if (get_command(args->arg->ntcopy) == NULL) {
Packit 34410b
		/* if so help is missing interface and method name */
Packit 34410b
		arg1 = args->arg->next->ntcopy;
Packit 34410b
		arg1_size = strlen(arg1) + 1;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2),
Packit 34410b
								&sb, &eb;;
Packit 34410b
Packit 34410b
	if (eb != NULL)
Packit 34410b
		haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
Packit 34410b
				arg1_size, arg1, (int) (sb - args->user_help),
Packit 34410b
				args->user_help, bold, (int) (eb - sb),
Packit 34410b
				sb, normal, eb);
Packit 34410b
	else
Packit 34410b
		haltest_info("%s %-*s%s\n", args->arg->ntcopy,
Packit 34410b
			arg1_size, arg1, args->user_help);
Packit 34410b
}
Packit 34410b
Packit 34410b
/* So we have empty enumeration */
Packit 34410b
static const char *return_null(void *user, int i)
Packit 34410b
{
Packit 34410b
	return NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
/*
Packit 34410b
 * parameter completion function
Packit 34410b
 * argc - number of elements in arg list
Packit 34410b
 * arg - list of arguments
Packit 34410b
 * method - method to get completion from (can be NULL)
Packit 34410b
 */
Packit 34410b
static void param_completion(int argc, const split_arg_t *arg,
Packit 34410b
					const struct method *method, int hlpix)
Packit 34410b
{
Packit 34410b
	int i;
Packit 34410b
	const char *argv[argc];
Packit 34410b
	const split_arg_t *tmp = arg;
Packit 34410b
	struct command_completion_args args = {
Packit 34410b
		.arg = arg,
Packit 34410b
		.func = return_null
Packit 34410b
	};
Packit 34410b
Packit 34410b
	/* prepare standard argv from arg */
Packit 34410b
	for (i = 0; i < argc; ++i) {
Packit 34410b
		argv[i] = tmp->ntcopy;
Packit 34410b
		tmp = tmp->next;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (method != NULL && method->complete != NULL) {
Packit 34410b
		/* ask method for completion function */
Packit 34410b
		method->complete(argc, argv, &args.func, &args.user);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* If method provided enumeration function call try to complete */
Packit 34410b
	if (args.func != NULL) {
Packit 34410b
		args.typed = argv[argc - 1];
Packit 34410b
		args.help = method_help;
Packit 34410b
		args.user_help = method ? method->help : NULL;
Packit 34410b
Packit 34410b
		tab_completion(&args);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
/*
Packit 34410b
 * This method gets called when user tapped tab key.
Packit 34410b
 * line - points to command line
Packit 34410b
 * len - size of line that should be used for completions. This should be
Packit 34410b
 *   cursor position during tab hit.
Packit 34410b
 */
Packit 34410b
void process_tab(const char *line, int len)
Packit 34410b
{
Packit 34410b
	int argc;
Packit 34410b
	static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)];
Packit 34410b
	const struct method *method;
Packit 34410b
Packit 34410b
	argc = split_command(line, len, buf, sizeof(buf));
Packit 34410b
	tab_hit_count++;
Packit 34410b
Packit 34410b
	if (argc == 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (argc == 1) {
Packit 34410b
		command_completion(buf);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	method = get_command(buf[0].ntcopy);
Packit 34410b
	if (method != NULL) {
Packit 34410b
		param_completion(argc, buf, method, 1);
Packit 34410b
	} else if (argc == 2) {
Packit 34410b
		method_completion(get_interface(buf[0].ntcopy), buf);
Packit 34410b
	} else {
Packit 34410b
		/* Find method for <interface, name> pair */
Packit 34410b
		method = get_interface_method(buf[0].ntcopy,
Packit 34410b
							buf[0].next->ntcopy);
Packit 34410b
		param_completion(argc, buf, method, 2);
Packit 34410b
	}
Packit 34410b
}