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