1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * The MDB command buffer is a simple structure that keeps track of the
31  * command history list, and provides operations to manipulate the current
32  * buffer according to the various emacs editing options.  The terminal
33  * code uses this code to keep track of the actual contents of the command
34  * line, and then uses this content to perform redraw operations.
35  */
36 
37 #include <strings.h>
38 #include <stdio.h>
39 #include <ctype.h>
40 
41 #include <mdb/mdb_modapi.h>
42 #include <mdb/mdb_cmdbuf.h>
43 #include <mdb/mdb_debug.h>
44 #include <mdb/mdb.h>
45 
46 #define	CMDBUF_LINELEN	BUFSIZ		/* Length of each buffer line */
47 #define	CMDBUF_TABLEN	8		/* Length of a tab in spaces */
48 
49 static void
50 cmdbuf_shiftr(mdb_cmdbuf_t *cmd, size_t nbytes)
51 {
52 	bcopy(&cmd->cmd_buf[cmd->cmd_bufidx],
53 	    &cmd->cmd_buf[cmd->cmd_bufidx + nbytes],
54 	    cmd->cmd_buflen - cmd->cmd_bufidx);
55 }
56 
57 void
58 mdb_cmdbuf_create(mdb_cmdbuf_t *cmd)
59 {
60 	size_t i;
61 
62 	/*
63 	 * This is pretty weak, but good enough for the moment: just allocate
64 	 * BUFSIZ-sized chunks in advance for every history element.  Later
65 	 * it would be nice to replace this with either code that allocates
66 	 * space for the history list on-the-fly so as not to waste so much
67 	 * memory, or that keeps a mapped history file like the shell.
68 	 */
69 	cmd->cmd_history = mdb_alloc(mdb.m_histlen * sizeof (char *), UM_SLEEP);
70 	cmd->cmd_linebuf = mdb_alloc(CMDBUF_LINELEN, UM_SLEEP);
71 
72 	for (i = 0; i < mdb.m_histlen; i++)
73 		cmd->cmd_history[i] = mdb_alloc(CMDBUF_LINELEN, UM_SLEEP);
74 
75 	cmd->cmd_buf = cmd->cmd_history[0];
76 	cmd->cmd_linelen = CMDBUF_LINELEN;
77 	cmd->cmd_histlen = mdb.m_histlen;
78 	cmd->cmd_buflen = 0;
79 	cmd->cmd_bufidx = 0;
80 	cmd->cmd_hold = 0;
81 	cmd->cmd_hnew = 0;
82 	cmd->cmd_hcur = 0;
83 	cmd->cmd_hlen = 0;
84 }
85 
86 void
87 mdb_cmdbuf_destroy(mdb_cmdbuf_t *cmd)
88 {
89 	size_t i;
90 
91 	for (i = 0; i < cmd->cmd_histlen; i++)
92 		mdb_free(cmd->cmd_history[i], CMDBUF_LINELEN);
93 
94 	mdb_free(cmd->cmd_linebuf, CMDBUF_LINELEN);
95 	mdb_free(cmd->cmd_history, cmd->cmd_histlen * sizeof (char *));
96 }
97 
98 int
99 mdb_cmdbuf_caninsert(mdb_cmdbuf_t *cmd, size_t nbytes)
100 {
101 	return (cmd->cmd_buflen + nbytes < cmd->cmd_linelen);
102 }
103 
104 int
105 mdb_cmdbuf_atstart(mdb_cmdbuf_t *cmd)
106 {
107 	return (cmd->cmd_bufidx == 0);
108 }
109 
110 int
111 mdb_cmdbuf_atend(mdb_cmdbuf_t *cmd)
112 {
113 	return (cmd->cmd_bufidx == cmd->cmd_buflen);
114 }
115 
116 int
117 mdb_cmdbuf_insert(mdb_cmdbuf_t *cmd, int c)
118 {
119 	if (c == '\t') {
120 		if (cmd->cmd_buflen + CMDBUF_TABLEN < cmd->cmd_linelen) {
121 			int i;
122 
123 			if (cmd->cmd_buflen != cmd->cmd_bufidx)
124 				cmdbuf_shiftr(cmd, CMDBUF_TABLEN);
125 
126 			for (i = 0; i < CMDBUF_TABLEN; i++)
127 				cmd->cmd_buf[cmd->cmd_bufidx++] = ' ';
128 
129 			cmd->cmd_buflen += CMDBUF_TABLEN;
130 			return (0);
131 		}
132 
133 		return (-1);
134 	}
135 
136 	if (c < ' ' || c > '~')
137 		return (-1);
138 
139 	if (cmd->cmd_buflen < cmd->cmd_linelen) {
140 		if (cmd->cmd_buflen != cmd->cmd_bufidx)
141 			cmdbuf_shiftr(cmd, 1);
142 
143 		cmd->cmd_buf[cmd->cmd_bufidx++] = (char)c;
144 		cmd->cmd_buflen++;
145 
146 		return (0);
147 	}
148 
149 	return (-1);
150 }
151 
152 const char *
153 mdb_cmdbuf_accept(mdb_cmdbuf_t *cmd)
154 {
155 	if (cmd->cmd_bufidx < cmd->cmd_linelen) {
156 		cmd->cmd_buf[cmd->cmd_buflen++] = '\0';
157 		(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
158 
159 		/*
160 		 * Don't bother inserting empty buffers into the history ring.
161 		 */
162 		if (cmd->cmd_buflen > 1) {
163 			cmd->cmd_hnew = (cmd->cmd_hnew + 1) % cmd->cmd_histlen;
164 			cmd->cmd_buf = cmd->cmd_history[cmd->cmd_hnew];
165 			cmd->cmd_hcur = cmd->cmd_hnew;
166 
167 			if (cmd->cmd_hlen + 1 == cmd->cmd_histlen)
168 				cmd->cmd_hold =
169 				    (cmd->cmd_hold + 1) % cmd->cmd_histlen;
170 			else
171 				cmd->cmd_hlen++;
172 		}
173 
174 		cmd->cmd_bufidx = 0;
175 		cmd->cmd_buflen = 0;
176 
177 		return ((const char *)cmd->cmd_linebuf);
178 	}
179 
180 	return (NULL);
181 }
182 
183 /*ARGSUSED*/
184 int
185 mdb_cmdbuf_backspace(mdb_cmdbuf_t *cmd, int c)
186 {
187 	if (cmd->cmd_bufidx > 0) {
188 		if (cmd->cmd_buflen != cmd->cmd_bufidx) {
189 			bcopy(&cmd->cmd_buf[cmd->cmd_bufidx],
190 			    &cmd->cmd_buf[cmd->cmd_bufidx - 1],
191 			    cmd->cmd_buflen - cmd->cmd_bufidx);
192 		}
193 
194 		cmd->cmd_bufidx--;
195 		cmd->cmd_buflen--;
196 
197 		return (0);
198 	}
199 
200 	return (-1);
201 }
202 
203 /*ARGSUSED*/
204 int
205 mdb_cmdbuf_delchar(mdb_cmdbuf_t *cmd, int c)
206 {
207 	if (cmd->cmd_bufidx < cmd->cmd_buflen) {
208 		if (cmd->cmd_bufidx < --cmd->cmd_buflen) {
209 			bcopy(&cmd->cmd_buf[cmd->cmd_bufidx + 1],
210 			    &cmd->cmd_buf[cmd->cmd_bufidx],
211 			    cmd->cmd_buflen - cmd->cmd_bufidx);
212 		}
213 
214 		return (0);
215 	}
216 
217 	return (-1);
218 }
219 
220 /*ARGSUSED*/
221 int
222 mdb_cmdbuf_fwdchar(mdb_cmdbuf_t *cmd, int c)
223 {
224 	if (cmd->cmd_bufidx < cmd->cmd_buflen) {
225 		cmd->cmd_bufidx++;
226 		return (0);
227 	}
228 
229 	return (-1);
230 }
231 
232 /*ARGSUSED*/
233 int
234 mdb_cmdbuf_backchar(mdb_cmdbuf_t *cmd, int c)
235 {
236 	if (cmd->cmd_bufidx > 0) {
237 		cmd->cmd_bufidx--;
238 		return (0);
239 	}
240 
241 	return (-1);
242 }
243 
244 int
245 mdb_cmdbuf_transpose(mdb_cmdbuf_t *cmd, int c)
246 {
247 	if (cmd->cmd_bufidx > 0 && cmd->cmd_buflen > 1) {
248 		c = cmd->cmd_buf[cmd->cmd_bufidx - 1];
249 
250 		if (cmd->cmd_bufidx == cmd->cmd_buflen) {
251 			cmd->cmd_buf[cmd->cmd_bufidx - 1] =
252 			    cmd->cmd_buf[cmd->cmd_bufidx - 2];
253 			cmd->cmd_buf[cmd->cmd_bufidx - 2] = (char)c;
254 		} else {
255 			cmd->cmd_buf[cmd->cmd_bufidx - 1] =
256 			    cmd->cmd_buf[cmd->cmd_bufidx];
257 			cmd->cmd_buf[cmd->cmd_bufidx++] = (char)c;
258 		}
259 
260 		return (0);
261 	}
262 
263 	return (-1);
264 }
265 
266 /*ARGSUSED*/
267 int
268 mdb_cmdbuf_home(mdb_cmdbuf_t *cmd, int c)
269 {
270 	cmd->cmd_bufidx = 0;
271 	return (0);
272 }
273 
274 /*ARGSUSED*/
275 int
276 mdb_cmdbuf_end(mdb_cmdbuf_t *cmd, int c)
277 {
278 	cmd->cmd_bufidx = cmd->cmd_buflen;
279 	return (0);
280 }
281 
282 static size_t
283 fwdword_index(mdb_cmdbuf_t *cmd)
284 {
285 	size_t i = cmd->cmd_bufidx + 1;
286 
287 	ASSERT(cmd->cmd_bufidx < cmd->cmd_buflen);
288 
289 	while (i < cmd->cmd_buflen && isspace(cmd->cmd_buf[i]))
290 		i++;
291 
292 	while (i < cmd->cmd_buflen && !isspace(cmd->cmd_buf[i]) &&
293 	    !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
294 		i++;
295 
296 	while (i < cmd->cmd_buflen &&
297 	    (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
298 		i++;
299 
300 	return (i);
301 }
302 
303 /*ARGSUSED*/
304 int
305 mdb_cmdbuf_fwdword(mdb_cmdbuf_t *cmd, int c)
306 {
307 	if (cmd->cmd_bufidx == cmd->cmd_buflen)
308 		return (-1);
309 
310 	cmd->cmd_bufidx = fwdword_index(cmd);
311 
312 	return (0);
313 }
314 
315 /*ARGSUSED*/
316 int
317 mdb_cmdbuf_killfwdword(mdb_cmdbuf_t *cmd, int c)
318 {
319 	size_t i;
320 
321 	if (cmd->cmd_bufidx == cmd->cmd_buflen)
322 		return (-1);
323 
324 	i = fwdword_index(cmd);
325 
326 	bcopy(&cmd->cmd_buf[i], &cmd->cmd_buf[cmd->cmd_bufidx],
327 	    cmd->cmd_buflen - i);
328 
329 	cmd->cmd_buflen -= i - cmd->cmd_bufidx;
330 
331 	return (0);
332 }
333 
334 static size_t
335 backword_index(mdb_cmdbuf_t *cmd)
336 {
337 	size_t i = cmd->cmd_bufidx - 1;
338 
339 	ASSERT(cmd->cmd_bufidx != 0);
340 
341 	while (i != 0 && isspace(cmd->cmd_buf[i]))
342 		i--;
343 
344 	while (i != 0 && !isspace(cmd->cmd_buf[i]) &&
345 	    !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
346 		i--;
347 
348 	while (i != 0 && (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
349 		i--;
350 
351 	if (i != 0)
352 		i++;
353 
354 	return (i);
355 }
356 
357 /*ARGSUSED*/
358 int
359 mdb_cmdbuf_backword(mdb_cmdbuf_t *cmd, int c)
360 {
361 	if (cmd->cmd_bufidx == 0)
362 		return (-1);
363 
364 	cmd->cmd_bufidx = backword_index(cmd);
365 
366 	return (0);
367 }
368 
369 /*ARGSUSED*/
370 int
371 mdb_cmdbuf_killbackword(mdb_cmdbuf_t *cmd, int c)
372 {
373 	size_t i;
374 
375 	if (cmd->cmd_bufidx == 0)
376 		return (-1);
377 
378 	i = backword_index(cmd);
379 
380 	bcopy(&cmd->cmd_buf[cmd->cmd_bufidx], &cmd->cmd_buf[i],
381 	    cmd->cmd_buflen - cmd->cmd_bufidx);
382 
383 	cmd->cmd_buflen -= cmd->cmd_bufidx - i;
384 	cmd->cmd_bufidx = i;
385 
386 	return (0);
387 }
388 
389 /*ARGSUSED*/
390 int
391 mdb_cmdbuf_kill(mdb_cmdbuf_t *cmd, int c)
392 {
393 	cmd->cmd_buflen = cmd->cmd_bufidx;
394 	return (0);
395 }
396 
397 /*ARGSUSED*/
398 int
399 mdb_cmdbuf_reset(mdb_cmdbuf_t *cmd, int c)
400 {
401 	cmd->cmd_buflen = 0;
402 	cmd->cmd_bufidx = 0;
403 	return (0);
404 }
405 
406 /*ARGSUSED*/
407 int
408 mdb_cmdbuf_prevhist(mdb_cmdbuf_t *cmd, int c)
409 {
410 	if (cmd->cmd_hcur != cmd->cmd_hold) {
411 		if (cmd->cmd_hcur-- == cmd->cmd_hnew) {
412 			cmd->cmd_buf[cmd->cmd_buflen] = 0;
413 			(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
414 		}
415 
416 		if (cmd->cmd_hcur < 0)
417 			cmd->cmd_hcur = cmd->cmd_histlen - 1;
418 
419 		(void) strcpy(cmd->cmd_buf, cmd->cmd_history[cmd->cmd_hcur]);
420 		cmd->cmd_bufidx = strlen(cmd->cmd_buf);
421 		cmd->cmd_buflen = cmd->cmd_bufidx;
422 
423 		return (0);
424 	}
425 
426 	return (-1);
427 }
428 
429 /*ARGSUSED*/
430 int
431 mdb_cmdbuf_nexthist(mdb_cmdbuf_t *cmd, int c)
432 {
433 	if (cmd->cmd_hcur != cmd->cmd_hnew) {
434 		cmd->cmd_hcur = (cmd->cmd_hcur + 1) % cmd->cmd_histlen;
435 
436 		if (cmd->cmd_hcur == cmd->cmd_hnew) {
437 			(void) strcpy(cmd->cmd_buf, cmd->cmd_linebuf);
438 		} else {
439 			(void) strcpy(cmd->cmd_buf,
440 			    cmd->cmd_history[cmd->cmd_hcur]);
441 		}
442 
443 		cmd->cmd_bufidx = strlen(cmd->cmd_buf);
444 		cmd->cmd_buflen = cmd->cmd_bufidx;
445 
446 		return (0);
447 	}
448 
449 	return (-1);
450 }
451 
452 /*ARGSUSED*/
453 int
454 mdb_cmdbuf_findhist(mdb_cmdbuf_t *cmd, int c)
455 {
456 	ssize_t i, n;
457 
458 	if (cmd->cmd_buflen != 0) {
459 		cmd->cmd_hcur = cmd->cmd_hnew;
460 		cmd->cmd_buf[cmd->cmd_buflen] = 0;
461 		(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
462 	}
463 
464 	for (i = cmd->cmd_hcur, n = 0; n < cmd->cmd_hlen; n++) {
465 		if (--i < 0)
466 			i = cmd->cmd_histlen - 1;
467 
468 		if (strstr(cmd->cmd_history[i], cmd->cmd_linebuf) != NULL) {
469 			(void) strcpy(cmd->cmd_buf, cmd->cmd_history[i]);
470 			cmd->cmd_bufidx = strlen(cmd->cmd_buf);
471 			cmd->cmd_buflen = cmd->cmd_bufidx;
472 			cmd->cmd_hcur = i;
473 
474 			return (0);
475 		}
476 	}
477 
478 	cmd->cmd_hcur = cmd->cmd_hnew;
479 
480 	cmd->cmd_bufidx = 0;
481 	cmd->cmd_buflen = 0;
482 
483 	return (-1);
484 }
485