1/*-
2 * Copyright (c) 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 *	Keith Bostic.  All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#include <sys/types.h>
13#include <sys/queue.h>
14#include <sys/time.h>
15
16#include <bitstring.h>
17#include <ctype.h>
18#include <errno.h>
19#include <limits.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include "common.h"
25
26/*
27 * seq_set --
28 *	Internal version to enter a sequence.
29 *
30 * PUBLIC: int seq_set(SCR *, CHAR_T *,
31 * PUBLIC:    size_t, CHAR_T *, size_t, CHAR_T *, size_t, seq_t, int);
32 */
33int
34seq_set(SCR *sp, CHAR_T *name, size_t nlen, CHAR_T *input, size_t ilen,
35    CHAR_T *output, size_t olen, seq_t stype, int flags)
36{
37	CHAR_T *p;
38	SEQ *lastqp, *qp;
39	int sv_errno;
40
41	/*
42	 * An input string must always be present.  The output string
43	 * can be NULL, when set internally, that's how we throw away
44	 * input.
45	 *
46	 * Just replace the output field if the string already set.
47	 */
48	if ((qp =
49	    seq_find(sp, &lastqp, NULL, input, ilen, stype, NULL)) != NULL) {
50		if (LF_ISSET(SEQ_NOOVERWRITE))
51			return (0);
52		if (output == NULL || olen == 0) {
53			p = NULL;
54			olen = 0;
55		} else if ((p = v_wstrdup(sp, output, olen)) == NULL) {
56			sv_errno = errno;
57			goto mem1;
58		}
59		free(qp->output);
60		qp->olen = olen;
61		qp->output = p;
62		return (0);
63	}
64
65	/* Allocate and initialize SEQ structure. */
66	CALLOC(sp, qp, 1, sizeof(SEQ));
67	if (qp == NULL) {
68		sv_errno = errno;
69		goto mem1;
70	}
71
72	/* Name. */
73	if (name == NULL || nlen == 0)
74		qp->name = NULL;
75	else if ((qp->name = v_wstrdup(sp, name, nlen)) == NULL) {
76		sv_errno = errno;
77		goto mem2;
78	}
79	qp->nlen = nlen;
80
81	/* Input. */
82	if ((qp->input = v_wstrdup(sp, input, ilen)) == NULL) {
83		sv_errno = errno;
84		goto mem3;
85	}
86	qp->ilen = ilen;
87
88	/* Output. */
89	if (output == NULL) {
90		qp->output = NULL;
91		olen = 0;
92	} else if ((qp->output = v_wstrdup(sp, output, olen)) == NULL) {
93		sv_errno = errno;
94		free(qp->input);
95mem3:		free(qp->name);
96mem2:		free(qp);
97mem1:		errno = sv_errno;
98		msgq(sp, M_SYSERR, NULL);
99		return (1);
100	}
101	qp->olen = olen;
102
103	/* Type, flags. */
104	qp->stype = stype;
105	qp->flags = flags;
106
107	/* Link into the chain. */
108	if (lastqp == NULL) {
109		SLIST_INSERT_HEAD(sp->gp->seqq, qp, q);
110	} else {
111		SLIST_INSERT_AFTER(lastqp, qp, q);
112	}
113
114	/* Set the fast lookup bit. */
115	if ((qp->input[0] & ~MAX_BIT_SEQ) == 0)
116		bit_set(sp->gp->seqb, qp->input[0]);
117
118	return (0);
119}
120
121/*
122 * seq_delete --
123 *	Delete a sequence.
124 *
125 * PUBLIC: int seq_delete(SCR *, CHAR_T *, size_t, seq_t);
126 */
127int
128seq_delete(SCR *sp, CHAR_T *input, size_t ilen, seq_t stype)
129{
130	SEQ *qp, *pre_qp = NULL;
131	int diff;
132
133	SLIST_FOREACH(qp, sp->gp->seqq, q) {
134		if (qp->stype == stype && qp->ilen == ilen) {
135			diff = MEMCMP(qp->input, input, ilen);
136			if (!diff) {
137				if (F_ISSET(qp, SEQ_FUNCMAP))
138					break;
139				if (qp == SLIST_FIRST(sp->gp->seqq))
140					SLIST_REMOVE_HEAD(sp->gp->seqq, q);
141				else
142					SLIST_REMOVE_AFTER(pre_qp, q);
143				return (seq_free(qp));
144			}
145			if (diff > 0)
146				break;
147		}
148		pre_qp = qp;
149	}
150	return (1);
151}
152
153/*
154 * seq_free --
155 *	Free a map entry.
156 *
157 * PUBLIC: int seq_free(SEQ *);
158 */
159int
160seq_free(SEQ *qp)
161{
162	free(qp->name);
163	free(qp->input);
164	free(qp->output);
165	free(qp);
166	return (0);
167}
168
169/*
170 * seq_find --
171 *	Search the sequence list for a match to a buffer, if ispartial
172 *	isn't NULL, partial matches count.
173 *
174 * PUBLIC: SEQ *seq_find
175 * PUBLIC:   (SCR *, SEQ **, EVENT *, CHAR_T *, size_t, seq_t, int *);
176 */
177SEQ *
178seq_find(SCR *sp, SEQ **lastqp, EVENT *e_input, CHAR_T *c_input, size_t ilen,
179    seq_t stype, int *ispartialp)
180{
181	SEQ *lqp = NULL, *qp;
182	int diff;
183
184	/*
185	 * Ispartialp is a location where we return if there was a
186	 * partial match, i.e. if the string were extended it might
187	 * match something.
188	 *
189	 * XXX
190	 * Overload the meaning of ispartialp; only the terminal key
191	 * search doesn't want the search limited to complete matches,
192	 * i.e. ilen may be longer than the match.
193	 */
194	if (ispartialp != NULL)
195		*ispartialp = 0;
196	for (qp = SLIST_FIRST(sp->gp->seqq); qp != NULL;
197	    lqp = qp, qp = SLIST_NEXT(qp, q)) {
198		/*
199		 * Fast checks on the first character and type, and then
200		 * a real comparison.
201		 */
202		if (e_input == NULL) {
203			if (qp->input[0] > c_input[0])
204				break;
205			if (qp->input[0] < c_input[0] ||
206			    qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
207				continue;
208			diff = MEMCMP(qp->input, c_input, MIN(qp->ilen, ilen));
209		} else {
210			if (qp->input[0] > e_input->e_c)
211				break;
212			if (qp->input[0] < e_input->e_c ||
213			    qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
214				continue;
215			diff =
216			    e_memcmp(qp->input, e_input, MIN(qp->ilen, ilen));
217		}
218		if (diff > 0)
219			break;
220		if (diff < 0)
221			continue;
222		/*
223		 * If the entry is the same length as the string, return a
224		 * match.  If the entry is shorter than the string, return a
225		 * match if called from the terminal key routine.  Otherwise,
226		 * keep searching for a complete match.
227		 */
228		if (qp->ilen <= ilen) {
229			if (qp->ilen == ilen || ispartialp != NULL) {
230				if (lastqp != NULL)
231					*lastqp = lqp;
232				return (qp);
233			}
234			continue;
235		}
236		/*
237		 * If the entry longer than the string, return partial match
238		 * if called from the terminal key routine.  Otherwise, no
239		 * match.
240		 */
241		if (ispartialp != NULL)
242			*ispartialp = 1;
243		break;
244	}
245	if (lastqp != NULL)
246		*lastqp = lqp;
247	return (NULL);
248}
249
250/*
251 * seq_close --
252 *	Discard all sequences.
253 *
254 * PUBLIC: void seq_close(GS *);
255 */
256void
257seq_close(GS *gp)
258{
259	SEQ *qp;
260
261	while ((qp = SLIST_FIRST(gp->seqq)) != NULL) {
262		SLIST_REMOVE_HEAD(gp->seqq, q);
263		(void)seq_free(qp);
264	}
265}
266
267/*
268 * seq_dump --
269 *	Display the sequence entries of a specified type.
270 *
271 * PUBLIC: int seq_dump(SCR *, seq_t, int);
272 */
273int
274seq_dump(SCR *sp, seq_t stype, int isname)
275{
276	CHAR_T *p;
277	GS *gp;
278	SEQ *qp;
279	int cnt, len, olen;
280
281	cnt = 0;
282	gp = sp->gp;
283	SLIST_FOREACH(qp, sp->gp->seqq, q) {
284		if (stype != qp->stype || F_ISSET(qp, SEQ_FUNCMAP))
285			continue;
286		++cnt;
287		for (p = qp->input,
288		    olen = qp->ilen, len = 0; olen > 0; --olen, ++p)
289			len += ex_puts(sp, KEY_NAME(sp, *p));
290		for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
291			len -= ex_puts(sp, " ");
292
293		if (qp->output != NULL)
294			for (p = qp->output,
295			    olen = qp->olen, len = 0; olen > 0; --olen, ++p)
296				len += ex_puts(sp, KEY_NAME(sp, *p));
297		else
298			len = 0;
299
300		if (isname && qp->name != NULL) {
301			for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
302				len -= ex_puts(sp, " ");
303			for (p = qp->name,
304			    olen = qp->nlen; olen > 0; --olen, ++p)
305				(void)ex_puts(sp, KEY_NAME(sp, *p));
306		}
307		(void)ex_puts(sp, "\n");
308	}
309	return (cnt);
310}
311
312/*
313 * seq_save --
314 *	Save the sequence entries to a file.
315 *
316 * PUBLIC: int seq_save(SCR *, FILE *, char *, seq_t);
317 */
318int
319seq_save(SCR *sp, FILE *fp, char *prefix, seq_t stype)
320{
321	CHAR_T *p;
322	SEQ *qp;
323	size_t olen;
324	int ch;
325
326	/* Write a sequence command for all keys the user defined. */
327	SLIST_FOREACH(qp, sp->gp->seqq, q) {
328		if (stype != qp->stype || !F_ISSET(qp, SEQ_USERDEF))
329			continue;
330		if (prefix)
331			(void)fprintf(fp, "%s", prefix);
332		for (p = qp->input, olen = qp->ilen; olen > 0; --olen) {
333			ch = *p++;
334			if (ch == CH_LITERAL || ch == '|' ||
335			    cmdskip(ch) || KEY_VAL(sp, ch) == K_NL)
336				(void)putc(CH_LITERAL, fp);
337			(void)putc(ch, fp);
338		}
339		(void)putc(' ', fp);
340		if (qp->output != NULL)
341			for (p = qp->output,
342			    olen = qp->olen; olen > 0; --olen) {
343				ch = *p++;
344				if (ch == CH_LITERAL || ch == '|' ||
345				    KEY_VAL(sp, ch) == K_NL)
346					(void)putc(CH_LITERAL, fp);
347				(void)putc(ch, fp);
348			}
349		(void)putc('\n', fp);
350	}
351	return (0);
352}
353
354/*
355 * e_memcmp --
356 *	Compare a string of EVENT's to a string of CHAR_T's.
357 *
358 * PUBLIC: int e_memcmp(CHAR_T *, EVENT *, size_t);
359 */
360int
361e_memcmp(CHAR_T *p1, EVENT *ep, size_t n)
362{
363	if (n != 0) {
364		do {
365			if (*p1++ != ep->e_c)
366				return (*--p1 - ep->e_c);
367			++ep;
368		} while (--n != 0);
369	}
370	return (0);
371}
372