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