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