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