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 #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 		int is_repeating = 0;
171 
172 		cmd->cmd_buf[cmd->cmd_buflen++] = '\0';
173 		(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
174 
175 		if (cmd->cmd_hold != cmd->cmd_hnew) {
176 			int lastidx = cmd->cmd_hnew == 0 ? cmd->cmd_halloc - 1 :
177 			    cmd->cmd_hnew - 1;
178 
179 			is_repeating = strcmp(cmd->cmd_buf,
180 			    cmd->cmd_history[lastidx]) == 0;
181 		}
182 
183 		/*
184 		 * Don't bother inserting empty or repeating buffers into the
185 		 * history ring.
186 		 */
187 		if (cmd->cmd_buflen > 1 && !is_repeating) {
188 			cmd->cmd_hnew = (cmd->cmd_hnew + 1) % cmd->cmd_histlen;
189 			if (cmd->cmd_hnew >= cmd->cmd_halloc)
190 				mdb_cmdbuf_allocchunk(cmd);
191 
192 			cmd->cmd_buf = cmd->cmd_history[cmd->cmd_hnew];
193 			cmd->cmd_hcur = cmd->cmd_hnew;
194 
195 			if (cmd->cmd_hlen + 1 == cmd->cmd_histlen)
196 				cmd->cmd_hold =
197 				    (cmd->cmd_hold + 1) % cmd->cmd_histlen;
198 			else
199 				cmd->cmd_hlen++;
200 		} else if (is_repeating) {
201 			cmd->cmd_hcur = cmd->cmd_hnew;
202 		}
203 
204 		cmd->cmd_bufidx = 0;
205 		cmd->cmd_buflen = 0;
206 
207 		return ((const char *)cmd->cmd_linebuf);
208 	}
209 
210 	return (NULL);
211 }
212 
213 /*ARGSUSED*/
214 int
215 mdb_cmdbuf_backspace(mdb_cmdbuf_t *cmd, int c)
216 {
217 	if (cmd->cmd_bufidx > 0) {
218 		if (cmd->cmd_buflen != cmd->cmd_bufidx) {
219 			bcopy(&cmd->cmd_buf[cmd->cmd_bufidx],
220 			    &cmd->cmd_buf[cmd->cmd_bufidx - 1],
221 			    cmd->cmd_buflen - cmd->cmd_bufidx);
222 		}
223 
224 		cmd->cmd_bufidx--;
225 		cmd->cmd_buflen--;
226 
227 		return (0);
228 	}
229 
230 	return (-1);
231 }
232 
233 /*ARGSUSED*/
234 int
235 mdb_cmdbuf_delchar(mdb_cmdbuf_t *cmd, int c)
236 {
237 	if (cmd->cmd_bufidx < cmd->cmd_buflen) {
238 		if (cmd->cmd_bufidx < --cmd->cmd_buflen) {
239 			bcopy(&cmd->cmd_buf[cmd->cmd_bufidx + 1],
240 			    &cmd->cmd_buf[cmd->cmd_bufidx],
241 			    cmd->cmd_buflen - cmd->cmd_bufidx);
242 		}
243 
244 		return (0);
245 	}
246 
247 	return (-1);
248 }
249 
250 /*ARGSUSED*/
251 int
252 mdb_cmdbuf_fwdchar(mdb_cmdbuf_t *cmd, int c)
253 {
254 	if (cmd->cmd_bufidx < cmd->cmd_buflen) {
255 		cmd->cmd_bufidx++;
256 		return (0);
257 	}
258 
259 	return (-1);
260 }
261 
262 /*ARGSUSED*/
263 int
264 mdb_cmdbuf_backchar(mdb_cmdbuf_t *cmd, int c)
265 {
266 	if (cmd->cmd_bufidx > 0) {
267 		cmd->cmd_bufidx--;
268 		return (0);
269 	}
270 
271 	return (-1);
272 }
273 
274 int
275 mdb_cmdbuf_transpose(mdb_cmdbuf_t *cmd, int c)
276 {
277 	if (cmd->cmd_bufidx > 0 && cmd->cmd_buflen > 1) {
278 		c = cmd->cmd_buf[cmd->cmd_bufidx - 1];
279 
280 		if (cmd->cmd_bufidx == cmd->cmd_buflen) {
281 			cmd->cmd_buf[cmd->cmd_bufidx - 1] =
282 			    cmd->cmd_buf[cmd->cmd_bufidx - 2];
283 			cmd->cmd_buf[cmd->cmd_bufidx - 2] = (char)c;
284 		} else {
285 			cmd->cmd_buf[cmd->cmd_bufidx - 1] =
286 			    cmd->cmd_buf[cmd->cmd_bufidx];
287 			cmd->cmd_buf[cmd->cmd_bufidx++] = (char)c;
288 		}
289 
290 		return (0);
291 	}
292 
293 	return (-1);
294 }
295 
296 /*ARGSUSED*/
297 int
298 mdb_cmdbuf_home(mdb_cmdbuf_t *cmd, int c)
299 {
300 	cmd->cmd_bufidx = 0;
301 	return (0);
302 }
303 
304 /*ARGSUSED*/
305 int
306 mdb_cmdbuf_end(mdb_cmdbuf_t *cmd, int c)
307 {
308 	cmd->cmd_bufidx = cmd->cmd_buflen;
309 	return (0);
310 }
311 
312 static size_t
313 fwdword_index(mdb_cmdbuf_t *cmd)
314 {
315 	size_t i = cmd->cmd_bufidx + 1;
316 
317 	ASSERT(cmd->cmd_bufidx < cmd->cmd_buflen);
318 
319 	while (i < cmd->cmd_buflen && isspace(cmd->cmd_buf[i]))
320 		i++;
321 
322 	while (i < cmd->cmd_buflen && !isspace(cmd->cmd_buf[i]) &&
323 	    !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
324 		i++;
325 
326 	while (i < cmd->cmd_buflen &&
327 	    (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
328 		i++;
329 
330 	return (i);
331 }
332 
333 /*ARGSUSED*/
334 int
335 mdb_cmdbuf_fwdword(mdb_cmdbuf_t *cmd, int c)
336 {
337 	if (cmd->cmd_bufidx == cmd->cmd_buflen)
338 		return (-1);
339 
340 	cmd->cmd_bufidx = fwdword_index(cmd);
341 
342 	return (0);
343 }
344 
345 /*ARGSUSED*/
346 int
347 mdb_cmdbuf_killfwdword(mdb_cmdbuf_t *cmd, int c)
348 {
349 	size_t i;
350 
351 	if (cmd->cmd_bufidx == cmd->cmd_buflen)
352 		return (-1);
353 
354 	i = fwdword_index(cmd);
355 
356 	bcopy(&cmd->cmd_buf[i], &cmd->cmd_buf[cmd->cmd_bufidx],
357 	    cmd->cmd_buflen - i);
358 
359 	cmd->cmd_buflen -= i - cmd->cmd_bufidx;
360 
361 	return (0);
362 }
363 
364 static size_t
365 backword_index(mdb_cmdbuf_t *cmd)
366 {
367 	size_t i = cmd->cmd_bufidx - 1;
368 
369 	ASSERT(cmd->cmd_bufidx != 0);
370 
371 	while (i != 0 && isspace(cmd->cmd_buf[i]))
372 		i--;
373 
374 	while (i != 0 && !isspace(cmd->cmd_buf[i]) &&
375 	    !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
376 		i--;
377 
378 	while (i != 0 && (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
379 		i--;
380 
381 	if (i != 0)
382 		i++;
383 
384 	return (i);
385 }
386 
387 /*ARGSUSED*/
388 int
389 mdb_cmdbuf_backword(mdb_cmdbuf_t *cmd, int c)
390 {
391 	if (cmd->cmd_bufidx == 0)
392 		return (-1);
393 
394 	cmd->cmd_bufidx = backword_index(cmd);
395 
396 	return (0);
397 }
398 
399 /*ARGSUSED*/
400 int
401 mdb_cmdbuf_killbackword(mdb_cmdbuf_t *cmd, int c)
402 {
403 	size_t i;
404 
405 	if (cmd->cmd_bufidx == 0)
406 		return (-1);
407 
408 	i = backword_index(cmd);
409 
410 	bcopy(&cmd->cmd_buf[cmd->cmd_bufidx], &cmd->cmd_buf[i],
411 	    cmd->cmd_buflen - cmd->cmd_bufidx);
412 
413 	cmd->cmd_buflen -= cmd->cmd_bufidx - i;
414 	cmd->cmd_bufidx = i;
415 
416 	return (0);
417 }
418 
419 /*ARGSUSED*/
420 int
421 mdb_cmdbuf_kill(mdb_cmdbuf_t *cmd, int c)
422 {
423 	cmd->cmd_buflen = cmd->cmd_bufidx;
424 	return (0);
425 }
426 
427 /*ARGSUSED*/
428 int
429 mdb_cmdbuf_reset(mdb_cmdbuf_t *cmd, int c)
430 {
431 	cmd->cmd_buflen = 0;
432 	cmd->cmd_bufidx = 0;
433 	return (0);
434 }
435 
436 /*ARGSUSED*/
437 int
438 mdb_cmdbuf_prevhist(mdb_cmdbuf_t *cmd, int c)
439 {
440 	if (cmd->cmd_hcur != cmd->cmd_hold) {
441 		if (cmd->cmd_hcur-- == cmd->cmd_hnew) {
442 			cmd->cmd_buf[cmd->cmd_buflen] = 0;
443 			(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
444 		}
445 
446 		if (cmd->cmd_hcur < 0)
447 			cmd->cmd_hcur = cmd->cmd_halloc - 1;
448 
449 		(void) strcpy(cmd->cmd_buf, cmd->cmd_history[cmd->cmd_hcur]);
450 		cmd->cmd_bufidx = strlen(cmd->cmd_buf);
451 		cmd->cmd_buflen = cmd->cmd_bufidx;
452 
453 		return (0);
454 	}
455 
456 	return (-1);
457 }
458 
459 /*ARGSUSED*/
460 int
461 mdb_cmdbuf_nexthist(mdb_cmdbuf_t *cmd, int c)
462 {
463 	if (cmd->cmd_hcur != cmd->cmd_hnew) {
464 		cmd->cmd_hcur = (cmd->cmd_hcur + 1) % cmd->cmd_halloc;
465 
466 		if (cmd->cmd_hcur == cmd->cmd_hnew) {
467 			(void) strcpy(cmd->cmd_buf, cmd->cmd_linebuf);
468 		} else {
469 			(void) strcpy(cmd->cmd_buf,
470 			    cmd->cmd_history[cmd->cmd_hcur]);
471 		}
472 
473 		cmd->cmd_bufidx = strlen(cmd->cmd_buf);
474 		cmd->cmd_buflen = cmd->cmd_bufidx;
475 
476 		return (0);
477 	}
478 
479 	return (-1);
480 }
481 
482 /*ARGSUSED*/
483 int
484 mdb_cmdbuf_findhist(mdb_cmdbuf_t *cmd, int c)
485 {
486 	ssize_t i, n;
487 
488 	if (cmd->cmd_buflen != 0) {
489 		cmd->cmd_hcur = cmd->cmd_hnew;
490 		cmd->cmd_buf[cmd->cmd_buflen] = 0;
491 		(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
492 	}
493 
494 	for (i = cmd->cmd_hcur, n = 0; n < cmd->cmd_hlen; n++) {
495 		if (--i < 0)
496 			i = cmd->cmd_halloc - 1;
497 
498 		if (strstr(cmd->cmd_history[i], cmd->cmd_linebuf) != NULL) {
499 			(void) strcpy(cmd->cmd_buf, cmd->cmd_history[i]);
500 			cmd->cmd_bufidx = strlen(cmd->cmd_buf);
501 			cmd->cmd_buflen = cmd->cmd_bufidx;
502 			cmd->cmd_hcur = i;
503 
504 			return (0);
505 		}
506 	}
507 
508 	cmd->cmd_hcur = cmd->cmd_hnew;
509 
510 	cmd->cmd_bufidx = 0;
511 	cmd->cmd_buflen = 0;
512 
513 	return (-1);
514 }
515