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