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