示例#1
0
char *
readline(const char *prompt)
{
    int cur_char;
    char *new_line;


    /* start with a string of MAXBUF chars */
    if (line_len != 0) {
	free(cur_line);
	line_len = 0;
    }
    cur_line = gp_alloc(MAXBUF, "readline");
    line_len = MAXBUF;

    /* set the termio so we can do our own input processing */
    set_termio();

    /* print the prompt */
    fputs(prompt, stderr);
    cur_line[0] = '\0';
    cur_pos = 0;
    max_pos = 0;
    cur_entry = NULL;

    /* get characters */
    for (;;) {

	cur_char = special_getc();

	/* Accumulate ascii (7bit) printable characters
	 * and all leading 8bit characters.
	 */
	if ((isprint(cur_char)
	      || (((cur_char & 0x80) != 0) && (cur_char != EOF)))
	    && (cur_char != 0x09) /* TAB is a printable character in some locales */
	    ) {
	    size_t i;

	    if (max_pos + 1 >= line_len) {
		extend_cur_line();
	    }
	    for (i = max_pos; i > cur_pos; i--) {
		cur_line[i] = cur_line[i - 1];
	    }
	    user_putc(cur_char);

	    cur_line[cur_pos] = cur_char;
	    cur_pos += 1;
	    max_pos += 1;
	    cur_line[max_pos] = '\0';

	    if (cur_pos < max_pos) {
		switch (encoding) {
		case S_ENC_UTF8:
		    if ((cur_char & 0xc0) == 0)
			fix_line(); /* Normal ascii character */
		    else if ((cur_char & 0xc0) == 0xc0)
			; /* start of a multibyte sequence. */
		    else if (((cur_char & 0xc0) == 0x80) &&
			 ((unsigned char)(cur_line[cur_pos-2]) >= 0xe0))
			; /* second byte of a >2 byte sequence */
		    else {
			/* Last char of multi-byte sequence */
			fix_line();
		    }
		    break;
		default:
		    fix_line();
		    break;
		}
	    }

	/* else interpret unix terminal driver characters */
#ifdef VERASE
	} else if (cur_char == term_chars[VERASE]) {	/* ^H */
	    delete_backward();
#endif /* VERASE */
#ifdef VEOF
	} else if (cur_char == term_chars[VEOF]) {	/* ^D? */
	    if (max_pos == 0) {
		reset_termio();
		return ((char *) NULL);
	    }
	    delete_forward();
#endif /* VEOF */
#ifdef VKILL
	} else if (cur_char == term_chars[VKILL]) {	/* ^U? */
	    clear_line(prompt);
#endif /* VKILL */
#ifdef VWERASE
	} else if (cur_char == term_chars[VWERASE]) {	/* ^W? */
	    delete_previous_word();
#endif /* VWERASE */
#ifdef VREPRINT
	} else if (cur_char == term_chars[VREPRINT]) {	/* ^R? */
	    putc(NEWLINE, stderr);	/* go to a fresh line */
	    redraw_line(prompt);
#endif /* VREPRINT */
#ifdef VSUSP
	} else if (cur_char == term_chars[VSUSP]) {
	    reset_termio();
	    kill(0, SIGTSTP);

	    /* process stops here */

	    set_termio();
	    /* print the prompt */
	    redraw_line(prompt);
#endif /* VSUSP */
	} else {
	    /* do normal editing commands */
	    /* some of these are also done above */
	    switch (cur_char) {
	    case EOF:
		reset_termio();
		return ((char *) NULL);
	    case 001:		/* ^A */
		while (cur_pos > 0)
		    backspace();
		break;
	    case 002:		/* ^B */
		if (cur_pos > 0)
		    backspace();
		break;
	    case 005:		/* ^E */
		while (cur_pos < max_pos) {
		    user_putc(cur_line[cur_pos]);
		    cur_pos += 1;
		}
		break;
	    case 006:		/* ^F */
		if (cur_pos < max_pos) {
		    step_forward();
		}
		break;
#if defined(HAVE_DIRENT_H) || defined(WIN32)
	    case 011:		/* ^I / TAB */
		tab_completion(TRUE); /* next tab completion */
		break;
	    case 034:		/* remapped by wtext.c or ansi_getc from Shift-Tab */
		tab_completion(FALSE); /* previous tab completion */
		break;
#endif
	    case 013:		/* ^K */
		clear_eoline(prompt);
		max_pos = cur_pos;
		break;
	    case 020:		/* ^P */
		if (history != NULL) {
		    if (cur_entry == NULL) {
			cur_entry = history;
			clear_line(prompt);
			copy_line(cur_entry->line);
		    } else if (cur_entry->prev != NULL) {
			cur_entry = cur_entry->prev;
			clear_line(prompt);
			copy_line(cur_entry->line);
		    }
		}
		break;
	    case 016:		/* ^N */
		if (cur_entry != NULL) {
		    cur_entry = cur_entry->next;
		    clear_line(prompt);
		    if (cur_entry != NULL)
			copy_line(cur_entry->line);
		    else
			cur_pos = max_pos = 0;
		}
		break;
	    case 014:		/* ^L */
	    case 022:		/* ^R */
		putc(NEWLINE, stderr);	/* go to a fresh line */
		redraw_line(prompt);
		break;
#ifndef DEL_ERASES_CURRENT_CHAR
	    case 0177:		/* DEL */
	    case 023:		/* Re-mapped from CSI~3 in ansi_getc() */
#endif
	    case 010:		/* ^H */
		delete_backward();
		break;
	    case 004:		/* ^D */
		if (max_pos == 0) {
		    reset_termio();
		    return ((char *) NULL);
		}
		/* intentionally omitting break */
#ifdef DEL_ERASES_CURRENT_CHAR
	    case 0177:		/* DEL */
	    case 023:		/* Re-mapped from CSI~3 in ansi_getc() */
#endif
		delete_forward();
		break;
	    case 025:		/* ^U */
		clear_line(prompt);
		break;
	    case 027:		/* ^W */
		delete_previous_word();
		break;
	    case '\n':		/* ^J */
	    case '\r':		/* ^M */
		cur_line[max_pos + 1] = '\0';
#ifdef OS2
		while (cur_pos < max_pos) {
		    user_putc(cur_line[cur_pos]);
		    cur_pos += 1;
		}
#endif
		putc(NEWLINE, stderr);

		/* Shrink the block down to fit the string ?
		 * if the alloc fails, we still own block at cur_line,
		 * but this shouldn't really fail.
		 */
		new_line = (char *) gp_realloc(cur_line, strlen(cur_line) + 1,
					       "line resize");
		if (new_line)
		    cur_line = new_line;
		/* else we just hang on to what we had - it's not a problem */

		line_len = 0;
		FPRINTF((stderr, "Resizing input line to %d chars\n", strlen(cur_line)));
		reset_termio();
		return (cur_line);
	    default:
		break;
	    }
	}
    }
}
/* interface completion */
static void command_completion(split_arg_t *arg)
{
	struct command_completion_args args = {
		.arg = arg,
		.typed = arg->ntcopy,
		.func = command_name
	};

	tab_completion(&args);
}

