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
13static 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 */
37int
38seq_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);
108mem3:		if (qp->name != NULL)
109			free(qp->name);
110mem2:		free(qp);
111mem1:		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 */
141int
142seq_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 */
177int
178seq_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 */
198SEQ *
199seq_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 */
283void
284seq_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 */
300int
301seq_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 */
348int
349seq_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 */
394int
395e_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