Branch data 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/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 : : const char *key;
84 : : const char *version;
85 : : int cout;
86 : :
87 : 0 : pkg_get(pkg, PKG_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 %S", 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%s", 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 : : case '!':
141 : 0 : printf(" Comparison failed");
142 : 0 : break;
143 : : }
144 : 0 : }
145 : :
146 : 0 : printf("\n");
147 : 0 : return;
148 : 0 : }
149 : :
150 : : static int
151 : 44 : do_testversion(unsigned int opt, int argc, char ** restrict argv)
152 : : {
153 : : /* -t must be unique and takes two arguments */
154 [ + - - + ]: 44 : if ( opt != VERSION_TESTVERSION || argc < 2 ) {
155 : 0 : usage_version();
156 : 0 : return (EXIT_FAILURE);
157 : : }
158 : :
159 [ - + + + ]: 44 : switch (pkg_version_cmp(argv[0], argv[1])) {
160 : : case -1:
161 : 36 : printf("<\n");
162 : 36 : break;
163 : : case 0:
164 : 4 : printf("=\n");
165 : 4 : break;
166 : : case 1:
167 : 4 : printf(">\n");
168 : 4 : break;
169 : : }
170 : :
171 : 44 : return (EXIT_SUCCESS);
172 : 44 : }
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 : have_indexfile = false;
385 : :
386 [ # # # # ]: 0 : if (show_error && !have_indexfile)
387 : 0 : warn("Can't access %s", *indexfile);
388 : :
389 : 0 : return (have_indexfile);
390 : : }
391 : :
392 : : static int
393 : 0 : do_source_index(unsigned int opt, char limchar, char *pattern, match_t match,
394 : : const char *matchorigin, const char *matchname, const char *indexfile)
395 : : {
396 : : pkghash *index;
397 : : struct index_entry *ie;
398 : 0 : struct pkgdb *db = NULL;
399 : 0 : struct pkgdb_it *it = NULL;
400 : 0 : struct pkg *pkg = NULL;
401 : : const char *name;
402 : : const char *origin;
403 : :
404 [ # # ]: 0 : if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_INDEX) {
405 : 0 : usage_version();
406 : 0 : return (EXIT_FAILURE);
407 : : }
408 : :
409 [ # # ]: 0 : if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
410 : 0 : return (EXIT_FAILURE);
411 : :
412 : 0 : index = hash_indexfile(indexfile);
413 : :
414 [ # # ]: 0 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
415 : 0 : pkgdb_close(db);
416 : 0 : free_index(index);
417 : 0 : warnx("Cannot get a read lock on the database. "
418 : : "It is locked by another process");
419 : 0 : return (EXIT_FAILURE);
420 : : }
421 : :
422 : 0 : it = pkgdb_query(db, pattern, match);
423 [ # # ]: 0 : if (it == NULL)
424 : 0 : goto cleanup;
425 : :
426 [ # # ]: 0 : while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
427 : 0 : pkg_get(pkg, PKG_NAME, &name, PKG_ORIGIN, &origin);
428 : :
429 : : /* If -O was specified, check if this origin matches */
430 [ # # # # ]: 0 : if ((opt & VERSION_WITHORIGIN) &&
431 : 0 : strcmp(origin, matchorigin) != 0)
432 : 0 : continue;
433 : :
434 : : /* If -n was specified, check if this name matches */
435 [ # # # # ]: 0 : if ((opt & VERSION_WITHNAME) &&
436 : 0 : strcmp(name, matchname) != 0)
437 : 0 : continue;
438 : :
439 : 0 : ie = pkghash_get_value(index, name);
440 [ # # ]: 0 : print_version(pkg, "index", ie != NULL ? ie->version : NULL,
441 : 0 : limchar, opt);
442 : : }
443 : :
444 : : cleanup:
445 : 0 : pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
446 : 0 : free_index(index);
447 : 0 : pkg_free(pkg);
448 : 0 : pkgdb_it_free(it);
449 : 0 : pkgdb_close(db);
450 : :
451 : 0 : return (EPKG_OK);
452 : 0 : }
453 : :
454 : : static int
455 : 0 : do_source_remote(unsigned int opt, char limchar, char *pattern, match_t match,
456 : : bool auto_update, const char *reponame, const char *matchorigin,
457 : : const char *matchname)
458 : : {
459 : 0 : struct pkgdb *db = NULL;
460 : 0 : struct pkgdb_it *it = NULL;
461 : 0 : struct pkgdb_it *it_remote = NULL;
462 : 0 : struct pkg *pkg = NULL;
463 : 0 : struct pkg *pkg_remote = NULL;
464 : : const char *name;
465 : : const char *origin;
466 : : const char *version_remote;
467 : 0 : bool is_origin = false;
468 : :
469 : 0 : int retcode = EPKG_OK;
470 : :
471 [ # # ]: 0 : if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_REMOTE ) {
472 : 0 : usage_version();
473 : 0 : return (EXIT_FAILURE);
474 : : }
475 : :
476 : : /* Only force remote mode if looking up remote, otherwise
477 : : user is forced to have a repo.sqlite */
478 : :
479 [ # # ]: 0 : if (auto_update) {
480 : 0 : retcode = pkgcli_update(false, false, reponame);
481 [ # # ]: 0 : if (retcode != EPKG_OK)
482 : 0 : return (retcode);
483 : 0 : }
484 : :
485 [ # # ]: 0 : if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
486 : 0 : return (EXIT_FAILURE);
487 : :
488 [ # # ]: 0 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
489 : 0 : pkgdb_close(db);
490 : 0 : warnx("Cannot get a read lock on a database. "
491 : : "It is locked by another process");
492 : 0 : return (EXIT_FAILURE);
493 : : }
494 : :
495 : 0 : it = pkgdb_query(db, pattern, match);
496 [ # # ]: 0 : if (it == NULL) {
497 : 0 : retcode = EXIT_FAILURE;
498 : 0 : goto cleanup;
499 : : }
500 : :
501 [ # # ]: 0 : while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
502 : 0 : pkg_get(pkg, PKG_NAME, &name, PKG_ORIGIN, &origin);
503 : :
504 : : /* If -O was specified, check if this origin matches */
505 [ # # # # ]: 0 : if ((opt & VERSION_WITHORIGIN) &&
506 : 0 : strcmp(origin, matchorigin) != 0) {
507 : 0 : is_origin = true;
508 : 0 : continue;
509 : : }
510 : :
511 : : /* If -n was specified, check if this name matches */
512 [ # # # # ]: 0 : if ((opt & VERSION_WITHNAME) &&
513 : 0 : strcmp(name, matchname) != 0) {
514 : 0 : is_origin = false;
515 : 0 : continue;
516 : : }
517 : :
518 [ # # ]: 0 : it_remote = pkgdb_repo_query(db, is_origin ? origin : name, MATCH_EXACT, reponame);
519 [ # # ]: 0 : if (it_remote == NULL) {
520 : 0 : retcode = EXIT_FAILURE;
521 : 0 : goto cleanup;
522 : : }
523 : :
524 [ # # # # ]: 0 : if (pkgdb_it_next(it_remote, &pkg_remote, PKG_LOAD_BASIC)
525 : 0 : == EPKG_OK) {
526 : 0 : pkg_get(pkg_remote, PKG_VERSION, &version_remote);
527 : 0 : print_version(pkg, "remote", version_remote, limchar,
528 : 0 : opt);
529 : 0 : } else {
530 : 0 : print_version(pkg, "remote", NULL, limchar, opt);
531 : : }
532 : 0 : pkgdb_it_free(it_remote);
533 : : }
534 : :
535 : : cleanup:
536 : 0 : pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
537 : :
538 : 0 : pkg_free(pkg);
539 : 0 : pkg_free(pkg_remote);
540 : 0 : pkgdb_it_free(it);
541 : 0 : pkgdb_close(db);
542 : :
543 : 0 : return (retcode);
544 : 0 : }
545 : :
546 : : static int
547 : 0 : exec_buf(xstring *res, char **argv) {
548 : : char buf[BUFSIZ];
549 : : int spawn_err;
550 : : pid_t pid;
551 : : int pfd[2];
552 : : int r, pstat;
553 : : posix_spawn_file_actions_t actions;
554 : :
555 [ # # ]: 0 : if (pipe(pfd) < 0) {
556 : 0 : warn("pipe()");
557 : 0 : return (0);
558 : : }
559 : :
560 [ # # ]: 0 : if ((spawn_err = posix_spawn_file_actions_init(&actions)) != 0) {
561 : 0 : warnx("%s:%s", argv[0], strerror(spawn_err));
562 : 0 : return (0);
563 : : }
564 : :
565 [ # # ]: 0 : if ((spawn_err = posix_spawn_file_actions_addopen(&actions,
566 [ # # ]: 0 : STDERR_FILENO, "/dev/null", O_RDWR, 0)) != 0 ||
567 : 0 : (spawn_err = posix_spawn_file_actions_addopen(&actions,
568 [ # # ]: 0 : STDIN_FILENO, "/dev/null", O_RDONLY, 0)) != 0 ||
569 : 0 : (spawn_err = posix_spawn_file_actions_adddup2(&actions,
570 [ # # # # : 0 : pfd[1], STDOUT_FILENO)!= 0) ||
# # ]
571 : 0 : (spawn_err = posix_spawnp(&pid, argv[0], &actions, NULL,
572 : 0 : argv, environ)) != 0) {
573 : 0 : posix_spawn_file_actions_destroy(&actions);
574 : 0 : warnx("%s:%s", argv[0], strerror(spawn_err));
575 : 0 : return (0);
576 : : }
577 : 0 : posix_spawn_file_actions_destroy(&actions);
578 : :
579 : 0 : close(pfd[1]);
580 : :
581 : 0 : xstring_reset(res);
582 [ # # ]: 0 : while ((r = read(pfd[0], buf, BUFSIZ)) > 0)
583 : 0 : fwrite(buf, sizeof(char), r, res->fp);
584 : :
585 : 0 : close(pfd[0]);
586 [ # # ]: 0 : while (waitpid(pid, &pstat, 0) == -1) {
587 [ # # ]: 0 : if (errno != EINTR)
588 : 0 : return (-1);
589 : : }
590 [ # # ]: 0 : if (WEXITSTATUS(pstat) != 0)
591 : 0 : return (-1);
592 : :
593 : 0 : fflush(res->fp);
594 : 0 : return (strlen(res->buf));
595 : 0 : }
596 : :
597 : : static struct category *
598 : 0 : category_new(char *categorypath, const char *category)
599 : : {
600 : 0 : struct category *cat = NULL;
601 : : xstring *makecmd;
602 : : char *results, *d, *key;
603 : : char *argv[5];
604 : :
605 : 0 : makecmd = xstring_new();
606 : :
607 : 0 : argv[0] = "make";
608 : 0 : argv[1] = "-C";
609 : 0 : argv[2] = categorypath;
610 : 0 : argv[3] = "-VSUBDIR";
611 : 0 : argv[4] = NULL;
612 : :
613 [ # # ]: 0 : if (exec_buf(makecmd, argv) <= 0)
614 : 0 : goto cleanup;
615 : :
616 : 0 : fflush(makecmd->fp);
617 : 0 : results = makecmd->buf;
618 : :
619 [ # # ]: 0 : if (categories == NULL)
620 : 0 : categories = pkghash_new();
621 : :
622 : 0 : cat = xcalloc(1, sizeof(*cat));
623 : 0 : cat->name = xstrdup(category);
624 : :
625 : 0 : pkghash_add(categories, cat->name, cat, NULL);
626 [ # # ]: 0 : while ((d = strsep(&results, " \n")) != NULL)
627 [ # # # # ]: 0 : pkghash_safe_add(cat->ports, key, NULL, NULL);
628 : :
629 : : cleanup:
630 : 0 : xstring_free(makecmd);
631 : :
632 : 0 : return (cat);
633 : : }
634 : :
635 : : static bool
636 : 0 : validate_origin(const char *portsdir, const char *origin)
637 : : {
638 : : struct category *cat;
639 : : char *category, *buf;
640 : : char categorypath[MAXPATHLEN];
641 : :
642 : : /* If the origin does not contain a / ignore it like for
643 : : * "base"
644 : : */
645 [ # # ]: 0 : if (strchr(origin, '/') == NULL)
646 : 0 : return (false);
647 : :
648 : 0 : snprintf(categorypath, MAXPATHLEN, "%s/%s", portsdir, origin);
649 : :
650 : 0 : buf = strrchr(categorypath, '/');
651 : 0 : buf[0] = '\0';
652 : 0 : category = strrchr(categorypath, '/');
653 : 0 : category++;
654 : :
655 : 0 : cat = pkghash_get_value(categories, category);
656 [ # # ]: 0 : if (cat == NULL)
657 : 0 : cat = category_new(categorypath, category);
658 [ # # ]: 0 : if (cat == NULL)
659 : 0 : return (false);
660 : :
661 : 0 : buf = strrchr(origin, '/');
662 : 0 : buf++;
663 : :
664 [ # # ]: 0 : if (strcmp(origin, "base") == 0)
665 : 0 : return (false);
666 : :
667 : 0 : return (pkghash_get(cat->ports, buf) != NULL);
668 : 0 : }
669 : :
670 : : static const char *
671 : 0 : port_version(xstring *cmd, const char *portsdir, const char *origin,
672 : : const char *pkgname)
673 : : {
674 : : char *output, *walk, *name;
675 : 0 : char *version = NULL;
676 : : char *argv[5];
677 : :
678 : : /* Validate the port origin -- check the SUBDIR settings
679 : : in the ports and category Makefiles, then extract the
680 : : version from the port itself. */
681 : :
682 [ # # ]: 0 : if (validate_origin(portsdir, origin)) {
683 : 0 : fprintf(cmd->fp, "%s/%s", portsdir, origin);
684 : :
685 : 0 : fflush(cmd->fp);
686 : 0 : argv[0] = "make";
687 : 0 : argv[1] = "-C";
688 : 0 : argv[2] = cmd->buf;
689 : 0 : argv[3] = "flavors-package-names";
690 : 0 : argv[4] = NULL;
691 : :
692 [ # # ]: 0 : if (exec_buf(cmd, argv) > 0) {
693 : 0 : fflush(cmd->fp);
694 : 0 : output = cmd->buf;
695 [ # # ]: 0 : while ((walk = strsep(&output, "\n")) != NULL) {
696 : 0 : name = walk;
697 : 0 : walk = strrchr(walk, '-');
698 [ # # ]: 0 : if (walk == NULL)
699 : 0 : continue;
700 : 0 : walk[0] = '\0';
701 : 0 : walk++;
702 [ # # ]: 0 : if (strcmp(name, pkgname) == 0) {
703 : 0 : version = walk;
704 : 0 : break;
705 : : }
706 : : }
707 : 0 : }
708 : 0 : }
709 : :
710 : 0 : return (version);
711 : : }
712 : :
713 : : static int
714 : 0 : do_source_ports(unsigned int opt, char limchar, char *pattern, match_t match,
715 : : const char *matchorigin, const char *matchname, const char *portsdir)
716 : : {
717 : 0 : struct pkgdb *db = NULL;
718 : 0 : struct pkgdb_it *it = NULL;
719 : 0 : struct pkg *pkg = NULL;
720 : : xstring *cmd;
721 : : const char *name;
722 : : const char *origin;
723 : : const char *version;
724 : :
725 [ # # ]: 0 : if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_PORTS ) {
726 : 0 : usage_version();
727 : 0 : return (EXIT_FAILURE);
728 : : }
729 : :
730 [ # # ]: 0 : if (chdir(portsdir) != 0)
731 : 0 : err(EXIT_FAILURE, "Cannot chdir to %s\n", portsdir);
732 : :
733 [ # # ]: 0 : if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
734 : 0 : return (EXIT_FAILURE);
735 : :
736 [ # # ]: 0 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
737 : 0 : pkgdb_close(db);
738 : 0 : warnx("Cannot get a read lock on a database. "
739 : : "It is locked by another process");
740 : 0 : return (EXIT_FAILURE);
741 : : }
742 : :
743 [ # # ]: 0 : if ((it = pkgdb_query(db, pattern, match)) == NULL)
744 : 0 : goto cleanup;
745 : :
746 : 0 : cmd = xstring_new();
747 : :
748 [ # # ]: 0 : while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
749 : 0 : pkg_get(pkg, PKG_NAME, &name, PKG_ORIGIN, &origin);
750 : :
751 : : /* If -O was specified, check if this origin matches */
752 [ # # # # ]: 0 : if ((opt & VERSION_WITHORIGIN) &&
753 : 0 : strcmp(origin, matchorigin) != 0)
754 : 0 : continue;
755 : :
756 : : /* If -n was specified, check if this name matches */
757 [ # # # # ]: 0 : if ((opt & VERSION_WITHNAME) &&
758 : 0 : strcmp(name, matchname) != 0)
759 : 0 : continue;
760 : :
761 : 0 : version = port_version(cmd, portsdir, origin, name);
762 : 0 : print_version(pkg, "port", version, limchar, opt);
763 : 0 : xstring_reset(cmd);
764 : : }
765 : :
766 : 0 : xstring_free(cmd);
767 : :
768 : : cleanup:
769 : 0 : pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
770 : :
771 : 0 : free_categories();
772 : 0 : pkg_free(pkg);
773 : 0 : pkgdb_it_free(it);
774 : 0 : pkgdb_close(db);
775 : :
776 : 0 : return (EPKG_OK);
777 : 0 : }
778 : :
779 : : int
780 : 44 : exec_version(int argc, char **argv)
781 : : {
782 : 44 : unsigned int opt = 0;
783 : 44 : char limchar = '-';
784 : 44 : const char *matchorigin = NULL;
785 : 44 : const char *matchname = NULL;
786 : 44 : const char *reponame = NULL;
787 : : const char *portsdir;
788 : : const char *indexfile;
789 : : const char *versionsource;
790 : : char filebuf[MAXPATHLEN];
791 : 44 : match_t match = MATCH_ALL;
792 : 44 : char *pattern = NULL;
793 : : int ch;
794 : :
795 : 44 : struct option longopts[] = {
796 : : { "case-sensitive", no_argument, NULL, 'C' },
797 : : { "exact", required_argument, NULL, 'e' },
798 : : { "glob", required_argument, NULL, 'g' },
799 : : { "help", no_argument, NULL, 'h' },
800 : : { "index", no_argument, NULL, 'I' },
801 : : { "case-insensitive", no_argument, NULL, 'i' },
802 : : { "not-like", required_argument, NULL, 'L' },
803 : : { "like", required_argument, NULL, 'l' },
804 : : { "match-name", required_argument, NULL, 'n' },
805 : : { "match-origin", required_argument, NULL, 'O' },
806 : : { "origin", no_argument, NULL, 'o' },
807 : : { "ports", no_argument, NULL, 'P' },
808 : : { "quiet", no_argument, NULL, 'q' },
809 : : { "remote", no_argument, NULL, 'R' },
810 : : { "repository", required_argument, NULL, 'r' },
811 : : { "test-pattern", no_argument, NULL, 'T' },
812 : : { "test-version", no_argument, NULL, 't' },
813 : : { "no-repo-update", no_argument, NULL, 'U' },
814 : : { "verbose", no_argument, NULL, 'v' },
815 : : { "regex", required_argument, NULL, 'x' },
816 : : { NULL, 0, NULL, 0 },
817 : : };
818 : :
819 [ + + + + : 88 : while ((ch = getopt_long(argc, argv, "+Ce:g:hIiL:l:n:O:oPqRr:TtUvx:",
+ + ]
820 : 176 : longopts, NULL)) != -1) {
821 [ - - - - : 44 : switch (ch) {
- - - - -
- - - - -
- + - - -
- - ]
822 : : case 'C':
823 : 0 : pkgdb_set_case_sensitivity(true);
824 : 0 : break;
825 : : case 'e':
826 : 0 : match = MATCH_EXACT;
827 : 0 : pattern = optarg;
828 : 0 : break;
829 : : case 'g':
830 : 0 : match = MATCH_GLOB;
831 : 0 : pattern = optarg;
832 : 0 : break;
833 : : case 'h':
834 : 0 : usage_version();
835 : 0 : return (EXIT_SUCCESS);
836 : : case 'I':
837 : 0 : opt |= VERSION_SOURCE_INDEX;
838 : 0 : break;
839 : : case 'i':
840 : 0 : pkgdb_set_case_sensitivity(false);
841 : 0 : break;
842 : : case 'L':
843 : 0 : opt |= VERSION_NOSTATUS;
844 : 0 : limchar = *optarg;
845 : 0 : break;
846 : : case 'l':
847 : 0 : opt |= VERSION_STATUS;
848 : 0 : limchar = *optarg;
849 : 0 : break;
850 : : case 'n':
851 : 0 : opt |= VERSION_WITHNAME;
852 : 0 : matchname = optarg;
853 : 0 : break;
854 : : case 'O':
855 : 0 : opt |= VERSION_WITHORIGIN;
856 : 0 : matchorigin = optarg;
857 : 0 : break;
858 : : case 'o':
859 : 0 : opt |= VERSION_ORIGIN;
860 : 0 : break;
861 : : case 'P':
862 : 0 : opt |= VERSION_SOURCE_PORTS;
863 : 0 : break;
864 : : case 'q':
865 : 0 : opt |= VERSION_QUIET;
866 : 0 : break;
867 : : case 'R':
868 : 0 : opt |= VERSION_SOURCE_REMOTE;
869 : 0 : break;
870 : : case 'r':
871 : 0 : opt |= VERSION_SOURCE_REMOTE;
872 : 0 : reponame = optarg;
873 : 0 : break;
874 : : case 'T':
875 : 0 : opt |= VERSION_TESTPATTERN;
876 : 0 : break;
877 : : case 't':
878 : 44 : opt |= VERSION_TESTVERSION;
879 : 44 : break;
880 : : case 'U':
881 : 0 : auto_update = false;
882 : 0 : break;
883 : : case 'v':
884 : 0 : opt |= VERSION_VERBOSE;
885 : 0 : break;
886 : : case 'x':
887 : 0 : match = MATCH_REGEX;
888 : 0 : pattern = optarg;
889 : 0 : break;
890 : : default:
891 : 0 : usage_version();
892 : 0 : return (EXIT_FAILURE);
893 : : }
894 : : }
895 : 44 : argc -= optind;
896 : 44 : argv += optind;
897 : :
898 : : /*
899 : : * Allowed option combinations:
900 : : * -t ver1 ver2 -- only
901 : : * -T pkgname pattern -- only
902 : : * Only one of -I -P -R can be given
903 : : */
904 : :
905 [ - + # # ]: 44 : if (matchorigin != NULL && matchname != NULL) {
906 : 0 : usage_version();
907 : 0 : return (EXIT_FAILURE);
908 : : }
909 : :
910 [ + - ]: 44 : if ( (opt & VERSION_TESTVERSION) == VERSION_TESTVERSION )
911 : 44 : return (do_testversion(opt, argc, argv));
912 : :
913 [ # # ]: 0 : if ( (opt & VERSION_TESTPATTERN) == VERSION_TESTPATTERN )
914 : 0 : return (do_testpattern(opt, argc, argv));
915 : :
916 [ # # ]: 0 : if (opt & (VERSION_STATUS|VERSION_NOSTATUS)) {
917 [ # # # # ]: 0 : if (limchar != '<' &&
918 [ # # ]: 0 : limchar != '>' &&
919 [ # # ]: 0 : limchar != '=' &&
920 [ # # ]: 0 : limchar != '?' &&
921 : 0 : limchar != '!') {
922 : 0 : usage_version();
923 : 0 : return (EXIT_FAILURE);
924 : : }
925 : 0 : }
926 : :
927 [ # # ]: 0 : if (opt & VERSION_QUIET)
928 : 0 : quiet = true;
929 : :
930 [ # # ]: 0 : if (argc > 1) {
931 : 0 : usage_version();
932 : 0 : return (EXIT_FAILURE);
933 : : }
934 : :
935 [ # # ]: 0 : if ( !(opt & VERSION_SOURCES ) ) {
936 : 0 : versionsource = pkg_object_string(
937 : 0 : pkg_config_get("VERSION_SOURCE"));
938 [ # # ]: 0 : if (versionsource != NULL) {
939 [ # # # # ]: 0 : switch (versionsource[0]) {
940 : : case 'I':
941 : 0 : opt |= VERSION_SOURCE_INDEX;
942 : 0 : break;
943 : : case 'P':
944 : 0 : opt |= VERSION_SOURCE_PORTS;
945 : 0 : break;
946 : : case 'R':
947 : 0 : opt |= VERSION_SOURCE_REMOTE;
948 : 0 : break;
949 : : default:
950 : 0 : warnx("Invalid VERSION_SOURCE"
951 : : " in configuration.");
952 : 0 : }
953 : 0 : }
954 : 0 : }
955 : :
956 [ # # ]: 0 : if ( (opt & VERSION_SOURCE_INDEX) == VERSION_SOURCE_INDEX ) {
957 [ # # # # ]: 0 : if (!have_indexfile(&indexfile, filebuf, sizeof(filebuf),
958 : 0 : argc, argv, true))
959 : 0 : return (EXIT_FAILURE);
960 : : else
961 : 0 : return (do_source_index(opt, limchar, pattern, match,
962 : 0 : matchorigin, matchname, indexfile));
963 : : }
964 : :
965 [ # # ]: 0 : if ( (opt & VERSION_SOURCE_REMOTE) == VERSION_SOURCE_REMOTE )
966 : 0 : return (do_source_remote(opt, limchar, pattern, match,
967 : 0 : auto_update, reponame, matchorigin, matchname));
968 : :
969 [ # # ]: 0 : if ( (opt & VERSION_SOURCE_PORTS) == VERSION_SOURCE_PORTS ) {
970 [ # # ]: 0 : if (!have_ports(&portsdir, true))
971 : 0 : return (EXIT_FAILURE);
972 : : else
973 : 0 : return (do_source_ports(opt, limchar, pattern,
974 : 0 : match, matchorigin, matchname, portsdir));
975 : : }
976 : :
977 : : /* If none of -IPR were specified, and INDEX exists use that.
978 : : Failing that, if portsdir exists and is valid, use that
979 : : (slow) otherwise fallback to remote. */
980 : :
981 [ # # ]: 0 : if (have_indexfile(&indexfile, filebuf, sizeof(filebuf), argc, argv,
982 : : false)) {
983 : 0 : opt |= VERSION_SOURCE_INDEX;
984 : 0 : return (do_source_index(opt, limchar, pattern, match,
985 : 0 : matchorigin, matchname, indexfile));
986 [ # # ]: 0 : } else if (have_ports(&portsdir, false)) {
987 [ # # ]: 0 : if (argc == 1) {
988 : 0 : warnx("No such INDEX file: '%s'", argv[0]);
989 : 0 : return (EXIT_FAILURE);
990 : : }
991 : 0 : opt |= VERSION_SOURCE_PORTS;
992 : 0 : return (do_source_ports(opt, limchar, pattern, match,
993 : 0 : matchorigin, matchname, portsdir));
994 : : } else {
995 : 0 : opt |= VERSION_SOURCE_REMOTE;
996 : 0 : return (do_source_remote(opt, limchar, pattern, match,
997 : 0 : auto_update, reponame, matchorigin, matchname));
998 : : }
999 : :
1000 : : /* NOTREACHED */
1001 : : return (EXIT_FAILURE);
1002 : 44 : }
1003 : : /*
1004 : : * That's All Folks!
1005 : : */
|