/* method completion */
static void method_completion(const struct interface *iface, split_arg_t *arg)
{
	struct command_completion_args args = {
		.arg = arg,
		.typed = arg->next->ntcopy,
		.func = methods_name,
		.user = (void *) iface
	};

	if (iface == NULL)
		return;

	tab_completion(&args);
}

static const char *bold = "\x1b[1m";
static const char *normal = "\x1b[0m";

static bool find_nth_argument(const char *str, int n, const char **s,
								const char **e)
{
	const char *p = str;
	int argc = 0;
	*e = NULL;

	while (p != NULL && *p != 0) {

		while (isspace(*p))
			++p;

		if (n == argc)
			*s = p;

		if (*p == '[') {
			p = strchr(p, ']');
			if (p != NULL)
				*e = ++p;
		} else if (*p == '<') {
			p = strchr(p, '>');
			if (p != NULL)
				*e = ++p;
		} else {
			*e = strchr(p, ' ');
			if (*e == NULL)
				*e = p + strlen(p);
			p = *e;
		}

		if (n == argc)
			break;

		argc++;
		*e = NULL;
	}
	return *e != NULL;
}

/* prints short help on method for interface */
static void method_help(struct command_completion_args *args)
{
	int argc;
	const split_arg_t *arg = args->arg;
	const char *sb = NULL;
	const char *eb = NULL;
	const char *arg1 = "";
	int arg1_size = 0; /* size of method field (for methods > 0) */

	if (args->user_help == NULL)
		return;

	for (argc = 0; arg != NULL; argc++)
		arg = arg->next;

	/* Check if this is method from interface */
	if (get_command(args->arg->ntcopy) == NULL) {
		/* if so help is missing interface and method name */
		arg1 = args->arg->next->ntcopy;
		arg1_size = strlen(arg1) + 1;
	}

	find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2),
								&sb, &eb);

	if (eb != NULL)
		haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
				arg1_size, arg1, (int) (sb - args->user_help),
				args->user_help, bold, (int) (eb - sb),
				sb, normal, eb);
	else
		haltest_info("%s %-*s%s\n", args->arg->ntcopy,
			arg1_size, arg1, args->user_help);
}

/* So we have empty enumeration */
static const char *return_null(void *user, int i)
{
	return NULL;
}

/*
 * parameter completion function
 * argc - number of elements in arg list
 * arg - list of arguments
 * method - method to get completion from (can be NULL)
 */
static void param_completion(int argc, const split_arg_t *arg,
					const struct method *method, int hlpix)
{
	int i;
	const char *argv[argc];
	const split_arg_t *tmp = arg;
	struct command_completion_args args = {
		.arg = arg,
		.func = return_null
	};

	/* prepare standard argv from arg */
	for (i = 0; i < argc; ++i) {
		argv[i] = tmp->ntcopy;
		tmp = tmp->next;
	}

	if (method != NULL && method->complete != NULL) {
		/* ask method for completion function */
		method->complete(argc, argv, &args.func, &args.user);
	}

	/* If method provided enumeration function call try to complete */
	if (args.func != NULL) {
		args.typed = argv[argc - 1];
		args.help = method_help;
		args.user_help = method ? method->help : NULL;

		tab_completion(&args);
	}
}