info.c 8.96 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1
2
3
4
5
6
/* info.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2009 Natanael Copa <n@tanael.org>
 * Copyright (C) 2009 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
Timo Teräs's avatar
Timo Teräs committed
7
 * This program is free software; you can redistribute it and/or modify it
Natanael Copa's avatar
Natanael Copa committed
8
9
10
11
12
 * 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.
 */

#include <stdio.h>
13
#include <unistd.h>
Natanael Copa's avatar
Natanael Copa committed
14
15
#include "apk_defines.h"
#include "apk_applet.h"
Timo Teräs's avatar
Timo Teräs committed
16
#include "apk_package.h"
Natanael Copa's avatar
Natanael Copa committed
17
#include "apk_database.h"
Timo Teräs's avatar
Timo Teräs committed
18
#include "apk_state.h"
Natanael Copa's avatar
Natanael Copa committed
19

Timo Teräs's avatar
Timo Teräs committed
20
struct info_ctx {
21
22
	int (*action)(struct info_ctx *ctx, struct apk_database *db,
		      int argc, char **argv);
23
24
25
26
27
	int subaction_mask;
};

struct info_subaction {
	int mask;
28
	void (*subaction)(struct apk_package *pkg);
Timo Teräs's avatar
Timo Teräs committed
29
30
};

31
32
33
34
35
static void info_print_depends(struct apk_package *pkg);
static void info_print_url(struct apk_package *pkg);
static void info_print_required_by(struct apk_package *pkg);
static void info_print_size(struct apk_package *pkg);
static void info_print_contents(struct apk_package *pkg);
36
static void info_print_description(struct apk_package *pkg);
37
38
39
40
41
42

#define APK_INFO_URL		0x01
#define APK_INFO_DEPENDS	0x02
#define APK_INFO_RDEPENDS	0x04
#define APK_INFO_SIZE		0x08
#define APK_INFO_CONTENTS	0x10
43
#define APK_INFO_DESC		0x20
44

45
#define APK_INFO_NUM_SUBACTIONS 6
46
static struct info_subaction info_package_actions[] = {
47
	{ APK_INFO_DESC,	info_print_description },
48
49
50
	{ APK_INFO_URL,		info_print_url },
	{ APK_INFO_SIZE,	info_print_size },
	{ APK_INFO_CONTENTS,	info_print_contents },
51
52
	{ APK_INFO_DEPENDS,	info_print_depends },
	{ APK_INFO_RDEPENDS,	info_print_required_by },
53
54
};

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
static void verbose_print_pkg(struct apk_package *pkg, int minimal_verbosity)
{
	int verbosity = apk_verbosity;
	if (verbosity < minimal_verbosity)
		verbosity = minimal_verbosity;

	if (pkg == NULL || verbosity < 1)
		return;

	printf("%s", pkg->name->name);
	if (apk_verbosity > 1)
		printf("-%s", pkg->version);
	if (apk_verbosity > 2)
		printf(" - %s", pkg->description);
	printf("\n");
}


73
74
static int info_list(struct info_ctx *ctx, struct apk_database *db,
		     int argc, char **argv)
Natanael Copa's avatar
Natanael Copa committed
75
76
77
{
	struct apk_package *pkg;

78
79
	list_for_each_entry(pkg, &db->installed.packages, installed_pkgs_list)
		verbose_print_pkg(pkg, 1);
Timo Teräs's avatar
Timo Teräs committed
80
81
82
	return 0;
}

83
84
static int info_exists(struct info_ctx *ctx, struct apk_database *db,
		       int argc, char **argv)
Timo Teräs's avatar
Timo Teräs committed
85
86
{
	struct apk_name *name;
87
88
89
	struct apk_package *pkg = NULL;
	struct apk_dependency dep;
	int r, i, j, ok = 0;
Timo Teräs's avatar
Timo Teräs committed
90
91

	for (i = 0; i < argc; i++) {
92
93
		r = apk_dep_from_blob(&dep, db, APK_BLOB_STR(argv[i]));
		if (r != 0)
94
			continue;
Timo Teräs's avatar
Timo Teräs committed
95

96
		name = dep.name;
97
98
99
		if (name->pkgs == NULL)
			continue;
		
Timo Teräs's avatar
Timo Teräs committed
100
		for (j = 0; j < name->pkgs->num; j++) {
101
102
			pkg = name->pkgs->item[j];
			if (apk_pkg_get_state(pkg) == APK_PKG_INSTALLED)
Timo Teräs's avatar
Timo Teräs committed
103
104
				break;
		}
105
106
107
108
109
110
111
112
113
		if (j >= name->pkgs->num)
			continue;

		if (!(apk_version_compare(pkg->version, dep.version)
		      & dep.result_mask))
			continue;

		verbose_print_pkg(pkg, 0);
		ok++;
Timo Teräs's avatar
Timo Teräs committed
114
	}
Natanael Copa's avatar
Natanael Copa committed
115

116
	return argc - ok;
Natanael Copa's avatar
Natanael Copa committed
117
118
}

