Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/src/version.c
2065 views
1
/*-
2
* Copyright (c) 2011-2023 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011-2012 Julien Laffaye <[email protected]>
4
* Copyright (c) 2011 Philippe Pepiot <[email protected]>
5
* Copyright (c) 2011-2012 Marin Atanasov Nikolov <[email protected]>
6
* Copyright (c) 2012 Bryan Drewery <[email protected]>
7
* Copyright (c) 2013-2014 Matthew Seaman <[email protected]>
8
* All rights reserved.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer
15
* in this position and unchanged.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
21
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
24
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
*/
31
32
#include <sys/param.h>
33
#include <sys/utsname.h>
34
#include <sys/wait.h>
35
36
#include <err.h>
37
#include <errno.h>
38
#include <getopt.h>
39
#include <fcntl.h>
40
#include <pkg.h>
41
#include <stdbool.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <unistd.h>
46
#include <fnmatch.h>
47
#include <spawn.h>
48
#include <sys/types.h>
49
#include <sys/stat.h>
50
#include <pkghash.h>
51
#include <xmalloc.h>
52
53
#include "pkgcli.h"
54
55
extern char **environ;
56
57
struct index_entry {
58
char *name;
59
char *version;
60
};
61
62
struct category {
63
char *name;
64
pkghash *ports;
65
};
66
67
pkghash *categories = NULL;
68
69
void
70
usage_version(void)
71
{
72
fprintf(stderr, "Usage: pkg version [-IPR] [-hoqvU] [-l limchar] [-L limchar] [-Cegix pattern]\n");
73
fprintf(stderr, " [-r reponame] [-O origin|-n pkgname] [index]\n");
74
fprintf(stderr, " pkg version -t <version1> <version2>\n");
75
fprintf(stderr, " pkg version -T <pkgname> <pattern>\n\n");
76
fprintf(stderr, "For more information see 'pkg help version'.\n");
77
}
78
79
static void
80
print_version(struct pkg *pkg, const char *source, const char *ver,
81
char limchar, unsigned int opt)
82
{
83
char key;
84
const char *version = NULL;
85
int cout;
86
87
pkg_get(pkg, PKG_ATTR_VERSION, &version);
88
if (ver == NULL) {
89
if (source == NULL)
90
key = '!';
91
else
92
key = '?';
93
} else {
94
switch (pkg_version_cmp(version, ver)) {
95
case -1:
96
key = '<';
97
break;
98
case 0:
99
key = '=';
100
break;
101
case 1:
102
key = '>';
103
break;
104
default:
105
key = '!';
106
break;
107
}
108
}
109
110
if ((opt & VERSION_STATUS) && limchar != key)
111
return;
112
113
if ((opt & VERSION_NOSTATUS) && limchar == key)
114
return;
115
116
if (opt & VERSION_ORIGIN) {
117
pkg_printf("%-34o", pkg);
118
printf("%c", key);
119
}
120
else {
121
cout = pkg_printf("%n-%v", pkg, pkg);
122
cout = 35 - cout;
123
if (cout < 1)
124
cout = 1;
125
printf("%*s%c", cout, " ", key);
126
}
127
128
if (opt & VERSION_VERBOSE) {
129
switch (key) {
130
case '<':
131
printf(" needs updating (%s has %s)", source, ver);
132
break;
133
case '=':
134
printf(" up-to-date with %s", source);
135
break;
136
case '>':
137
printf(" succeeds %s (%s has %s)", source, source, ver);
138
break;
139
case '?':
140
pkg_printf(" orphaned: %o", pkg);
141
break;
142
case '!':
143
default:
144
printf(" Comparison failed");
145
break;
146
}
147
}
148
149
putchar('\n');
150
}
151
152
static int
153
do_testversion(unsigned int opt, int argc, char ** restrict argv)
154
{
155
/* -t must be unique and takes two arguments */
156
if ( opt != VERSION_TESTVERSION || argc < 2 ) {
157
usage_version();
158
return (EXIT_FAILURE);
159
}
160
161
switch (pkg_version_cmp(argv[0], argv[1])) {
162
case -1:
163
printf("<\n");
164
break;
165
case 0:
166
printf("=\n");
167
break;
168
case 1:
169
printf(">\n");
170
break;
171
}
172
173
return (EXIT_SUCCESS);
174
}
175
176
static int
177
do_testpattern(unsigned int opt, int argc, char ** restrict argv)
178
{
179
bool pattern_from_stdin = false;
180
bool pkgname_from_stdin = false;
181
char *line = NULL;
182
size_t linecap = 0;
183
ssize_t linelen;
184
int retval = FNM_NOMATCH;
185
186
/* -T must be unique and takes two arguments */
187
if ( opt != VERSION_TESTPATTERN || argc < 2 ) {
188
usage_version();
189
return (EXIT_FAILURE);
190
}
191
192
if (strncmp(argv[0], "-", 1) == 0)
193
pattern_from_stdin = true;
194
195
if (strncmp(argv[1], "-", 1) == 0)
196
pkgname_from_stdin = true;
197
198
if (pattern_from_stdin && pkgname_from_stdin) {
199
usage_version();
200
return (EXIT_FAILURE);
201
}
202
203
if (!pattern_from_stdin && !pkgname_from_stdin)
204
return (fnmatch(argv[1], argv[0], 0));
205
206
while ((linelen = getline(&line, &linecap, stdin)) > 0) {
207
line[linelen - 1] = '\0'; /* Strip trailing newline */
208
209
if ((pattern_from_stdin && (fnmatch(argv[1], line, 0) == 0)) ||
210
(pkgname_from_stdin && (fnmatch(line, argv[0], 0) == 0))) {
211
retval = EPKG_OK;
212
printf("%.*s\n", (int)linelen, line);
213
}
214
}
215
216
free(line);
217
218
return (retval);
219
}
220
221
static bool
222
have_ports(const char **portsdir, bool show_error)
223
{
224
char portsdirmakefile[MAXPATHLEN];
225
struct stat sb;
226
bool have_ports;
227
228
/* Look for Makefile within $PORTSDIR as indicative of
229
* installed ports tree. */
230
231
*portsdir = pkg_object_string(pkg_config_get("PORTSDIR"));
232
if (*portsdir == NULL)
233
err(1, "Cannot get portsdir config entry!");
234
235
snprintf(portsdirmakefile, sizeof(portsdirmakefile),
236
"%s/Makefile", *portsdir);
237
238
have_ports = (stat(portsdirmakefile, &sb) == 0 && S_ISREG(sb.st_mode));
239
240
if (show_error && !have_ports)
241
warnx("Cannot find ports tree: unable to open %s",
242
portsdirmakefile);
243
244
return (have_ports);
245
}
246
247
static const char*
248
indexfilename(char *filebuf, size_t filebuflen)
249
{
250
const char *indexdir;
251
const char *indexfile;
252
253
/* Construct the canonical name of the indexfile from the
254
* ports directory and the major version number of the OS.
255
* Overridden by INDEXDIR and INDEXFILE if defined. (Mimics
256
* the behaviour of ${PORTSDIR}/Makefile) */
257
258
indexdir = pkg_object_string(pkg_config_get("INDEXDIR"));
259
if (indexdir == NULL) {
260
indexdir = pkg_object_string(pkg_config_get("PORTSDIR"));
261
262
if (indexdir == NULL)
263
err(EXIT_FAILURE, "Cannot get either INDEXDIR or "
264
"PORTSDIR config entry!");
265
}
266
267
indexfile = pkg_object_string(pkg_config_get("INDEXFILE"));
268
if (indexfile == NULL)
269
err(EXIT_FAILURE, "Cannot get INDEXFILE config entry!");
270
271
strlcpy(filebuf, indexdir, filebuflen);
272
273
if (filebuf[0] != '\0' && filebuf[strlen(filebuf) - 1] != '/')
274
strlcat(filebuf, "/", filebuflen);
275
276
strlcat(filebuf, indexfile, filebuflen);
277
278
return (filebuf);
279
}
280
281
static pkghash *
282
hash_indexfile(const char *indexfilename)
283
{
284
FILE *indexfile;
285
pkghash *index = NULL;
286
struct index_entry *entry;
287
char *version, *name;
288
char *line = NULL, *l;
289
size_t linecap = 0;
290
291
292
/* Create a hash table of all the package names and port
293
* directories from the index file. */
294
295
indexfile = fopen(indexfilename, "re");
296
if (!indexfile)
297
err(EXIT_FAILURE, "Unable to open %s", indexfilename);
298
299
while (getline(&line, &linecap, indexfile) > 0) {
300
/* line is pkgname|portdir|... */
301
302
l = line;
303
304
version = strsep(&l, "|");
305
name = version;
306
version = strrchr(version, '-');
307
if (version == NULL)
308
errx(EXIT_FAILURE, "Invalid INDEX file format: %s",
309
indexfilename);
310
version[0] = '\0';
311
version++;
312
313
entry = xmalloc(sizeof(struct index_entry));
314
entry->name = xstrdup(name);
315
entry->version = xstrdup(version);
316
317
if (index == NULL)
318
index = pkghash_new();
319
320
if (!pkghash_add(index, entry->name, entry, NULL)) {
321
free(entry->version);
322
free(entry->name);
323
free(entry);
324
}
325
}
326
327
free(line);
328
fclose(indexfile);
329
330
if (index == NULL)
331
errx(EXIT_FAILURE, "No valid entries found in '%s'",
332
indexfilename);
333
334
return (index);
335
}
336
337
static void
338
free_categories(void)
339
{
340
struct category *cat;
341
pkghash_it it;
342
343
it = pkghash_iterator(categories);
344
while (pkghash_next(&it)) {
345
cat = (struct category *) it.value;
346
free(cat->name);
347
pkghash_destroy(cat->ports);
348
free(cat);
349
}
350
pkghash_destroy(categories);
351
}
352
353
static void
354
free_index(pkghash *index)
355
{
356
pkghash_it it;
357
struct index_entry *entry;
358
359
it = pkghash_iterator(index);
360
while (pkghash_next(&it)) {
361
entry = (struct index_entry *)it.value;
362
free(entry->version);
363
free(entry->name);
364
free(entry);
365
}
366
pkghash_destroy(index);
367
}
368
369
static bool
370
have_indexfile(const char **indexfile, char *filebuf, size_t filebuflen,
371
int argc, char ** restrict argv, bool show_error)
372
{
373
bool have_indexfile = true;
374
struct stat sb;
375
376
/* If there is a remaining command line argument, take
377
that as the name of the INDEX file to use. Otherwise,
378
search for INDEX-N within the ports tree */
379
380
if (argc == 0)
381
*indexfile = indexfilename(filebuf, filebuflen);
382
else
383
*indexfile = argv[0];
384
385
if (stat(*indexfile, &sb) == -1) {
386
if (errno == ENOENT)
387
have_indexfile = false;
388
else
389
warn("Failed to get stat for the INDEX file!");
390
}
391
392
if (show_error && !have_indexfile)
393
warn("Can't access %s", *indexfile);
394
395
return (have_indexfile);
396
}
397
398
static int
399
do_source_index(unsigned int opt, char limchar, char *pattern, match_t match,
400
const char *matchorigin, const char *matchname, const char *indexfile)
401
{
402
pkghash *index;
403
struct index_entry *ie;
404
struct pkgdb *db = NULL;
405
struct pkgdb_it *it = NULL;
406
struct pkg *pkg = NULL;
407
const char *name = NULL;
408
const char *origin = NULL;
409
bool gotnone = true;
410
411
if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_INDEX) {
412
usage_version();
413
return (EXIT_FAILURE);
414
}
415
416
if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
417
return (EXIT_FAILURE);
418
419
index = hash_indexfile(indexfile);
420
421
if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
422
pkgdb_close(db);
423
free_index(index);
424
warnx("Cannot get a read lock on the database. "
425
"It is locked by another process");
426
return (EXIT_FAILURE);
427
}
428
429
it = pkgdb_query(db, pattern, match);
430
if (it == NULL)
431
goto cleanup;
432
433
while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
434
pkg_get(pkg, PKG_ATTR_NAME, &name);
435
pkg_get(pkg, PKG_ATTR_ORIGIN, &origin);
436
437
/* If -O was specified, check if this origin matches */
438
if ((opt & VERSION_WITHORIGIN) &&
439
!STREQ(origin, matchorigin))
440
continue;
441
442
/* If -n was specified, check if this name matches */
443
if ((opt & VERSION_WITHNAME) &&
444
!STREQ(name, matchname))
445
continue;
446
447
ie = pkghash_get_value(index, name);
448
print_version(pkg, "index", ie != NULL ? ie->version : NULL,
449
limchar, opt);
450
451
/* If we reach here, it means at least one package
452
has matched with our query. */
453
gotnone = false;
454
}
455
456
cleanup:
457
pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
458
free_index(index);
459
pkg_free(pkg);
460
pkgdb_it_free(it);
461
pkgdb_close(db);
462
463
return (gotnone);
464
}
465
466
static int
467
do_source_remote(unsigned int opt, char limchar, char *pattern, match_t match,
468
bool auto_update, c_charv_t *reponames, const char *matchorigin,
469
const char *matchname)
470
{
471
struct pkgdb *db = NULL;
472
struct pkgdb_it *it = NULL;
473
struct pkgdb_it *it_remote = NULL;
474
struct pkg *pkg = NULL;
475
struct pkg *pkg_remote = NULL;
476
const char *name = NULL;
477
const char *origin = NULL;
478
const char *version_remote = NULL;
479
bool is_origin = false;
480
int retcode = EXIT_FAILURE;
481
482
if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_REMOTE ) {
483
usage_version();
484
return (EXIT_FAILURE);
485
}
486
487
/* Only force remote mode if looking up remote, otherwise
488
user is forced to have a repo.sqlite */
489
490
if (auto_update) {
491
retcode = pkgcli_update(false, false, reponames);
492
if (retcode != EPKG_OK)
493
return (retcode);
494
else
495
retcode = EXIT_FAILURE;
496
}
497
498
if (pkgdb_open_all2(&db, PKGDB_REMOTE, reponames) != EPKG_OK)
499
return (EXIT_FAILURE);
500
501
if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
502
pkgdb_close(db);
503
warnx("Cannot get a read lock on a database. "
504
"It is locked by another process");
505
return (EXIT_FAILURE);
506
}
507
508
it = pkgdb_query(db, pattern, match);
509
if (it == NULL)
510
goto cleanup;
511
512
while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
513
pkg_get(pkg, PKG_ATTR_NAME, &name);
514
pkg_get(pkg, PKG_ATTR_ORIGIN, &origin);
515
516
/* If -O was specified, check if this origin matches */
517
if ((opt & VERSION_WITHORIGIN) &&
518
!STREQ(origin, matchorigin)) {
519
is_origin = true;
520
continue;
521
}
522
523
/* If -n was specified, check if this name matches */
524
if ((opt & VERSION_WITHNAME) &&
525
!STREQ(name, matchname)) {
526
is_origin = false;
527
continue;
528
}
529
530
it_remote = pkgdb_repo_query2(db, is_origin ? origin : name, MATCH_EXACT, reponames);
531
if (it_remote == NULL) {
532
retcode = EXIT_FAILURE;
533
goto cleanup;
534
}
535
536
if (pkgdb_it_next(it_remote, &pkg_remote, PKG_LOAD_BASIC)
537
== EPKG_OK) {
538
pkg_get(pkg_remote, PKG_ATTR_VERSION, &version_remote);
539
print_version(pkg, "remote", version_remote, limchar,
540
opt);
541
} else {
542
print_version(pkg, "remote", NULL, limchar, opt);
543
}
544
pkgdb_it_free(it_remote);
545
546
/* If we reach here, it means at least one package
547
has matched with our query. */
548
retcode = EXIT_SUCCESS;
549
}
550
551
cleanup:
552
pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
553
554
pkg_free(pkg);
555
pkg_free(pkg_remote);
556
pkgdb_it_free(it);
557
pkgdb_close(db);
558
559
return (retcode);
560
}
561
562
static int
563
exec_buf(xstring *res, char **argv) {
564
char buf[BUFSIZ];
565
int spawn_err;
566
pid_t pid;
567
int pfd[2];
568
int r, pstat;
569
posix_spawn_file_actions_t actions;
570
571
if (pipe(pfd) < 0) {
572
warn("pipe()");
573
return (0);
574
}
575
576
if ((spawn_err = posix_spawn_file_actions_init(&actions)) != 0) {
577
warnx("%s:%s", argv[0], strerror(spawn_err));
578
return (0);
579
}
580
581
if ((spawn_err = posix_spawn_file_actions_addopen(&actions,
582
STDERR_FILENO, "/dev/null", O_RDWR, 0)) != 0 ||
583
(spawn_err = posix_spawn_file_actions_addopen(&actions,
584
STDIN_FILENO, "/dev/null", O_RDONLY, 0)) != 0 ||
585
(spawn_err = posix_spawn_file_actions_adddup2(&actions,
586
pfd[1], STDOUT_FILENO)!= 0) ||
587
(spawn_err = posix_spawnp(&pid, argv[0], &actions, NULL,
588
argv, environ)) != 0) {
589
posix_spawn_file_actions_destroy(&actions);
590
warnx("%s:%s", argv[0], strerror(spawn_err));
591
return (0);
592
}
593
posix_spawn_file_actions_destroy(&actions);
594
595
close(pfd[1]);
596
597
xstring_reset(res);
598
while ((r = read(pfd[0], buf, BUFSIZ)) > 0)
599
fwrite(buf, sizeof(char), r, res->fp);
600
601
close(pfd[0]);
602
while (waitpid(pid, &pstat, 0) == -1) {
603
if (errno != EINTR)
604
return (-1);
605
}
606
if (WEXITSTATUS(pstat) != 0)
607
return (-1);
608
609
fflush(res->fp);
610
return (strlen(res->buf));
611
}
612
613
static struct category *
614
category_new(int portsfd, const char *category)
615
{
616
struct category *cat = NULL;
617
xstring *makecmd;
618
char *results, *d;
619
char *argv[5];
620
621
makecmd = xstring_new();
622
fchdir(portsfd);
623
624
argv[0] = "make";
625
argv[1] = "-C";
626
argv[2] = (char *)category;
627
argv[3] = "-VSUBDIR";
628
argv[4] = NULL;
629
630
if (exec_buf(makecmd, argv) <= 0)
631
goto cleanup;
632
633
fflush(makecmd->fp);
634
results = makecmd->buf;
635
636
if (categories == NULL)
637
categories = pkghash_new();
638
639
cat = xcalloc(1, sizeof(*cat));
640
cat->name = xstrdup(category);
641
642
pkghash_add(categories, cat->name, cat, NULL);
643
while ((d = strsep(&results, " \n")) != NULL)
644
pkghash_safe_add(cat->ports, d, NULL, NULL);
645
646
cleanup:
647
xstring_free(makecmd);
648
649
return (cat);
650
}
651
652
static bool
653
validate_origin(int portsfd, const char *origin)
654
{
655
struct category *cat;
656
char *category, *buf;
657
658
/* If the origin does not contain a / ignore it like for
659
* "base"
660
*/
661
if (strchr(origin, '/') == NULL)
662
return (false);
663
664
category = xstrdup(origin);
665
buf = strrchr(category, '/');
666
buf[0] = '\0';
667
668
cat = pkghash_get_value(categories, category);
669
if (cat == NULL)
670
cat = category_new(portsfd, category);
671
if (cat == NULL)
672
return (false);
673
674
buf = strrchr(origin, '/');
675
buf++;
676
677
if (STREQ(origin, "base"))
678
return (false);
679
680
return (pkghash_get(cat->ports, buf) != NULL);
681
}
682
683
static const char *
684
port_version(xstring *cmd, int portsfd, const char *origin, const char *pkgname)
685
{
686
char *output, *walk, *name;
687
char *version = NULL;
688
char *argv[5];
689
690
/* Validate the port origin -- check the SUBDIR settings
691
in the ports and category Makefiles, then extract the
692
version from the port itself. */
693
694
if (validate_origin(portsfd, origin)) {
695
argv[0] = "make";
696
argv[1] = "-C";
697
argv[2] = (char *)origin;
698
argv[3] = "flavors-package-names";
699
argv[4] = NULL;
700
701
if (exec_buf(cmd, argv) > 0) {
702
fflush(cmd->fp);
703
output = cmd->buf;
704
while ((walk = strsep(&output, "\n")) != NULL) {
705
name = walk;
706
walk = strrchr(walk, '-');
707
if (walk == NULL)
708
continue;
709
walk[0] = '\0';
710
walk++;
711
if (STREQ(name, pkgname)) {
712
version = walk;
713
break;
714
}
715
}
716
}
717
}
718
719
return (version);
720
}
721
722
static int
723
do_source_ports(unsigned int opt, char limchar, char *pattern, match_t match,
724
const char *matchorigin, const char *matchname, const char *portsdir)
725
{
726
struct pkgdb *db = NULL;
727
struct pkgdb_it *it = NULL;
728
struct pkg *pkg = NULL;
729
xstring *cmd;
730
const char *name = NULL;
731
const char *origin = NULL;
732
const char *version = NULL;
733
int portsfd;
734
bool gotnone = true;
735
736
if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_PORTS ) {
737
usage_version();
738
return (EXIT_FAILURE);
739
}
740
741
portsfd = open(portsdir, O_DIRECTORY);
742
if (portsfd == -1)
743
err(EXIT_FAILURE, "Cannot open '%s'", portsdir);
744
745
if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
746
return (EXIT_FAILURE);
747
748
if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
749
pkgdb_close(db);
750
warnx("Cannot get a read lock on a database. "
751
"It is locked by another process");
752
return (EXIT_FAILURE);
753
}
754
755
if ((it = pkgdb_query(db, pattern, match)) == NULL)
756
goto cleanup;
757
758
cmd = xstring_new();
759
760
while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
761
pkg_get(pkg, PKG_ATTR_NAME, &name);
762
pkg_get(pkg, PKG_ATTR_ORIGIN, &origin);
763
764
/* If -O was specified, check if this origin matches */
765
if ((opt & VERSION_WITHORIGIN) &&
766
!STREQ(origin, matchorigin))
767
continue;
768
769
/* If -n was specified, check if this name matches */
770
if ((opt & VERSION_WITHNAME) &&
771
!STREQ(name, matchname))
772
continue;
773
774
version = port_version(cmd, portsfd, origin, name);
775
print_version(pkg, "port", version, limchar, opt);
776
xstring_reset(cmd);
777
778
/* If we reach here, it means at least one package
779
has matched with our query. */
780
gotnone = false;
781
}
782
783
xstring_free(cmd);
784
785
cleanup:
786
pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
787
788
free_categories();
789
pkg_free(pkg);
790
pkgdb_it_free(it);
791
pkgdb_close(db);
792
793
return (gotnone);
794
}
795
796
int
797
exec_version(int argc, char **argv)
798
{
799
unsigned int opt = 0;
800
char limchar = '-';
801
const char *matchorigin = NULL;
802
const char *matchname = NULL;
803
const char *portsdir;
804
const char *indexfile;
805
const char *versionsource;
806
char filebuf[MAXPATHLEN];
807
match_t match = MATCH_ALL;
808
char *pattern = NULL;
809
int ch;
810
c_charv_t reponames = vec_init();
811
812
struct option longopts[] = {
813
{ "case-sensitive", no_argument, NULL, 'C' },
814
{ "exact", required_argument, NULL, 'e' },
815
{ "glob", required_argument, NULL, 'g' },
816
{ "help", no_argument, NULL, 'h' },
817
{ "index", no_argument, NULL, 'I' },
818
{ "case-insensitive", no_argument, NULL, 'i' },
819
{ "not-like", required_argument, NULL, 'L' },
820
{ "like", required_argument, NULL, 'l' },
821
{ "match-name", required_argument, NULL, 'n' },
822
{ "match-origin", required_argument, NULL, 'O' },
823
{ "origin", no_argument, NULL, 'o' },
824
{ "ports", no_argument, NULL, 'P' },
825
{ "quiet", no_argument, NULL, 'q' },
826
{ "remote", no_argument, NULL, 'R' },
827
{ "repository", required_argument, NULL, 'r' },
828
{ "test-pattern", no_argument, NULL, 'T' },
829
{ "test-version", no_argument, NULL, 't' },
830
{ "no-repo-update", no_argument, NULL, 'U' },
831
{ "verbose", no_argument, NULL, 'v' },
832
{ "regex", required_argument, NULL, 'x' },
833
{ NULL, 0, NULL, 0 },
834
};
835
836
while ((ch = getopt_long(argc, argv, "+Ce:g:hIiL:l:n:O:oPqRr:TtUvx:",
837
longopts, NULL)) != -1) {
838
switch (ch) {
839
case 'C':
840
pkgdb_set_case_sensitivity(true);
841
break;
842
case 'e':
843
match = MATCH_EXACT;
844
pattern = optarg;
845
break;
846
case 'g':
847
match = MATCH_GLOB;
848
pattern = optarg;
849
break;
850
case 'h':
851
usage_version();
852
return (EXIT_SUCCESS);
853
case 'I':
854
opt |= VERSION_SOURCE_INDEX;
855
break;
856
case 'i':
857
pkgdb_set_case_sensitivity(false);
858
break;
859
case 'L':
860
opt |= VERSION_NOSTATUS;
861
limchar = *optarg;
862
break;
863
case 'l':
864
opt |= VERSION_STATUS;
865
limchar = *optarg;
866
break;
867
case 'n':
868
opt |= VERSION_WITHNAME;
869
matchname = optarg;
870
break;
871
case 'O':
872
opt |= VERSION_WITHORIGIN;
873
matchorigin = optarg;
874
break;
875
case 'o':
876
opt |= VERSION_ORIGIN;
877
break;
878
case 'P':
879
opt |= VERSION_SOURCE_PORTS;
880
break;
881
case 'q':
882
opt |= VERSION_QUIET;
883
break;
884
case 'R':
885
opt |= VERSION_SOURCE_REMOTE;
886
break;
887
case 'r':
888
opt |= VERSION_SOURCE_REMOTE;
889
vec_push(&reponames, optarg);
890
break;
891
case 'T':
892
opt |= VERSION_TESTPATTERN;
893
break;
894
case 't':
895
opt |= VERSION_TESTVERSION;
896
break;
897
case 'U':
898
auto_update = false;
899
break;
900
case 'v':
901
opt |= VERSION_VERBOSE;
902
break;
903
case 'x':
904
match = MATCH_REGEX;
905
pattern = optarg;
906
break;
907
default:
908
usage_version();
909
return (EXIT_FAILURE);
910
}
911
}
912
argc -= optind;
913
argv += optind;
914
915
/*
916
* Allowed option combinations:
917
* -t ver1 ver2 -- only
918
* -T pkgname pattern -- only
919
* Only one of -I -P -R can be given
920
*/
921
922
if (matchorigin != NULL && matchname != NULL) {
923
usage_version();
924
return (EXIT_FAILURE);
925
}
926
927
if ( (opt & VERSION_TESTVERSION) == VERSION_TESTVERSION )
928
return (do_testversion(opt, argc, argv));
929
930
if ( (opt & VERSION_TESTPATTERN) == VERSION_TESTPATTERN )
931
return (do_testpattern(opt, argc, argv));
932
933
if (opt & (VERSION_STATUS|VERSION_NOSTATUS)) {
934
if (limchar != '<' &&
935
limchar != '>' &&
936
limchar != '=' &&
937
limchar != '?' &&
938
limchar != '!') {
939
usage_version();
940
return (EXIT_FAILURE);
941
}
942
}
943
944
if (opt & VERSION_QUIET)
945
quiet = true;
946
947
if (argc > 1) {
948
usage_version();
949
return (EXIT_FAILURE);
950
}
951
952
if ( !(opt & VERSION_SOURCES ) ) {
953
versionsource = pkg_object_string(
954
pkg_config_get("VERSION_SOURCE"));
955
if (versionsource != NULL) {
956
switch (versionsource[0]) {
957
case 'I':
958
opt |= VERSION_SOURCE_INDEX;
959
break;
960
case 'P':
961
opt |= VERSION_SOURCE_PORTS;
962
break;
963
case 'R':
964
opt |= VERSION_SOURCE_REMOTE;
965
break;
966
default:
967
warnx("Invalid VERSION_SOURCE"
968
" in configuration.");
969
}
970
}
971
}
972
973
if ( (opt & VERSION_SOURCE_INDEX) == VERSION_SOURCE_INDEX ) {
974
if (!have_indexfile(&indexfile, filebuf, sizeof(filebuf),
975
argc, argv, true))
976
return (EXIT_FAILURE);
977
else
978
return (do_source_index(opt, limchar, pattern, match,
979
matchorigin, matchname, indexfile));
980
}
981
982
if ( (opt & VERSION_SOURCE_REMOTE) == VERSION_SOURCE_REMOTE )
983
return (do_source_remote(opt, limchar, pattern, match,
984
auto_update, &reponames, matchorigin, matchname));
985
986
if ( (opt & VERSION_SOURCE_PORTS) == VERSION_SOURCE_PORTS ) {
987
if (!have_ports(&portsdir, true))
988
return (EXIT_FAILURE);
989
else
990
return (do_source_ports(opt, limchar, pattern,
991
match, matchorigin, matchname, portsdir));
992
}
993
994
/* If none of -IPR were specified, and INDEX exists use that.
995
Failing that, if portsdir exists and is valid, use that
996
(slow) otherwise fallback to remote. */
997
998
if (have_indexfile(&indexfile, filebuf, sizeof(filebuf), argc, argv,
999
false)) {
1000
opt |= VERSION_SOURCE_INDEX;
1001
return (do_source_index(opt, limchar, pattern, match,
1002
matchorigin, matchname, indexfile));
1003
} else if (have_ports(&portsdir, false)) {
1004
if (argc == 1) {
1005
warnx("No such INDEX file: '%s'", argv[0]);
1006
return (EXIT_FAILURE);
1007
}
1008
opt |= VERSION_SOURCE_PORTS;
1009
return (do_source_ports(opt, limchar, pattern, match,
1010
matchorigin, matchname, portsdir));
1011
} else {
1012
opt |= VERSION_SOURCE_REMOTE;
1013
return (do_source_remote(opt, limchar, pattern, match,
1014
auto_update, &reponames, matchorigin, matchname));
1015
}
1016
1017
/* NOTREACHED */
1018
return (EXIT_FAILURE);
1019
}
1020
/*
1021
* That's All Folks!
1022
*/
1023
1024