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