search.c 5.57 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
};

32
static void print_package_name(struct search_ctx *ctx, struct apk_package *pkg)
Cameron Banta's avatar
Cameron Banta committed
33 34 35
{
	printf("%s", pkg->name->name);
	if (apk_verbosity > 0)
36 37
		printf("-" BLOB_FMT, BLOB_PRINTF(*pkg->version));
	if (apk_verbosity > 1)
Cameron Banta's avatar
Cameron Banta committed
38
		printf(" - %s", pkg->description);
39
	printf("\n");
40
}
Cameron Banta's avatar
Cameron Banta committed
41

42 43 44 45 46 47 48 49
static void print_origin_name(struct search_ctx *ctx, struct apk_package *pkg)
{
	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));
50
	printf("\n");
Cameron Banta's avatar
Cameron Banta committed
51 52
}

53
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
54
{
55 56 57
	struct search_ctx *ctx = (struct search_ctx *) pctx;
	ctx->print_package(ctx, pkg0);
}
Cameron Banta's avatar
Cameron Banta committed
58

59 60 61
static void print_rdepends(struct search_ctx *ctx, struct apk_package *pkg)
{
	if (apk_verbosity > 0) {
62
		ctx->matches = apk_foreach_genid() | APK_DEP_SATISFIES;
63
		printf(PKG_VER_FMT " is required by:\n", PKG_VER_PRINTF(pkg));
64
	}
65
	apk_pkg_foreach_reverse_dependency(pkg, ctx->matches, print_rdep_pkg, ctx);
Cameron Banta's avatar
Cameron Banta committed
66 67
}

68
static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
Cameron Banta's avatar
Cameron Banta committed
69 70 71 72
{
	struct search_ctx *ictx = (struct search_ctx *) ctx;

	switch (optch) {
73 74 75
	case 'a':
		ictx->show_all = 1;
		break;
Cameron Banta's avatar
Cameron Banta committed
76
	case 'd':
77 78 79
		ictx->search_description = 1;
		ictx->search_exact = 1;
		ictx->show_all = 1;
80
		break;
81
	case 'e':
Dubiousjim's avatar
Dubiousjim committed
82
	case 'x':
83
		ictx->search_exact = 1;
84 85 86
		break;
	case 'o':
		ictx->print_package = print_origin_name;
Cameron Banta's avatar
Cameron Banta committed
87
		break;
88 89 90
	case 'r':
		ictx->print_result = print_rdepends;
		break;
Natanael Copa's avatar
Natanael Copa committed
91 92 93 94 95
	case 0x10000:
		ictx->search_origin = 1;
		ictx->search_exact = 1;
		ictx->show_all = 1;
		break;
Cameron Banta's avatar
Cameron Banta committed
96 97 98 99 100 101
	default:
		return -1;
	}
	return 0;
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
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,
};

119
static void print_result_pkg(struct search_ctx *ctx, struct apk_package *pkg)
120
{
121
	char **pmatch;
122 123

	if (ctx->search_description) {
124
		foreach_array_item(pmatch, ctx->filter) {
125 126
			if (strstr(pkg->description, *pmatch) != NULL ||
			    strstr(pkg->name->name, *pmatch) != NULL)
127
				goto match;
128
		}
129
		return;
130
	}
Natanael Copa's avatar
Natanael Copa committed
131 132
	if (ctx->search_origin) {
		foreach_array_item(pmatch, ctx->filter) {
133
			if (pkg->origin && apk_blob_compare(APK_BLOB_STR(*pmatch), *pkg->origin) == 0)
Natanael Copa's avatar
Natanael Copa committed
134 135 136 137
				goto match;
		}
		return;
	}
138
match:
139 140 141
	ctx->print_result(ctx, pkg);
}

142
static void print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
143
{
144
	struct search_ctx *ctx = pctx;
145 146
	struct apk_provider *p;
	struct apk_package *pkg = NULL;
147

148 149
	if (!name) return;

150
	if (ctx->show_all) {
151 152
		foreach_array_item(p, name->providers)
			print_result_pkg(ctx, p->pkg);
153
	} else {
154
		foreach_array_item(p, name->providers) {
155 156
			if (pkg == NULL ||
			    apk_version_compare_blob(*p->version, *pkg->version) == APK_VERSION_GREATER)
157
				pkg = p->pkg;
158
		}
159 160
		if (pkg)
			print_result_pkg(ctx, pkg);
161
	}
162 163
}

164
static int print_pkg(apk_hash_item item, void *pctx)
165
{
166
	print_result_pkg((struct search_ctx *) pctx, (struct apk_package *) item);
167 168 169
	return 0;
}

170
static int search_main(void *pctx, struct apk_database *db, struct apk_string_array *args)
Cameron Banta's avatar
Cameron Banta committed
171
{
172
	struct search_ctx *ctx = (struct search_ctx *) pctx;
173
	char *tmp, **pmatch;
174

175
	ctx->filter = args;
176
	ctx->matches = apk_foreach_genid() | APK_DEP_SATISFIES;
177 178 179 180
	if (ctx->print_package == NULL)
		ctx->print_package = print_package_name;
	if (ctx->print_result == NULL)
		ctx->print_result = ctx->print_package;
181

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

185
	if (!ctx->search_exact) {
186 187 188 189
		foreach_array_item(pmatch, ctx->filter) {
			tmp = alloca(strlen(*pmatch) + 3);
			sprintf(tmp, "*%s*", *pmatch);
			*pmatch = tmp;
190
		}
191
	}
192 193 194
	apk_name_foreach_matching(
		db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(),
		print_result, ctx);
195
	return 0;
Cameron Banta's avatar
Cameron Banta committed
196 197 198 199
}

static struct apk_applet apk_search = {
	.name = "search",
200
	.help = "Search package by PATTERNs or by indexed dependencies",
Timo Teräs's avatar
Timo Teräs committed
201
	.arguments = "PATTERN",
202
	.open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE,
Cameron Banta's avatar
Cameron Banta committed
203
	.context_size = sizeof(struct search_ctx),
204
	.optgroups = { &optgroup_global, &optgroup_applet },
Cameron Banta's avatar
Cameron Banta committed
205 206 207 208
	.main = search_main,
};

APK_DEFINE_APPLET(apk_search);