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