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