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