xref: /dpdk/lib/cmdline/cmdline_rdline.c (revision 30a1de105a5f40d77b344a891c4a68f79e815c43)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation.
399a2dd95SBruce Richardson  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
499a2dd95SBruce Richardson  * All rights reserved.
599a2dd95SBruce Richardson  */
699a2dd95SBruce Richardson 
799a2dd95SBruce Richardson #include <stdlib.h>
899a2dd95SBruce Richardson #include <stdio.h>
999a2dd95SBruce Richardson #include <string.h>
1099a2dd95SBruce Richardson #include <errno.h>
1199a2dd95SBruce Richardson #include <ctype.h>
1299a2dd95SBruce Richardson 
1399a2dd95SBruce Richardson #include "cmdline_cirbuf.h"
14*f8f8dc28SDmitry Kozlyuk #include "cmdline_private.h"
1599a2dd95SBruce Richardson #include "cmdline_rdline.h"
1699a2dd95SBruce Richardson 
1799a2dd95SBruce Richardson static void rdline_puts(struct rdline *rdl, const char *buf);
1899a2dd95SBruce Richardson static void rdline_miniprintf(struct rdline *rdl,
1999a2dd95SBruce Richardson 			      const char *buf, unsigned int val);
2099a2dd95SBruce Richardson 
2199a2dd95SBruce Richardson static void rdline_remove_old_history_item(struct rdline *rdl);
2299a2dd95SBruce Richardson static void rdline_remove_first_history_item(struct rdline *rdl);
2399a2dd95SBruce Richardson static unsigned int rdline_get_history_size(struct rdline *rdl);
2499a2dd95SBruce Richardson 
2599a2dd95SBruce Richardson 
2699a2dd95SBruce Richardson /* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
2799a2dd95SBruce Richardson  * own. */
2899a2dd95SBruce Richardson static int
isblank2(char c)2999a2dd95SBruce Richardson isblank2(char c)
3099a2dd95SBruce Richardson {
3199a2dd95SBruce Richardson 	if (c == ' ' ||
3299a2dd95SBruce Richardson 	    c == '\t' )
3399a2dd95SBruce Richardson 		return 1;
3499a2dd95SBruce Richardson 	return 0;
3599a2dd95SBruce Richardson }
3699a2dd95SBruce Richardson 
3799a2dd95SBruce Richardson int
rdline_init(struct rdline * rdl,rdline_write_char_t * write_char,rdline_validate_t * validate,rdline_complete_t * complete,void * opaque)3899a2dd95SBruce Richardson rdline_init(struct rdline *rdl,
3999a2dd95SBruce Richardson 	    rdline_write_char_t *write_char,
4099a2dd95SBruce Richardson 	    rdline_validate_t *validate,
41*f8f8dc28SDmitry Kozlyuk 	    rdline_complete_t *complete,
42*f8f8dc28SDmitry Kozlyuk 	    void *opaque)
4399a2dd95SBruce Richardson {
4499a2dd95SBruce Richardson 	if (!rdl || !write_char || !validate || !complete)
4599a2dd95SBruce Richardson 		return -EINVAL;
4699a2dd95SBruce Richardson 	memset(rdl, 0, sizeof(*rdl));
4799a2dd95SBruce Richardson 	rdl->validate = validate;
4899a2dd95SBruce Richardson 	rdl->complete = complete;
4999a2dd95SBruce Richardson 	rdl->write_char = write_char;
50*f8f8dc28SDmitry Kozlyuk 	rdl->opaque = opaque;
5199a2dd95SBruce Richardson 	rdl->status = RDLINE_INIT;
5299a2dd95SBruce Richardson 	return cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
5399a2dd95SBruce Richardson }
5499a2dd95SBruce Richardson 
55*f8f8dc28SDmitry Kozlyuk struct rdline *
rdline_new(rdline_write_char_t * write_char,rdline_validate_t * validate,rdline_complete_t * complete,void * opaque)56*f8f8dc28SDmitry Kozlyuk rdline_new(rdline_write_char_t *write_char,
57*f8f8dc28SDmitry Kozlyuk 	   rdline_validate_t *validate,
58*f8f8dc28SDmitry Kozlyuk 	   rdline_complete_t *complete,
59*f8f8dc28SDmitry Kozlyuk 	   void *opaque)
60*f8f8dc28SDmitry Kozlyuk {
61*f8f8dc28SDmitry Kozlyuk 	struct rdline *rdl;
62*f8f8dc28SDmitry Kozlyuk 
63*f8f8dc28SDmitry Kozlyuk 	rdl = malloc(sizeof(*rdl));
64*f8f8dc28SDmitry Kozlyuk 	if (rdline_init(rdl, write_char, validate, complete, opaque) < 0) {
65*f8f8dc28SDmitry Kozlyuk 		free(rdl);
66*f8f8dc28SDmitry Kozlyuk 		rdl = NULL;
67*f8f8dc28SDmitry Kozlyuk 	}
68*f8f8dc28SDmitry Kozlyuk 	return rdl;
69*f8f8dc28SDmitry Kozlyuk }
70*f8f8dc28SDmitry Kozlyuk 
71*f8f8dc28SDmitry Kozlyuk void
rdline_free(struct rdline * rdl)72*f8f8dc28SDmitry Kozlyuk rdline_free(struct rdline *rdl)
73*f8f8dc28SDmitry Kozlyuk {
74*f8f8dc28SDmitry Kozlyuk 	free(rdl);
75*f8f8dc28SDmitry Kozlyuk }
76*f8f8dc28SDmitry Kozlyuk 
7799a2dd95SBruce Richardson void
rdline_newline(struct rdline * rdl,const char * prompt)7899a2dd95SBruce Richardson rdline_newline(struct rdline *rdl, const char *prompt)
7999a2dd95SBruce Richardson {
8099a2dd95SBruce Richardson 	unsigned int i;
8199a2dd95SBruce Richardson 
8299a2dd95SBruce Richardson 	if (!rdl || !prompt)
8399a2dd95SBruce Richardson 		return;
8499a2dd95SBruce Richardson 
8599a2dd95SBruce Richardson 	vt100_init(&rdl->vt100);
8699a2dd95SBruce Richardson 	cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
8799a2dd95SBruce Richardson 	cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
8899a2dd95SBruce Richardson 
8999a2dd95SBruce Richardson 	rdl->prompt_size = strnlen(prompt, RDLINE_PROMPT_SIZE-1);
9099a2dd95SBruce Richardson 	if (prompt != rdl->prompt)
9199a2dd95SBruce Richardson 		memcpy(rdl->prompt, prompt, rdl->prompt_size);
9299a2dd95SBruce Richardson 	rdl->prompt[RDLINE_PROMPT_SIZE-1] = '\0';
9399a2dd95SBruce Richardson 
9499a2dd95SBruce Richardson 	for (i=0 ; i<rdl->prompt_size ; i++)
9599a2dd95SBruce Richardson 		rdl->write_char(rdl, rdl->prompt[i]);
9699a2dd95SBruce Richardson 	rdl->status = RDLINE_RUNNING;
9799a2dd95SBruce Richardson 
9899a2dd95SBruce Richardson 	rdl->history_cur_line = -1;
9999a2dd95SBruce Richardson }
10099a2dd95SBruce Richardson 
10199a2dd95SBruce Richardson void
rdline_stop(struct rdline * rdl)10299a2dd95SBruce Richardson rdline_stop(struct rdline *rdl)
10399a2dd95SBruce Richardson {
10499a2dd95SBruce Richardson 	if (!rdl)
10599a2dd95SBruce Richardson 		return;
10699a2dd95SBruce Richardson 	rdl->status = RDLINE_INIT;
10799a2dd95SBruce Richardson }
10899a2dd95SBruce Richardson 
10999a2dd95SBruce Richardson void
rdline_quit(struct rdline * rdl)11099a2dd95SBruce Richardson rdline_quit(struct rdline *rdl)
11199a2dd95SBruce Richardson {
11299a2dd95SBruce Richardson 	if (!rdl)
11399a2dd95SBruce Richardson 		return;
11499a2dd95SBruce Richardson 	rdl->status = RDLINE_EXITED;
11599a2dd95SBruce Richardson }
11699a2dd95SBruce Richardson 
11799a2dd95SBruce Richardson void
rdline_restart(struct rdline * rdl)11899a2dd95SBruce Richardson rdline_restart(struct rdline *rdl)
11999a2dd95SBruce Richardson {
12099a2dd95SBruce Richardson 	if (!rdl)
12199a2dd95SBruce Richardson 		return;
12299a2dd95SBruce Richardson 	rdl->status = RDLINE_RUNNING;
12399a2dd95SBruce Richardson }
12499a2dd95SBruce Richardson 
12599a2dd95SBruce Richardson void
rdline_reset(struct rdline * rdl)12699a2dd95SBruce Richardson rdline_reset(struct rdline *rdl)
12799a2dd95SBruce Richardson {
12899a2dd95SBruce Richardson 	if (!rdl)
12999a2dd95SBruce Richardson 		return;
13099a2dd95SBruce Richardson 	vt100_init(&rdl->vt100);
13199a2dd95SBruce Richardson 	cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
13299a2dd95SBruce Richardson 	cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
13399a2dd95SBruce Richardson 
13499a2dd95SBruce Richardson 	rdl->status = RDLINE_RUNNING;
13599a2dd95SBruce Richardson 
13699a2dd95SBruce Richardson 	rdl->history_cur_line = -1;
13799a2dd95SBruce Richardson }
13899a2dd95SBruce Richardson 
13999a2dd95SBruce Richardson const char *
rdline_get_buffer(struct rdline * rdl)14099a2dd95SBruce Richardson rdline_get_buffer(struct rdline *rdl)
14199a2dd95SBruce Richardson {
14299a2dd95SBruce Richardson 	if (!rdl)
14399a2dd95SBruce Richardson 		return NULL;
14499a2dd95SBruce Richardson 	unsigned int len_l, len_r;
14599a2dd95SBruce Richardson 	cirbuf_align_left(&rdl->left);
14699a2dd95SBruce Richardson 	cirbuf_align_left(&rdl->right);
14799a2dd95SBruce Richardson 
14899a2dd95SBruce Richardson 	len_l = CIRBUF_GET_LEN(&rdl->left);
14999a2dd95SBruce Richardson 	len_r = CIRBUF_GET_LEN(&rdl->right);
15099a2dd95SBruce Richardson 	memcpy(rdl->left_buf+len_l, rdl->right_buf, len_r);
15199a2dd95SBruce Richardson 
15299a2dd95SBruce Richardson 	rdl->left_buf[len_l + len_r] = '\n';
15399a2dd95SBruce Richardson 	rdl->left_buf[len_l + len_r + 1] = '\0';
15499a2dd95SBruce Richardson 	return rdl->left_buf;
15599a2dd95SBruce Richardson }
15699a2dd95SBruce Richardson 
15799a2dd95SBruce Richardson static void
display_right_buffer(struct rdline * rdl,int force)15899a2dd95SBruce Richardson display_right_buffer(struct rdline *rdl, int force)
15999a2dd95SBruce Richardson {
16099a2dd95SBruce Richardson 	unsigned int i;
16199a2dd95SBruce Richardson 	char tmp;
16299a2dd95SBruce Richardson 
16399a2dd95SBruce Richardson 	if (!force && CIRBUF_IS_EMPTY(&rdl->right))
16499a2dd95SBruce Richardson 		return;
16599a2dd95SBruce Richardson 
16699a2dd95SBruce Richardson 	rdline_puts(rdl, vt100_clear_right);
16799a2dd95SBruce Richardson 	CIRBUF_FOREACH(&rdl->right, i, tmp) {
16899a2dd95SBruce Richardson 		rdl->write_char(rdl, tmp);
16999a2dd95SBruce Richardson 	}
17099a2dd95SBruce Richardson 	if (!CIRBUF_IS_EMPTY(&rdl->right))
17199a2dd95SBruce Richardson 		rdline_miniprintf(rdl, vt100_multi_left,
17299a2dd95SBruce Richardson 				  CIRBUF_GET_LEN(&rdl->right));
17399a2dd95SBruce Richardson }
17499a2dd95SBruce Richardson 
17599a2dd95SBruce Richardson void
rdline_redisplay(struct rdline * rdl)17699a2dd95SBruce Richardson rdline_redisplay(struct rdline *rdl)
17799a2dd95SBruce Richardson {
17899a2dd95SBruce Richardson 	unsigned int i;
17999a2dd95SBruce Richardson 	char tmp;
18099a2dd95SBruce Richardson 
18199a2dd95SBruce Richardson 	if (!rdl)
18299a2dd95SBruce Richardson 		return;
18399a2dd95SBruce Richardson 
18499a2dd95SBruce Richardson 	rdline_puts(rdl, vt100_home);
18599a2dd95SBruce Richardson 	for (i=0 ; i<rdl->prompt_size ; i++)
18699a2dd95SBruce Richardson 		rdl->write_char(rdl, rdl->prompt[i]);
18799a2dd95SBruce Richardson 	CIRBUF_FOREACH(&rdl->left, i, tmp) {
18899a2dd95SBruce Richardson 		rdl->write_char(rdl, tmp);
18999a2dd95SBruce Richardson 	}
19099a2dd95SBruce Richardson 	display_right_buffer(rdl, 1);
19199a2dd95SBruce Richardson }
19299a2dd95SBruce Richardson 
19399a2dd95SBruce Richardson int
rdline_char_in(struct rdline * rdl,char c)19499a2dd95SBruce Richardson rdline_char_in(struct rdline *rdl, char c)
19599a2dd95SBruce Richardson {
19699a2dd95SBruce Richardson 	unsigned int i;
19799a2dd95SBruce Richardson 	int cmd;
19899a2dd95SBruce Richardson 	char tmp;
19999a2dd95SBruce Richardson 	char *buf;
20099a2dd95SBruce Richardson 
20199a2dd95SBruce Richardson 	if (!rdl)
20299a2dd95SBruce Richardson 		return -EINVAL;
20399a2dd95SBruce Richardson 
20499a2dd95SBruce Richardson 	if (rdl->status == RDLINE_EXITED)
20599a2dd95SBruce Richardson 		return RDLINE_RES_EXITED;
20699a2dd95SBruce Richardson 	if (rdl->status != RDLINE_RUNNING)
20799a2dd95SBruce Richardson 		return RDLINE_RES_NOT_RUNNING;
20899a2dd95SBruce Richardson 
20999a2dd95SBruce Richardson 	cmd = vt100_parser(&rdl->vt100, c);
21099a2dd95SBruce Richardson 	if (cmd == -2)
21199a2dd95SBruce Richardson 		return RDLINE_RES_SUCCESS;
21299a2dd95SBruce Richardson 
21399a2dd95SBruce Richardson 	if (cmd >= 0) {
21499a2dd95SBruce Richardson 		switch (cmd) {
21599a2dd95SBruce Richardson 		/* move caret 1 char to the left */
21699a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_B:
21799a2dd95SBruce Richardson 		case CMDLINE_KEY_LEFT_ARR:
21899a2dd95SBruce Richardson 			if (CIRBUF_IS_EMPTY(&rdl->left))
21999a2dd95SBruce Richardson 				break;
22099a2dd95SBruce Richardson 			tmp = cirbuf_get_tail(&rdl->left);
22199a2dd95SBruce Richardson 			cirbuf_del_tail(&rdl->left);
22299a2dd95SBruce Richardson 			cirbuf_add_head(&rdl->right, tmp);
22399a2dd95SBruce Richardson 			rdline_puts(rdl, vt100_left_arr);
22499a2dd95SBruce Richardson 			break;
22599a2dd95SBruce Richardson 
22699a2dd95SBruce Richardson 		/* move caret 1 char to the right */
22799a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_F:
22899a2dd95SBruce Richardson 		case CMDLINE_KEY_RIGHT_ARR:
22999a2dd95SBruce Richardson 			if (CIRBUF_IS_EMPTY(&rdl->right))
23099a2dd95SBruce Richardson 				break;
23199a2dd95SBruce Richardson 			tmp = cirbuf_get_head(&rdl->right);
23299a2dd95SBruce Richardson 			cirbuf_del_head(&rdl->right);
23399a2dd95SBruce Richardson 			cirbuf_add_tail(&rdl->left, tmp);
23499a2dd95SBruce Richardson 			rdline_puts(rdl, vt100_right_arr);
23599a2dd95SBruce Richardson 			break;
23699a2dd95SBruce Richardson 
23799a2dd95SBruce Richardson 		/* move caret 1 word to the left */
23899a2dd95SBruce Richardson 		/* keyboard equivalent: Alt+B */
23999a2dd95SBruce Richardson 		case CMDLINE_KEY_WLEFT:
24099a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->left) &&
24199a2dd95SBruce Richardson 			       (tmp = cirbuf_get_tail(&rdl->left)) &&
24299a2dd95SBruce Richardson 			       isblank2(tmp)) {
24399a2dd95SBruce Richardson 				rdline_puts(rdl, vt100_left_arr);
24499a2dd95SBruce Richardson 				cirbuf_del_tail(&rdl->left);
24599a2dd95SBruce Richardson 				cirbuf_add_head(&rdl->right, tmp);
24699a2dd95SBruce Richardson 			}
24799a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->left) &&
24899a2dd95SBruce Richardson 			       (tmp = cirbuf_get_tail(&rdl->left)) &&
24999a2dd95SBruce Richardson 			       !isblank2(tmp)) {
25099a2dd95SBruce Richardson 				rdline_puts(rdl, vt100_left_arr);
25199a2dd95SBruce Richardson 				cirbuf_del_tail(&rdl->left);
25299a2dd95SBruce Richardson 				cirbuf_add_head(&rdl->right, tmp);
25399a2dd95SBruce Richardson 			}
25499a2dd95SBruce Richardson 			break;
25599a2dd95SBruce Richardson 
25699a2dd95SBruce Richardson 		/* move caret 1 word to the right */
25799a2dd95SBruce Richardson 		/* keyboard equivalent: Alt+F */
25899a2dd95SBruce Richardson 		case CMDLINE_KEY_WRIGHT:
25999a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->right) &&
26099a2dd95SBruce Richardson 			       (tmp = cirbuf_get_head(&rdl->right)) &&
26199a2dd95SBruce Richardson 			       isblank2(tmp)) {
26299a2dd95SBruce Richardson 				rdline_puts(rdl, vt100_right_arr);
26399a2dd95SBruce Richardson 				cirbuf_del_head(&rdl->right);
26499a2dd95SBruce Richardson 				cirbuf_add_tail(&rdl->left, tmp);
26599a2dd95SBruce Richardson 			}
26699a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->right) &&
26799a2dd95SBruce Richardson 			       (tmp = cirbuf_get_head(&rdl->right)) &&
26899a2dd95SBruce Richardson 			       !isblank2(tmp)) {
26999a2dd95SBruce Richardson 				rdline_puts(rdl, vt100_right_arr);
27099a2dd95SBruce Richardson 				cirbuf_del_head(&rdl->right);
27199a2dd95SBruce Richardson 				cirbuf_add_tail(&rdl->left, tmp);
27299a2dd95SBruce Richardson 			}
27399a2dd95SBruce Richardson 			break;
27499a2dd95SBruce Richardson 
27599a2dd95SBruce Richardson 		/* move caret to the left */
27699a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_A:
27799a2dd95SBruce Richardson 			if (CIRBUF_IS_EMPTY(&rdl->left))
27899a2dd95SBruce Richardson 				break;
27999a2dd95SBruce Richardson 			rdline_miniprintf(rdl, vt100_multi_left,
28099a2dd95SBruce Richardson 						CIRBUF_GET_LEN(&rdl->left));
28199a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->left)) {
28299a2dd95SBruce Richardson 				tmp = cirbuf_get_tail(&rdl->left);
28399a2dd95SBruce Richardson 				cirbuf_del_tail(&rdl->left);
28499a2dd95SBruce Richardson 				cirbuf_add_head(&rdl->right, tmp);
28599a2dd95SBruce Richardson 			}
28699a2dd95SBruce Richardson 			break;
28799a2dd95SBruce Richardson 
28899a2dd95SBruce Richardson 		/* move caret to the right */
28999a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_E:
29099a2dd95SBruce Richardson 			if (CIRBUF_IS_EMPTY(&rdl->right))
29199a2dd95SBruce Richardson 				break;
29299a2dd95SBruce Richardson 			rdline_miniprintf(rdl, vt100_multi_right,
29399a2dd95SBruce Richardson 						CIRBUF_GET_LEN(&rdl->right));
29499a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->right)) {
29599a2dd95SBruce Richardson 				tmp = cirbuf_get_head(&rdl->right);
29699a2dd95SBruce Richardson 				cirbuf_del_head(&rdl->right);
29799a2dd95SBruce Richardson 				cirbuf_add_tail(&rdl->left, tmp);
29899a2dd95SBruce Richardson 			}
29999a2dd95SBruce Richardson 			break;
30099a2dd95SBruce Richardson 
30199a2dd95SBruce Richardson 		/* delete 1 char from the left */
30299a2dd95SBruce Richardson 		case CMDLINE_KEY_BKSPACE:
30399a2dd95SBruce Richardson 		case CMDLINE_KEY_BKSPACE2:
30499a2dd95SBruce Richardson 			if(!cirbuf_del_tail_safe(&rdl->left)) {
30599a2dd95SBruce Richardson 				rdline_puts(rdl, vt100_bs);
30699a2dd95SBruce Richardson 				display_right_buffer(rdl, 1);
30799a2dd95SBruce Richardson 			}
30899a2dd95SBruce Richardson 			break;
30999a2dd95SBruce Richardson 
31099a2dd95SBruce Richardson 		/* delete 1 char from the right */
31199a2dd95SBruce Richardson 		case CMDLINE_KEY_SUPPR:
31299a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_D:
31399a2dd95SBruce Richardson 			if (cmd == CMDLINE_KEY_CTRL_D &&
31499a2dd95SBruce Richardson 			    CIRBUF_IS_EMPTY(&rdl->left) &&
31599a2dd95SBruce Richardson 			    CIRBUF_IS_EMPTY(&rdl->right)) {
31699a2dd95SBruce Richardson 				return RDLINE_RES_EOF;
31799a2dd95SBruce Richardson 			}
31899a2dd95SBruce Richardson 			if (!cirbuf_del_head_safe(&rdl->right)) {
31999a2dd95SBruce Richardson 				display_right_buffer(rdl, 1);
32099a2dd95SBruce Richardson 			}
32199a2dd95SBruce Richardson 			break;
32299a2dd95SBruce Richardson 
32399a2dd95SBruce Richardson 		/* delete 1 word from the left */
32499a2dd95SBruce Richardson 		case CMDLINE_KEY_META_BKSPACE:
32599a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_W:
32699a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->left) && isblank2(cirbuf_get_tail(&rdl->left))) {
32799a2dd95SBruce Richardson 				rdline_puts(rdl, vt100_bs);
32899a2dd95SBruce Richardson 				cirbuf_del_tail(&rdl->left);
32999a2dd95SBruce Richardson 			}
33099a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->left) && !isblank2(cirbuf_get_tail(&rdl->left))) {
33199a2dd95SBruce Richardson 				rdline_puts(rdl, vt100_bs);
33299a2dd95SBruce Richardson 				cirbuf_del_tail(&rdl->left);
33399a2dd95SBruce Richardson 			}
33499a2dd95SBruce Richardson 			display_right_buffer(rdl, 1);
33599a2dd95SBruce Richardson 			break;
33699a2dd95SBruce Richardson 
33799a2dd95SBruce Richardson 		/* delete 1 word from the right */
33899a2dd95SBruce Richardson 		case CMDLINE_KEY_META_D:
33999a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->right) && isblank2(cirbuf_get_head(&rdl->right)))
34099a2dd95SBruce Richardson 				cirbuf_del_head(&rdl->right);
34199a2dd95SBruce Richardson 			while (! CIRBUF_IS_EMPTY(&rdl->right) && !isblank2(cirbuf_get_head(&rdl->right)))
34299a2dd95SBruce Richardson 				cirbuf_del_head(&rdl->right);
34399a2dd95SBruce Richardson 			display_right_buffer(rdl, 1);
34499a2dd95SBruce Richardson 			break;
34599a2dd95SBruce Richardson 
34699a2dd95SBruce Richardson 		/* set kill buffer to contents on the right side of caret */
34799a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_K:
34899a2dd95SBruce Richardson 			cirbuf_get_buf_head(&rdl->right, rdl->kill_buf, RDLINE_BUF_SIZE);
34999a2dd95SBruce Richardson 			rdl->kill_size = CIRBUF_GET_LEN(&rdl->right);
35099a2dd95SBruce Richardson 			cirbuf_del_buf_head(&rdl->right, rdl->kill_size);
35199a2dd95SBruce Richardson 			rdline_puts(rdl, vt100_clear_right);
35299a2dd95SBruce Richardson 			break;
35399a2dd95SBruce Richardson 
35499a2dd95SBruce Richardson 		/* paste contents of kill buffer to the left side of caret */
35599a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_Y:
35699a2dd95SBruce Richardson 			i=0;
35799a2dd95SBruce Richardson 			while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
35899a2dd95SBruce Richardson 			      RDLINE_BUF_SIZE &&
35999a2dd95SBruce Richardson 			      i < rdl->kill_size) {
36099a2dd95SBruce Richardson 				cirbuf_add_tail(&rdl->left, rdl->kill_buf[i]);
36199a2dd95SBruce Richardson 				rdl->write_char(rdl, rdl->kill_buf[i]);
36299a2dd95SBruce Richardson 				i++;
36399a2dd95SBruce Richardson 			}
36499a2dd95SBruce Richardson 			display_right_buffer(rdl, 0);
36599a2dd95SBruce Richardson 			break;
36699a2dd95SBruce Richardson 
36799a2dd95SBruce Richardson 		/* clear and newline */
36899a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_C:
36999a2dd95SBruce Richardson 			rdline_puts(rdl, "\r\n");
37099a2dd95SBruce Richardson 			rdline_newline(rdl, rdl->prompt);
37199a2dd95SBruce Richardson 			break;
37299a2dd95SBruce Richardson 
37399a2dd95SBruce Richardson 		/* redisplay (helps when prompt is lost in other output) */
37499a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_L:
37599a2dd95SBruce Richardson 			rdline_redisplay(rdl);
37699a2dd95SBruce Richardson 			break;
37799a2dd95SBruce Richardson 
37899a2dd95SBruce Richardson 		/* autocomplete */
37999a2dd95SBruce Richardson 		case CMDLINE_KEY_TAB:
38099a2dd95SBruce Richardson 		case CMDLINE_KEY_HELP:
38199a2dd95SBruce Richardson 			cirbuf_align_left(&rdl->left);
38299a2dd95SBruce Richardson 			rdl->left_buf[CIRBUF_GET_LEN(&rdl->left)] = '\0';
38399a2dd95SBruce Richardson 			if (rdl->complete) {
38499a2dd95SBruce Richardson 				char tmp_buf[BUFSIZ];
38599a2dd95SBruce Richardson 				int complete_state;
38699a2dd95SBruce Richardson 				int ret;
38799a2dd95SBruce Richardson 				unsigned int tmp_size;
38899a2dd95SBruce Richardson 
38999a2dd95SBruce Richardson 				if (cmd == CMDLINE_KEY_TAB)
39099a2dd95SBruce Richardson 					complete_state = 0;
39199a2dd95SBruce Richardson 				else
39299a2dd95SBruce Richardson 					complete_state = -1;
39399a2dd95SBruce Richardson 
39499a2dd95SBruce Richardson 				/* see in parse.h for help on complete() */
39599a2dd95SBruce Richardson 				ret = rdl->complete(rdl, rdl->left_buf,
39699a2dd95SBruce Richardson 						    tmp_buf, sizeof(tmp_buf),
39799a2dd95SBruce Richardson 						    &complete_state);
39899a2dd95SBruce Richardson 				/* no completion or error */
39999a2dd95SBruce Richardson 				if (ret <= 0) {
40099a2dd95SBruce Richardson 					return RDLINE_RES_COMPLETE;
40199a2dd95SBruce Richardson 				}
40299a2dd95SBruce Richardson 
40399a2dd95SBruce Richardson 				tmp_size = strnlen(tmp_buf, sizeof(tmp_buf));
40499a2dd95SBruce Richardson 				/* add chars */
40599a2dd95SBruce Richardson 				if (ret == RDLINE_RES_COMPLETE) {
40699a2dd95SBruce Richardson 					i=0;
40799a2dd95SBruce Richardson 					while(CIRBUF_GET_LEN(&rdl->right) + CIRBUF_GET_LEN(&rdl->left) <
40899a2dd95SBruce Richardson 					      RDLINE_BUF_SIZE &&
40999a2dd95SBruce Richardson 					      i < tmp_size) {
41099a2dd95SBruce Richardson 						cirbuf_add_tail(&rdl->left, tmp_buf[i]);
41199a2dd95SBruce Richardson 						rdl->write_char(rdl, tmp_buf[i]);
41299a2dd95SBruce Richardson 						i++;
41399a2dd95SBruce Richardson 					}
41499a2dd95SBruce Richardson 					display_right_buffer(rdl, 1);
41599a2dd95SBruce Richardson 					return RDLINE_RES_COMPLETE; /* ?? */
41699a2dd95SBruce Richardson 				}
41799a2dd95SBruce Richardson 
41899a2dd95SBruce Richardson 				/* choice */
41999a2dd95SBruce Richardson 				rdline_puts(rdl, "\r\n");
42099a2dd95SBruce Richardson 				while (ret) {
42199a2dd95SBruce Richardson 					rdl->write_char(rdl, ' ');
42299a2dd95SBruce Richardson 					for (i=0 ; tmp_buf[i] ; i++)
42399a2dd95SBruce Richardson 						rdl->write_char(rdl, tmp_buf[i]);
42499a2dd95SBruce Richardson 					rdline_puts(rdl, "\r\n");
42599a2dd95SBruce Richardson 					ret = rdl->complete(rdl, rdl->left_buf,
42699a2dd95SBruce Richardson 							    tmp_buf, sizeof(tmp_buf),
42799a2dd95SBruce Richardson 							    &complete_state);
42899a2dd95SBruce Richardson 				}
42999a2dd95SBruce Richardson 
43099a2dd95SBruce Richardson 				rdline_redisplay(rdl);
43199a2dd95SBruce Richardson 			}
43299a2dd95SBruce Richardson 			return RDLINE_RES_COMPLETE;
43399a2dd95SBruce Richardson 
43499a2dd95SBruce Richardson 		/* complete buffer */
43599a2dd95SBruce Richardson 		case CMDLINE_KEY_RETURN:
43699a2dd95SBruce Richardson 		case CMDLINE_KEY_RETURN2:
43799a2dd95SBruce Richardson 			rdline_get_buffer(rdl);
43899a2dd95SBruce Richardson 			rdl->status = RDLINE_INIT;
43999a2dd95SBruce Richardson 			rdline_puts(rdl, "\r\n");
44099a2dd95SBruce Richardson 			if (rdl->history_cur_line != -1)
44199a2dd95SBruce Richardson 				rdline_remove_first_history_item(rdl);
44299a2dd95SBruce Richardson 
44399a2dd95SBruce Richardson 			if (rdl->validate)
44499a2dd95SBruce Richardson 				rdl->validate(rdl, rdl->left_buf, CIRBUF_GET_LEN(&rdl->left)+2);
44599a2dd95SBruce Richardson 			/* user may have stopped rdline */
44699a2dd95SBruce Richardson 			if (rdl->status == RDLINE_EXITED)
44799a2dd95SBruce Richardson 				return RDLINE_RES_EXITED;
44899a2dd95SBruce Richardson 			return RDLINE_RES_VALIDATED;
44999a2dd95SBruce Richardson 
45099a2dd95SBruce Richardson 		/* previous element in history */
45199a2dd95SBruce Richardson 		case CMDLINE_KEY_UP_ARR:
45299a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_P:
45399a2dd95SBruce Richardson 			if (rdl->history_cur_line == 0) {
45499a2dd95SBruce Richardson 				rdline_remove_first_history_item(rdl);
45599a2dd95SBruce Richardson 			}
45699a2dd95SBruce Richardson 			if (rdl->history_cur_line <= 0) {
45799a2dd95SBruce Richardson 				rdline_add_history(rdl, rdline_get_buffer(rdl));
45899a2dd95SBruce Richardson 				rdl->history_cur_line = 0;
45999a2dd95SBruce Richardson 			}
46099a2dd95SBruce Richardson 
46199a2dd95SBruce Richardson 			buf = rdline_get_history_item(rdl, rdl->history_cur_line + 1);
46299a2dd95SBruce Richardson 			if (!buf)
46399a2dd95SBruce Richardson 				break;
46499a2dd95SBruce Richardson 
46599a2dd95SBruce Richardson 			rdl->history_cur_line ++;
46699a2dd95SBruce Richardson 			vt100_init(&rdl->vt100);
46799a2dd95SBruce Richardson 			cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
46899a2dd95SBruce Richardson 			cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
46999a2dd95SBruce Richardson 			cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
47099a2dd95SBruce Richardson 			rdline_redisplay(rdl);
47199a2dd95SBruce Richardson 			break;
47299a2dd95SBruce Richardson 
47399a2dd95SBruce Richardson 		/* next element in history */
47499a2dd95SBruce Richardson 		case CMDLINE_KEY_DOWN_ARR:
47599a2dd95SBruce Richardson 		case CMDLINE_KEY_CTRL_N:
47699a2dd95SBruce Richardson 			if (rdl->history_cur_line - 1 < 0)
47799a2dd95SBruce Richardson 				break;
47899a2dd95SBruce Richardson 
47999a2dd95SBruce Richardson 			rdl->history_cur_line --;
48099a2dd95SBruce Richardson 			buf = rdline_get_history_item(rdl, rdl->history_cur_line);
48199a2dd95SBruce Richardson 			if (!buf)
48299a2dd95SBruce Richardson 				break;
48399a2dd95SBruce Richardson 			vt100_init(&rdl->vt100);
48499a2dd95SBruce Richardson 			cirbuf_init(&rdl->left, rdl->left_buf, 0, RDLINE_BUF_SIZE);
48599a2dd95SBruce Richardson 			cirbuf_init(&rdl->right, rdl->right_buf, 0, RDLINE_BUF_SIZE);
48699a2dd95SBruce Richardson 			cirbuf_add_buf_tail(&rdl->left, buf, strnlen(buf, RDLINE_BUF_SIZE));
48799a2dd95SBruce Richardson 			rdline_redisplay(rdl);
48899a2dd95SBruce Richardson 
48999a2dd95SBruce Richardson 			break;
49099a2dd95SBruce Richardson 
49199a2dd95SBruce Richardson 
49299a2dd95SBruce Richardson 		default:
49399a2dd95SBruce Richardson 			break;
49499a2dd95SBruce Richardson 		}
49599a2dd95SBruce Richardson 
49699a2dd95SBruce Richardson 		return RDLINE_RES_SUCCESS;
49799a2dd95SBruce Richardson 	}
49899a2dd95SBruce Richardson 
49999a2dd95SBruce Richardson 	if (!isprint((int)c))
50099a2dd95SBruce Richardson 		return RDLINE_RES_SUCCESS;
50199a2dd95SBruce Richardson 
50299a2dd95SBruce Richardson 	/* standard chars */
50399a2dd95SBruce Richardson 	if (CIRBUF_GET_LEN(&rdl->left) + CIRBUF_GET_LEN(&rdl->right) >= RDLINE_BUF_SIZE)
50499a2dd95SBruce Richardson 		return RDLINE_RES_SUCCESS;
50599a2dd95SBruce Richardson 
50699a2dd95SBruce Richardson 	if (cirbuf_add_tail_safe(&rdl->left, c))
50799a2dd95SBruce Richardson 		return RDLINE_RES_SUCCESS;
50899a2dd95SBruce Richardson 
50999a2dd95SBruce Richardson 	rdl->write_char(rdl, c);
51099a2dd95SBruce Richardson 	display_right_buffer(rdl, 0);
51199a2dd95SBruce Richardson 
51299a2dd95SBruce Richardson 	return RDLINE_RES_SUCCESS;
51399a2dd95SBruce Richardson }
51499a2dd95SBruce Richardson 
51599a2dd95SBruce Richardson 
51699a2dd95SBruce Richardson /* HISTORY */
51799a2dd95SBruce Richardson 
51899a2dd95SBruce Richardson static void
rdline_remove_old_history_item(struct rdline * rdl)51999a2dd95SBruce Richardson rdline_remove_old_history_item(struct rdline * rdl)
52099a2dd95SBruce Richardson {
52199a2dd95SBruce Richardson 	char tmp;
52299a2dd95SBruce Richardson 
52399a2dd95SBruce Richardson 	while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
52499a2dd95SBruce Richardson 		tmp = cirbuf_get_head(&rdl->history);
52599a2dd95SBruce Richardson 		cirbuf_del_head(&rdl->history);
52699a2dd95SBruce Richardson 		if (!tmp)
52799a2dd95SBruce Richardson 			break;
52899a2dd95SBruce Richardson 	}
52999a2dd95SBruce Richardson }
53099a2dd95SBruce Richardson 
53199a2dd95SBruce Richardson static void
rdline_remove_first_history_item(struct rdline * rdl)53299a2dd95SBruce Richardson rdline_remove_first_history_item(struct rdline * rdl)
53399a2dd95SBruce Richardson {
53499a2dd95SBruce Richardson 	char tmp;
53599a2dd95SBruce Richardson 
53699a2dd95SBruce Richardson 	if ( CIRBUF_IS_EMPTY(&rdl->history) ) {
53799a2dd95SBruce Richardson 		return;
53899a2dd95SBruce Richardson 	}
53999a2dd95SBruce Richardson 	else {
54099a2dd95SBruce Richardson 		cirbuf_del_tail(&rdl->history);
54199a2dd95SBruce Richardson 	}
54299a2dd95SBruce Richardson 
54399a2dd95SBruce Richardson 	while (! CIRBUF_IS_EMPTY(&rdl->history) ) {
54499a2dd95SBruce Richardson 		tmp = cirbuf_get_tail(&rdl->history);
54599a2dd95SBruce Richardson 		if (!tmp)
54699a2dd95SBruce Richardson 			break;
54799a2dd95SBruce Richardson 		cirbuf_del_tail(&rdl->history);
54899a2dd95SBruce Richardson 	}
54999a2dd95SBruce Richardson }
55099a2dd95SBruce Richardson 
55199a2dd95SBruce Richardson static unsigned int
rdline_get_history_size(struct rdline * rdl)55299a2dd95SBruce Richardson rdline_get_history_size(struct rdline * rdl)
55399a2dd95SBruce Richardson {
55499a2dd95SBruce Richardson 	unsigned int i, tmp, ret=0;
55599a2dd95SBruce Richardson 
55699a2dd95SBruce Richardson 	CIRBUF_FOREACH(&rdl->history, i, tmp) {
55799a2dd95SBruce Richardson 		if (tmp == 0)
55899a2dd95SBruce Richardson 			ret ++;
55999a2dd95SBruce Richardson 	}
56099a2dd95SBruce Richardson 
56199a2dd95SBruce Richardson 	return ret;
56299a2dd95SBruce Richardson }
56399a2dd95SBruce Richardson 
56499a2dd95SBruce Richardson char *
rdline_get_history_item(struct rdline * rdl,unsigned int idx)56599a2dd95SBruce Richardson rdline_get_history_item(struct rdline * rdl, unsigned int idx)
56699a2dd95SBruce Richardson {
56799a2dd95SBruce Richardson 	unsigned int len, i, tmp;
56899a2dd95SBruce Richardson 
56999a2dd95SBruce Richardson 	if (!rdl)
57099a2dd95SBruce Richardson 		return NULL;
57199a2dd95SBruce Richardson 
57299a2dd95SBruce Richardson 	len = rdline_get_history_size(rdl);
57399a2dd95SBruce Richardson 	if ( idx >= len ) {
57499a2dd95SBruce Richardson 		return NULL;
57599a2dd95SBruce Richardson 	}
57699a2dd95SBruce Richardson 
57799a2dd95SBruce Richardson 	cirbuf_align_left(&rdl->history);
57899a2dd95SBruce Richardson 
57999a2dd95SBruce Richardson 	CIRBUF_FOREACH(&rdl->history, i, tmp) {
58099a2dd95SBruce Richardson 		if ( idx == len - 1) {
58199a2dd95SBruce Richardson 			return rdl->history_buf + i;
58299a2dd95SBruce Richardson 		}
58399a2dd95SBruce Richardson 		if (tmp == 0)
58499a2dd95SBruce Richardson 			len --;
58599a2dd95SBruce Richardson 	}
58699a2dd95SBruce Richardson 
58799a2dd95SBruce Richardson 	return NULL;
58899a2dd95SBruce Richardson }
58999a2dd95SBruce Richardson 
590*f8f8dc28SDmitry Kozlyuk size_t
rdline_get_history_buffer_size(struct rdline * rdl)591*f8f8dc28SDmitry Kozlyuk rdline_get_history_buffer_size(struct rdline *rdl)
592*f8f8dc28SDmitry Kozlyuk {
593*f8f8dc28SDmitry Kozlyuk 	return sizeof(rdl->history_buf);
594*f8f8dc28SDmitry Kozlyuk }
595*f8f8dc28SDmitry Kozlyuk 
596*f8f8dc28SDmitry Kozlyuk void *
rdline_get_opaque(struct rdline * rdl)597*f8f8dc28SDmitry Kozlyuk rdline_get_opaque(struct rdline *rdl)
598*f8f8dc28SDmitry Kozlyuk {
599*f8f8dc28SDmitry Kozlyuk 	return rdl != NULL ? rdl->opaque : NULL;
600*f8f8dc28SDmitry Kozlyuk }
601*f8f8dc28SDmitry Kozlyuk 
60299a2dd95SBruce Richardson int
rdline_add_history(struct rdline * rdl,const char * buf)60399a2dd95SBruce Richardson rdline_add_history(struct rdline * rdl, const char * buf)
60499a2dd95SBruce Richardson {
60599a2dd95SBruce Richardson 	unsigned int len, i;
60699a2dd95SBruce Richardson 
60799a2dd95SBruce Richardson 	if (!rdl || !buf)
60899a2dd95SBruce Richardson 		return -EINVAL;
60999a2dd95SBruce Richardson 
61099a2dd95SBruce Richardson 	len = strnlen(buf, RDLINE_BUF_SIZE);
61199a2dd95SBruce Richardson 	for (i=0; i<len ; i++) {
61299a2dd95SBruce Richardson 		if (buf[i] == '\n') {
61399a2dd95SBruce Richardson 			len = i;
61499a2dd95SBruce Richardson 			break;
61599a2dd95SBruce Richardson 		}
61699a2dd95SBruce Richardson 	}
61799a2dd95SBruce Richardson 
61899a2dd95SBruce Richardson 	if ( len >= RDLINE_HISTORY_BUF_SIZE )
61999a2dd95SBruce Richardson 		return -1;
62099a2dd95SBruce Richardson 
62199a2dd95SBruce Richardson 	while ( len >= CIRBUF_GET_FREELEN(&rdl->history) ) {
62299a2dd95SBruce Richardson 		rdline_remove_old_history_item(rdl);
62399a2dd95SBruce Richardson 	}
62499a2dd95SBruce Richardson 
62599a2dd95SBruce Richardson 	cirbuf_add_buf_tail(&rdl->history, buf, len);
62699a2dd95SBruce Richardson 	cirbuf_add_tail(&rdl->history, 0);
62799a2dd95SBruce Richardson 
62899a2dd95SBruce Richardson 	return 0;
62999a2dd95SBruce Richardson }
63099a2dd95SBruce Richardson 
63199a2dd95SBruce Richardson void
rdline_clear_history(struct rdline * rdl)63299a2dd95SBruce Richardson rdline_clear_history(struct rdline * rdl)
63399a2dd95SBruce Richardson {
63499a2dd95SBruce Richardson 	if (!rdl)
63599a2dd95SBruce Richardson 		return;
63699a2dd95SBruce Richardson 	cirbuf_init(&rdl->history, rdl->history_buf, 0, RDLINE_HISTORY_BUF_SIZE);
63799a2dd95SBruce Richardson }
63899a2dd95SBruce Richardson 
63999a2dd95SBruce Richardson 
64099a2dd95SBruce Richardson /* STATIC USEFUL FUNCS */
64199a2dd95SBruce Richardson 
64299a2dd95SBruce Richardson static void
rdline_puts(struct rdline * rdl,const char * buf)64399a2dd95SBruce Richardson rdline_puts(struct rdline * rdl, const char * buf)
64499a2dd95SBruce Richardson {
64599a2dd95SBruce Richardson 	char c;
64699a2dd95SBruce Richardson 	while ( (c = *(buf++)) != '\0' ) {
64799a2dd95SBruce Richardson 		rdl->write_char(rdl, c);
64899a2dd95SBruce Richardson 	}
64999a2dd95SBruce Richardson }
65099a2dd95SBruce Richardson 
65199a2dd95SBruce Richardson /* a very very basic printf with one arg and one format 'u' */
65299a2dd95SBruce Richardson static void
rdline_miniprintf(struct rdline * rdl,const char * buf,unsigned int val)65399a2dd95SBruce Richardson rdline_miniprintf(struct rdline *rdl, const char * buf, unsigned int val)
65499a2dd95SBruce Richardson {
65599a2dd95SBruce Richardson 	char c, started=0, div=100;
65699a2dd95SBruce Richardson 
65799a2dd95SBruce Richardson 	while ( (c=*(buf++)) ) {
65899a2dd95SBruce Richardson 		if (c != '%') {
65999a2dd95SBruce Richardson 			rdl->write_char(rdl, c);
66099a2dd95SBruce Richardson 			continue;
66199a2dd95SBruce Richardson 		}
66299a2dd95SBruce Richardson 		c = *(buf++);
66399a2dd95SBruce Richardson 		if (c != 'u') {
66499a2dd95SBruce Richardson 			rdl->write_char(rdl, '%');
66599a2dd95SBruce Richardson 			rdl->write_char(rdl, c);
66699a2dd95SBruce Richardson 			continue;
66799a2dd95SBruce Richardson 		}
66899a2dd95SBruce Richardson 		/* val is never more than 255 */
66999a2dd95SBruce Richardson 		while (div) {
67099a2dd95SBruce Richardson 			c = (char)(val / div);
67199a2dd95SBruce Richardson 			if (c || started) {
67299a2dd95SBruce Richardson 				rdl->write_char(rdl, (char)(c+'0'));
67399a2dd95SBruce Richardson 				started = 1;
67499a2dd95SBruce Richardson 			}
67599a2dd95SBruce Richardson 			val %= div;
67699a2dd95SBruce Richardson 			div /= 10;
67799a2dd95SBruce Richardson 		}
67899a2dd95SBruce Richardson 	}
67999a2dd95SBruce Richardson }
680