/ libpkg / pkg_deps.c
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  }