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
48static void
49cmdbuf_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
56static void
57mdb_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
74void
75mdb_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
100void
101mdb_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
112int
113mdb_cmdbuf_caninsert(mdb_cmdbuf_t *cmd, size_t nbytes)
114{
115	return (cmd->cmd_buflen + nbytes < cmd->cmd_linelen);
116}
117
118int
119mdb_cmdbuf_atstart(mdb_cmdbuf_t *cmd)
120{
121	return (cmd->cmd_bufidx == 0);
122}
123
124int
125mdb_cmdbuf_atend(mdb_cmdbuf_t *cmd)
126{
127	return (cmd->cmd_bufidx == cmd->cmd_buflen);
128}
129
130int
131mdb_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
166const char *
167mdb_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*/
214int
215mdb_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*/
234int
235mdb_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*/
251int
252mdb_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*/
263int
264mdb_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
274int
275mdb_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*/
297int
298mdb_cmdbuf_home(mdb_cmdbuf_t *cmd, int c)
299{
300	cmd->cmd_bufidx = 0;
301	return (0);
302}
303
304/*ARGSUSED*/
305int
306mdb_cmdbuf_end(mdb_cmdbuf_t *cmd, int c)
307{
308	cmd->cmd_bufidx = cmd->cmd_buflen;
309	return (0);
310}
311
312static size_t
313fwdword_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*/
334int
335mdb_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*/
346int
347mdb_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
364static size_t
365backword_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*/
388int
389mdb_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*/
400int
401mdb_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*/
420int
421mdb_cmdbuf_kill(mdb_cmdbuf_t *cmd, int c)
422{
423	cmd->cmd_buflen = cmd->cmd_bufidx;
424	return (0);
425}
426
427/*ARGSUSED*/
428int
429mdb_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*/
437int
438mdb_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*/
460int
461mdb_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*/
483int
484mdb_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