119
120
static int info_who_owns(struct info_ctx *ctx, struct apk_database *db,
			 int argc, char **argv)
121
122
123
124
125
126
127
128
129
130
131
{
	struct apk_package *pkg;
	struct apk_dependency_array *deps = NULL;
	struct apk_dependency dep;
	int i;

	for (i = 0; i < argc; i++) {
		pkg = apk_db_get_file_owner(db, APK_BLOB_STR(argv[i]));
		if (pkg == NULL)
			continue;

132
		if (apk_verbosity < 1) {
133
134
			dep = (struct apk_dependency) {
				.name = pkg->name,
135
				.result_mask = APK_DEPMASK_REQUIRE,
136
137
138
139
140
141
142
			};
			apk_deps_add(&deps, &dep);
		} else {
			printf("%s is owned by %s-%s\n", argv[i],
			       pkg->name->name, pkg->version);
		}
	}
143
	if (apk_verbosity < 1 && deps != NULL) {
144
145
146
147
148
149
150
		struct apk_ostream *os;

		os = apk_ostream_to_fd(STDOUT_FILENO);
		apk_deps_write(deps, os);
		os->write(os, "\n", 1);
		os->close(os);

151
152
153
154
155
156
		free(deps);
	}

	return 0;
}

157
158
159
160
161
162
163
164
165
166
static void info_subaction(struct info_ctx *ctx, struct apk_package *pkg)
{
	int i;
	for (i = 0; i < APK_INFO_NUM_SUBACTIONS; i++)
		if (info_package_actions[i].mask & ctx->subaction_mask) {
			info_package_actions[i].subaction(pkg);
			puts("");
		}
}

167
168
169
170
171
172
173
174
175
176
177
178
179
180
static int info_package(struct info_ctx *ctx, struct apk_database *db,
			int argc, char **argv)
{
	struct apk_name *name;
	int i, j;

	for (i = 0; i < argc; i++) {
		name = apk_db_query_name(db, APK_BLOB_STR(argv[i]));
		if (name == NULL) {
			apk_error("Not found: %s", name);
			return 1;
		}
		for (j = 0; j < name->pkgs->num; j++) {
			struct apk_package *pkg = name->pkgs->item[j];
181
			if (apk_pkg_get_state(pkg) == APK_PKG_INSTALLED)
182
				info_subaction(ctx, pkg);
183
184
185
186
187
		}
	}
	return 0;
}

Natanael Copa's avatar
Natanael Copa committed
188
189
190
191
192
193
static void info_print_contents(struct apk_package *pkg)
{
	struct apk_db_dir_instance *diri;
	struct apk_db_file *file;
	struct hlist_node *dc, *dn, *fc, *fn;

194
195
196
	if (apk_verbosity == 1)
		printf("%s-%s contains:\n", pkg->name->name, pkg->version);

Natanael Copa's avatar
Natanael Copa committed
197
198
199
200
201
	hlist_for_each_entry_safe(diri, dc, dn, &pkg->owned_dirs,
				  pkg_dirs_list) {
		hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files,
					  diri_files_list) {
			if (apk_verbosity > 1)
202
				printf("%s: ", pkg->name->name);
Timo Teräs's avatar
Timo Teräs committed
203
			printf("%s/%s\n", diri->dir->name, file->name);
Natanael Copa's avatar
Natanael Copa committed
204
205
206
207
		}
	}
}

208
209
210
211
static void info_print_depends(struct apk_package *pkg)
{
	int i;
	char *separator = apk_verbosity > 1 ? " " : "\n";
Timo Teräs's avatar
Timo Teräs committed
212
	if (apk_verbosity == 1)
213
214
215
216
217
218
219
220
221
222
		printf("%s-%s depends on:\n", pkg->name->name, pkg->version);
	if (pkg->depends == NULL)
		return;
	if (apk_verbosity > 1)
		printf("%s: ", pkg->name->name);
	for (i = 0; i < pkg->depends->num; i++) {
		printf("%s%s", pkg->depends->item[i].name->name, separator);
	}
}

223
static void info_print_required_by(struct apk_package *pkg)
224
{
225
226
	int i, j, k;
	char *separator = apk_verbosity > 1 ? " " : "\n";
227

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
	if (apk_verbosity == 1)
		printf("%s-%s is required by:\n", pkg->name->name, pkg->version);
	if (pkg->name->rdepends == NULL)
		return;
	if (apk_verbosity > 1)
		printf("%s: ", pkg->name->name);
	for (i = 0; i < pkg->name->rdepends->num; i++) {
		struct apk_name *name0 = pkg->name->rdepends->item[i];

		/* Check only the package that is installed, and that
		 * it actually has this package as dependency. */
		if (name0->pkgs == NULL)
			continue;
		for (j = 0; j < name0->pkgs->num; j++) {
			struct apk_package *pkg0 = name0->pkgs->item[j];

244
			if (apk_pkg_get_state(pkg0) != APK_PKG_INSTALLED ||
245
246
247
248
249
250
251
252
253
			    pkg0->depends == NULL)
				continue;
			for (k = 0; k < pkg0->depends->num; k++) {
				if (pkg0->depends->item[k].name != pkg->name)
					continue;
				printf("%s-%s%s", pkg0->name->name,
				       pkg0->version, separator);
				break;
			}
254
255
256
257
		}
	}
}

