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