pkg_deps.c
1 /*- 2 * Copyright (c) 2015-2017, Vsevolod Stakhov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #ifdef HAVE_CONFIG_H 26 #include "pkg_config.h" 27 #endif 28 29 #include "bsd_compat.h" 30 31 #include <stddef.h> 32 #include <ctype.h> 33 #include <assert.h> 34 #include <string.h> 35 #include <stdlib.h> 36 37 #include "pkg.h" 38 #include "private/event.h" 39 #include "private/pkg_deps.h" 40 #include "xmalloc.h" 41 #include "utlist.h" 42 43 struct pkg_dep_formula * 44 pkg_deps_parse_formula(const char *in) 45 { 46 struct pkg_dep_formula *res = NULL, *cur = NULL; 47 struct pkg_dep_formula_item *cur_item = NULL; 48 struct pkg_dep_version_item *cur_ver = NULL; 49 struct pkg_dep_option_item *cur_opt = NULL; 50 const char *p, *c, *end; 51 enum pkg_dep_version_op cur_op = VERSION_ANY; 52 enum { 53 st_parse_dep_name = 0, 54 st_parse_after_name, 55 st_parse_ver_op, 56 st_parse_after_op, 57 st_parse_version_number, 58 st_parse_after_version, 59 st_parse_option_start, 60 st_parse_option, 61 st_parse_after_option, 62 st_parse_comma, 63 st_parse_or, 64 st_skip_spaces, 65 st_error 66 } state = 0, next_state = 0; 67 68 c = in; 69 p = in; 70 71 end = p + strlen(p); 72 73 while (p <= end) { 74 switch (state) { 75 case st_parse_dep_name: 76 if (isspace(*p) || *p == '\0') { 77 state = st_skip_spaces; 78 79 if (p == c) { 80 /* Spaces at the beginning */ 81 next_state = st_parse_dep_name; 82 } 83 else { 84 /* Spaces after the name */ 85 cur_item = xcalloc(1, sizeof(*cur_item)); 86 cur_item->name = xmalloc(p - c + 1); 87 strlcpy(cur_item->name, c, p - c + 1); 88 next_state = st_parse_after_name; 89 } 90 } 91 else if (*p == ',') { 92 if (p == c) { 93 state = st_error; 94 } 95 else { 96 cur_item = xcalloc(1, sizeof(*cur_item)); 97 cur_item->name = xmalloc(p - c + 1); 98 strlcpy(cur_item->name, c, p - c + 1); 99 state = st_parse_after_name; 100 } 101 } 102 else if (!isprint(*p)) { 103 state = st_error; 104 } 105 else { 106 p++; 107 } 108 break; 109 110 case st_parse_after_name: 111 case st_parse_after_version: 112 case st_parse_after_option: { 113 switch (*p) { 114 case ',': 115 case '\0': 116 state = st_parse_comma; 117 break; 118 case '|': 119 state = st_parse_or; 120 break; 121 case '+': 122 case '-': 123 c = p; 124 state = st_parse_option_start; 125 break; 126 case '>': 127 case '<': 128 case '=': 129 case '!': 130 c = p; 131 cur_op = VERSION_ANY; 132 state = st_parse_ver_op; 133 break; 134 default: 135 state = st_error; 136 break; 137 } 138 break; 139 } 140 141 case st_parse_ver_op: { 142 switch (*p) { 143 case '>': 144 case '<': 145 case '=': 146 case '!': 147 p ++; 148 break; 149 default: 150 if (p - c == 2) { 151 if (memcmp(c, ">=", 2) == 0) { 152 cur_op = VERSION_GE; 153 } 154 else if (memcmp(c, "<=", 2) == 0) { 155 cur_op = VERSION_LE; 156 } 157 else if (memcmp(c, "!=", 2) == 0) { 158 cur_op = VERSION_NOT; 159 } 160 else if (memcmp(c, "==", 2) == 0) { 161 cur_op = VERSION_EQ; 162 } 163 else { 164 state = st_error; 165 } 166 } 167 else if (p - c == 1) { 168 if (*c == '>') { 169 cur_op = VERSION_GT; 170 } 171 else if (*c == '<') { 172 cur_op = VERSION_LT; 173 } 174 else if (*c == '!') { 175 cur_op = VERSION_NOT; 176 } 177 else if (*c == '=') { 178 cur_op = VERSION_EQ; 179 } 180 else { 181 state = st_error; 182 } 183 } 184 else { 185 state = st_error; 186 } 187 188 if (state != st_error) { 189 state = st_skip_spaces; 190 next_state = st_parse_after_op; 191 } 192 break; 193 } 194 break; 195 } 196 197 case st_parse_after_op: 198 if (cur_op == VERSION_ANY) { 199 state = st_error; 200 } 201 else { 202 state = st_parse_version_number; 203 } 204 break; 205 206 case st_parse_version_number: 207 if (isalnum(*p) || *p == '-' || *p == '_' || *p == '.' || 208 (*p == ',' && isdigit(*(p + 1)))) { 209 p ++; 210 } 211 else { 212 if (p - c > 0) { 213 cur_ver = xcalloc(1, sizeof(*cur_ver)); 214 cur_ver->ver = xmalloc(p - c + 1); 215 strlcpy(cur_ver->ver, c, p - c + 1); 216 cur_ver->op = cur_op; 217 assert(cur_item != NULL); 218 DL_APPEND(cur_item->versions, cur_ver); 219 state = st_skip_spaces; 220 next_state = st_parse_after_version; 221 } 222 else { 223 state = st_error; 224 } 225 } 226 break; 227 228 case st_parse_option_start: 229 cur_opt = xcalloc(1, sizeof(*cur_opt)); 230 if (*p == '+') { 231 cur_opt->on = true; 232 } 233 else { 234 cur_opt->on = false; 235 } 236 237 p ++; 238 c = p; 239 state = st_parse_option; 240 break; 241 242 case st_parse_option: 243 if (isalnum(*p) || *p == '-' || *p == '_') { 244 p ++; 245 } 246 else { 247 if (p - c > 0) { 248 cur_opt->opt = xmalloc(p - c + 1); 249 strlcpy(cur_opt->opt, c, p - c + 1); 250 assert(cur_item != NULL); 251 DL_APPEND(cur_item->options, cur_opt); 252 state = st_skip_spaces; 253 next_state = st_parse_after_option; 254 } 255 else { 256 state = st_error; 257 } 258 } 259 break; 260 261 case st_parse_comma: 262 assert(cur_item != NULL); 263 264 if (cur == NULL) { 265 cur = xcalloc(1, sizeof(*cur)); 266 } 267 268 DL_APPEND(cur->items, cur_item); 269 DL_APPEND(res, cur); 270 cur_item = NULL; 271 cur = NULL; 272 p ++; 273 state = st_skip_spaces; 274 next_state = st_parse_dep_name; 275 break; 276 277 case st_parse_or: 278 assert(cur_item != NULL); 279 280 if (cur == NULL) { 281 cur = xcalloc(1, sizeof(*cur)); 282 } 283 284 DL_APPEND(cur->items, cur_item); 285 cur_item = NULL; 286 p ++; 287 state = st_skip_spaces; 288 next_state = st_parse_dep_name; 289 break; 290 291 case st_skip_spaces: 292 if (isspace(*p)) { 293 p ++; 294 } 295 else if (*p == '\0') { 296 state = st_parse_comma; 297 } 298 else { 299 c = p; 300 state = next_state; 301 } 302 break; 303 304 case st_error: 305 default: 306 pkg_emit_error("cannot parse pkg formula: %s", in); 307 pkg_deps_formula_free(res); 308 if (cur_item != NULL) { 309 free(cur_item->name); 310 free(cur_item); 311 } 312 313 return (NULL); 314 315 break; 316 } 317 } 318 319 if (state != st_skip_spaces && state != st_parse_comma) { 320 pkg_emit_error("cannot parse pkg formula: %s", in); 321 pkg_deps_formula_free(res); 322 if (cur_item != NULL) { 323 free(cur_item->name); 324 free(cur_item); 325 } 326 327 return (NULL); 328 } 329 330 return (res); 331 } 332 333 void 334 pkg_deps_formula_free(struct pkg_dep_formula *f) 335 { 336 struct pkg_dep_formula *cf, *cftmp; 337 struct pkg_dep_formula_item *cit, *cittmp; 338 struct pkg_dep_version_item *cver, *cvertmp; 339 struct pkg_dep_option_item *copt, *copttmp; 340 341 DL_FOREACH_SAFE(f, cf, cftmp) { 342 DL_FOREACH_SAFE(cf->items, cit, cittmp) { 343 free(cit->name); 344 345 DL_FOREACH_SAFE(cit->versions, cver, cvertmp) { 346 free(cver->ver); 347 free(cver); 348 } 349 350 DL_FOREACH_SAFE(cit->options, copt, copttmp) { 351 free(copt->opt); 352 free(copt); 353 } 354 355 free(cit); 356 } 357 358 free(cf); 359 } 360 } 361 362 static const char* 363 pkg_deps_op_tostring(enum pkg_dep_version_op op) 364 { 365 const char *op_str; 366 367 switch (op) { 368 case VERSION_ANY: 369 default: 370 op_str = "?"; 371 break; 372 case VERSION_EQ: 373 op_str = "="; 374 break; 375 case VERSION_LE: 376 op_str = "<="; 377 break; 378 case VERSION_GE: 379 op_str = ">="; 380 break; 381 case VERSION_LT: 382 op_str = "<"; 383 break; 384 case VERSION_GT: 385 op_str = ">"; 386 break; 387 case VERSION_NOT: 388 op_str = "!="; 389 break; 390 } 391 392 return (op_str); 393 } 394 395 char* 396 pkg_deps_formula_tostring(struct pkg_dep_formula *f) 397 { 398 struct pkg_dep_formula *cf, *cftmp; 399 struct pkg_dep_formula_item *cit, *cittmp; 400 struct pkg_dep_version_item *cver, *cvertmp; 401 struct pkg_dep_option_item *copt, *copttmp; 402 char *res = NULL, *p; 403 404 int rlen = 0, r; 405 406 DL_FOREACH_SAFE(f, cf, cftmp) { 407 DL_FOREACH_SAFE(cf->items, cit, cittmp) { 408 rlen += strlen(cit->name); 409 410 DL_FOREACH_SAFE(cit->versions, cver, cvertmp) { 411 rlen += strlen(cver->ver); 412 rlen += 4; /* <OP><SP><VER><SP> */ 413 } 414 415 DL_FOREACH_SAFE(cit->options, copt, copttmp) { 416 rlen += strlen(copt->opt); 417 rlen += 2; /* <+-><OPT><SP> */ 418 } 419 420 rlen += 2; /* |<SP> */ 421 } 422 423 rlen += 2; /* <,><SP> */ 424 } 425 426 if (rlen == 0) { 427 return (NULL); 428 } 429 430 res = xmalloc(rlen + 1); 431 432 p = res; 433 434 DL_FOREACH_SAFE(f, cf, cftmp) { 435 DL_FOREACH_SAFE(cf->items, cit, cittmp) { 436 r = snprintf(p, rlen, "%s", cit->name); 437 p += r; 438 rlen -= r; 439 440 DL_FOREACH_SAFE(cit->versions, cver, cvertmp) { 441 r = snprintf(p, rlen, " %s %s", pkg_deps_op_tostring(cver->op), 442 cver->ver); 443 p += r; 444 rlen -= r; 445 } 446 447 DL_FOREACH_SAFE(cit->options, copt, copttmp) { 448 r = snprintf(p, rlen, " %c%s", copt->on ? '+' : '-', copt->opt); 449 p += r; 450 rlen -= r; 451 } 452 453 r = snprintf(p, rlen, "%s", cit->next ? " | " : ""); 454 p += r; 455 rlen -= r; 456 } 457 458 r = snprintf(p, rlen, "%s", cf->next ? ", " : ""); 459 p += r; 460 rlen -= r; 461 } 462 463 return (res); 464 } 465 466 char* 467 pkg_deps_formula_tosql(struct pkg_dep_formula_item *f) 468 { 469 struct pkg_dep_formula_item *cit, *cittmp; 470 struct pkg_dep_version_item *cver, *cvertmp; 471 char *res = NULL, *p; 472 473 int rlen = 0, r; 474 475 DL_FOREACH_SAFE(f, cit, cittmp) { 476 rlen += sizeof("AND (name='' )"); 477 rlen += strlen(cit->name); 478 479 DL_FOREACH_SAFE(cit->versions, cver, cvertmp) { 480 rlen += sizeof(" AND vercmp(>=, version,'') "); 481 rlen += strlen(cver->ver); 482 } 483 484 rlen += sizeof(" OR "); 485 } 486 487 if (rlen == 0) { 488 return (NULL); 489 } 490 491 res = xmalloc(rlen + 1); 492 493 p = res; 494 495 DL_FOREACH_SAFE(f, cit, cittmp) { 496 r = snprintf(p, rlen, "(name='%s'", cit->name); 497 p += r; 498 rlen -= r; 499 500 DL_FOREACH_SAFE(cit->versions, cver, cvertmp) { 501 r = snprintf(p, rlen, " AND vercmp('%s',version,'%s')", 502 pkg_deps_op_tostring(cver->op), 503 cver->ver); 504 p += r; 505 rlen -= r; 506 } 507 r = snprintf(p, rlen, ")%s", cit->next ? " OR " : ""); 508 p += r; 509 rlen -= r; 510 } 511 512 return (res); 513 } 514 515 enum pkg_dep_version_op 516 pkg_deps_string_toop(const char *in) 517 { 518 enum pkg_dep_version_op ret = VERSION_ANY; 519 int len; 520 521 if (in != NULL) { 522 len = strlen(in); 523 524 if (len == 2) { 525 if (memcmp(in, ">=", 2) == 0) { 526 ret = VERSION_GE; 527 } 528 else if (memcmp(in, "<=", 2) == 0) { 529 ret = VERSION_LE; 530 } 531 else if (memcmp(in, "!=", 2) == 0) { 532 ret = VERSION_NOT; 533 } 534 else if (memcmp(in, "==", 2) == 0) { 535 ret = VERSION_EQ; 536 } 537 } 538 else if (len == 1) { 539 if (*in == '>') { 540 ret = VERSION_GT; 541 } 542 else if (*in == '<') { 543 ret = VERSION_LT; 544 } 545 else if (*in == '!') { 546 ret = VERSION_NOT; 547 } 548 else if (*in == '=') { 549 ret = VERSION_EQ; 550 } 551 } 552 } 553 554 return (ret); 555 }