xref: /dflybsd-src/usr.bin/dsynth/pkglist.c (revision 8b07b5e82a38bb7f04797b88aca14d3ccb08532c)
1 /*
2  * Copyright (c) 2019 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * This code uses concepts and configuration based on 'synth', by
8  * John R. Marino <draco@marino.st>, which was written in ada.
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  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  * 3. Neither the name of The DragonFly Project nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific, prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
28  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include "dsynth.h"
39 
40 #define PKG_HSIZE	32768
41 #define PKG_HMASK	32767
42 
43 static int parsepkglist_file(const char *path, int debugstop);
44 static void childGetPackageInfo(bulk_t *bulk);
45 static void childGetBinaryDistInfo(bulk_t *bulk);
46 static void childOptimizeEnv(bulk_t *bulk);
47 static pkg_t *resolveDeps(pkg_t *dep_list, pkg_t ***list_tailp, int gentopo);
48 static void resolveDepString(pkg_t *pkg, char *depstr,
49 			int gentopo, int dep_type);
50 static pkg_t *processPackageListBulk(int total);
51 static int scan_and_queue_dir(const char *path, const char *level1, int level);
52 static int scan_binary_repo(const char *path);
53 #if 0
54 static void pkgfree(pkg_t *pkg);
55 #endif
56 
57 pkg_t *PkgHash1[PKG_HSIZE];	/* by portdir */
58 pkg_t *PkgHash2[PKG_HSIZE];	/* by pkgfile */
59 
60 /*
61  * Allocate a new pkg structure plus basic initialization.
62  */
63 static __inline pkg_t *
64 allocpkg(void)
65 {
66 	pkg_t *pkg;
67 
68 	pkg = calloc(1, sizeof(*pkg));
69 	pkg->idepon_list.next = &pkg->idepon_list;
70 	pkg->idepon_list.prev = &pkg->idepon_list;
71 	pkg->deponi_list.next = &pkg->deponi_list;
72 	pkg->deponi_list.prev = &pkg->deponi_list;
73 
74 	return pkg;
75 }
76 
77 /*
78  * Simple hash for lookups
79  */
80 static __inline int
81 pkghash(const char *str)
82 {
83 	int hv = 0xABC32923;
84 	while (*str) {
85 		hv = (hv << 5) ^ *str;
86 		++str;
87 	}
88 	hv = hv ^ (hv / PKG_HSIZE) ^ (hv / PKG_HSIZE / PKG_HSIZE);
89 	return (hv & PKG_HMASK);
90 }
91 
92 static void
93 pkg_enter(pkg_t *pkg)
94 {
95 	pkg_t **pkgp;
96 	pkg_t *scan;
97 
98 	if (pkg->portdir) {
99 		pkgp = &PkgHash1[pkghash(pkg->portdir)];
100 		while ((scan = *pkgp) != NULL) {
101 			if (strcmp(pkg->portdir, scan->portdir) == 0)
102 				break;
103 			pkgp = &scan->hnext1;
104 		}
105 		if (scan && (scan->flags & PKGF_PLACEHOLD)) {
106 			*pkgp = pkg;
107 			pkg->hnext1 = scan->hnext1;
108 			free(scan->portdir);
109 			free(scan);
110 			scan = NULL;
111 		}
112 		if (scan == NULL)
113 			*pkgp = pkg;
114 	}
115 
116 	if (pkg->pkgfile) {
117 		pkgp = &PkgHash2[pkghash(pkg->pkgfile)];
118 		while ((scan = *pkgp) != NULL) {
119 			if (strcmp(pkg->pkgfile, scan->pkgfile) == 0)
120 				break;
121 			pkgp = &scan->hnext2;
122 		}
123 		if (scan == NULL)
124 			*pkgp = pkg;
125 	}
126 }
127 
128 static pkg_t *
129 pkg_find(const char *match)
130 {
131 	pkg_t **pkgp;
132 	pkg_t *pkg;
133 
134 	pkgp = &PkgHash1[pkghash(match)];
135 	for (pkg = *pkgp; pkg; pkg = pkg->hnext1) {
136 		if (strcmp(pkg->portdir, match) == 0)
137 			return pkg;
138 	}
139 	pkgp = &PkgHash2[pkghash(match)];
140 	for (pkg = *pkgp; pkg; pkg = pkg->hnext2) {
141 		if (strcmp(pkg->pkgfile, match) == 0)
142 			return pkg;
143 	}
144 	return NULL;
145 }
146 
147 /*
148  * Parse a specific list of ports via origin name (portdir/subdir)
149  */
150 pkg_t *
151 ParsePackageList(int n, char **ary, int debugstop)
152 {
153 	pkg_t *list;
154 	int i;
155 	int total;
156 
157 	total = 0;
158 	initbulk(childGetPackageInfo, MaxBulk);
159 
160 	/*
161 	 * Always include ports-mgmt/pkg.  s4 is "x" meaning not a manual
162 	 * selection, "d" meaning DEBUGSTOP mode, or NULL.
163 	 */
164 	queuebulk("ports-mgmt", "pkg", NULL, "x");
165 
166 	for (i = 0; i < n; ++i) {
167 		char *l1;
168 		char *l2;
169 		char *l3;
170 		struct stat st;
171 
172 		l1 = strdup(ary[i]);
173 		if (stat(l1, &st) == 0 && S_ISREG(st.st_mode)) {
174 			total += parsepkglist_file(l1, debugstop);
175 			continue;
176 		}
177 
178 		l2 = strchr(l1, '/');
179 		if (l2 == NULL) {
180 			printf("Bad portdir specification: %s\n", l1);
181 			free(l1);
182 			continue;
183 		}
184 		*l2++ = 0;
185 		l3 = strchr(l2, '@');
186 		if (l3)
187 			*l3++ = 0;
188 		queuebulk(l1, l2, l3, (debugstop ? "d" : NULL));
189 		++total;
190 		free(l1);
191 	}
192 	printf("Processing %d ports\n", total);
193 
194 	list = processPackageListBulk(total);
195 
196 	return list;
197 }
198 
199 static
200 int
201 parsepkglist_file(const char *path, int debugstop)
202 {
203 	FILE *fp;
204 	char *base;
205 	char *l1;
206 	char *l2;
207 	char *l3;
208 	size_t len;
209 	int total;
210 
211 	if ((fp = fopen(path, "r")) == NULL) {
212 		dpanic_errno("Cannot read %s\n", path);
213 		/* NOT REACHED */
214 		return 0;
215 	}
216 
217 	total = 0;
218 
219 	while ((base = fgetln(fp, &len)) != NULL) {
220 		if (len == 0 || base[len-1] != '\n')
221 			continue;
222 		base[--len] = 0;
223 		l1 = strtok(base, " \t\r\n");
224 		if (l1 == NULL) {
225 			printf("Badly formatted pkg info line: %s\n", base);
226 			continue;
227 		}
228 		l2 = strchr(l1, '/');
229 		if (l2 == NULL) {
230 			printf("Badly formatted specification: %s\n", l1);
231 			continue;
232 		}
233 		*l2++ = 0;
234 		l3 = strchr(l2, '@');
235 		if (l3)
236 			*l3++ = 0;
237 		queuebulk(l1, l2, l3, (debugstop ? "d" : NULL));
238 		++total;
239 	}
240 	fclose(fp);
241 
242 	return total;
243 }
244 
245 /*
246  * Parse packages from the list installed on the system.
247  */
248 pkg_t *
249 GetLocalPackageList(void)
250 {
251 	pkg_t *list;
252 	FILE *fp;
253 	char *base;
254 	char *l1;
255 	char *l2;
256 	char *l3;
257 	int total;
258 	size_t len;
259 
260 	initbulk(childGetPackageInfo, MaxBulk);
261 	total = 0;
262 
263 	fp = popen("pkg info -a -o", "r");
264 
265 	/*
266 	 * Always include ports-mgmt/pkg.  s4 is "x" meaning not a manual
267 	 * selection, "d" meaning DEBUGSTOP mode, or NULL.
268 	 */
269 	queuebulk("ports-mgmt", "pkg", NULL, "x");
270 
271 	while ((base = fgetln(fp, &len)) != NULL) {
272 		if (len == 0 || base[len-1] != '\n')
273 			continue;
274 		base[--len] = 0;
275 		if (strtok(base, " \t") == NULL) {
276 			printf("Badly formatted pkg info line: %s\n", base);
277 			continue;
278 		}
279 		l1 = strtok(NULL, " \t");
280 		if (l1 == NULL) {
281 			printf("Badly formatted pkg info line: %s\n", base);
282 			continue;
283 		}
284 
285 		l2 = strchr(l1, '/');
286 		if (l2 == NULL) {
287 			printf("Badly formatted specification: %s\n", l1);
288 			continue;
289 		}
290 		*l2++ = 0;
291 		l3 = strchr(l2, '@');
292 		if (l3)
293 			*l3++ = 0;
294 		queuebulk(l1, l2, l3, NULL);
295 		++total;
296 	}
297 	pclose(fp);
298 
299 	printf("Processing %d ports\n", total);
300 
301 	list = processPackageListBulk(total);
302 
303 	return list;
304 }
305 
306 pkg_t *
307 GetFullPackageList(void)
308 {
309 	int total;
310 
311 	initbulk(childGetPackageInfo, MaxBulk);
312 
313 	total = scan_and_queue_dir(DPortsPath, NULL, 1);
314 	printf("Scanning %d ports\n", total);
315 
316 	return processPackageListBulk(total);
317 }
318 
319 /*
320  * Caller has queued the process list for bulk operation.  We retrieve
321  * the results and clean up the bulk operation (we may have to do a second
322  * bulk operation so we have to be the ones to clean it up).
323  */
324 static pkg_t *
325 processPackageListBulk(int total)
326 {
327 	bulk_t *bulk;
328 	pkg_t *scan;
329 	pkg_t *list;
330 	pkg_t *dep_list;
331 	pkg_t **list_tail;
332 	int count;
333 
334 	list = NULL;
335 	list_tail = &list;
336 	count = 0;
337 
338 	while ((bulk = getbulk()) != NULL) {
339 		++count;
340 		if ((count & 255) == 0) {
341 			printf("%6.2f%%\r",
342 				(double)count * 100.0 / (double)total + 0.001);
343 			fflush(stdout);
344 		}
345 		if (bulk->list) {
346 			*list_tail = bulk->list;
347 			bulk->list = NULL;
348 			while ((scan = *list_tail) != NULL) {
349 				if (bulk->s4 == NULL || bulk->s4[0] != 'x')
350 					scan->flags |= PKGF_MANUALSEL;
351 				pkg_enter(scan);
352 				list_tail = &scan->bnext;
353 			}
354 		}
355 		freebulk(bulk);
356 	}
357 	printf("100.00%%\n");
358 	printf("\nTotal %d\n", count);
359 	fflush(stdout);
360 
361 	/*
362 	 * Resolve all dependencies for the related packages, potentially
363 	 * adding anything that could not be found to the list.  This will
364 	 * continue to issue bulk operations and process the result until
365 	 * no dependencies are left.
366 	 */
367 	printf("Resolving dependencies...");
368 	fflush(stdout);
369 	dep_list = list;
370 	while (dep_list) {
371 		dep_list = resolveDeps(dep_list, &list_tail, 0);
372 	}
373 	printf("done\n");
374 
375 	donebulk();
376 
377 	/*
378 	 * Generate the topology
379 	 */
380 	resolveDeps(list, NULL, 1);
381 
382 	/*
383 	 * Do a final count, ignore place holders.
384 	 */
385 	count = 0;
386 	for (scan = list; scan; scan = scan->bnext) {
387 		if ((scan->flags & PKGF_ERROR) == 0) {
388 			++count;
389 		}
390 	}
391 	printf("Total Returned %d\n", count);
392 
393 	/*
394 	 * Scan our binary distributions and related dependencies looking
395 	 * for any packages that have already been built.
396 	 */
397 	initbulk(childGetBinaryDistInfo, MaxBulk);
398 	total = scan_binary_repo(RepositoryPath);
399 	count = 0;
400 	printf("Scanning %d packages\n", total);
401 
402 	while ((bulk = getbulk()) != NULL) {
403 		++count;
404 		if ((count & 255) == 0) {
405 			printf("%6.2f%%\r",
406 				(double)count * 100.0 / (double)total + 0.001);
407 			fflush(stdout);
408 		}
409 		freebulk(bulk);
410 	}
411 	printf("100.00%%\n");
412 	printf("\nTotal %d\n", count);
413 	fflush(stdout);
414 	donebulk();
415 
416 	printf("all done\n");
417 
418 	return list;
419 }
420 
421 pkg_t *
422 GetPkgPkg(pkg_t *list)
423 {
424 	bulk_t *bulk;
425 	pkg_t *scan;
426 
427 	for (scan = list; scan; scan = scan->bnext) {
428 		if (strcmp(scan->portdir, "ports-mgmt/pkg") == 0)
429 			return scan;
430 	}
431 
432 	/*
433 	 * This will force pkg to be built, but generally this code
434 	 * is not reached because the package list processing code
435 	 * adds ports-mgmt/pkg unconditionally.
436 	 */
437 	initbulk(childGetPackageInfo, MaxBulk);
438 	queuebulk("ports-mgmt", "pkg", NULL, "x");
439 	bulk = getbulk();
440 	dassert(bulk, "Cannot find ports-mgmt/pkg");
441 	scan = bulk->list;
442 	bulk->list = NULL;
443 	freebulk(bulk);
444 	donebulk();
445 
446 	return scan;
447 }
448 
449 /*
450  * Try to optimize the environment by supplying information that
451  * the ports system would generally have to run stuff to get on
452  * every package.
453  *
454  * See childOptimizeEnv() for the actual handling.  We execute
455  * a single make -V... -V... for ports-mgmt/pkg from within the
456  * bulk system (which handles the environment and disables
457  * /etc/make.conf), and we then call addbuildenv() as appropriate.
458  *
459  * _PERL5_FROM_BIN
460  * add others...
461  */
462 void
463 OptimizeEnv(void)
464 {
465 	bulk_t *bulk;
466 
467 	initbulk(childOptimizeEnv, MaxBulk);
468 	queuebulk("ports-mgmt", "pkg", NULL, NULL);
469 	bulk = getbulk();
470 	freebulk(bulk);
471 	donebulk();
472 }
473 
474 /*
475  * Run through the list resolving dependencies and constructing the topology
476  * linkages.   This may append packages to the list.
477  */
478 static pkg_t *
479 resolveDeps(pkg_t *list, pkg_t ***list_tailp, int gentopo)
480 {
481 	pkg_t *scan;
482 	pkg_t *ret_list = NULL;
483 	bulk_t *bulk;
484 
485 	for (scan = list; scan; scan = scan->bnext) {
486 		resolveDepString(scan, scan->fetch_deps,
487 				 gentopo, DEP_TYPE_FETCH);
488 		resolveDepString(scan, scan->ext_deps,
489 				 gentopo, DEP_TYPE_EXT);
490 		resolveDepString(scan, scan->patch_deps,
491 				 gentopo, DEP_TYPE_PATCH);
492 		resolveDepString(scan, scan->build_deps,
493 				 gentopo, DEP_TYPE_BUILD);
494 		resolveDepString(scan, scan->lib_deps,
495 				 gentopo, DEP_TYPE_LIB);
496 		resolveDepString(scan, scan->run_deps,
497 				 gentopo, DEP_TYPE_RUN);
498 	}
499 
500 	/*
501 	 * No bulk ops are queued when doing the final topology
502 	 * generation.
503 	 */
504 	if (gentopo)
505 		return NULL;
506 	while ((bulk = getbulk()) != NULL) {
507 		if (bulk->list) {
508 			if (ret_list == NULL)
509 				ret_list = bulk->list;
510 			**list_tailp = bulk->list;
511 			bulk->list = NULL;
512 			while (**list_tailp) {
513 				pkg_enter(**list_tailp);
514 				*list_tailp = &(**list_tailp)->bnext;
515 			}
516 		}
517 		freebulk(bulk);
518 	}
519 	return (ret_list);
520 }
521 
522 static void
523 resolveDepString(pkg_t *pkg, char *depstr, int gentopo, int dep_type)
524 {
525 	char *copy_base;
526 	char *copy;
527 	char *dep;
528 	char *sep;
529 	char *tag;
530 	char *flavor;
531 	pkg_t *dpkg;
532 
533 	if (depstr == NULL || depstr[0] == 0)
534 		return;
535 
536 	copy_base = strdup(depstr);
537 	copy = copy_base;
538 
539 	for (;;) {
540 		do {
541 			dep = strsep(&copy, " \t");
542 		} while (dep && *dep == 0);
543 		if (dep == NULL)
544 			break;
545 
546 		/*
547 		 * Ignore dependencies prefixed with ${NONEXISTENT}
548 		 */
549 		if (strncmp(dep, "/nonexistent:", 13) == 0)
550 			continue;
551 
552 		dep = strchr(dep, ':');
553 		if (dep == NULL || *dep != ':') {
554 			printf("Error parsing dependency for %s: %s\n",
555 			       pkg->portdir, copy_base);
556 			continue;
557 		}
558 		++dep;
559 
560 		/*
561 		 * Strip-off any DPortsPath prefix.  EXTRACT_DEPENDS
562 		 * often (always?) generates this prefix.
563 		 */
564 		if (strncmp(dep, DPortsPath, strlen(DPortsPath)) == 0) {
565 			dep += strlen(DPortsPath);
566 			if (*dep == '/')
567 				++dep;
568 		}
569 
570 		/*
571 		 * Strip-off any tag (such as :patch).  We don't try to
572 		 * organize dependencies at this fine a grain (for now).
573 		 */
574 		tag = strchr(dep, ':');
575 		if (tag)
576 			*tag++ = 0;
577 
578 		/*
579 		 * Locate the dependency
580 		 */
581 		if ((dpkg = pkg_find(dep)) != NULL) {
582 			if (gentopo) {
583 				pkglink_t *link;
584 
585 				/*
586 				 * NOTE: idep_count is calculated recursively
587 				 *	 at build-time
588 				 */
589 				ddprintf(0, "Add Dependency %s -> %s\n",
590 					pkg->portdir, dpkg->portdir);
591 				link = calloc(1, sizeof(*link));
592 				link->pkg = dpkg;
593 				link->next = &pkg->idepon_list;
594 				link->prev = pkg->idepon_list.prev;
595 				link->next->prev = link;
596 				link->prev->next = link;
597 				link->dep_type = dep_type;
598 
599 				link = calloc(1, sizeof(*link));
600 				link->pkg = pkg;
601 				link->next = &dpkg->deponi_list;
602 				link->prev = dpkg->deponi_list.prev;
603 				link->next->prev = link;
604 				link->prev->next = link;
605 				link->dep_type = dep_type;
606 				++dpkg->depi_count;
607 			}
608 			continue;
609 		}
610 
611 		/*
612 		 * This shouldn't happen because we already took a first
613 		 * pass and should have generated the pkgs.
614 		 */
615 		if (gentopo) {
616 			printf("Topology Generate failed for %s: %s\n",
617 				pkg->portdir, copy_base);
618 			continue;
619 		}
620 
621 		/*
622 		 * Separate out the two dports directory components and
623 		 * extract the optional '@flavor' specification.
624 		 */
625 		sep = strchr(dep, '/');
626 		if (sep == NULL) {
627 			printf("Error parsing dependency for %s: %s\n",
628 			       pkg->portdir, copy_base);
629 			continue;
630 		}
631 		*sep++ = 0;
632 
633 		if (tag)
634 			flavor = strrchr(tag, '@');
635 		else
636 			flavor = strrchr(sep, '@');
637 
638 		if (flavor)
639 			*flavor++ = 0;
640 
641 		if (flavor)
642 			ddprintf(0, "QUEUE DEPENDENCY FROM PKG %s: %s/%s@%s\n",
643 			       pkg->portdir, dep, sep, flavor);
644 		else
645 			ddprintf(0, "QUEUE DEPENDENCY FROM PKG %s: %s/%s\n",
646 			       pkg->portdir, dep, sep);
647 
648 		/*
649 		 * Use a place-holder to prevent duplicate dependencies from
650 		 * being processed.  The placeholder will be replaced by
651 		 * the actual dependency.
652 		 */
653 		dpkg = allocpkg();
654 		if (flavor)
655 			asprintf(&dpkg->portdir, "%s/%s@%s", dep, sep, flavor);
656 		else
657 			asprintf(&dpkg->portdir, "%s/%s", dep, sep);
658 		dpkg->flags = PKGF_PLACEHOLD;
659 		pkg_enter(dpkg);
660 
661 		queuebulk(dep, sep, flavor, NULL);
662 	}
663 	free(copy_base);
664 }
665 
666 void
667 FreePackageList(pkg_t *pkgs __unused)
668 {
669 	dfatal("not implemented");
670 }
671 
672 /*
673  * Scan some or all dports to allocate the related pkg structure.  Dependencies
674  * are stored but not processed.
675  *
676  * Threaded function
677  */
678 static void
679 childGetPackageInfo(bulk_t *bulk)
680 {
681 	pkg_t *pkg;
682 	pkg_t *dummy_node;
683 	pkg_t **list_tail;
684 	char *flavors_save;
685 	char *flavors;
686 	char *flavor;
687 	char *ptr;
688 	FILE *fp;
689 	int line;
690 	size_t len;
691 	char *portpath;
692 	char *flavarg;
693 	const char *cav[MAXCAC];
694 	pid_t pid;
695 	int cac;
696 
697 	/*
698 	 * If the package has flavors we will loop on each one.  If a flavor
699 	 * is not passed in s3 we will loop on all flavors, otherwise we will
700 	 * only process the passed-in flavor.
701 	 */
702 	flavor = bulk->s3;	/* usually NULL */
703 	flavors = NULL;
704 	flavors_save = NULL;
705 	dummy_node = NULL;
706 
707 	bulk->list = NULL;
708 	list_tail = &bulk->list;
709 again:
710 	asprintf(&portpath, "%s/%s/%s", DPortsPath, bulk->s1, bulk->s2);
711 	if (flavor)
712 		asprintf(&flavarg, "FLAVOR=%s", flavor);
713 	else
714 		flavarg = NULL;
715 
716 	cac = 0;
717 	cav[cac++] = MAKE_BINARY;
718 	cav[cac++] = "-C";
719 	cav[cac++] = portpath;
720 	if (flavarg)
721 		cav[cac++] = flavarg;
722 	cav[cac++] = "-VPKGVERSION";
723 	cav[cac++] = "-VPKGFILE:T";
724 	cav[cac++] = "-VDISTFILES";
725 	cav[cac++] = "-VDIST_SUBDIR";
726 	cav[cac++] = "-VMAKE_JOBS_NUMBER";
727 	cav[cac++] = "-VIGNORE";
728 	cav[cac++] = "-VFETCH_DEPENDS";
729 	cav[cac++] = "-VEXTRACT_DEPENDS";
730 	cav[cac++] = "-VPATCH_DEPENDS";
731 	cav[cac++] = "-VBUILD_DEPENDS";
732 	cav[cac++] = "-VLIB_DEPENDS";
733 	cav[cac++] = "-VRUN_DEPENDS";
734 	cav[cac++] = "-VSELECTED_OPTIONS";
735 	cav[cac++] = "-VDESELECTED_OPTIONS";
736 	cav[cac++] = "-VUSE_LINUX";
737 	cav[cac++] = "-VFLAVORS";
738 	cav[cac++] = "-VUSES";
739 
740 	fp = dexec_open(cav, cac, &pid, NULL, 1, 1);
741 	free(portpath);
742 	freestrp(&flavarg);
743 
744 	pkg = allocpkg();
745 	if (flavor)
746 		asprintf(&pkg->portdir, "%s/%s@%s", bulk->s1, bulk->s2, flavor);
747 	else
748 		asprintf(&pkg->portdir, "%s/%s", bulk->s1, bulk->s2);
749 
750 	line = 1;
751 	while ((ptr = fgetln(fp, &len)) != NULL) {
752 		if (len == 0 || ptr[len-1] != '\n') {
753 			dfatal("Bad package info for %s/%s response line %d",
754 			       bulk->s1, bulk->s2, line);
755 		}
756 		ptr[--len] = 0;
757 
758 		switch(line) {
759 		case 1:		/* PKGVERSION */
760 			asprintf(&pkg->version, "%s", ptr);
761 			break;
762 		case 2:		/* PKGFILE */
763 			asprintf(&pkg->pkgfile, "%s", ptr);
764 			break;
765 		case 3:		/* DISTFILES */
766 			asprintf(&pkg->distfiles, "%s", ptr);
767 			break;
768 		case 4:		/* DIST_SUBDIR */
769 			pkg->distsubdir = strdup_or_null(ptr);
770 			break;
771 		case 5:		/* MAKE_JOBS_NUMBER */
772 			pkg->make_jobs_number = strtol(ptr, NULL, 0);
773 			break;
774 		case 6:		/* IGNORE */
775 			pkg->ignore = strdup_or_null(ptr);
776 			break;
777 		case 7:		/* FETCH_DEPENDS */
778 			pkg->fetch_deps = strdup_or_null(ptr);
779 			break;
780 		case 8:		/* EXTRACT_DEPENDS */
781 			pkg->ext_deps = strdup_or_null(ptr);
782 			break;
783 		case 9:		/* PATCH_DEPENDS */
784 			pkg->patch_deps = strdup_or_null(ptr);
785 			break;
786 		case 10:	/* BUILD_DEPENDS */
787 			pkg->build_deps = strdup_or_null(ptr);
788 			break;
789 		case 11:	/* LIB_DEPENDS */
790 			pkg->lib_deps = strdup_or_null(ptr);
791 			break;
792 		case 12:	/* RUN_DEPENDS */
793 			pkg->run_deps = strdup_or_null(ptr);
794 			break;
795 		case 13:	/* SELECTED_OPTIONS */
796 			pkg->pos_options = strdup_or_null(ptr);
797 			break;
798 		case 14:	/* DESELECTED_OPTIONS */
799 			pkg->neg_options = strdup_or_null(ptr);
800 			break;
801 		case 15:	/* USE_LINUX */
802 			if (ptr[0])
803 				pkg->use_linux = 1;
804 			break;
805 		case 16:	/* FLAVORS */
806 			asprintf(&pkg->flavors, "%s", ptr);
807 			break;
808 		case 17:	/* USES */
809 			asprintf(&pkg->uses, "%s", ptr);
810 			if (strstr(pkg->uses, "metaport"))
811 				pkg->flags |= PKGF_META;
812 			break;
813 		default:
814 			printf("EXTRA LINE: %s\n", ptr);
815 			break;
816 		}
817 		++line;
818 	}
819 	if (line == 1) {
820 		printf("DPort not found: %s/%s\n", bulk->s1, bulk->s2);
821 		pkg->flags |= PKGF_NOTFOUND;
822 	} else if (line != 17 + 1) {
823 		printf("DPort corrupt: %s/%s\n", bulk->s1, bulk->s2);
824 		pkg->flags |= PKGF_CORRUPT;
825 	}
826 	if (dexec_close(fp, pid)) {
827 		printf("make -V* command for %s/%s failed\n",
828 			bulk->s1, bulk->s2);
829 		pkg->flags |= PKGF_CORRUPT;
830 	}
831 	ddassert(bulk->s1);
832 
833 	/*
834 	 * DEBUGSTOP mode
835 	 */
836 	if (bulk->s4 && bulk->s4[0] == 'd')
837 		pkg->flags |= PKGF_DEBUGSTOP;
838 
839 	/*
840 	 * Generate flavors
841 	 */
842 	if (flavor == NULL) {
843 		/*
844 		 * If there are flavors add the current unflavored pkg
845 		 * as a dummy node so dependencies can attach to it,
846 		 * then iterate the first flavor and loop.
847 		 *
848 		 * We must NULL out pkgfile because it will have the
849 		 * default flavor and conflict with the actual flavored
850 		 * pkg.
851 		 */
852 		if (pkg->flavors && pkg->flavors[0]) {
853 			dummy_node = pkg;
854 
855 			pkg->flags |= PKGF_DUMMY;
856 
857 			freestrp(&pkg->fetch_deps);
858 			freestrp(&pkg->ext_deps);
859 			freestrp(&pkg->patch_deps);
860 			freestrp(&pkg->build_deps);
861 			freestrp(&pkg->lib_deps);
862 			freestrp(&pkg->run_deps);
863 
864 			freestrp(&pkg->pkgfile);
865 			*list_tail = pkg;
866 			while (*list_tail)
867 				list_tail = &(*list_tail)->bnext;
868 
869 			flavors_save = strdup(pkg->flavors);
870 			flavors = flavors_save;
871 			do {
872 				flavor = strsep(&flavors, " \t");
873 			} while (flavor && *flavor == 0);
874 			goto again;
875 		}
876 
877 		/*
878 		 * No flavors, add the current unflavored pkg as a real
879 		 * node.
880 		 */
881 		*list_tail = pkg;
882 		while (*list_tail)
883 			list_tail = &(*list_tail)->bnext;
884 	} else {
885 		/*
886 		 * Add flavored package and iterate.
887 		 */
888 		*list_tail = pkg;
889 		while (*list_tail)
890 			list_tail = &(*list_tail)->bnext;
891 
892 		/*
893 		 * Flavor iteration under dummy node, add dependency
894 		 */
895 		if (dummy_node) {
896 			pkglink_t *link;
897 
898 			ddprintf(0, "Add Dependency %s -> %s (flavor rollup)\n",
899 				dummy_node->portdir, pkg->portdir);
900 			link = calloc(1, sizeof(*link));
901 			link->pkg = pkg;
902 			link->next = &dummy_node->idepon_list;
903 			link->prev = dummy_node->idepon_list.prev;
904 			link->next->prev = link;
905 			link->prev->next = link;
906 			link->dep_type = DEP_TYPE_BUILD;
907 
908 			link = calloc(1, sizeof(*link));
909 			link->pkg = dummy_node;
910 			link->next = &pkg->deponi_list;
911 			link->prev = pkg->deponi_list.prev;
912 			link->next->prev = link;
913 			link->prev->next = link;
914 			link->dep_type = DEP_TYPE_BUILD;
915 			++pkg->depi_count;
916 		}
917 
918 		if (flavors) {
919 			do {
920 				flavor = strsep(&flavors, " \t");
921 			} while (flavor && *flavor == 0);
922 			if (flavor)
923 				goto again;
924 			free(flavors);
925 		}
926 	}
927 }
928 
929 /*
930  * Query the package (at least to make sure it hasn't been truncated)
931  * and mark it as PACKAGED if found.
932  *
933  * Threaded function
934  */
935 static void
936 childGetBinaryDistInfo(bulk_t *bulk)
937 {
938 	char *ptr;
939 	FILE *fp;
940 	size_t len;
941 	pkg_t *pkg;
942 	const char *cav[MAXCAC];
943 	char *repopath;
944 	char buf[1024];
945 	pid_t pid;
946 	int cac;
947 
948 	asprintf(&repopath, "%s/%s", RepositoryPath, bulk->s1);
949 
950 	cac = 0;
951 	cav[cac++] = PKG_BINARY;
952 	cav[cac++] = "query";
953 	cav[cac++] = "-F";
954 	cav[cac++] = repopath;
955 	cav[cac++] = "%n-%v";
956 
957 	fp = dexec_open(cav, cac, &pid, NULL, 1, 0);
958 
959 	while ((ptr = fgetln(fp, &len)) != NULL) {
960 		if (len == 0 || ptr[len-1] != '\n')
961 			continue;
962 		ptr[len-1] = 0;
963 		snprintf(buf, sizeof(buf), "%s%s", ptr, UsePkgSufx);
964 
965 		pkg = pkg_find(buf);
966 		if (pkg) {
967 			pkg->flags |= PKGF_PACKAGED;
968 		} else {
969 			ddprintf(0, "Note: package scan, not in list, "
970 				    "skipping %s\n", buf);
971 		}
972 	}
973 	if (dexec_close(fp, pid)) {
974 		printf("pkg query command failed for %s\n", repopath);
975 	}
976 	free(repopath);
977 }
978 
979 static void
980 childOptimizeEnv(bulk_t *bulk)
981 {
982 	char *portpath;
983 	char *ptr;
984 	FILE *fp;
985 	int line;
986 	size_t len;
987 	const char *cav[MAXCAC];
988 	pid_t pid;
989 	int cac;
990 
991 	asprintf(&portpath, "%s/%s/%s", DPortsPath, bulk->s1, bulk->s2);
992 
993 	cac = 0;
994 	cav[cac++] = MAKE_BINARY;
995 	cav[cac++] = "-C";
996 	cav[cac++] = portpath;
997 	cav[cac++] = "-V_PERL5_FROM_BIN";
998 
999 	fp = dexec_open(cav, cac, &pid, NULL, 1, 1);
1000 	free(portpath);
1001 
1002 	line = 1;
1003 	while ((ptr = fgetln(fp, &len)) != NULL) {
1004 		if (len == 0 || ptr[len-1] != '\n') {
1005 			dfatal("Bad package info for %s/%s response line %d",
1006 			       bulk->s1, bulk->s2, line);
1007 		}
1008 		ptr[--len] = 0;
1009 
1010 		switch(line) {
1011 		case 1:		/* _PERL5_FROM_BIN */
1012 			addbuildenv("_PERL5_FROM_BIN", ptr, BENV_ENVIRONMENT);
1013 			break;
1014 		default:
1015 			printf("childOptimizeEnv: EXTRA LINE: %s\n", ptr);
1016 			break;
1017 		}
1018 		++line;
1019 	}
1020 	if (line == 1) {
1021 		printf("DPort not found: %s/%s\n", bulk->s1, bulk->s2);
1022 	} else if (line != 1 + 1) {
1023 		printf("DPort corrupt: %s/%s\n", bulk->s1, bulk->s2);
1024 	}
1025 	if (dexec_close(fp, pid)) {
1026 		printf("childOptimizeEnv() failed\n");
1027 	}
1028 }
1029 
1030 static int
1031 scan_and_queue_dir(const char *path, const char *level1, int level)
1032 {
1033 	DIR *dir;
1034 	char *s1;
1035 	char *s2;
1036 	struct dirent *den;
1037 	struct stat st;
1038 	int count = 0;
1039 
1040 	dir = opendir(path);
1041 	dassert(dir, "Cannot open dports path \"%s\"", path);
1042 
1043 	while ((den = readdir(dir)) != NULL) {
1044 		if (den->d_namlen == 1 && den->d_name[0] == '.')
1045 			continue;
1046 		if (den->d_namlen == 2 &&
1047 		    den->d_name[0] == '.' && den->d_name[1] == '.')
1048 			continue;
1049 		asprintf(&s1, "%s/%s", path, den->d_name);
1050 		if (lstat(s1, &st) < 0 || !S_ISDIR(st.st_mode)) {
1051 			free(s1);
1052 			continue;
1053 		}
1054 		if (level == 1) {
1055 			count += scan_and_queue_dir(s1, den->d_name, 2);
1056 			free(s1);
1057 			continue;
1058 		}
1059 		asprintf(&s2, "%s/Makefile", s1);
1060 		if (lstat(s2, &st) == 0) {
1061 			queuebulk(level1, den->d_name, NULL, NULL);
1062 			++count;
1063 		}
1064 		free(s1);
1065 		free(s2);
1066 	}
1067 	closedir(dir);
1068 
1069 	return count;
1070 }
1071 
1072 static int
1073 scan_binary_repo(const char *path)
1074 {
1075 	DIR *dir;
1076 	struct dirent *den;
1077 	size_t len;
1078 	int count;
1079 
1080 	count = 0;
1081 	dir = opendir(path);
1082 	dassert(dir, "Cannot open repository path \"%s\"", path);
1083 
1084 	/*
1085 	 * NOTE: Test includes the '.' in the suffix.
1086 	 */
1087 	while ((den = readdir(dir)) != NULL) {
1088 		len = strlen(den->d_name);
1089 		if (len > 4 &&
1090 		    strcmp(den->d_name + len - 4, UsePkgSufx) == 0) {
1091 			queuebulk(den->d_name, NULL, NULL, NULL);
1092 			++count;
1093 		}
1094 	}
1095 	closedir(dir);
1096 
1097 	return count;
1098 }
1099 
1100 #if 0
1101 static void
1102 pkgfree(pkg_t *pkg)
1103 {
1104 	freestrp(&pkg->portdir);
1105 	freestrp(&pkg->version);
1106 	freestrp(&pkg->pkgfile);
1107 	freestrp(&pkg->ignore);
1108 	freestrp(&pkg->fetch_deps);
1109 	freestrp(&pkg->ext_deps);
1110 	freestrp(&pkg->patch_deps);
1111 	freestrp(&pkg->build_deps);
1112 	freestrp(&pkg->lib_deps);
1113 	freestrp(&pkg->run_deps);
1114 	freestrp(&pkg->pos_options);
1115 	freestrp(&pkg->neg_options);
1116 	freestrp(&pkg->flavors);
1117 	free(pkg);
1118 }
1119 #endif
1120