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