1 /*	$OpenBSD: clit.c,v 1.5 2003/10/31 08:47:31 otto Exp $	*/
2 /*
3  * Copyright (c) 2002 Artur Grabowski <art@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <histedit.h>
30 #include <err.h>
31 #include <string.h>
32 
33 #include "clit.h"
34 
35 extern char *__progname;
36 
37 char *prompt_add;
38 
39 static char *
prompt(EditLine * el)40 prompt(EditLine *el)
41 {
42 	static char p[64];
43 
44 	snprintf(p, sizeof(p), "%s%s> ", __progname,
45 	    prompt_add ? prompt_add : "");
46 
47 	return p;
48 }
49 
50 /*
51  * Returns number of commands that (at least partially) match "name".
52  */
53 static int
name_to_cmd(const char * name,struct clit * cmds,int ncmds,struct clit ** res)54 name_to_cmd(const char *name, struct clit *cmds, int ncmds, struct clit **res)
55 {
56 	int i, len, ret;
57 
58 	len = strlen(name);
59 	ret = 0;
60 
61 	for (i = 0; i < ncmds; i++) {
62 		if (strncmp(cmds[i].cmd, name, len) == 0) {
63 			*res = &cmds[i];
64 			ret++;
65 		}
66 	}
67 
68 	return ret;
69 }
70 
71 struct clitenv {
72 	struct clit *cmds;
73 	int ncmds;
74 	EditLine *el;
75 	History *hist;
76 };
77 
78 int
cmd_help(int argc,char ** argv,void * arg)79 cmd_help(int argc, char **argv, void *arg)
80 {
81 	struct clitenv *env = arg;
82 	struct clit *cmds = env->cmds, *cmdp;
83 	int ncmds = env->ncmds;
84 	int i, res;
85 
86 	if (argc > 1) {
87 		res = name_to_cmd(argv[1], cmds, ncmds, &cmdp);
88 		if (res == 1) {
89 			printf("%s\t%s\n", cmdp->cmd, cmdp->help);
90 		} else {
91 			fprintf(stderr, "%s command: %s\n",
92 			    res == 0 ? "unknown" : "ambiguous", argv[1]);
93 		}
94 
95 		return 0;
96 	}
97 	for (i = 0; i < ncmds; i++) {
98 		cmdp = &cmds[i];
99 
100 		printf("%s\t%s\n", cmdp->cmd, cmdp->help);
101 	}
102 
103 	return 0;
104 }
105 
106 /*
107  * XXX - there is no way to push external args into this function.
108  */
109 unsigned char
complt(EditLine * el,int ch)110 complt(EditLine *el, int ch)
111 {
112 	const LineInfo *line;
113 	char str[1024];
114 	int len, ret;
115 
116 	line = el_line(el);
117 	if (line->cursor != line->lastchar)
118 		return CC_ERROR;
119 
120 	len = line->lastchar - line->buffer;
121 
122 	if (len >= 1023)
123 		return CC_ERROR;
124 
125 	memcpy(str, line->buffer, len);
126 	str[len] = '\0';
127 
128 	ret = cmd_complt(str, sizeof(str));
129 
130 	el_push(el, &str[len]);
131 
132 	return ret ? CC_ERROR : CC_REDISPLAY;
133 }
134 
135 void *
cmdinit(struct clit * cmds,int ncmds)136 cmdinit(struct clit *cmds, int ncmds)
137 {
138 	struct clitenv *env;
139 	HistEvent ev;
140 
141 	if ((env = malloc(sizeof(*env))) == NULL)
142 		err(1, "Can't init cmd interpreter.");
143 
144 	env->cmds = cmds;
145 	env->ncmds = ncmds;
146 
147 	env->hist = history_init();
148 	history(env->hist, &ev, H_SETSIZE, 100);
149 
150 	env->el = el_init(__progname, stdin, stdout, stderr);
151 
152 	el_set(env->el, EL_EDITOR, "emacs");
153 	el_set(env->el, EL_PROMPT, prompt);
154 	el_set(env->el, EL_HIST, history, env->hist);
155 	el_set(env->el, EL_ADDFN, "complt", "complete", complt);
156 	el_set(env->el, EL_BIND, "\t", "complt");
157 	el_source(env->el, NULL);
158 
159 	/* XXX - EL_SIGNAL ? */
160 
161 	return env;
162 }
163 
164 void
cmdend(void * arg)165 cmdend(void *arg)
166 {
167 	struct clitenv *env = arg;
168 
169 	el_end(env->el);
170 	history_end(env->hist);
171 
172 	free(env);
173 }
174 
175 int
cmdloop(void * arg)176 cmdloop(void *arg)
177 {
178 	struct clitenv *env = arg;
179 	EditLine *el = env->el;
180 	History *hist = env->hist;
181 	const char *elline;
182 	int cnt;
183 	char **argv;
184 	int maxargs = 16;	/* XXX */
185 	int stop;
186 
187 	stop = 0;
188 
189 	if ((argv = malloc(sizeof(char *) * maxargs)) == NULL)
190 		err(1, "malloc");
191 
192 	while (!stop && (elline = el_gets(el, &cnt)) != NULL) {
193 		char *line, *orgline;
194 		struct clit *cmdp;
195 		char **ap;
196 		int argc, res;
197 		HistEvent ev;
198 
199 		memset(argv, 0, sizeof(char *) * maxargs);
200 
201 		history(hist, &ev, H_ENTER, elline);
202 
203 		orgline = line = strdup(elline);
204 		if (line == NULL)
205 			err(1, "strdup");
206 
207 		argc = 0;
208 		for (ap = argv; (*ap = strsep(&line, " \t\n")) != NULL;) {
209 			if (**ap != '\0') {
210 				++ap;
211 				if (++argc == maxargs)
212 					break;
213 			}
214 		}
215 		if (argc == maxargs) {
216 			fprintf(stderr, "Too many arguments\n");
217 			goto cmdout;
218 		}
219 		if (!argc)
220 			goto cmdout;
221 
222 		/*
223 		 * Editline commands.
224 		 */
225 		if (el_parse(el, argc, (const char **)argv) != -1)
226 			goto cmdout;
227 
228 		if ((res = name_to_cmd(argv[0], env->cmds, env->ncmds,
229 		    &cmdp)) == 1) {
230 			if (argc - 1 > cmdp->maxargc)
231 				fprintf(stderr, "Too many arguments\n");
232 			else if (argc - 1 < cmdp->minargc)
233 				fprintf(stderr, "Too few arguments\n");
234 			else
235 				stop = (*cmdp->handler)(argc, argv,
236 				    cmdp->arg ? cmdp->arg : env);
237 		} else {
238 			fprintf(stderr, "%s command: %s\n",
239 			    res == 0 ? "unknown" : "ambiguous", argv[0]);
240 		}
241 cmdout:
242 		free(orgline);
243 	}
244 	free(argv);
245 
246 	return stop;
247 }
248 
249