+/* Can return NULL: _info may not support prop. */
+static char **get_one_prop(const void *ctx, const char *dir, const char *prop,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ char *cmd, **lines;
+
+ cmd = tal_fmt(ctx, "%s %s", get_info(ctx, dir), prop);
+ lines = lines_from_cmd(cmd, "%s", cmd);
+ /* Strip final NULL. */
+ if (lines)
+ tal_resize(&lines, tal_count(lines)-1);
+ return lines;
+}
+
+static char **get_one_libs(const void *ctx, const char *dir,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ return get_one_prop(ctx, dir, "libs", get_info);
+}
+
+static char **get_one_cflags(const void *ctx, const char *dir,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ return get_one_prop(ctx, dir, "cflags", get_info);
+}
+
+/* O(n^2) but n is small. */
+static char **add_deps(char **deps1, char **deps2)
+{
+ unsigned int i, len;
+
+ len = tal_count(deps1);
+
+ for (i = 0; deps2[i]; i++) {
+ if (have_dep(deps1, deps2[i]))
+ continue;
+ tal_resize(&deps1, len + 1);
+ deps1[len-1] = tal_strdup(deps1, deps2[i]);
+ deps1[len++] = NULL;
+ }
+ return deps1;
+}
+
+char **get_cflags(const void *ctx, const char *dir,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ char **flags;
+ unsigned int len;
+ flags = get_one_cflags(ctx, dir, get_info);
+ len = tal_count(flags);
+ tal_resize(&flags, len + 1);
+ flags[len] = NULL;
+ return flags;
+}
+
+static char *get_one_ported(const void *ctx, const char *dir,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ char **ported = get_one_prop(ctx, dir, "ported", get_info);
+
+ /* No news is good news. */
+ if (!ported || tal_count(ported) == 0)
+ return NULL;
+
+ if (tal_count(ported) != 1)
+ errx(1, "%s/_info ported gave %zu lines, not one",
+ dir, tal_count(ported));
+
+ if (streq(ported[0], ""))
+ return NULL;
+ else
+ return ported[0];
+}
+
+char *get_ported(const void *ctx, const char *dir, bool recurse,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ char *msg;
+
+ msg = get_one_ported(ctx, dir, get_info);
+ if (msg)
+ return msg;
+
+ if (recurse) {
+ size_t i;
+ char **deps = get_deps(ctx, dir, "depends", true, get_info);
+ for (i = 0; deps[i]; i++) {
+ char *subdir;
+ if (!strstarts(deps[i], "ccan/"))
+ continue;
+
+ subdir = path_join(ctx, find_ccan_dir(dir), deps[i]);
+ msg = get_one_ported(ctx, subdir, get_info);
+ if (msg)
+ return msg;
+ }
+ }
+ return NULL;
+}
+
+char **get_libs(const void *ctx, const char *dir, const char *style,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ char **deps, **libs;
+ unsigned int i, len;
+
+ libs = get_one_libs(ctx, dir, get_info);
+ len = tal_count(libs);
+
+ if (style) {
+ deps = get_deps(ctx, dir, style, true, get_info);
+ if (streq(style, "testdepends"))
+ deps = add_deps(deps,
+ get_deps(ctx, dir, "depends", true,
+ get_info));
+
+ for (i = 0; deps[i]; i++) {
+ char **newlibs, *subdir;
+ size_t newlen;
+
+ if (!strstarts(deps[i], "ccan/"))
+ continue;
+
+ subdir = path_join(ctx, find_ccan_dir(dir), deps[i]);
+
+ newlibs = get_one_libs(ctx, subdir, get_info);
+ newlen = tal_count(newlibs);
+ tal_resize(&libs, len + newlen);
+ memcpy(&libs[len], newlibs,
+ sizeof(newlibs[0])*newlen);
+ len += newlen;
+ }
+ }
+
+ /* Append NULL entry. */
+ tal_resize(&libs, len + 1);
+ libs[len] = NULL;
+ return libs;
+}
+
+/* FIXME: This is O(n^2), which is dumb. */
+static char **uniquify_deps(char **deps)
+{
+ unsigned int i, j, num;
+
+ if (!deps)
+ return NULL;
+
+ num = tal_count(deps) - 1;
+ for (i = 0; i < num; i++) {
+ for (j = i + 1; j < num; j++) {
+ if (streq(deps[i], deps[j])) {
+ memmove(&deps[j], &deps[j+1],
+ (num - j - 1) * sizeof(char *));
+ num--;
+ }
+ }
+ }
+ deps[num] = NULL;
+ /* Make sure tal_count() works */
+ tal_resize(&deps, num + 1);
+ return deps;
+}
+
+char **get_deps(const void *ctx, const char *dir, const char *style,
+ bool recurse,
+ char *(*get_info)(const void *ctx, const char *dir))
+{
+ char **ret;
+
+ if (!recurse) {
+ ret = get_one_deps(ctx, dir, style, get_info);
+ } else
+ ret = get_all_deps(ctx, dir, style, get_info, get_one_deps);
+
+ return uniquify_deps(ret);
+}
+
+char **get_safe_ccan_deps(const void *ctx, const char *dir, const char *style,
+ bool recurse)
+{
+ char **ret;
+ if (!recurse) {
+ ret = get_one_safe_deps(ctx, dir, style, NULL);
+ } else {
+ ret = get_all_deps(ctx, dir, style, NULL, get_one_safe_deps);
+ }
+ return uniquify_deps(ret);
+}