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