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