search.c 5.76 KB
Newer Older
1
/* search.c - Alpine Package Keeper (APK)
Cameron Banta's avatar
Cameron Banta committed
2 3
 *
 * Copyright (C) 2005-2009 Natanael Copa <n@tanael.org>
4
 * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
Cameron Banta's avatar
Cameron Banta committed
5 6 7 8 9 10 11
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

12
#include <fnmatch.h>
Cameron Banta's avatar
Cameron Banta committed
13 14 15 16 17 18 19
#include <stdio.h>
#include "apk_defines.h"
#include "apk_applet.h"
#include "apk_package.h"
#include "apk_database.h"

struct search_ctx {
20 21
	void (*print_result)(struct search_ctx *ctx, struct apk_package *pkg);
	void (*print_package)(struct search_ctx *ctx, struct apk_package *pkg);
22 23 24 25

	int show_all : 1;
	int search_exact : 1;
	int search_description : 1;
Natanael Copa's avatar
Natanael Copa committed
26
	int search_origin : 1;
27

28
	unsigned int matches;
29
	struct apk_string_array *filter;
Cameron Banta's avatar
Cameron Banta committed
30 31
};

Timo Teräs's avatar
Timo Teräs committed
32 33 34 35 36 37 38
static int unique_match(struct apk_package *pkg)
{
	if (pkg->state_int) return 0;
	pkg->state_int = 1;
	return 1;
}

39
static void print_package_name(struct search_ctx *ctx, struct apk_package *pkg)
Cameron Banta's avatar
Cameron Banta committed
40
{
Timo Teräs's avatar
Timo Teräs committed
41
	if (!unique_match(pkg)) return;
Cameron Banta's avatar
Cameron Banta committed
42 43
	printf("%s", pkg->name->name);
	if (apk_verbosity > 0)
44 45
		printf("-" BLOB_FMT, BLOB_PRINTF(*pkg->version));
	if (apk_verbosity > 1)
Cameron Banta's avatar
Cameron Banta committed
46
		printf(" - %s", pkg->description);
47
	printf("\n");
48
}
Cameron Banta's avatar
Cameron Banta committed
49

50 51
static void print_origin_name(struct search_ctx *ctx, struct apk_package *pkg)
{
Timo Teräs's avatar
Timo Teräs committed
52
	if (!unique_match(pkg)) return;
53 54 55 56 57 58
	if (pkg->origin != NULL)
		printf(BLOB_FMT, BLOB_PRINTF(*pkg->origin));
	else
		printf("%s", pkg->name->name);
	if (apk_verbosity > 0)
		printf("-" BLOB_FMT, BLOB_PRINTF(*pkg->version));
59
	printf("\n");
Cameron Banta's avatar
Cameron Banta committed
60 61
}

62
static void print_rdep_pkg(struct apk_package *pkg0, struct apk_dependency *dep0, struct apk_package *pkg, void *pctx)
Cameron Banta's avatar
Cameron Banta committed
63
{
64 65 66
	struct search_ctx *ctx = (struct search_ctx *) pctx;
	ctx->print_package(ctx, pkg0);
}
Cameron Banta's avatar
Cameron Banta committed
67

68 69 70
static void print_rdepends(struct search_ctx *ctx, struct apk_package *pkg)
{
	if (apk_verbosity > 0) {
71
		ctx->matches = apk_foreach_genid() | APK_DEP_SATISFIES;
72
		printf(PKG_VER_FMT " is required by:\n", PKG_VER_PRINTF(pkg));
73
	}
74
	apk_pkg_foreach_reverse_dependency(pkg, ctx->matches, print_rdep_pkg, ctx);
Cameron Banta's avatar
Cameron Banta committed
75 76
}

77
static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
Cameron Banta's avatar
Cameron Banta committed
78 79 80 81
{
	struct search_ctx *ictx = (struct search_ctx *) ctx;

	switch (optch) {
82 83 84
	case 'a':
		ictx->show_all = 1;
		break;
Cameron Banta's avatar
Cameron Banta committed
85
	case 'd':
86 87 88
		ictx->search_description = 1;
		ictx->search_exact = 1;
		ictx->show_all = 1;
89
		break;
90
	case 'e':
Dubiousjim's avatar
Dubiousjim committed
91
	case 'x':
92
		ictx->search_exact = 1;
93 94 95
		break;
	case 'o':
		ictx->print_package = print_origin_name;
Cameron Banta's avatar
Cameron Banta committed
96
		break;
97 98 99
	case 'r':
		ictx->print_result = print_rdepends;
		break;
Natanael Copa's avatar
Natanael Copa committed
100 101 102 103 104
	case 0x10000:
		ictx->search_origin = 1;
		ictx->search_exact = 1;
		ictx->show_all = 1;
		break;
Cameron Banta's avatar
Cameron Banta committed
105
	default:
106
		return -ENOTSUP;
Cameron Banta's avatar
Cameron Banta committed
107 108 109 110
	}
	return 0;
}

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
static const struct apk_option options_applet[] = {
	{ 'a', "all",		"Show all package versions (instead of latest only)" },
	{ 'd', "description",	"Search package descriptions (implies -a)" },
	{ 'x', "exact",		"Require exact match (instead of substring match)" },
	{ 'e', NULL,	        "Synonym for -x (deprecated)" },
	{ 'o', "origin",	"Print origin package name instead of the subpackage" },
	{ 'r', "rdepends",	"Print reverse dependencies of package" },
	{ 0x10000, "has-origin","List packages that have the given origin" },
};

static const struct apk_option_group optgroup_applet = {
	.name = "Search",
	.options = options_applet,
	.num_options = ARRAY_SIZE(options_applet),
	.parse = option_parse_applet,
};

128
static void print_result_pkg(struct search_ctx *ctx, struct apk_package *pkg)
129
{
130
	char **pmatch;
131 132

	if (ctx->search_description) {
133
		foreach_array_item(pmatch, ctx->filter) {
134 135
			if (strstr(pkg->description, *pmatch) != NULL ||
			    strstr(pkg->name->name, *pmatch) != NULL)
136
				goto match;
137
		}
138
		return;
139
	}
Natanael Copa's avatar
Natanael Copa committed
140 141
	if (ctx->search_origin) {
		foreach_array_item(pmatch, ctx->filter) {
142
			if (pkg->origin && apk_blob_compare(APK_BLOB_STR(*pmatch), *pkg->origin) == 0)
Natanael Copa's avatar
Natanael Copa committed
143 144 145 146
				goto match;
		}
		return;
	}
147
match:
148 149 150
	ctx->print_result(ctx, pkg);
}

151
static void print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
152
{
153
	struct search_ctx *ctx = pctx;
154 155
	struct apk_provider *p;
	struct apk_package *pkg = NULL;
156

157 158
	if (!name) return;

159
	if (ctx->show_all) {
160 161
		foreach_array_item(p, name->providers)
			print_result_pkg(ctx, p->pkg);
162
	} else {
163
		foreach_array_item(p, name->providers) {
164 165
			if (pkg == NULL ||
			    apk_version_compare_blob(*p->version, *pkg->version) == APK_VERSION_GREATER)
166
				pkg = p->pkg;
167
		}
168 169
		if (pkg)
			print_result_pkg(ctx, pkg);
170
	}
171 172
}

173
static int print_pkg(apk_hash_item item, void *pctx)
174
{
175
	print_result_pkg((struct search_ctx *) pctx, (struct apk_package *) item);
176 177 178
	return 0;
}

179
static int search_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
Cameron Banta's avatar
Cameron Banta committed
180
{
181
	struct search_ctx *ctx = (struct search_ctx *) pctx;
182
	char *tmp, **pmatch;
183

184
	ctx->filter = args;
185
	ctx->matches = apk_foreach_genid() | APK_DEP_SATISFIES;
186 187 188 189
	if (ctx->print_package == NULL)
		ctx->print_package = print_package_name;
	if (ctx->print_result == NULL)
		ctx->print_result = ctx->print_package;
190

Natanael Copa's avatar
Natanael Copa committed
191
	if (ctx->search_description || ctx->search_origin)
192
		return apk_hash_foreach(&db->available.packages, print_pkg, ctx);
Cameron Banta's avatar
Cameron Banta committed
193

194
	if (!ctx->search_exact) {
195 196 197 198
		foreach_array_item(pmatch, ctx->filter) {
			tmp = alloca(strlen(*pmatch) + 3);
			sprintf(tmp, "*%s*", *pmatch);
			*pmatch = tmp;
199
		}
200
	}
201 202 203
	apk_name_foreach_matching(
		db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(),
		print_result, ctx);
204
	return 0;
Cameron Banta's avatar
Cameron Banta committed
205 206 207 208
}

static struct apk_applet apk_search = {
	.name = "search",
209
	.help = "Search package by PATTERNs or by indexed dependencies",
Timo Teräs's avatar
Timo Teräs committed
210
	.arguments = "PATTERN",
211
	.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE,
Cameron Banta's avatar
Cameron Banta committed
212
	.context_size = sizeof(struct search_ctx),
213
	.optgroups = { &optgroup_global, &optgroup_applet },
Cameron Banta's avatar
Cameron Banta committed
214 215 216 217
	.main = search_main,
};

APK_DEFINE_APPLET(apk_search);