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