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