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
seq_set(sp,name,nlen,input,ilen,output,olen,stype,flags)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
seq_delete(sp,input,ilen,stype)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
seq_mdel(qp)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 *
seq_find(sp,lastqp,e_input,c_input,ilen,stype,ispartialp)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
seq_close(gp)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
seq_dump(sp,stype,isname)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
seq_save(sp,fp,prefix,stype)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
e_memcmp(p1,ep,n)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