Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_deps.c
2065 views
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
}
556
557