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