258
259
260
static void info_print_url(struct apk_package *pkg)
{
	if (apk_verbosity > 1)
261
		printf("%s: %s", pkg->name->name, pkg->url);
262
	else
263
		printf("%s-%s webpage:\n%s\n", pkg->name->name, pkg->version,
264
265
266
267
268
269
		       pkg->url);
}

static void info_print_size(struct apk_package *pkg)
{
	if (apk_verbosity > 1)
270
		printf("%s: %zu", pkg->name->name, pkg->installed_size);
271
	else
272
		printf("%s-%s installed size:\n%zu\n", pkg->name->name, pkg->version,
273
274
275
		       pkg->installed_size);
}

276
277
278
279
280
281
282
283
static void info_print_description(struct apk_package *pkg)
{
	if (apk_verbosity > 1)
		printf("%s: %s", pkg->name->name, pkg->description);
	else
		printf("%s-%s description:\n%s\n", pkg->name->name,
		       pkg->version, pkg->description);
}
Timo Teräs's avatar
Timo Teräs committed
284
285
286
287
static int info_parse(void *ctx, int optch, int optindex, const char *optarg)
{
	struct info_ctx *ictx = (struct info_ctx *) ctx;

288
	ictx->action = info_package;
Timo Teräs's avatar
Timo Teräs committed
289
290
291
292
	switch (optch) {
	case 'e':
		ictx->action = info_exists;
		break;
293
294
295
	case 'W':
		ictx->action = info_who_owns;
		break;
296
	case 'w':
297
		ictx->subaction_mask |= APK_INFO_URL;
298
		break;
Natanael Copa's avatar
Natanael Copa committed
299
	case 'L':
300
		ictx->subaction_mask |= APK_INFO_CONTENTS;
Natanael Copa's avatar
Natanael Copa committed
301
		break;
302
	case 'R':
303
		ictx->subaction_mask |= APK_INFO_DEPENDS;
304
305
		break;
	case 'r':
306
		ictx->subaction_mask |= APK_INFO_RDEPENDS;
307
		break;
308
	case 's':
309
		ictx->subaction_mask |= APK_INFO_SIZE;
310
		break;
311
312
313
	case 'd':
		ictx->subaction_mask |= APK_INFO_DESC;
		break;
Timo Teräs's avatar
Timo Teräs committed
314
315
316
317
318
319
320
321
322
323
324
325
	default:
		return -1;
	}
	return 0;
}

static int info_main(void *ctx, int argc, char **argv)
{
	struct info_ctx *ictx = (struct info_ctx *) ctx;
	struct apk_database db;
	int r;

Timo Teräs's avatar
Timo Teräs committed
326
	if (apk_db_open(&db, apk_root, APK_OPENF_READ | APK_OPENF_NO_REPOS) < 0)
Timo Teräs's avatar
Timo Teräs committed
327
328
329
		return -1;

	if (ictx->action != NULL)
330
		r = ictx->action(ictx, &db, argc, argv);
Timo Teräs's avatar
Timo Teräs committed
331
	else
332
		r = info_list(ictx, &db, argc, argv);
Timo Teräs's avatar
Timo Teräs committed
333
334
335
336
337

	apk_db_close(&db);
	return r;
}

Timo Teräs's avatar
Timo Teräs committed
338
339
340
341
342
343
344
345
346
static struct apk_option info_options[] = {
	{ 'L', "contents",	"List contents of the PACKAGE" },
	{ 'e', "installed",	"Check if PACKAGE is installed" },
	{ 'W', "who-owns",	"Print the package owning the specified file" },
	{ 'R', "depends",	"List packages that the PACKAGE depends on" },
	{ 'r', "rdepends",	"List all packages depending on PACKAGE" },
	{ 'w', "webpage",	"Show URL for more information about PACKAGE" },
	{ 's', "size",		"Show installed size of PACKAGE" },
	{ 'd', "description",	"Print description for PACKAGE" },
Timo Teräs's avatar
Timo Teräs committed
347
348
};

Natanael Copa's avatar
Natanael Copa committed
349
350
static struct apk_applet apk_info = {
	.name = "info",
Timo Teräs's avatar
Timo Teräs committed
351
352
	.help = "Give detailed information about PACKAGEs.",
	.arguments = "PACKAGE...",
Timo Teräs's avatar
Timo Teräs committed
353
354
355
356
	.context_size = sizeof(struct info_ctx),
	.num_options = ARRAY_SIZE(info_options),
	.options = info_options,
	.parse = info_parse,
Natanael Copa's avatar
Natanael Copa committed
357
358
359
360
361
	.main = info_main,
};

APK_DEFINE_APPLET(apk_info);