xref: /openbsd/usr.bin/vi/common/seq.c (revision f6aab3d8)
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  */
35 int
36 seq_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);
97 mem3:		free(qp->name);
98 mem2:		free(qp);
99 mem1:		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  */
129 int
130 seq_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  */
145 int
146 seq_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  */
164 SEQ *
165 seq_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  */
243 void
244 seq_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  */
263 int
264 seq_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  */
308 int
309 seq_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  */
350 int
351 e_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