Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/src/query.c
2065 views
1
/*-
2
* Copyright (c) 2011-2012 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011-2012 Marin Atanasov Nikolov <[email protected]>
4
* Copyright (c) 2012 Bryan Drewery <[email protected]>
5
* Copyright (c) 2013-2014 Matthew Seaman <[email protected]>
6
* All rights reserved.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer
13
* in this position and unchanged.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
19
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
22
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
*/
29
30
#include <sys/types.h>
31
32
#include <ctype.h>
33
#include <err.h>
34
#include <getopt.h>
35
#include <inttypes.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
#include <pkg.h>
42
#include "pkgcli.h"
43
44
static struct query_flags accepted_query_flags[] = {
45
{ 'd', "nov", 1, PKG_LOAD_DEPS },
46
{ 'r', "nov", 1, PKG_LOAD_RDEPS },
47
{ 'C', "", 1, PKG_LOAD_CATEGORIES },
48
{ 'F', "ps", 1, PKG_LOAD_FILES },
49
{ 'O', "kvdD", 1, PKG_LOAD_OPTIONS },
50
{ 'D', "", 1, PKG_LOAD_DIRS },
51
{ 'L', "", 1, PKG_LOAD_LICENSES },
52
{ 'U', "", 1, PKG_LOAD_USERS },
53
{ 'G', "", 1, PKG_LOAD_GROUPS },
54
{ 'B', "", 1, PKG_LOAD_SHLIBS_REQUIRED },
55
{ 'b', "", 1, PKG_LOAD_SHLIBS_PROVIDED },
56
{ 'A', "tv", 1, PKG_LOAD_ANNOTATIONS },
57
{ '?', "drCFODLUGBbA", 1, PKG_LOAD_BASIC }, /* dbflags handled in analyse_query_string() */
58
{ '#', "drCFODLUGBbA", 1, PKG_LOAD_BASIC }, /* dbflags handled in analyse_query_string() */
59
{ 's', "hb", 0, PKG_LOAD_BASIC },
60
{ 'Q', "", 0, PKG_LOAD_BASIC },
61
{ 'n', "", 0, PKG_LOAD_BASIC },
62
{ 'v', "", 0, PKG_LOAD_BASIC },
63
{ 'o', "", 0, PKG_LOAD_BASIC },
64
{ 'p', "", 0, PKG_LOAD_BASIC },
65
{ 'm', "", 0, PKG_LOAD_BASIC },
66
{ 'c', "", 0, PKG_LOAD_BASIC },
67
{ 'e', "", 0, PKG_LOAD_BASIC },
68
{ 'w', "", 0, PKG_LOAD_BASIC },
69
{ 'l', "", 0, PKG_LOAD_BASIC },
70
{ 'q', "", 0, PKG_LOAD_BASIC },
71
{ 'a', "", 0, PKG_LOAD_BASIC },
72
{ 'k', "", 0, PKG_LOAD_BASIC },
73
{ 'M', "", 0, PKG_LOAD_BASIC },
74
{ 't', "", 0, PKG_LOAD_BASIC },
75
{ 'R', "", 0, PKG_LOAD_ANNOTATIONS },
76
{ 'V', "", 0, PKG_LOAD_BASIC },
77
{ 'X', "", 0, PKG_LOAD_BASIC | PKG_LOAD_SCRIPTS | PKG_LOAD_LUA_SCRIPTS },
78
};
79
80
static void
81
format_str(struct pkg *pkg, xstring *dest, const char *qstr, const void *data)
82
{
83
bool automatic = false;
84
bool locked = false;
85
bool vital = false;
86
87
xstring_reset(dest);
88
89
while (qstr[0] != '\0') {
90
if (qstr[0] == '%') {
91
qstr++;
92
switch (qstr[0]) {
93
case 'n':
94
pkg_fprintf(dest->fp, "%n", pkg);
95
break;
96
case 'v':
97
pkg_fprintf(dest->fp, "%v", pkg);
98
break;
99
case 'o':
100
pkg_fprintf(dest->fp, "%o", pkg);
101
break;
102
case 'R':
103
pkg_fprintf(dest->fp, "%N", pkg);
104
break;
105
case 'p':
106
pkg_fprintf(dest->fp, "%p", pkg);
107
break;
108
case 'm':
109
pkg_fprintf(dest->fp, "%m", pkg);
110
break;
111
case 'c':
112
pkg_fprintf(dest->fp, "%c", pkg);
113
break;
114
case 'w':
115
pkg_fprintf(dest->fp, "%w", pkg);
116
break;
117
case 'a':
118
pkg_get(pkg, PKG_ATTR_AUTOMATIC, &automatic);
119
fprintf(dest->fp, "%d", automatic);
120
break;
121
case 'k':
122
pkg_get(pkg, PKG_ATTR_LOCKED, &locked);
123
fprintf(dest->fp, "%d", locked);
124
break;
125
case 't':
126
pkg_fprintf(dest->fp, "%t", pkg);
127
break;
128
case 's':
129
qstr++;
130
if (qstr[0] == 'h')
131
pkg_fprintf(dest->fp, "%#sB", pkg);
132
else if (qstr[0] == 'b')
133
pkg_fprintf(dest->fp, "%s", pkg);
134
break;
135
case 'e':
136
pkg_fprintf(dest->fp, "%e", pkg);
137
break;
138
case '?':
139
qstr++;
140
switch (qstr[0]) {
141
case 'd':
142
pkg_fprintf(dest->fp, "%?d", pkg);
143
break;
144
case 'r':
145
pkg_fprintf(dest->fp, "%?r", pkg);
146
break;
147
case 'C':
148
pkg_fprintf(dest->fp, "%?C", pkg);
149
break;
150
case 'F':
151
pkg_fprintf(dest->fp, "%?F", pkg);
152
break;
153
case 'O':
154
pkg_fprintf(dest->fp, "%?O", pkg);
155
break;
156
case 'D':
157
pkg_fprintf(dest->fp, "%?D", pkg);
158
break;
159
case 'L':
160
pkg_fprintf(dest->fp, "%?L", pkg);
161
break;
162
case 'U':
163
pkg_fprintf(dest->fp, "%?U", pkg);
164
break;
165
case 'G':
166
pkg_fprintf(dest->fp, "%?G", pkg);
167
break;
168
case 'B':
169
pkg_fprintf(dest->fp, "%?B", pkg);
170
break;
171
case 'b':
172
pkg_fprintf(dest->fp, "%?b", pkg);
173
break;
174
case 'A':
175
pkg_fprintf(dest->fp, "%?A", pkg);
176
break;
177
}
178
break;
179
case '#':
180
qstr++;
181
switch (qstr[0]) {
182
case 'd':
183
pkg_fprintf(dest->fp, "%#d", pkg);
184
break;
185
case 'r':
186
pkg_fprintf(dest->fp, "%#r", pkg);
187
break;
188
case 'C':
189
pkg_fprintf(dest->fp, "%#C", pkg);
190
break;
191
case 'F':
192
pkg_fprintf(dest->fp, "%#F", pkg);
193
break;
194
case 'O':
195
pkg_fprintf(dest->fp, "%#O", pkg);
196
break;
197
case 'D':
198
pkg_fprintf(dest->fp, "%#D", pkg);
199
break;
200
case 'L':
201
pkg_fprintf(dest->fp, "%#L", pkg);
202
break;
203
case 'U':
204
pkg_fprintf(dest->fp, "%#U", pkg);
205
break;
206
case 'G':
207
pkg_fprintf(dest->fp, "%#G", pkg);
208
break;
209
case 'B':
210
pkg_fprintf(dest->fp, "%#B", pkg);
211
break;
212
case 'b':
213
pkg_fprintf(dest->fp, "%#b", pkg);
214
break;
215
case 'A':
216
pkg_fprintf(dest->fp, "%#A", pkg);
217
break;
218
}
219
break;
220
case 'Q':
221
pkg_fprintf(dest->fp, "%Q", pkg);
222
break;
223
case 'q':
224
pkg_fprintf(dest->fp, "%q", pkg);
225
break;
226
case 'l':
227
pkg_fprintf(dest->fp, "%l", pkg);
228
break;
229
case 'd':
230
qstr++;
231
if (qstr[0] == 'n')
232
pkg_fprintf(dest->fp, "%dn", data);
233
else if (qstr[0] == 'o')
234
pkg_fprintf(dest->fp, "%do", data);
235
else if (qstr[0] == 'v')
236
pkg_fprintf(dest->fp, "%dv", data);
237
break;
238
case 'r':
239
qstr++;
240
if (qstr[0] == 'n')
241
pkg_fprintf(dest->fp, "%rn", data);
242
else if (qstr[0] == 'o')
243
pkg_fprintf(dest->fp, "%ro", data);
244
else if (qstr[0] == 'v')
245
pkg_fprintf(dest->fp, "%rv", data);
246
break;
247
case 'C':
248
pkg_fprintf(dest->fp, "%Cn", data);
249
break;
250
case 'F':
251
qstr++;
252
if (qstr[0] == 'p')
253
pkg_fprintf(dest->fp, "%Fn", data);
254
else if (qstr[0] == 's')
255
pkg_fprintf(dest->fp, "%Fs", data);
256
break;
257
case 'O':
258
qstr++;
259
if (qstr[0] == 'k')
260
pkg_fprintf(dest->fp, "%On", data);
261
else if (qstr[0] == 'v')
262
pkg_fprintf(dest->fp, "%Ov", data);
263
else if (qstr[0] == 'd') /* default value */
264
pkg_fprintf(dest->fp, "%Od", data);
265
else if (qstr[0] == 'D') /* description */
266
pkg_fprintf(dest->fp, "%OD", data);
267
break;
268
case 'D':
269
pkg_fprintf(dest->fp, "%Dn", data);
270
break;
271
case 'L':
272
pkg_fprintf(dest->fp, "%Ln", data);
273
break;
274
case 'U':
275
pkg_fprintf(dest->fp, "%Un", data);
276
break;
277
case 'G':
278
pkg_fprintf(dest->fp, "%Gn", data);
279
break;
280
case 'B':
281
pkg_fprintf(dest->fp, "%Bn", data);
282
break;
283
case 'b':
284
pkg_fprintf(dest->fp, "%bn", data);
285
break;
286
case 'A':
287
qstr++;
288
if (qstr[0] == 't')
289
pkg_fprintf(dest->fp, "%An", data);
290
else if (qstr[0] == 'v')
291
pkg_fprintf(dest->fp, "%Av", data);
292
break;
293
case 'M':
294
if (pkg_has_message(pkg))
295
pkg_fprintf(dest->fp, "%M", pkg);
296
break;
297
case 'V':
298
pkg_get(pkg, PKG_ATTR_VITAL, &vital);
299
fprintf(dest->fp, "%d", vital);
300
break;
301
case 'X':
302
pkg_fprintf(dest->fp, "%X", pkg);
303
break;
304
case '%':
305
fprintf(dest->fp, "%c", '%');
306
break;
307
}
308
} else if (qstr[0] == '\\') {
309
qstr++;
310
switch (qstr[0]) {
311
case 'n':
312
fprintf(dest->fp, "%c", '\n');
313
break;
314
case 'a':
315
fprintf(dest->fp, "%c", '\a');
316
break;
317
case 'b':
318
fprintf(dest->fp, "%c", '\b');
319
break;
320
case 'f':
321
fprintf(dest->fp, "%c", '\f');
322
break;
323
case 'r':
324
fprintf(dest->fp, "%c", '\r');
325
break;
326
case '\\':
327
fprintf(dest->fp, "%c", '\\');
328
break;
329
case 't':
330
fprintf(dest->fp, "%c", '\t');
331
break;
332
}
333
} else {
334
fprintf(dest->fp, "%c", qstr[0]);
335
}
336
qstr++;
337
}
338
fflush(dest->fp);
339
}
340
341
void
342
print_query(struct pkg *pkg, char *qstr, char multiline)
343
{
344
xstring *output;
345
struct pkg_dep *dep = NULL;
346
struct pkg_option *option = NULL;
347
struct pkg_file *file = NULL;
348
struct pkg_dir *dir = NULL;
349
const char *str = NULL;
350
struct pkg_kv *kv = NULL;
351
struct pkg_stringlist *sl = NULL;
352
struct pkg_stringlist_iterator *slit;
353
struct pkg_kvlist *kl = NULL;
354
struct pkg_kvlist_iterator *kit;
355
356
output = xstring_new();
357
358
switch (multiline) {
359
case 'd':
360
while (pkg_deps(pkg, &dep) == EPKG_OK) {
361
format_str(pkg, output, qstr, dep);
362
printf("%s\n", output->buf);
363
}
364
break;
365
case 'r':
366
while (pkg_rdeps(pkg, &dep) == EPKG_OK) {
367
format_str(pkg, output, qstr, dep);
368
printf("%s\n", output->buf);
369
}
370
break;
371
case 'C':
372
pkg_get(pkg, PKG_ATTR_CATEGORIES, &sl);
373
slit = pkg_stringlist_iterator(sl);
374
while ((str = pkg_stringlist_next(slit))) {
375
format_str(pkg, output, qstr, str);
376
printf("%s\n", output->buf);
377
}
378
free(slit);
379
free(sl);
380
break;
381
case 'O':
382
while (pkg_options(pkg, &option) == EPKG_OK) {
383
format_str(pkg, output, qstr, option);
384
printf("%s\n", output->buf);
385
}
386
break;
387
case 'F':
388
while (pkg_files(pkg, &file) == EPKG_OK) {
389
format_str(pkg, output, qstr, file);
390
printf("%s\n", output->buf);
391
}
392
break;
393
case 'D':
394
while (pkg_dirs(pkg, &dir) == EPKG_OK) {
395
format_str(pkg, output, qstr, dir);
396
printf("%s\n", output->buf);
397
}
398
break;
399
case 'L':
400
pkg_get(pkg, PKG_ATTR_LICENSES, &sl);
401
slit = pkg_stringlist_iterator(sl);
402
while ((str = pkg_stringlist_next(slit))) {
403
format_str(pkg, output, qstr, str);
404
printf("%s\n", output->buf);
405
}
406
free(slit);
407
free(sl);
408
break;
409
case 'U':
410
pkg_get(pkg, PKG_ATTR_USERS, &sl);
411
slit = pkg_stringlist_iterator(sl);
412
while ((str = pkg_stringlist_next(slit))) {
413
format_str(pkg, output, qstr, str);
414
printf("%s\n", output->buf);
415
}
416
break;
417
case 'G':
418
pkg_get(pkg, PKG_ATTR_GROUPS, &sl);
419
slit = pkg_stringlist_iterator(sl);
420
while ((str = pkg_stringlist_next(slit))) {
421
format_str(pkg, output, qstr, str);
422
printf("%s\n", output->buf);
423
}
424
break;
425
case 'B':
426
pkg_get(pkg, PKG_ATTR_SHLIBS_REQUIRED, &sl);
427
slit = pkg_stringlist_iterator(sl);
428
while ((str = pkg_stringlist_next(slit))) {
429
format_str(pkg, output, qstr, str);
430
printf("%s\n", output->buf);
431
}
432
break;
433
case 'b':
434
pkg_get(pkg, PKG_ATTR_SHLIBS_PROVIDED, &sl);
435
slit = pkg_stringlist_iterator(sl);
436
while ((str = pkg_stringlist_next(slit))) {
437
format_str(pkg, output, qstr, str);
438
printf("%s\n", output->buf);
439
}
440
break;
441
case 'A':
442
pkg_get(pkg, PKG_ATTR_ANNOTATIONS, &kl);
443
kit = pkg_kvlist_iterator(kl);
444
while ((kv = pkg_kvlist_next(kit))) {
445
format_str(pkg, output, qstr, kv);
446
printf("%s\n", output->buf);
447
}
448
free(kit);
449
free(kl);
450
break;
451
default:
452
format_str(pkg, output, qstr, dep);
453
printf("%s\n", output->buf);
454
break;
455
}
456
xstring_free(output);
457
}
458
459
typedef enum {
460
NONE,
461
NEXT_IS_INT,
462
OPERATOR_INT,
463
INT,
464
NEXT_IS_STRING,
465
OPERATOR_STRING,
466
STRING,
467
QUOTEDSTRING,
468
SQUOTEDSTRING,
469
POST_EXPR,
470
} state_t;
471
472
int
473
format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
474
{
475
state_t state = NONE;
476
unsigned int bracket_level = 0;
477
const char *sqlop;
478
bool collate_nocase = false;
479
480
fprintf(sqlcond->fp, " WHERE ");
481
while (str[0] != '\0') {
482
if (state == NONE) {
483
if (str[0] == '%') {
484
str++;
485
switch (str[0]) {
486
case 'n':
487
fprintf(sqlcond->fp, "p.name");
488
state = OPERATOR_STRING;
489
break;
490
case 'o':
491
fprintf(sqlcond->fp, "origin");
492
state = OPERATOR_STRING;
493
break;
494
case 'p':
495
fprintf(sqlcond->fp, "prefix");
496
state = OPERATOR_STRING;
497
break;
498
case 'm':
499
fprintf(sqlcond->fp, "maintainer");
500
state = OPERATOR_STRING;
501
break;
502
case 'c':
503
fprintf(sqlcond->fp, "comment");
504
state = OPERATOR_STRING;
505
break;
506
case 'w':
507
fprintf(sqlcond->fp, "www");
508
state = OPERATOR_STRING;
509
break;
510
case 's':
511
fprintf(sqlcond->fp, "flatsize");
512
state = OPERATOR_INT;
513
break;
514
case 'a':
515
if (for_remote)
516
goto bad_option;
517
fprintf(sqlcond->fp, "automatic");
518
state = OPERATOR_INT;
519
break;
520
case 'q':
521
fprintf(sqlcond->fp, "arch");
522
state = OPERATOR_STRING;
523
break;
524
case 'k':
525
if (for_remote)
526
goto bad_option;
527
fprintf(sqlcond->fp, "locked");
528
state = OPERATOR_INT;
529
break;
530
case 'M':
531
if (for_remote)
532
goto bad_option;
533
fprintf(sqlcond->fp, "message");
534
state = OPERATOR_STRING;
535
break;
536
case 't':
537
if (for_remote)
538
goto bad_option;
539
fprintf(sqlcond->fp, "time");
540
state = OPERATOR_INT;
541
break;
542
case 'e':
543
fprintf(sqlcond->fp, "desc");
544
state = OPERATOR_STRING;
545
break;
546
case 'V':
547
if (for_remote)
548
goto bad_option;
549
fprintf(sqlcond->fp, "vital");
550
state = OPERATOR_INT;
551
break;
552
case '#': /* FALLTHROUGH */
553
case '?':
554
sqlop = (str[0] == '#' ? "COUNT(*)" : "COUNT(*) > 0");
555
str++;
556
switch (str[0]) {
557
case 'd':
558
fprintf(sqlcond->fp, "(SELECT %s FROM deps AS d WHERE d.package_id=p.id)", sqlop);
559
break;
560
case 'r':
561
fprintf(sqlcond->fp, "(SELECT %s FROM deps AS d WHERE d.name=p.name)", sqlop);
562
break;
563
case 'C':
564
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_categories AS d WHERE d.package_id=p.id)", sqlop);
565
break;
566
case 'F':
567
if (for_remote)
568
goto bad_option;
569
fprintf(sqlcond->fp, "(SELECT %s FROM files AS d WHERE d.package_id=p.id)", sqlop);
570
break;
571
case 'O':
572
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_option AS d WHERE d.package_id=p.id)", sqlop);
573
break;
574
case 'D':
575
if (for_remote)
576
goto bad_option;
577
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_directories AS d WHERE d.package_id=p.id)", sqlop);
578
break;
579
case 'L':
580
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_licenses AS d WHERE d.package_id=p.id)", sqlop);
581
break;
582
case 'U':
583
if (for_remote)
584
goto bad_option;
585
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_users AS d WHERE d.package_id=p.id)", sqlop);
586
break;
587
case 'G':
588
if (for_remote)
589
goto bad_option;
590
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_groups AS d WHERE d.package_id=p.id)", sqlop);
591
break;
592
case 'B':
593
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_shlibs_required AS d WHERE d.package_id=p.id)", sqlop);
594
break;
595
case 'b':
596
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_shlibs_provided AS d WHERE d.package_id=p.id)", sqlop);
597
break;
598
case 'A':
599
fprintf(sqlcond->fp, "(SELECT %s FROM pkg_annotation AS d WHERE d.package_id=p.id)", sqlop);
600
break;
601
default:
602
goto bad_option;
603
}
604
state = OPERATOR_INT;
605
break;
606
default:
607
bad_option:
608
fprintf(stderr, "malformed evaluation string\n");
609
return (EPKG_FATAL);
610
}
611
} else {
612
switch (str[0]) {
613
case '(':
614
bracket_level++;
615
fprintf(sqlcond->fp, "%c", str[0]);
616
break;
617
case ' ':
618
case '\t':
619
break;
620
default:
621
fprintf(stderr, "unexpected character: %c\n", str[0]);
622
return (EPKG_FATAL);
623
}
624
}
625
} else if (state == POST_EXPR) {
626
switch (str[0]) {
627
case ')':
628
if (bracket_level == 0) {
629
fprintf(stderr, "too many closing brackets.\n");
630
return (EPKG_FATAL);
631
}
632
bracket_level--;
633
fprintf(sqlcond->fp, "%c", str[0]);
634
break;
635
case ' ':
636
case '\t':
637
break;
638
case '|':
639
if (str[1] == '|') {
640
str++;
641
state = NONE;
642
fprintf(sqlcond->fp, " OR ");
643
break;
644
} else {
645
fprintf(stderr, "unexpected character %c\n", str[1]);
646
return (EPKG_FATAL);
647
}
648
case '&':
649
if (str[1] == '&') {
650
str++;
651
state = NONE;
652
fprintf(sqlcond->fp, " AND ");
653
break;
654
} else {
655
fprintf(stderr, "unexpected character %c\n", str[1]);
656
return (EPKG_FATAL);
657
}
658
default:
659
fprintf(stderr, "unexpected character %c\n", str[0]);
660
return (EPKG_FATAL);
661
}
662
} else if (state == OPERATOR_STRING || state == OPERATOR_INT) {
663
/* only operators or space are allowed here */
664
if (isspace(str[0])) {
665
/* do nothing */
666
} else if (str[0] == '~' ) {
667
if (state != OPERATOR_STRING) {
668
fprintf(stderr, "~ expected only for string testing\n");
669
return (EPKG_FATAL);
670
}
671
state = NEXT_IS_STRING;
672
fprintf(sqlcond->fp, " GLOB ");
673
} else if (str[0] == '>' || str[0] == '<') {
674
if (state != OPERATOR_INT) {
675
fprintf(stderr, "> expected only for integers\n");
676
return (EPKG_FATAL);
677
}
678
state = NEXT_IS_INT;
679
fprintf(sqlcond->fp, "%c", str[0]);
680
if (str[1] == '=') {
681
str++;
682
fprintf(sqlcond->fp, "%c", str[0]);
683
}
684
} else if (str[0] == '=') {
685
if (state == OPERATOR_STRING) {
686
state = NEXT_IS_STRING;
687
} else {
688
state = NEXT_IS_INT;
689
}
690
fprintf(sqlcond->fp, "%c", str[0]);
691
if (str[1] == '=') {
692
str++;
693
} else if (str[1] == '~' && state == NEXT_IS_STRING) {
694
str++;
695
collate_nocase = true;
696
}
697
} else if (str[0] == '!') {
698
if (str[1] == '=') {
699
fprintf(sqlcond->fp, "%c", str[0]);
700
fprintf(sqlcond->fp, "%c", str[1]);
701
} else if (str[1] == '~') {
702
fprintf(sqlcond->fp, " NOT GLOB ");
703
} else {
704
fprintf(stderr, "expecting = or ~ after !\n");
705
return (EPKG_FATAL);
706
}
707
str++;
708
if (state == OPERATOR_STRING) {
709
state = NEXT_IS_STRING;
710
} else {
711
state = NEXT_IS_INT;
712
}
713
if (str[0] == '~' && state == NEXT_IS_STRING) {
714
str++;
715
collate_nocase = true;
716
}
717
} else {
718
fprintf(stderr, "an operator is expected, got %c\n", str[0]);
719
return (EPKG_FATAL);
720
}
721
} else if (state == NEXT_IS_STRING || state == NEXT_IS_INT) {
722
if (isspace(str[0])) {
723
/* do nothing */
724
} else {
725
if (state == NEXT_IS_STRING) {
726
if (str[0] == '"') {
727
state = QUOTEDSTRING;
728
} else if (str[0] == '\'') {
729
state = SQUOTEDSTRING;
730
} else {
731
state = STRING;
732
str--;
733
}
734
fprintf(sqlcond->fp, "%c", '\'');
735
} else {
736
if (!isdigit(str[0])) {
737
fprintf(stderr, "a number is expected, got: %c\n", str[0]);
738
return (EPKG_FATAL);
739
}
740
state = INT;
741
fprintf(sqlcond->fp, "%c", str[0]);
742
}
743
}
744
} else if (state == INT) {
745
if (!isdigit(str[0])) {
746
state = POST_EXPR;
747
str--;
748
} else {
749
fprintf(sqlcond->fp, "%c", str[0]);
750
}
751
} else if (state == STRING || state == QUOTEDSTRING || state == SQUOTEDSTRING) {
752
if ((state == STRING && isspace(str[0])) ||
753
(state == QUOTEDSTRING && str[0] == '"') ||
754
(state == SQUOTEDSTRING && str[0] == '\'')) {
755
fprintf(sqlcond->fp, "%c", '\'');
756
state = POST_EXPR;
757
if (collate_nocase) {
758
fprintf(sqlcond->fp, " COLLATE NOCASE ");
759
collate_nocase = false;
760
}
761
} else {
762
fprintf(sqlcond->fp, "%c", str[0]);
763
if (str[0] == '\'')
764
fprintf(sqlcond->fp, "%c", str[0]);
765
else if (str[0] == '%' && for_remote)
766
fprintf(sqlcond->fp, "%c", str[0]);
767
}
768
}
769
str++;
770
}
771
if (state == STRING) {
772
fprintf(sqlcond->fp, "%c", '\'');
773
state = POST_EXPR;
774
if (collate_nocase) {
775
fprintf(sqlcond->fp, " COLLATE NOCASE ");
776
collate_nocase = false;
777
}
778
}
779
780
if (state != POST_EXPR && state != INT) {
781
fprintf(stderr, "unexpected end of expression\n");
782
return (EPKG_FATAL);
783
} else if (bracket_level > 0) {
784
fprintf(stderr, "unexpected end of expression (too many open brackets)\n");
785
return (EPKG_FATAL);
786
}
787
788
return (EPKG_OK);
789
}
790
791
int
792
analyse_query_string(char *qstr, struct query_flags *q_flags, const unsigned int q_flags_len, int *flags, char *multiline)
793
{
794
unsigned int i, j, k;
795
unsigned int valid_flag = 0;
796
unsigned int valid_opts = 0;
797
size_t len;
798
799
j = 0; /* shut up scanbuild */
800
801
if (strchr(qstr, '%') == NULL) {
802
fprintf(stderr, "Invalid query: query should contain a format string\n");
803
return (EPKG_FATAL);
804
}
805
806
while (qstr[0] != '\0') {
807
if (qstr[0] == '%') {
808
qstr++;
809
valid_flag = 0;
810
811
for (i = 0; i < q_flags_len; i++) {
812
/* found the flag */
813
if (qstr[0] == q_flags[i].flag) {
814
valid_flag = 1;
815
816
/* if the flag is followed by additional options */
817
if (q_flags[i].options[0] != '\0') {
818
qstr++;
819
valid_opts = 0;
820
821
len = strlen(q_flags[i].options);
822
for (j = 0; j < len; j++) {
823
if (qstr[0] == q_flags[i].options[j]) {
824
valid_opts = 1;
825
break;
826
}
827
}
828
829
if (valid_opts == 0) {
830
fprintf(stderr, "Invalid query: '%%%c' should be followed by:", q_flags[i].flag);
831
832
len = strlen(q_flags[i].options);
833
for (j = 0; j < len; j++)
834
fprintf(stderr, " %c%c", q_flags[i].options[j],
835
q_flags[i].options[j + 1] == '\0' ?
836
'\n' : ',');
837
838
return (EPKG_FATAL);
839
}
840
}
841
842
/* if this is a multiline flag */
843
if (q_flags[i].multiline == 1) {
844
if (*multiline != 0 && *multiline != q_flags[i].flag) {
845
fprintf(stderr, "Invalid query: '%%%c' and '%%%c' cannot be queried at the same time\n",
846
*multiline, q_flags[i].flag);
847
return (EPKG_FATAL);
848
} else {
849
*multiline = q_flags[i].flag;
850
}
851
}
852
853
/* handle the '?' flag cases */
854
if (q_flags[i].flag == '?' || q_flags[i].flag == '#') {
855
for (k = 0; k < q_flags_len; k++)
856
if (q_flags[k].flag == q_flags[i].options[j]) {
857
*flags |= q_flags[k].dbflags;
858
break;
859
}
860
} else {
861
*flags |= q_flags[i].dbflags;
862
}
863
864
break; /* don't iterate over the rest of the flags */
865
}
866
}
867
868
if (valid_flag == 0) {
869
fprintf(stderr, "Unknown query format key: '%%%c'\n", qstr[0]);
870
return (EPKG_FATAL);
871
}
872
}
873
874
qstr++;
875
}
876
877
return (EPKG_OK);
878
}
879
880
void
881
usage_query(void)
882
{
883
fprintf(stderr, "Usage: pkg query <query-format> <pkg-name>\n");
884
fprintf(stderr, " pkg query [-a] <query-format>\n");
885
fprintf(stderr, " pkg query -F <pkg-name> <query-format>\n");
886
fprintf(stderr, " pkg query -e <evaluation> <query-format>\n");
887
fprintf(stderr, " pkg query [-Cgix] <query-format> <pattern> <...>\n\n");
888
fprintf(stderr, "For more information see 'pkg help query.'\n");
889
}
890
891
int
892
exec_query(int argc, char **argv)
893
{
894
struct pkgdb *db = NULL;
895
struct pkgdb_it *it = NULL;
896
struct pkg *pkg = NULL;
897
char *pkgname = NULL;
898
int query_flags = PKG_LOAD_BASIC;
899
match_t match = MATCH_EXACT;
900
int ch;
901
int ret;
902
int retcode = EXIT_SUCCESS;
903
int i;
904
char multiline = 0;
905
int nprinted = 0;
906
char *condition = NULL;
907
const char *condition_sql = NULL;
908
xstring *sqlcond = NULL;
909
const unsigned int q_flags_len = NELEM(accepted_query_flags);
910
911
struct option longopts[] = {
912
{ "all", no_argument, NULL, 'a' },
913
{ "case-sensitive", no_argument, NULL, 'C' },
914
{ "evaluate", required_argument, NULL, 'e' },
915
{ "file", required_argument, NULL, 'F' },
916
{ "glob", no_argument, NULL, 'g' },
917
{ "case-insensitive", no_argument, NULL, 'i' },
918
{ "regex", no_argument, NULL, 'x' },
919
{ NULL, 0, NULL, 0 },
920
};
921
922
while ((ch = getopt_long(argc, argv, "+aCe:F:gix", longopts, NULL)) != -1) {
923
switch (ch) {
924
case 'a':
925
match = MATCH_ALL;
926
break;
927
case 'C':
928
pkgdb_set_case_sensitivity(true);
929
break;
930
case 'e':
931
condition = optarg;
932
break;
933
case 'F':
934
pkgname = optarg;
935
break;
936
case 'g':
937
match = MATCH_GLOB;
938
break;
939
case 'i':
940
pkgdb_set_case_sensitivity(false);
941
break;
942
case 'x':
943
match = MATCH_REGEX;
944
break;
945
default:
946
usage_query();
947
return (EXIT_FAILURE);
948
}
949
}
950
951
argc -= optind;
952
argv += optind;
953
954
if ((match == MATCH_ALL || pkgname != NULL)
955
&& argc > 1) {
956
usage_query();
957
retcode = EXIT_FAILURE;
958
goto cleanup;
959
}
960
961
if (argc == 0) {
962
usage_query();
963
retcode = EXIT_FAILURE;
964
goto cleanup;
965
}
966
967
/* Default to all packages if no pkg provided */
968
if (argc == 1 && pkgname == NULL && match == MATCH_EXACT) {
969
match = MATCH_ALL;
970
} else if (((argc == 1) ^ (match == MATCH_ALL)) && pkgname == NULL
971
&& condition == NULL) {
972
usage_query();
973
retcode = EXIT_FAILURE;
974
goto cleanup;
975
}
976
977
if (analyse_query_string(argv[0], accepted_query_flags, q_flags_len,
978
&query_flags, &multiline) != EPKG_OK) {
979
retcode = EXIT_FAILURE;
980
goto cleanup;
981
}
982
983
if (pkgname != NULL) {
984
/* Use a manifest or compact manifest if possible. */
985
int open_flags = 0;
986
if ((query_flags & ~(PKG_LOAD_DEPS|
987
PKG_LOAD_OPTIONS|
988
PKG_LOAD_CATEGORIES|
989
PKG_LOAD_LICENSES|
990
PKG_LOAD_USERS|
991
PKG_LOAD_GROUPS|
992
PKG_LOAD_SHLIBS_REQUIRED|
993
PKG_LOAD_SHLIBS_PROVIDED|
994
PKG_LOAD_ANNOTATIONS|
995
PKG_LOAD_CONFLICTS|
996
PKG_LOAD_PROVIDES|
997
PKG_LOAD_REQUIRES)) == 0) {
998
open_flags = PKG_OPEN_MANIFEST_COMPACT;
999
} else if ((query_flags & PKG_LOAD_FILES) == 0) {
1000
open_flags = PKG_OPEN_MANIFEST_ONLY;
1001
}
1002
if (pkg_open(&pkg, pkgname, open_flags) != EPKG_OK) {
1003
retcode = EXIT_FAILURE;
1004
goto cleanup;
1005
}
1006
1007
print_query(pkg, argv[0], multiline);
1008
retcode = EXIT_SUCCESS;
1009
goto cleanup;
1010
}
1011
1012
if (condition != NULL) {
1013
sqlcond = xstring_new();
1014
if (format_sql_condition(condition, sqlcond, false) != EPKG_OK) {
1015
retcode = EXIT_FAILURE;
1016
goto cleanup;
1017
}
1018
}
1019
1020
ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
1021
if (ret == EPKG_ENOACCESS) {
1022
warnx("Insufficient privileges to query the package database");
1023
retcode = EXIT_FAILURE;
1024
goto cleanup;
1025
} else if (ret == EPKG_ENODB) {
1026
if (!quiet)
1027
warnx("No packages installed");
1028
retcode = EXIT_SUCCESS;
1029
goto cleanup;
1030
} else if (ret != EPKG_OK) {
1031
retcode = EXIT_FAILURE;
1032
goto cleanup;
1033
}
1034
1035
ret = pkgdb_open(&db, PKGDB_DEFAULT);
1036
if (ret != EPKG_OK) {
1037
retcode = EXIT_FAILURE;
1038
goto cleanup;
1039
}
1040
1041
pkg_drop_privileges();
1042
if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
1043
warnx("Cannot get a read lock on a database, it is locked by another process");
1044
retcode = EXIT_FAILURE;
1045
goto cleanup;
1046
}
1047
1048
if (sqlcond) {
1049
fflush(sqlcond->fp);
1050
condition_sql = sqlcond->buf;
1051
}
1052
i = 1;
1053
do {
1054
pkgname = i < argc ? argv[i] : NULL;
1055
1056
if ((it = pkgdb_query_cond(db, condition_sql, pkgname, match)) == NULL) {
1057
warnx("DEBUG: %s/%s\n", condition_sql ? condition_sql : "-", pkgname ? pkgname : "-");
1058
retcode = EXIT_FAILURE;
1059
break;
1060
}
1061
1062
while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
1063
nprinted++;
1064
print_query(pkg, argv[0], multiline);
1065
}
1066
1067
if (ret != EPKG_END) {
1068
retcode = EXIT_FAILURE;
1069
break;
1070
}
1071
1072
pkgdb_it_free(it);
1073
i++;
1074
} while (i < argc);
1075
1076
if (nprinted == 0 && match != MATCH_ALL && retcode == EXIT_SUCCESS) {
1077
/* ensure to return a non-zero status when no package
1078
were found. */
1079
retcode = EXIT_FAILURE;
1080
}
1081
1082
cleanup:
1083
xstring_free(sqlcond);
1084
1085
pkg_free(pkg);
1086
1087
pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
1088
pkgdb_close(db);
1089
1090
return (retcode);
1091
}
1092
1093