sys/vfs/msdosfs: Cleanups
[dragonfly.git] / usr.bin / dsynth / pkglist.c
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, 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, 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, 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