nvi: Upgrade from version 1.79 to 2.1.1 (multibyte support)
[dragonfly.git] / contrib / nvi / common / seq.c
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 }