Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/src/search.c
2065 views
1
/*-
2
* Copyright (c) 2011-2012 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011-2012 Julien Laffaye <[email protected]>
4
* Copyright (c) 2011-2012 Marin Atanasov Nikolov <[email protected]>
5
* Copyright (c) 2012-2013 Bryan Drewery <[email protected]>
6
* Copyright (c) 2014 Matthew Seaman <[email protected]>
7
* All rights reserved.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer
14
* in this position and unchanged.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
20
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
23
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
*/
30
31
#include <err.h>
32
#include <getopt.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <unistd.h>
37
38
#include <pkg.h>
39
40
#include "pkgcli.h"
41
42
typedef struct _cliopt {
43
const char *option;
44
char key;
45
} cliopt;
46
47
/* an option string should not be a prefix of any other option string */
48
static const cliopt search_label[] = {
49
{ "comment", 'c' },
50
{ "description", 'd' },
51
{ "name", 'n' },
52
{ "origin", 'o' },
53
{ "pkg-name", 'p' },
54
{ NULL, '\0' },
55
};
56
57
static const cliopt modifiers[] = {
58
{ "annotations", 'A' },
59
{ "arch", 'a' },
60
{ "categories", 'C' },
61
{ "comment", 'c' },
62
{ "depends-on", 'd' },
63
{ "description", 'D' },
64
{ "full", 'f' },
65
{ "licenses", 'l' },
66
{ "maintainer", 'm' },
67
{ "name", 'n' },
68
{ "options", 'o' },
69
{ "pkg-size", 'P' },
70
{ "prefix", 'p' },
71
{ "repository", 'R' },
72
{ "required-by", 'r' },
73
{ "shared-libs-required", 'B' },
74
{ "shared-libs-provided", 'b' },
75
{ "size", 's' },
76
{ "url", 'u' },
77
{ "version", 'v' },
78
{ "www", 'w' },
79
{ NULL, '\0' },
80
};
81
82
static char
83
match_optarg(const cliopt *optlist, const char *opt)
84
{
85
int i, matched = -1;
86
char key = '\0';
87
size_t optlen;
88
89
optlen = strlen(opt);
90
91
/* Match any unique prefix from optlist */
92
for (i = 0; optlist[i].option != NULL; i++) {
93
if (strncmp(opt, optlist[i].option, optlen) != 0)
94
continue;
95
if (matched > 0) {
96
warnx("\"%s\" is ambiguous. Was "
97
"\"%s\" or \"%s\" meant?", opt,
98
optlist[matched].option, optlist[i].option);
99
key = '\0';
100
break;
101
}
102
matched = i;
103
key = optlist[i].key;
104
}
105
return (key);
106
}
107
108
static pkgdb_field
109
search_label_opt(const char *optionarg)
110
{
111
pkgdb_field field;
112
113
/* label options */
114
switch(match_optarg(search_label, optionarg)) {
115
case 'o':
116
field = FIELD_ORIGIN;
117
break;
118
case 'n':
119
field = FIELD_NAME;
120
break;
121
case 'p':
122
field = FIELD_NAMEVER;
123
break;
124
case 'c':
125
field = FIELD_COMMENT;
126
break;
127
case 'd':
128
field = FIELD_DESC;
129
break;
130
default:
131
usage_search();
132
errx(EXIT_FAILURE, "Unknown search/label option: %s", optionarg);
133
/* NOTREACHED */
134
}
135
return field;
136
}
137
138
static unsigned int
139
modifier_opt(const char *optionarg)
140
{
141
unsigned int opt;
142
143
/* output modifiers */
144
switch(match_optarg(modifiers, optionarg)) {
145
case 'A':
146
opt = INFO_ANNOTATIONS;
147
break;
148
case 'a':
149
opt = INFO_ARCH;
150
break;
151
case 'C':
152
opt = INFO_CATEGORIES;
153
break;
154
case 'c':
155
opt = INFO_COMMENT;
156
break;
157
case 'd':
158
opt = INFO_DEPS;
159
break;
160
case 'D':
161
opt = INFO_DESCR;
162
break;
163
case 'f':
164
opt = INFO_FULL;
165
break;
166
case 'l':
167
opt = INFO_LICENSES;
168
break;
169
case 'm':
170
opt = INFO_MAINTAINER;
171
break;
172
case 'n':
173
opt = INFO_NAME;
174
break;
175
case 'o':
176
opt = INFO_OPTIONS;
177
break;
178
case 'P':
179
opt = INFO_PKGSIZE;
180
break;
181
case 'p':
182
opt = INFO_PREFIX;
183
break;
184
case 'R':
185
opt = INFO_REPOSITORY;
186
break;
187
case 'r':
188
opt = INFO_RDEPS;
189
break;
190
case 'B':
191
opt = INFO_SHLIBS_REQUIRED;
192
break;
193
case 'b':
194
opt = INFO_SHLIBS_PROVIDED;
195
break;
196
case 's':
197
opt = INFO_FLATSIZE;
198
break;
199
case 'u':
200
opt = INFO_REPOURL;
201
break;
202
case 'v':
203
opt = INFO_VERSION;
204
break;
205
case 'w':
206
opt = INFO_WWW;
207
break;
208
default:
209
usage_search();
210
errx(EXIT_FAILURE, "Unkown modifier option %s", optionarg);
211
/* NOTREACHED */
212
}
213
return opt;
214
}
215
216
void
217
usage_search(void)
218
{
219
int i, n;
220
221
fprintf(stderr, "Usage: pkg search [-eU] [-r repo] [-S search] "
222
"[-L label] [-Q mod]... [-Cgix] <pkg-name>\n");
223
fprintf(stderr, " pkg search [-cDdefopqRU] [-r repo] "
224
"[-Cgix] <pattern>\n\n");
225
n = fprintf(stderr, " Search and Label options:");
226
for (i = 0; search_label[i].option != NULL; i++) {
227
if (n > 72)
228
n = fprintf(stderr, "\n ");
229
n += fprintf(stderr, " %s", search_label[i].option);
230
}
231
fprintf(stderr, "\n");
232
n = fprintf(stderr, " Output Modifiers:");
233
for (i = 0; modifiers[i].option != NULL; i++) {
234
if (n > 68)
235
n = fprintf(stderr, "\n ");
236
n += fprintf(stderr, " %s", modifiers[i].option);
237
}
238
fprintf(stderr, "\n");
239
fprintf(stderr, "For more information see 'pkg help search'.\n");
240
}
241
242
int
243
exec_search(int argc, char **argv)
244
{
245
const char *pattern = NULL;
246
int ret = EPKG_OK, ch;
247
int flags;
248
uint64_t opt = 0;
249
match_t match = MATCH_REGEX;
250
pkgdb_field search = FIELD_NONE;
251
pkgdb_field label = FIELD_NONE;
252
struct pkgdb *db = NULL;
253
struct pkgdb_it *it = NULL;
254
struct pkg *pkg = NULL;
255
bool atleastone = false;
256
bool old_quiet;
257
c_charv_t reponames = vec_init();
258
259
struct option longopts[] = {
260
{ "case-sensitive", no_argument, NULL, 'C' },
261
{ "comment", no_argument, NULL, 'c' },
262
{ "description", no_argument, NULL, 'D' },
263
{ "depends-on", no_argument, NULL, 'd' },
264
{ "exact", no_argument, NULL, 'e' },
265
{ "full", no_argument, NULL, 'f' },
266
{ "glob", no_argument, NULL, 'g' },
267
{ "case-insensitive", no_argument, NULL, 'i' },
268
{ "label", required_argument, NULL, 'L' },
269
{ "origins", no_argument, NULL, 'o' },
270
{ "prefix", no_argument, NULL, 'p' },
271
{ "quiet", no_argument, NULL, 'q' },
272
{ "query-modifier", required_argument, NULL, 'Q' },
273
{ "repository", required_argument, NULL, 'r' },
274
{ "raw", no_argument, NULL, 'R' },
275
{ "search", required_argument, NULL, 'S' },
276
{ "size", no_argument, NULL, 's' },
277
{ "no-repo-update", no_argument, NULL, 'U' },
278
{ "regex", no_argument, NULL, 'x' },
279
{ "raw-format", required_argument, NULL, 1 },
280
{ NULL, 0, NULL, 0 },
281
};
282
283
while ((ch = getopt_long(argc, argv, "+CcDdefgiL:opqQ:r:RS:sUx", longopts, NULL)) != -1) {
284
switch (ch) {
285
case 'C':
286
pkgdb_set_case_sensitivity(true);
287
break;
288
case 'c': /* Same as -S comment */
289
search = search_label_opt("comment");
290
break;
291
case 'D': /* Same as -S description */
292
search = search_label_opt("description");
293
break;
294
case 'd': /* Same as -Q depends-on */
295
opt |= modifier_opt("depends-on");
296
break;
297
case 'e':
298
match = MATCH_EXACT;
299
break;
300
case 'f': /* Same as -Q full */
301
opt |= modifier_opt("full");
302
break;
303
case 'g':
304
match = MATCH_GLOB;
305
break;
306
case 'i':
307
pkgdb_set_case_sensitivity(false);
308
break;
309
case 'L':
310
label = search_label_opt(optarg);
311
break;
312
case 'o': /* Same as -L origin */
313
label = search_label_opt("origin");
314
break;
315
case 'p': /* Same as -Q prefix */
316
opt |= modifier_opt("prefix");
317
break;
318
case 'q':
319
quiet = true;
320
break;
321
case 'Q':
322
opt |= modifier_opt(optarg);
323
break;
324
case 'r':
325
vec_push(&reponames, optarg);
326
break;
327
case 'R':
328
opt = INFO_RAW;
329
break;
330
case 'S':
331
search = search_label_opt(optarg);
332
break;
333
case 's': /* Same as -Q size */
334
opt |= modifier_opt("size");
335
break;
336
case 'U':
337
auto_update = false;
338
break;
339
case 'x':
340
match = MATCH_REGEX;
341
break;
342
case 1:
343
if (STRIEQ(optarg, "json"))
344
opt |= INFO_RAW_JSON;
345
else if (STRIEQ(optarg, "json-compact"))
346
opt |= INFO_RAW_JSON_COMPACT;
347
else if (STRIEQ(optarg, "yaml"))
348
opt |= INFO_RAW_YAML;
349
else if (STRIEQ(optarg, "ucl"))
350
opt |= INFO_RAW_UCL;
351
else
352
errx(EXIT_FAILURE, "Invalid format '%s' for the "
353
"raw output, expecting json, json-compact "
354
"or yaml", optarg);
355
break;
356
default:
357
usage_search();
358
return (EXIT_FAILURE);
359
}
360
}
361
362
argc -= optind;
363
argv += optind;
364
365
if (argc != 1) {
366
usage_search();
367
return (EXIT_FAILURE);
368
}
369
370
pattern = argv[0];
371
if (pattern[0] == '\0') {
372
fprintf(stderr, "Pattern must not be empty.\n");
373
return (EXIT_FAILURE);
374
}
375
if (search == FIELD_NONE) {
376
if (strchr(pattern, '/') != NULL) {
377
if (strchr(pattern, '@') != NULL)
378
search = FIELD_FLAVOR;
379
else
380
search = FIELD_ORIGIN;
381
} else
382
search = FIELD_NAMEVER; /* Default search */
383
}
384
if (label == FIELD_NONE)
385
label = search; /* By default, show what was searched */
386
387
switch(label) {
388
case FIELD_NONE:
389
break; /* should never happen */
390
case FIELD_ORIGIN:
391
if (quiet) {
392
opt = INFO_TAG_ORIGIN;
393
quiet = false;
394
} else {
395
opt |= INFO_TAG_ORIGIN|INFO_COMMENT;
396
}
397
break;
398
case FIELD_FLAVOR:
399
case FIELD_NAME:
400
opt |= INFO_TAG_NAME|INFO_COMMENT;
401
break;
402
case FIELD_NAMEVER:
403
opt |= INFO_TAG_NAMEVER|INFO_COMMENT;
404
break;
405
case FIELD_COMMENT:
406
opt |= INFO_TAG_NAMEVER|INFO_COMMENT;
407
break;
408
case FIELD_DESC:
409
opt |= INFO_TAG_NAMEVER|INFO_DESCR;
410
break;
411
}
412
413
if (quiet) {
414
opt = INFO_TAG_NAMEVER;
415
quiet = false;
416
}
417
418
ret = pkgdb_access2(PKGDB_MODE_READ, PKGDB_DB_REPO, &reponames);
419
switch(ret) {
420
case EPKG_ENOACCESS:
421
warnx("Insufficient privileges to query the package database");
422
return (EXIT_FAILURE);
423
case EPKG_ENODB:
424
if (!auto_update) {
425
warnx("Unable to open remote repository catalogues. Try running '%s update' first.", getprogname());
426
return (EXIT_FAILURE);
427
}
428
break;
429
case EPKG_OK:
430
break;
431
default:
432
return (EXIT_FAILURE);
433
}
434
435
/* first update the remote repositories if needed */
436
old_quiet = quiet;
437
quiet = true;
438
if (auto_update && (ret = pkgcli_update(false, false, &reponames)) != EPKG_OK)
439
return (ret);
440
quiet = old_quiet;
441
442
if (pkgdb_open_all2(&db, PKGDB_REMOTE, &reponames) != EPKG_OK)
443
return (EXIT_FAILURE);
444
445
if ((it = pkgdb_repo_search2(db, pattern, match, search, search,
446
&reponames)) == NULL) {
447
pkgdb_close(db);
448
return (EXIT_FAILURE);
449
}
450
451
if (opt & INFO_RAW) {
452
if ((opt & (INFO_RAW_JSON|INFO_RAW_JSON_COMPACT|INFO_RAW_UCL)) == 0)
453
opt |= INFO_RAW_YAML;
454
}
455
456
flags = info_flags(opt, true);
457
while ((ret = pkgdb_it_next(it, &pkg, flags)) == EPKG_OK) {
458
print_info(NULL, pkg, opt);
459
atleastone = true;
460
}
461
462
pkg_free(pkg);
463
pkgdb_it_free(it);
464
pkgdb_close(db);
465
466
if (!atleastone)
467
ret = EPKG_FATAL;
468
469
return ((ret == EPKG_OK || ret == EPKG_END) ? EXIT_SUCCESS : EXIT_FAILURE);
470
}
471
472