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