librazor/razor.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jul 14 12:49:48 2016 +0100 (2016-07-14)
changeset 491 b18e0bf48a91
parent 475 008c75a5e08d
child 499 c89e5edb8eae
permissions -rw-r--r--
razor_path_to_uri() should cope with relative paths better
rhughes@241
     1
/*
rhughes@241
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
rhughes@241
     3
 * Copyright (C) 2008  Red Hat, Inc
ali@475
     4
 * Copyright (C) 2009-2012, 2016  J. Ali Harlow <ali@juiblex.co.uk>
rhughes@241
     5
 *
rhughes@241
     6
 * This program is free software; you can redistribute it and/or modify
rhughes@241
     7
 * it under the terms of the GNU General Public License as published by
rhughes@241
     8
 * the Free Software Foundation; either version 2 of the License, or
rhughes@241
     9
 * (at your option) any later version.
rhughes@241
    10
 *
rhughes@241
    11
 * This program is distributed in the hope that it will be useful,
rhughes@241
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rhughes@241
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
rhughes@241
    14
 * GNU General Public License for more details.
rhughes@241
    15
 *
rhughes@241
    16
 * You should have received a copy of the GNU General Public License along
rhughes@241
    17
 * with this program; if not, write to the Free Software Foundation, Inc.,
rhughes@241
    18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
rhughes@241
    19
 */
rhughes@241
    20
rhughes@241
    21
#define _GNU_SOURCE
rhughes@241
    22
ali@334
    23
#include "config.h"
ali@334
    24
rhughes@241
    25
#include <stdlib.h>
rhughes@241
    26
#include <stddef.h>
rhughes@241
    27
#include <stdint.h>
rhughes@241
    28
#include <stdio.h>
richard@302
    29
#include <stdarg.h>
rhughes@241
    30
#include <string.h>
ali@475
    31
#include <fcntl.h>
rhughes@241
    32
#include <unistd.h>
rhughes@241
    33
#include <errno.h>
rhughes@241
    34
#include <ctype.h>
rhughes@241
    35
#include <fnmatch.h>
ali@369
    36
#include <limits.h>
richard@301
    37
#include <assert.h>
ali@388
    38
#ifdef MSWIN_API
ali@388
    39
#include <windows.h>
ali@388
    40
#endif
rhughes@241
    41
krh@253
    42
#include "razor-internal.h"
rhughes@241
    43
#include "razor.h"
rhughes@241
    44
ali@345
    45
#ifndef O_BINARY
ali@345
    46
#define O_BINARY	0
ali@345
    47
#endif
ali@345
    48
jbowes@318
    49
struct razor_set_section_index {
jbowes@318
    50
	const char *name;
jbowes@318
    51
	uint32_t offset;
krh@373
    52
	uint32_t flags;
jbowes@318
    53
};
jbowes@318
    54
krh@373
    55
#define MAIN(type, field) \
krh@373
    56
	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_MAIN }
krh@373
    57
#define FILES(type, field) \
krh@373
    58
	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_FILES }
krh@373
    59
#define DETAILS(type, field) \
krh@373
    60
	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_DETAILS }
krh@373
    61
jbowes@318
    62
struct razor_set_section_index razor_sections[] = {
krh@373
    63
	MAIN(RAZOR_STRING_POOL, string_pool),
krh@373
    64
	MAIN(RAZOR_PACKAGES, packages),
krh@373
    65
	MAIN(RAZOR_PROPERTIES, properties),
krh@373
    66
	MAIN(RAZOR_PACKAGE_POOL, package_pool),
krh@373
    67
	MAIN(RAZOR_PROPERTY_POOL, property_pool),
krh@373
    68
	MAIN(RAZOR_PREFIX_POOL, prefix_pool),
krh@373
    69
	FILES(RAZOR_FILES, files),
krh@373
    70
	FILES(RAZOR_FILE_POOL, file_pool),
krh@373
    71
	FILES(RAZOR_FILE_STRING_POOL, file_string_pool),
krh@373
    72
	DETAILS(RAZOR_DETAILS_STRING_POOL, details_string_pool)
jbowes@258
    73
};
krh@262
    74
krh@269
    75
RAZOR_EXPORT struct razor_set *
ali@363
    76
razor_set_create_without_root(void)
ali@363
    77
{
ali@363
    78
	struct razor_set *set;
ali@363
    79
	char *empty;
ali@363
    80
ali@363
    81
	set = zalloc(sizeof *set);
ali@363
    82
ali@403
    83
	if (set) {
ali@403
    84
		empty = array_add(&set->string_pool, 1);
ali@403
    85
		*empty = '\0';
ali@363
    86
ali@403
    87
		set->lock_fd = -1;
ali@403
    88
ali@403
    89
		set->ref_count = 1;
ali@403
    90
ali@403
    91
		set->header_version = RAZOR_HEADER_VERSION;
ali@424
    92
ali@424
    93
		set->flags = RAZOR_SET_PRIVATE;
ali@403
    94
	}
ali@388
    95
ali@363
    96
	return set;
ali@363
    97
}
ali@363
    98
ali@363
    99
RAZOR_EXPORT struct razor_set *
rhughes@241
   100
razor_set_create(void)
rhughes@241
   101
{
rhughes@241
   102
	struct razor_set *set;
rhughes@241
   103
	struct razor_entry *e;
rhughes@241
   104
ali@363
   105
	set = razor_set_create_without_root();
rhughes@241
   106
rhughes@241
   107
	e = array_add(&set->files, sizeof *e);
rhughes@241
   108
	e->name = 0;
rhughes@241
   109
	e->flags = RAZOR_ENTRY_LAST;
rhughes@241
   110
	e->start = 0;
rhughes@241
   111
	list_set_empty(&e->packages);
rhughes@241
   112
rhughes@241
   113
	return set;
rhughes@241
   114
}
rhughes@241
   115
ali@403
   116
RAZOR_EXPORT uint32_t
ali@403
   117
razor_set_get_header_version(struct razor_set *set)
ali@403
   118
{
ali@403
   119
	return set->header_version;
ali@403
   120
}
ali@403
   121
ali@403
   122
RAZOR_EXPORT int
ali@403
   123
razor_set_set_header_version(struct razor_set *set, uint32_t header_version)
ali@403
   124
{
ali@403
   125
	if (header_version<RAZOR_HEADER_VERSION_MIN ||
ali@403
   126
	    header_version>RAZOR_HEADER_VERSION)
ali@403
   127
		return -1;
ali@403
   128
	else {
ali@403
   129
		set->header_version = header_version;
ali@403
   130
		return 0;
ali@403
   131
	}
ali@403
   132
}
ali@403
   133
krh@373
   134
struct razor_mapped_file {
krh@373
   135
	struct razor_set_header *header;
krh@373
   136
	size_t size;
krh@373
   137
	struct razor_mapped_file *next;
krh@373
   138
};
krh@373
   139
krh@373
   140
RAZOR_EXPORT int
ali@475
   141
razor_set_bind_sections(struct razor_set *set, const char *uri,
ali@424
   142
			enum razor_set_flags flags, struct razor_error **error)
rhughes@241
   143
{
jbowes@318
   144
	struct razor_set_section *s, *sections;
krh@373
   145
	struct razor_mapped_file *file;
ali@403
   146
	const char *pool, *reason;
rhughes@241
   147
	struct array *array;
ali@447
   148
	int i, j, code;
richard@301
   149
krh@373
   150
	file = zalloc(sizeof *file);
ali@403
   151
	if (file == NULL) {
ali@447
   152
		razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
ali@447
   153
				"Not enough memory");
jbowes@288
   154
		return -1;
ali@403
   155
	}
jbowes@258
   156
ali@475
   157
	file->header = razor_uri_get_contents(uri, &file->size,
ali@475
   158
					      flags & RAZOR_SET_PRIVATE,
ali@475
   159
					      error);
krh@373
   160
	if (!file->header) {
krh@373
   161
		free(file);
krh@373
   162
		return -1;
krh@373
   163
	}
jbowes@318
   164
ali@447
   165
	if (file->size < sizeof *file->header) {
ali@447
   166
		code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED;
ali@403
   167
		reason = "Premature EOF";
ali@447
   168
	} else if (file->header->magic != RAZOR_MAGIC) {
ali@447
   169
		code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED;
ali@403
   170
		reason = "Bad magic number";
ali@447
   171
	} else if (file->header->version < RAZOR_HEADER_VERSION_MIN ||
ali@447
   172
		   file->header->version > RAZOR_HEADER_VERSION) {
ali@447
   173
		code = RAZOR_GENERAL_ERROR_DATABASE_INCOMPATIBLE;
ali@403
   174
		reason = "Incompatible file version";
ali@447
   175
	} else
ali@403
   176
		reason = NULL;
ali@403
   177
ali@403
   178
	if (reason) {
ali@475
   179
		razor_set_error(error, RAZOR_GENERAL_ERROR, code, uri, reason);
ali@475
   180
		razor_uri_free_contents(file->header, file->size);
ali@390
   181
		free(file);
ali@390
   182
		return -1;
ali@390
   183
	}
ali@390
   184
ali@424
   185
	set->flags = flags & RAZOR_SET_PRIVATE;
ali@424
   186
ali@403
   187
	set->header_version = file->header->version;
ali@403
   188
ali@388
   189
	if (set->mapped_files == NULL) {
ali@388
   190
		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
ali@388
   191
			array = (void *) set + razor_sections[i].offset;
ali@388
   192
			array_release(array);
ali@388
   193
		}
ali@388
   194
	}
ali@388
   195
krh@373
   196
	file->next = set->mapped_files;
krh@373
   197
	set->mapped_files = file;
krh@373
   198
krh@373
   199
	sections = (void *) file->header + sizeof *file->header;
krh@373
   200
	pool = (void *) sections +
krh@373
   201
		file->header->num_sections * sizeof *sections;
krh@373
   202
krh@373
   203
	for (i = 0; i < file->header->num_sections; i++) {
jbowes@318
   204
		s = sections + i;
krh@373
   205
		for (j = 0; j < ARRAY_SIZE(razor_sections); j++)
krh@373
   206
			if (!strcmp(razor_sections[j].name, &pool[s->name]))
jbowes@318
   207
				break;
krh@373
   208
		if (j == ARRAY_SIZE(razor_sections))
jbowes@258
   209
			continue;
krh@373
   210
		array = (void *) set + razor_sections[j].offset;
krh@373
   211
		array->data = (void *) file->header + s->offset;
jbowes@258
   212
		array->size = s->size;
jbowes@258
   213
		array->alloc = s->size;
jbowes@258
   214
	}
jbowes@288
   215
jbowes@288
   216
	return 0;
jbowes@258
   217
}
jbowes@258
   218
jbowes@318
   219
RAZOR_EXPORT struct razor_set *
ali@475
   220
razor_set_open(const char *uri, enum razor_set_flags flags,
ali@424
   221
	       struct razor_error **error)
jbowes@318
   222
{
jbowes@318
   223
	struct razor_set *set;
jbowes@318
   224
jbowes@318
   225
	set = zalloc(sizeof *set);
ali@403
   226
	if (!set) {
ali@447
   227
		razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
ali@447
   228
				"Not enough memory");
ali@403
   229
		return NULL;
ali@403
   230
	}
ali@403
   231
ali@388
   232
	set->lock_fd = -1;
ali@406
   233
	set->ref_count = 1;
ali@475
   234
	if (razor_set_bind_sections(set, uri, flags, error)) {
jbowes@318
   235
		free(set);
jbowes@318
   236
		return NULL;
jbowes@318
   237
	}
jbowes@318
   238
	return set;
jbowes@318
   239
}
jbowes@318
   240
ali@388
   241
int
ali@475
   242
razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive)
ali@388
   243
{
ali@388
   244
	int fd;
ali@388
   245
	assert(set != NULL);
ali@388
   246
ali@475
   247
	if (uri) {
ali@475
   248
		fd = razor_uri_open(uri, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
ali@475
   249
				    0666, NULL);
ali@388
   250
		if (fd < 0)
ali@388
   251
			return -1;
ali@475
   252
	} else
ali@388
   253
		fd = -1;
ali@388
   254
ali@388
   255
#ifdef MSWIN_API
ali@388
   256
	DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
ali@388
   257
	OVERLAPPED lock = {0};
ali@388
   258
ali@388
   259
	if (exclusive)
ali@388
   260
		flags |= LOCKFILE_EXCLUSIVE_LOCK;
ali@403
   261
	if (fd >= 0 && !LockFileEx((HANDLE)_get_osfhandle(fd), flags, 0, 1, 0,
ali@403
   262
				   &lock)) {
ali@388
   263
		close(fd);
ali@388
   264
		return -1;
ali@388
   265
	}
ali@388
   266
	if (set->lock_fd >= 0)
ali@403
   267
		(void)UnlockFile((HANDLE)_get_osfhandle(set->lock_fd), 0, 0, 1,
ali@403
   268
				 0);
ali@388
   269
#else
ali@388
   270
	struct flock lock = {0};
ali@388
   271
ali@388
   272
	lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
ali@388
   273
	lock.l_whence = SEEK_SET;
ali@388
   274
	lock.l_start = 0;
ali@388
   275
	lock.l_len = 0;
ali@388
   276
	if (fd >= 0 && fcntl(fd, F_SETLK, &lock) < 0) {
ali@388
   277
		close(fd);
ali@388
   278
		return -1;
ali@388
   279
	}
ali@388
   280
	if (set->lock_fd >= 0) {
ali@388
   281
		lock.l_type = F_UNLCK;
ali@388
   282
		(void)fcntl(set->lock_fd, F_SETLK, &lock);
ali@388
   283
	}
ali@388
   284
#endif
ali@388
   285
ali@388
   286
	if (set->lock_fd >= 0)
ali@388
   287
		close(set->lock_fd);
ali@388
   288
	set->lock_fd = fd;
ali@388
   289
ali@388
   290
	return 0;
ali@388
   291
}
ali@388
   292
ali@403
   293
static void
rhughes@241
   294
razor_set_destroy(struct razor_set *set)
rhughes@241
   295
{
krh@373
   296
	struct razor_mapped_file *file, *next;
krh@373
   297
	struct array *array;
rhughes@241
   298
	int i;
rhughes@241
   299
richard@301
   300
	assert (set != NULL);
richard@301
   301
krh@373
   302
	if (set->mapped_files == NULL) {
krh@373
   303
		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
krh@373
   304
			array = (void *) set + razor_sections[i].offset;
krh@373
   305
			array_release(array);
krh@373
   306
		}
rhughes@241
   307
	} else {
krh@373
   308
		for (file = set->mapped_files; file != NULL; file = next) {
krh@373
   309
			next = file->next;
ali@475
   310
			razor_uri_free_contents(file->header, file->size);
krh@373
   311
			free(file);
jbowes@258
   312
		}
jbowes@258
   313
	}
jbowes@258
   314
ali@475
   315
	razor_set_acquire_lock(set, NULL, 0);
rhughes@241
   316
	free(set);
rhughes@241
   317
}
rhughes@241
   318
ali@403
   319
RAZOR_EXPORT void
ali@403
   320
razor_set_unref(struct razor_set *set)
ali@403
   321
{
ali@403
   322
	if (set && !--set->ref_count)
ali@403
   323
		razor_set_destroy(set);
ali@403
   324
}
ali@403
   325
ali@403
   326
RAZOR_EXPORT struct razor_set *
ali@403
   327
razor_set_ref(struct razor_set *set)
ali@403
   328
{
ali@403
   329
	if (set)
ali@403
   330
		set->ref_count++;
ali@403
   331
	return set;
ali@403
   332
}
ali@403
   333
ali@403
   334
RAZOR_EXPORT void
ali@403
   335
razor_set_write_to_handle(struct razor_set *set, struct razor_atomic *atomic,
ali@403
   336
			  int handle, uint32_t section_mask)
rhughes@241
   337
{
jbowes@318
   338
	struct razor_set_header header;
krh@373
   339
	struct razor_set_section sections[ARRAY_SIZE(razor_sections)];
jbowes@318
   340
	struct hashtable table;
krh@373
   341
	struct array pool, *arrays[ARRAY_SIZE(razor_sections)];
rhughes@241
   342
	uint32_t offset;
krh@373
   343
	int count, i, j;
krh@373
   344
	static const char padding[4];
jbowes@318
   345
jbowes@318
   346
	array_init(&pool);
jbowes@318
   347
	hashtable_init(&table, &pool);
jbowes@318
   348
krh@373
   349
	j = 0;
krh@373
   350
	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
krh@373
   351
		if ((razor_sections[i].flags & section_mask) == 0)
krh@373
   352
			continue;
jbowes@318
   353
krh@373
   354
		arrays[j] = (void *) set + razor_sections[i].offset;
krh@373
   355
		sections[j].name =
krh@373
   356
			hashtable_tokenize(&table, razor_sections[i].name);
krh@373
   357
		j++;
krh@373
   358
	}
rhughes@241
   359
krh@373
   360
	count = j;
krh@373
   361
	header.magic = RAZOR_MAGIC;
ali@403
   362
	header.version = set->header_version;
krh@373
   363
	header.num_sections = count;
krh@373
   364
	offset = sizeof header + count * sizeof *sections + ALIGN(pool.size, 4);
krh@373
   365
krh@373
   366
	for (i = 0; i < count; i++) {
krh@373
   367
		sections[i].offset = offset;
krh@373
   368
		sections[i].size = arrays[i]->size;
krh@373
   369
		offset += ALIGN(arrays[i]->size, 4);
rhughes@241
   370
	}
rhughes@241
   371
ali@403
   372
	razor_atomic_write(atomic, handle, &header, sizeof header);
ali@403
   373
	razor_atomic_write(atomic, handle, sections, count * sizeof *sections);
ali@403
   374
	razor_atomic_write(atomic, handle, pool.data, pool.size);
ali@403
   375
	razor_atomic_write(atomic, handle, padding, PADDING(pool.size, 4));
rhughes@241
   376
krh@373
   377
	for (i = 0; i < count; i++) {
ali@403
   378
		razor_atomic_write(atomic, handle, arrays[i]->data,
ali@403
   379
				   arrays[i]->size);
ali@403
   380
		razor_atomic_write(atomic, handle, padding,
ali@403
   381
				   PADDING(arrays[i]->size, 4));
rhughes@241
   382
	}
rhughes@241
   383
krh@373
   384
	array_release(&pool);
krh@373
   385
	hashtable_release(&table);
rhughes@241
   386
}
rhughes@241
   387
krh@269
   388
RAZOR_EXPORT int
ali@403
   389
razor_set_write(struct razor_set *set, struct razor_atomic *atomic,
ali@475
   390
		const char *uri, uint32_t sections)
rhughes@241
   391
{
ali@403
   392
	int h;
rhughes@241
   393
ali@475
   394
	h = razor_atomic_create_file(atomic, uri,
ali@403
   395
				     S_IRWXU | S_IRWXG | S_IRWXO);
ali@403
   396
	if (h < 0)
rhughes@241
   397
		return -1;
rhughes@241
   398
ali@403
   399
	razor_set_write_to_handle(set, atomic, h, sections);
rhughes@241
   400
ali@403
   401
	return razor_atomic_close(atomic, h);
rhughes@241
   402
}
krh@269
   403
krh@269
   404
RAZOR_EXPORT void
rhughes@241
   405
razor_build_evr(char *evr_buf, int size, const char *epoch,
rhughes@241
   406
		const char *version, const char *release)
rhughes@241
   407
{
rhughes@241
   408
	int len;
rhughes@241
   409
rhughes@241
   410
	if (!version || !*version) {
rhughes@241
   411
		*evr_buf = '\0';
rhughes@241
   412
		return;
rhughes@241
   413
	}
rhughes@241
   414
rhughes@241
   415
	if (epoch && *epoch && strcmp(epoch, "0") != 0) {
rhughes@241
   416
		len = snprintf(evr_buf, size, "%s:", epoch);
rhughes@241
   417
		evr_buf += len;
rhughes@241
   418
		size -= len;
rhughes@241
   419
	}
rhughes@241
   420
	len = snprintf(evr_buf, size, "%s", version);
rhughes@241
   421
	evr_buf += len;
rhughes@241
   422
	size -= len;
rhughes@241
   423
	if (release && *release)
rhughes@241
   424
		snprintf(evr_buf, size, "-%s", release);
rhughes@241
   425
}
rhughes@241
   426
krh@269
   427
RAZOR_EXPORT int
krh@248
   428
razor_versioncmp(const char *s1, const char *s2)
rhughes@241
   429
{
rhughes@241
   430
	const char *p1, *p2;
rhughes@241
   431
	long n1, n2;
rhughes@241
   432
	int res;
rhughes@241
   433
richard@301
   434
	assert (s1 != NULL);
richard@301
   435
	assert (s2 != NULL);
richard@301
   436
rhughes@241
   437
	n1 = strtol(s1, (char **) &p1, 10);
rhughes@241
   438
	n2 = strtol(s2, (char **) &p2, 10);
rhughes@241
   439
rhughes@241
   440
	/* Epoch; if one but not the other has an epoch set, default
rhughes@241
   441
	 * the epoch-less version to 0. */
rhughes@241
   442
	res = (*p1 == ':') - (*p2 == ':');
rhughes@241
   443
	if (res < 0) {
rhughes@241
   444
		n1 = 0;
rhughes@241
   445
		p1 = s1;
rhughes@241
   446
		p2++;
rhughes@241
   447
	} else if (res > 0) {
rhughes@241
   448
		p1++;
rhughes@241
   449
		n2 = 0;
rhughes@241
   450
		p2 = s2;
rhughes@241
   451
	}
rhughes@241
   452
rhughes@241
   453
	if (n1 != n2)
rhughes@241
   454
		return n1 - n2;
rhughes@241
   455
	while (*p1 && *p2) {
rhughes@241
   456
		if (*p1 != *p2)
rhughes@241
   457
			return *p1 - *p2;
rhughes@241
   458
		p1++;
rhughes@241
   459
		p2++;
rhughes@241
   460
		if (isdigit(*p1) && isdigit(*p2))
krh@248
   461
			return razor_versioncmp(p1, p2);
rhughes@241
   462
	}
rhughes@241
   463
rhughes@241
   464
	return *p1 - *p2;
rhughes@241
   465
}
rhughes@241
   466
richard@302
   467
static const char *
ali@372
   468
razor_package_get_details_string(struct razor_set *set,
ali@372
   469
				 struct razor_package *package,
ali@372
   470
				 enum razor_detail_type type)
richard@302
   471
{
richard@302
   472
	const char *pool;
richard@302
   473
richard@302
   474
	switch (type) {
richard@302
   475
	case RAZOR_DETAIL_NAME:
richard@302
   476
		pool = set->string_pool.data;
richard@302
   477
		return &pool[package->name];
richard@302
   478
richard@302
   479
	case RAZOR_DETAIL_VERSION:
richard@302
   480
		pool = set->string_pool.data;
richard@302
   481
		return &pool[package->version];
richard@302
   482
richard@302
   483
	case RAZOR_DETAIL_ARCH:
richard@302
   484
		pool = set->string_pool.data;
richard@302
   485
		return &pool[package->arch];
richard@302
   486
richard@302
   487
	case RAZOR_DETAIL_SUMMARY:
ali@395
   488
		if (!set->details_string_pool.size)
ali@395
   489
			return "";
richard@302
   490
		pool = set->details_string_pool.data;
richard@302
   491
		return &pool[package->summary];
richard@302
   492
richard@302
   493
	case RAZOR_DETAIL_DESCRIPTION:
ali@395
   494
		if (!set->details_string_pool.size)
ali@395
   495
			return "";
richard@302
   496
		pool = set->details_string_pool.data;
richard@302
   497
		return &pool[package->description];
richard@302
   498
richard@302
   499
	case RAZOR_DETAIL_URL:
ali@395
   500
		if (!set->details_string_pool.size)
ali@395
   501
			return "";
richard@302
   502
		pool = set->details_string_pool.data;
richard@302
   503
		return &pool[package->url];
richard@302
   504
richard@302
   505
	case RAZOR_DETAIL_LICENSE:
ali@395
   506
		if (!set->details_string_pool.size)
ali@395
   507
			return "";
richard@302
   508
		pool = set->details_string_pool.data;
richard@302
   509
		return &pool[package->license];
richard@302
   510
ali@369
   511
	case RAZOR_DETAIL_PREUNPROG:
ali@369
   512
		pool = set->string_pool.data;
ali@369
   513
		return &pool[package->preun.program];
ali@369
   514
ali@369
   515
	case RAZOR_DETAIL_PREUN:
ali@369
   516
		pool = set->string_pool.data;
ali@369
   517
		return &pool[package->preun.body];
ali@369
   518
ali@369
   519
	case RAZOR_DETAIL_POSTUNPROG:
ali@369
   520
		pool = set->string_pool.data;
ali@369
   521
		return &pool[package->postun.program];
ali@369
   522
ali@369
   523
	case RAZOR_DETAIL_POSTUN:
ali@369
   524
		pool = set->string_pool.data;
ali@369
   525
		return &pool[package->postun.body];
ali@369
   526
richard@302
   527
	default:
richard@302
   528
		fprintf(stderr, "type %u not found\n", type);
richard@302
   529
		return NULL;
richard@302
   530
	}
richard@302
   531
}
richard@302
   532
ali@372
   533
static const char *const *
ali@372
   534
razor_package_get_details_array(struct razor_set *set,
ali@372
   535
				struct razor_package *package,
ali@372
   536
				enum razor_detail_type type)
ali@372
   537
{
ali@372
   538
	switch (type) {
ali@372
   539
	case RAZOR_DETAIL_PREFIXES:
ali@462
   540
		/* We don't track prefixes in packages. Install
ali@462
   541
		 * prefixes are tracked, and made available via
ali@462
   542
		 * razor_install_prefix_iterator_create().
ali@372
   543
		 */
ali@372
   544
		return NULL;
ali@372
   545
ali@372
   546
	default:
ali@372
   547
		fprintf(stderr, "type %u not found\n", type);
ali@372
   548
		return NULL;
ali@372
   549
	}
ali@372
   550
}
ali@372
   551
richard@302
   552
/**
richard@302
   553
 * razor_package_get_details_varg:
richard@302
   554
 * @set: a %razor_set
richard@302
   555
 * @package: a %razor_package
richard@302
   556
 * @args: a va_list of arguments to set
richard@302
   557
 **/
richard@302
   558
void
richard@302
   559
razor_package_get_details_varg(struct razor_set *set,
richard@302
   560
			       struct razor_package *package,
richard@302
   561
			       va_list args)
richard@302
   562
{
richard@302
   563
	int i;
richard@302
   564
	enum razor_detail_type type;
ali@372
   565
	const char **string;
ali@372
   566
	const char *const **array;
richard@302
   567
richard@302
   568
	for (i = 0;; i += 2) {
richard@302
   569
		type = va_arg(args, enum razor_detail_type);
richard@307
   570
		if (type == RAZOR_DETAIL_LAST)
richard@302
   571
			break;
ali@372
   572
		if (type == RAZOR_DETAIL_PREFIXES) {
ali@372
   573
			array = va_arg(args, const char *const **);
ali@372
   574
			*array = razor_package_get_details_array(set, package,
ali@372
   575
								 type);
ali@372
   576
		} else {
ali@372
   577
			string = va_arg(args, const char **);
ali@372
   578
			*string = razor_package_get_details_string(set, package,
ali@372
   579
								   type);
ali@372
   580
		}
richard@302
   581
	}
richard@302
   582
richard@302
   583
}
richard@302
   584
richard@302
   585
/**
richard@302
   586
 * razor_package_get_details:
richard@302
   587
 * @set: a %razor_set
richard@302
   588
 * @package: a %razor_package
richard@302
   589
 *
richard@302
   590
 * Gets details about a package using a varg interface
krh@308
   591
 * The vararg must be terminated with %RAZOR_DETAIL_LAST.
richard@302
   592
 *
richard@307
   593
 * Example: razor_package_get_details (set, package,
richard@307
   594
 *				       RAZOR_DETAIL_URL, &url,
richard@307
   595
 *				       RAZOR_DETAIL_LAST);
richard@302
   596
 **/
krh@269
   597
RAZOR_EXPORT void
richard@302
   598
razor_package_get_details(struct razor_set *set, struct razor_package *package, ...)
jbowes@258
   599
{
richard@302
   600
	va_list args;
jbowes@258
   601
richard@301
   602
	assert (set != NULL);
richard@301
   603
	assert (package != NULL);
richard@301
   604
richard@302
   605
	va_start(args, NULL);
richard@302
   606
	razor_package_get_details_varg (set, package, args);
richard@302
   607
	va_end (args);
jbowes@258
   608
}
jbowes@258
   609
ali@369
   610
/**
ali@369
   611
 * razor_package_remove:
ali@382
   612
 * @prev: The %razor_set before the current transaction
ali@382
   613
 * @next: The %razor_set after the current transaction is applied
ali@369
   614
 * @package: a %razor_package
ali@369
   615
 * @root: the root into which the package is currently installed
ali@376
   616
 * @install_count: the value to pass to uninstall scripts
ali@403
   617
 * @stage: Limit the removal to just the scripts or the files
ali@369
   618
 *
ali@369
   619
 * Removes an installed package.
ali@369
   620
 **/
ali@369
   621
RAZOR_EXPORT int
ali@382
   622
razor_package_remove(struct razor_set *prev, struct razor_set *next,
ali@403
   623
		     struct razor_atomic *atomic, struct razor_package *package,
ali@479
   624
		     const char *root_uri, int install_count,
ali@403
   625
		     enum razor_stage_type stage)
ali@369
   626
{
ali@369
   627
	struct razor_file_iterator *fi;
ali@369
   628
	struct razor_package_iterator *pi;
ali@369
   629
	struct razor_package *p;
ali@479
   630
	char *uri, *relative, *buffer, buf[32];
ali@369
   631
	const char *name, *program, *script;
ali@422
   632
	int i, count, retval = 0;
ali@372
   633
	struct environment env;
ali@372
   634
	struct list *link;
ali@372
   635
	const char *prefix;
ali@475
   636
	struct razor_error *tmp_error = NULL;
ali@369
   637
ali@403
   638
	if (stage & RAZOR_STAGE_SCRIPTS) {
ali@403
   639
		environment_init(&env);
ali@403
   640
		link = list_first(&package->install_prefixes,
ali@403
   641
				  &prev->prefix_pool);
ali@403
   642
		for (i = 0; link; i++) {
ali@403
   643
			prefix = (const char *)prev->string_pool.data +
ali@403
   644
				 link->data;
ali@411
   645
			sprintf(buf, "RPM_INSTALL_PREFIX%d", i);
ali@411
   646
			environment_add_variable(&env, buf, prefix);
ali@403
   647
			link = list_next(link);
ali@403
   648
		}
ali@403
   649
		environment_set(&env);
ali@375
   650
	}
ali@375
   651
ali@403
   652
	if (stage & RAZOR_STAGE_SCRIPTS_PRE) {
ali@403
   653
		razor_package_get_details(prev, package,
ali@403
   654
					  RAZOR_DETAIL_PREUNPROG, &program,
ali@403
   655
					  RAZOR_DETAIL_PREUN, &script,
ali@403
   656
					  RAZOR_DETAIL_LAST);
ali@369
   657
ali@479
   658
		retval = razor_run_script(root_uri, RAZOR_PROPERTY_PREUN,
ali@479
   659
					  program, script, install_count,
ali@479
   660
					  &tmp_error);
ali@475
   661
ali@475
   662
		if (retval < 0) {
ali@475
   663
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@475
   664
			tmp_error = NULL;
ali@475
   665
		}
ali@403
   666
	}
ali@375
   667
ali@422
   668
	if (!retval && (stage & RAZOR_STAGE_FILES)) {
ali@403
   669
		fi = razor_file_iterator_create(prev, package, 1);
ali@369
   670
ali@403
   671
		while (razor_file_iterator_next(fi, &name)) {
ali@403
   672
			pi = razor_package_iterator_create_for_file(next, name);
ali@403
   673
			count = 0;
ali@403
   674
			while (razor_package_iterator_next(pi, &p,
ali@403
   675
							   RAZOR_DETAIL_LAST))
ali@403
   676
				count++;
ali@403
   677
			razor_package_iterator_destroy(pi);
ali@403
   678
			if (count <= 0) {
ali@479
   679
				uri = razor_path_to_uri(name);
ali@479
   680
ali@479
   681
				if (str_has_prefix(uri, "file:///"))
ali@479
   682
					relative = uri + 8;
ali@479
   683
				else if (str_has_prefix(uri, "file:/"))
ali@479
   684
					relative = uri + 6;
ali@479
   685
				else if (str_has_prefix(uri, "file:"))
ali@479
   686
					relative = uri + 5;
ali@479
   687
				else if (str_has_prefix(uri, "/"))
ali@479
   688
					relative = uri + 1;
ali@479
   689
				else
ali@479
   690
					relative = uri;
ali@479
   691
ali@479
   692
				buffer = razor_resolve_uri_root(root_uri,
ali@479
   693
								relative, 1,
ali@479
   694
								&tmp_error);
ali@479
   695
				free(uri);
ali@479
   696
ali@479
   697
				if (buffer) {
ali@479
   698
					razor_atomic_remove(atomic, buffer);
ali@479
   699
					free(buffer);
ali@479
   700
				} else {
ali@479
   701
					razor_atomic_propagate_error(atomic,
ali@479
   702
								     tmp_error,
ali@479
   703
								     NULL);
ali@479
   704
					tmp_error = NULL;
ali@479
   705
					break;
ali@479
   706
				}
ali@377
   707
			}
ali@369
   708
		}
ali@403
   709
ali@403
   710
		razor_file_iterator_destroy(fi);
ali@422
   711
ali@422
   712
		retval = razor_atomic_in_error_state(atomic);
ali@369
   713
	}
ali@369
   714
ali@422
   715
	if (!retval && (stage & RAZOR_STAGE_SCRIPTS_POST)) {
ali@403
   716
		razor_package_get_details(prev, package,
ali@403
   717
					  RAZOR_DETAIL_POSTUNPROG, &program,
ali@403
   718
					  RAZOR_DETAIL_POSTUN, &script,
ali@403
   719
					  RAZOR_DETAIL_LAST);
ali@369
   720
ali@479
   721
		retval |= razor_run_script(root_uri, RAZOR_PROPERTY_POSTUN,
ali@479
   722
					   program, script, install_count,
ali@479
   723
					   &tmp_error);
ali@475
   724
ali@475
   725
		if (retval < 0) {
ali@475
   726
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@475
   727
			tmp_error = NULL;
ali@475
   728
		}
ali@403
   729
	}
ali@369
   730
ali@403
   731
	if (stage & RAZOR_STAGE_SCRIPTS) {
ali@403
   732
		environment_unset(&env);
ali@403
   733
		environment_release(&env);
ali@403
   734
	}
ali@377
   735
ali@422
   736
	return retval;
ali@369
   737
}
ali@369
   738
krh@270
   739
RAZOR_EXPORT const char *
krh@270
   740
razor_property_relation_to_string(struct razor_property *p)
krh@270
   741
{
richard@301
   742
	assert (p != NULL);
richard@301
   743
krh@270
   744
	switch (p->flags & RAZOR_PROPERTY_RELATION_MASK) {
krh@270
   745
	case RAZOR_PROPERTY_LESS:
krh@270
   746
		return "<";
krh@270
   747
krh@270
   748
	case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL:
krh@270
   749
		return "<=";
krh@270
   750
krh@270
   751
	case RAZOR_PROPERTY_EQUAL:
krh@270
   752
		return "=";
krh@270
   753
krh@270
   754
	case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL:
krh@270
   755
		return ">=";
krh@270
   756
krh@270
   757
	case RAZOR_PROPERTY_GREATER:
krh@270
   758
		return ">";
krh@270
   759
krh@270
   760
	default:
krh@270
   761
		return "?";
krh@270
   762
	}
krh@270
   763
}
krh@270
   764
krh@270
   765
RAZOR_EXPORT const char *
krh@270
   766
razor_property_type_to_string(struct razor_property *p)
krh@270
   767
{
richard@301
   768
	assert (p != NULL);
richard@301
   769
krh@270
   770
	switch (p->flags & RAZOR_PROPERTY_TYPE_MASK) {
krh@270
   771
	case RAZOR_PROPERTY_REQUIRES:
krh@270
   772
		return "requires";
krh@270
   773
	case RAZOR_PROPERTY_PROVIDES:
krh@270
   774
		return "provides";
krh@270
   775
	case RAZOR_PROPERTY_CONFLICTS:
krh@270
   776
		return "conflicts";
krh@270
   777
	case RAZOR_PROPERTY_OBSOLETES:
krh@270
   778
		return "obsoletes";
krh@270
   779
	default:
krh@270
   780
		return NULL;
krh@270
   781
	}
krh@270
   782
}
krh@270
   783
krh@269
   784
RAZOR_EXPORT struct razor_entry *
krh@248
   785
razor_set_find_entry(struct razor_set *set,
krh@248
   786
		     struct razor_entry *dir, const char *pattern)
rhughes@241
   787
{
ali@359
   788
	struct razor_entry *e, *subdir;
jbowes@264
   789
	const char *n, *pool = set->file_string_pool.data;
rhughes@241
   790
	int len;
rhughes@241
   791
richard@301
   792
	assert (set != NULL);
richard@301
   793
	assert (pattern != NULL);
richard@301
   794
ali@383
   795
	if (dir == NULL)
ali@383
   796
		return NULL;
ali@383
   797
ali@359
   798
	e = dir;
rhughes@241
   799
	do {
rhughes@241
   800
		n = pool + e->name;
ali@359
   801
		if (strcmp(pattern, n) == 0)
rhughes@241
   802
			return e;
rhughes@241
   803
		len = strlen(n);
ali@359
   804
		if (e->start != 0 && strncmp(pattern, n, len) == 0 &&
ali@359
   805
		    pattern[len] == '/') {
ali@359
   806
			subdir = (struct razor_entry *) set->files.data +
ali@359
   807
				 e->start;
ali@359
   808
			return razor_set_find_entry(set, subdir,
ali@359
   809
						    pattern + len + 1);
rhughes@241
   810
		}
rhughes@241
   811
	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
rhughes@241
   812
rhughes@241
   813
	return NULL;
rhughes@241
   814
}
rhughes@241
   815
rhughes@241
   816
static void
rhughes@241
   817
list_dir(struct razor_set *set, struct razor_entry *dir,
rhughes@241
   818
	 char *prefix, const char *pattern)
rhughes@241
   819
{
ali@359
   820
	struct razor_entry *e, *subdir;
jbowes@274
   821
	const char *n, *pool = set->file_string_pool.data;
rhughes@241
   822
ali@359
   823
	e = dir;
rhughes@241
   824
	do {
rhughes@241
   825
		n = pool + e->name;
rhughes@241
   826
		if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
rhughes@241
   827
			continue;
rhughes@241
   828
		printf("%s/%s\n", prefix, n);
rhughes@241
   829
		if (e->start) {
rhughes@241
   830
			char *sub = prefix + strlen (prefix);
rhughes@241
   831
			*sub = '/';
rhughes@241
   832
			strcpy (sub + 1, n);
ali@359
   833
			subdir = (struct razor_entry *) set->files.data +
ali@359
   834
				 e->start;
ali@359
   835
			list_dir(set, subdir, prefix, pattern);
rhughes@241
   836
			*sub = '\0';
rhughes@241
   837
		}
rhughes@241
   838
	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
rhughes@241
   839
}
rhughes@241
   840
krh@269
   841
RAZOR_EXPORT void
rhughes@241
   842
razor_set_list_files(struct razor_set *set, const char *pattern)
rhughes@241
   843
{
ali@359
   844
	struct razor_entry *root, *e;
rhughes@241
   845
	char buffer[512], *p, *base;
rhughes@241
   846
richard@301
   847
	assert (set != NULL);
richard@301
   848
ali@359
   849
	root = (struct razor_entry *) set->files.data;
ali@359
   850
ali@359
   851
	if (pattern == NULL) {
ali@359
   852
		p = set->file_string_pool.data;
ali@359
   853
		e = root;
ali@359
   854
		do {
ali@359
   855
			if (e->start) {
ali@359
   856
				strcpy(buffer, p + e->name);
ali@359
   857
				list_dir(set, root + e->start, buffer, NULL);
ali@359
   858
			}
ali@359
   859
		} while (!((e++)->flags & RAZOR_ENTRY_LAST));
rhughes@241
   860
		return;
rhughes@241
   861
	}
rhughes@241
   862
rhughes@241
   863
	strcpy(buffer, pattern);
ali@359
   864
	e = razor_set_find_entry(set, root, buffer);
ali@359
   865
	if (e && e->start) {
rhughes@241
   866
		base = NULL;
rhughes@241
   867
	} else {
rhughes@241
   868
		p = strrchr(buffer, '/');
rhughes@241
   869
		if (p) {
rhughes@241
   870
			*p = '\0';
rhughes@241
   871
			base = p + 1;
rhughes@241
   872
		} else {
rhughes@241
   873
			base = NULL;
rhughes@241
   874
		}
rhughes@241
   875
	}
ali@359
   876
	e = razor_set_find_entry(set, root, buffer);
ali@359
   877
	if (e && e->start)
ali@359
   878
		list_dir(set, root + e->start, buffer, base);
rhughes@241
   879
}
rhughes@241
   880
krh@269
   881
RAZOR_EXPORT void
krh@306
   882
razor_set_list_package_files(struct razor_set *set,
krh@306
   883
			     struct razor_package *package)
rhughes@241
   884
{
ali@351
   885
	struct razor_file_iterator *fi;
ali@351
   886
	const char *name;
rhughes@241
   887
richard@301
   888
	assert (set != NULL);
krh@306
   889
	assert (package != NULL);
rhughes@241
   890
ali@377
   891
	fi = razor_file_iterator_create(set, package, 0);
ali@351
   892
ali@351
   893
	while (razor_file_iterator_next(fi, &name))
ali@351
   894
		printf("%s\n", name);
ali@351
   895
ali@351
   896
	razor_file_iterator_destroy(fi);
rhughes@241
   897
}
rhughes@241
   898
ali@387
   899
/*
ali@387
   900
 * Package data can potentially come from two places. The so-called
ali@387
   901
 * metadata (eg., from comps.xml) and from an RPM file. We consider
ali@387
   902
 * a package which has additional data from an RPM file as "fixed".
ali@387
   903
 * If a package needs fixing, then razor_transaction_fixup_package()
ali@387
   904
 * will do so. When considering what packages to add and to remove
ali@387
   905
 * we need to take this into account since we always want to add
ali@387
   906
 * unfixed packages (otherwise we have a potential conflict between
ali@387
   907
 * the existing package data and that present in the RPM).
ali@387
   908
 */
ali@387
   909
static int
ali@387
   910
razor_package_is_fixed(struct razor_set *set, struct razor_package *p)
ali@387
   911
{
ali@387
   912
	const char *preunprog, *preun, *postunprog, *postun;
ali@387
   913
ali@387
   914
	if (!p)
ali@387
   915
		return 0;
ali@387
   916
	razor_package_get_details(set, p,
ali@387
   917
				  RAZOR_DETAIL_PREUNPROG, &preunprog,
ali@387
   918
				  RAZOR_DETAIL_PREUN, &preun,
ali@387
   919
				  RAZOR_DETAIL_POSTUNPROG, &postunprog,
ali@387
   920
				  RAZOR_DETAIL_POSTUN, &postun,
ali@387
   921
				  RAZOR_DETAIL_LAST);
ali@387
   922
	return *preunprog || *preun || *postunprog || *postun;
ali@387
   923
}
ali@387
   924
krh@269
   925
RAZOR_EXPORT void
rhughes@241
   926
razor_set_diff(struct razor_set *set, struct razor_set *upstream,
krh@253
   927
	       razor_diff_callback_t callback, void *data)
rhughes@241
   928
{
rhughes@241
   929
 	struct razor_package_iterator *pi1, *pi2;
rhughes@241
   930
 	struct razor_package *p1, *p2;
rhughes@241
   931
	const char *name1, *name2, *version1, *version2, *arch1, *arch2;
ali@387
   932
	int res, is_fixed1, is_fixed2;
rhughes@241
   933
richard@301
   934
	assert (set != NULL);
richard@301
   935
	assert (upstream != NULL);
richard@301
   936
rhughes@241
   937
	pi1 = razor_package_iterator_create(set);
rhughes@241
   938
	pi2 = razor_package_iterator_create(upstream);
rhughes@241
   939
richard@302
   940
	razor_package_iterator_next(pi1, &p1,
richard@302
   941
				    RAZOR_DETAIL_NAME, &name1,
richard@302
   942
				    RAZOR_DETAIL_VERSION, &version1,
richard@302
   943
				    RAZOR_DETAIL_ARCH, &arch1,
richard@307
   944
				    RAZOR_DETAIL_LAST);
ali@387
   945
	is_fixed1 = razor_package_is_fixed(set, p1);
richard@302
   946
	razor_package_iterator_next(pi2, &p2,
richard@302
   947
				    RAZOR_DETAIL_NAME, &name2,
richard@302
   948
				    RAZOR_DETAIL_VERSION, &version2,
richard@302
   949
				    RAZOR_DETAIL_ARCH, &arch2,
richard@307
   950
				    RAZOR_DETAIL_LAST);
ali@387
   951
	is_fixed2 = razor_package_is_fixed(upstream, p2);
rhughes@241
   952
rhughes@241
   953
	while (p1 || p2) {
rhughes@241
   954
		if (p1 && p2) {
rhughes@241
   955
			res = strcmp(name1, name2);
rhughes@241
   956
			if (res == 0)
krh@248
   957
				res = razor_versioncmp(version1, version2);
ali@387
   958
			if (res == 0)
ali@387
   959
				res = is_fixed1 - is_fixed2;
rhughes@241
   960
		} else {
rhughes@241
   961
			res = 0;
rhughes@241
   962
		}
rhughes@241
   963
rhughes@241
   964
		if (p2 == NULL || res < 0)
krh@253
   965
			callback(RAZOR_DIFF_ACTION_REMOVE,
krh@253
   966
				 p1, name1, version1, arch1, data);
rhughes@241
   967
		else if (p1 == NULL || res > 0)
krh@253
   968
			callback(RAZOR_DIFF_ACTION_ADD,
krh@253
   969
				 p2, name2, version2, arch2, data);
rhughes@241
   970
ali@387
   971
		if (p1 != NULL && res <= 0) {
rhughes@241
   972
			razor_package_iterator_next(pi1, &p1,
richard@302
   973
						    RAZOR_DETAIL_NAME, &name1,
richard@302
   974
						    RAZOR_DETAIL_VERSION, &version1,
richard@302
   975
						    RAZOR_DETAIL_ARCH, &arch1,
richard@307
   976
						    RAZOR_DETAIL_LAST);
ali@387
   977
			is_fixed1 = razor_package_is_fixed(set, p1);
ali@387
   978
		}
ali@387
   979
		if (p2 != NULL && res >= 0) {
rhughes@241
   980
			razor_package_iterator_next(pi2, &p2,
richard@302
   981
						    RAZOR_DETAIL_NAME, &name2,
richard@302
   982
						    RAZOR_DETAIL_VERSION, &version2,
richard@302
   983
						    RAZOR_DETAIL_ARCH, &arch2,
richard@307
   984
						    RAZOR_DETAIL_LAST);
ali@387
   985
			is_fixed2 = razor_package_is_fixed(upstream, p2);
ali@387
   986
		}
rhughes@241
   987
	}
rhughes@241
   988
rhughes@241
   989
	razor_package_iterator_destroy(pi1);
rhughes@241
   990
	razor_package_iterator_destroy(pi2);
rhughes@241
   991
}
krh@254
   992
krh@316
   993
struct install_action {
krh@316
   994
	enum razor_install_action action;
krh@316
   995
	struct razor_package *package;
krh@316
   996
};
krh@316
   997
krh@316
   998
struct razor_install_iterator {
krh@316
   999
	struct razor_set *set;
krh@316
  1000
	struct razor_set *next;
krh@316
  1001
	struct array actions;
ali@403
  1002
	struct deque *order, *left;
krh@316
  1003
};
krh@316
  1004
krh@254
  1005
static void
krh@316
  1006
add_action(enum razor_diff_action action,
krh@316
  1007
	   struct razor_package *package,
krh@316
  1008
	   const char *name,
krh@316
  1009
	   const char *version,
krh@316
  1010
	   const char *arch,
krh@316
  1011
	   void *data)
krh@254
  1012
{
krh@316
  1013
	struct razor_install_iterator *ii = data;
krh@316
  1014
	struct install_action *a;
krh@316
  1015
krh@316
  1016
	a = array_add(&ii->actions, sizeof *a);
krh@316
  1017
	a->package = package;
krh@316
  1018
krh@316
  1019
	switch (action) {
krh@316
  1020
	case RAZOR_DIFF_ACTION_ADD:
krh@316
  1021
		a->action = RAZOR_INSTALL_ACTION_ADD;
krh@316
  1022
		break;
krh@316
  1023
	case RAZOR_DIFF_ACTION_REMOVE:
krh@316
  1024
		a->action = RAZOR_INSTALL_ACTION_REMOVE;
krh@316
  1025
		break;
krh@316
  1026
	}
krh@254
  1027
}
krh@254
  1028
ali@418
  1029
/*
ali@418
  1030
 * Does <package> have a requirement for <script> which is
ali@418
  1031
 * satisfied by <provider> ?
ali@418
  1032
 * Note: We already know that <provider> is to be added as part of this install
ali@418
  1033
 * so there is no need to check the relation.
ali@418
  1034
 */
ali@418
  1035
static int
ali@418
  1036
package_script_requires(struct razor_set *set, struct razor_package *package,
ali@418
  1037
			enum razor_property_flags script,
ali@418
  1038
			struct razor_package *provider)
ali@418
  1039
{
ali@418
  1040
	struct list *link;
ali@418
  1041
	struct razor_property *prop;
ali@418
  1042
ali@418
  1043
	link = list_first(&package->properties, &set->property_pool);
ali@418
  1044
	for(; link; link = list_next(link)) {
ali@418
  1045
		prop = set->properties.data;
ali@418
  1046
		prop += link->data;
ali@418
  1047
		if ((prop->flags & RAZOR_PROPERTY_SCRIPT_MASK) & script &&
ali@418
  1048
		    (prop->flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES &&
ali@418
  1049
		    provider->name == prop->name)
ali@418
  1050
			return 1;
ali@418
  1051
	}
ali@418
  1052
	return 0;
ali@418
  1053
}
ali@418
  1054
krh@316
  1055
RAZOR_EXPORT struct razor_install_iterator *
krh@316
  1056
razor_set_create_install_iterator(struct razor_set *set,
krh@316
  1057
				  struct razor_set *next)
krh@254
  1058
{
krh@316
  1059
	struct razor_install_iterator *ii;
ali@367
  1060
	struct razor_property *prop;
ali@367
  1061
	/* A graph of the actions to be perfomed where
ali@367
  1062
	 * A->B means action A should follow action B.
ali@367
  1063
	 */
ali@367
  1064
	struct graph follows;
ali@418
  1065
	struct install_action *actions, *ai, *aj, *an;
ali@367
  1066
	int i, j, count, vertex_added;
ali@367
  1067
	struct list *link;
ali@367
  1068
	struct razor_set *rs;
ali@418
  1069
	struct deque *order;
ali@418
  1070
	int barrier_needed;
krh@254
  1071
richard@301
  1072
	assert (set != NULL);
richard@301
  1073
	assert (next != NULL);
richard@301
  1074
krh@316
  1075
	ii = zalloc(sizeof *ii);
krh@316
  1076
	ii->set = set;
krh@316
  1077
	ii->next = next;
krh@316
  1078
	
krh@316
  1079
	razor_set_diff(set, next, add_action, ii);
krh@254
  1080
ali@367
  1081
	actions = ii->actions.data;
ali@367
  1082
	count = ii->actions.size / sizeof (struct install_action);
krh@254
  1083
ali@367
  1084
	graph_init(&follows);
ali@367
  1085
ali@367
  1086
	for(i = 0; i < count; i++) {
ali@367
  1087
		ai = actions + i;
ali@367
  1088
		rs = ai->action == RAZOR_INSTALL_ACTION_ADD ? next : set;
ali@367
  1089
		vertex_added = 0;
ali@367
  1090
		link = list_first(&ai->package->properties, &rs->property_pool);
ali@367
  1091
		for(; link; link = list_next(link)) {
ali@367
  1092
			prop = rs->properties.data;
ali@367
  1093
			prop += link->data;
ali@367
  1094
			switch(prop->flags & RAZOR_PROPERTY_TYPE_MASK) {
ali@367
  1095
			case RAZOR_PROPERTY_REQUIRES:
ali@367
  1096
			case RAZOR_PROPERTY_CONFLICTS:
ali@367
  1097
				for(j = 0; j < count; j++) {
ali@367
  1098
					if (j == i)
ali@367
  1099
						continue;
ali@367
  1100
					aj = actions + j;
ali@367
  1101
					if (aj->package->name == prop->name) {
ali@367
  1102
						if (ai->action ==
ali@367
  1103
						    RAZOR_INSTALL_ACTION_ADD)
ali@367
  1104
							graph_add_edge(&follows,
ali@367
  1105
								       i, j);
ali@367
  1106
						else
ali@367
  1107
							graph_add_edge(&follows,
ali@367
  1108
								       j, i);
ali@367
  1109
						vertex_added++;
ali@367
  1110
					}
ali@367
  1111
				}
ali@367
  1112
				break;
ali@367
  1113
			}
ali@367
  1114
		}
ali@367
  1115
		if (ai->action == RAZOR_INSTALL_ACTION_ADD) {
ali@367
  1116
			for(j = 0; j < count; j++) {
ali@367
  1117
				if (j == i)
ali@367
  1118
					continue;
ali@367
  1119
				aj = actions + j;
ali@367
  1120
				if (aj->package == ai->package &&
ali@367
  1121
				    aj->action == RAZOR_INSTALL_ACTION_REMOVE) {
ali@367
  1122
					graph_add_edge(&follows, i, j);
ali@367
  1123
					vertex_added++;
ali@367
  1124
				}
ali@367
  1125
			}
ali@367
  1126
		}
ali@367
  1127
		if (!vertex_added)
ali@367
  1128
			graph_add_edge(&follows, i, i);
ali@367
  1129
	}
ali@367
  1130
ali@418
  1131
	/*
ali@418
  1132
	 * Because files are installed and removed using razor_atomic,
ali@418
  1133
	 * but scripts are run with no regard for these, we need some
ali@418
  1134
	 * means for synchronisation between the two. We support this
ali@418
  1135
	 * via transaction barriers which are points where
ali@418
  1136
	 * razor_atomic_commit() should be called so that scripts can
ali@418
  1137
	 * rely on the files they need being present.
ali@418
  1138
	 *
ali@418
  1139
	 * Rules:
ali@418
  1140
	 *   1)	Transaction barriers never occur within a dependency
ali@418
  1141
	 *	loop. Since we can't guarantee any ordering of actions
ali@418
  1142
	 *	within such a loop, they make no sense.
ali@418
  1143
	 *   2) Otherwise the following table applies:
ali@418
  1144
	 *	Action I    Action J	Barrier between I and J iff
ali@418
  1145
	 *	ADD P	    ADD Q	%pre(Q) requires P
ali@418
  1146
	 *	ADD P	    REMOVE Q	%preun(Q) requires P
ali@418
  1147
	 *	REMOVE P    ADD Q	never
ali@418
  1148
	 *	REMOVE P    REMOVE Q	%postun(P) requires Q
ali@418
  1149
	 *
ali@418
  1150
	 * FIXME:
ali@418
  1151
	 *	This should take account of conflicts somehow.
ali@418
  1152
	 */
ali@418
  1153
	order = graph_sort(&follows);
ali@418
  1154
	ii->order = deque_new(0);
ali@418
  1155
	if (!deque_empty(order)) {
ali@418
  1156
		j = deque_pop(order);
ali@418
  1157
		deque_unshift(ii->order, j);
ali@418
  1158
	}
ali@418
  1159
	while (!deque_empty(order)) {
ali@418
  1160
		i = j;
ali@418
  1161
		j = deque_pop(order);
ali@418
  1162
		ai = actions + i;
ali@418
  1163
		aj = actions + j;
ali@418
  1164
		if (graph_is_connected(&follows, i, j) &&
ali@418
  1165
		    graph_is_connected(&follows, j, i))
ali@418
  1166
			barrier_needed = 0;
ali@418
  1167
		else if (ai->action == RAZOR_INSTALL_ACTION_ADD &&
ali@418
  1168
			 aj->action == RAZOR_INSTALL_ACTION_ADD &&
ali@418
  1169
			 package_script_requires(next, aj->package,
ali@418
  1170
						 RAZOR_PROPERTY_PRE,
ali@418
  1171
						 ai->package))
ali@418
  1172
			barrier_needed = 1;
ali@418
  1173
		else if (ai->action == RAZOR_INSTALL_ACTION_ADD &&
ali@418
  1174
			 aj->action == RAZOR_INSTALL_ACTION_REMOVE &&
ali@418
  1175
			 package_script_requires(set, aj->package,
ali@418
  1176
						 RAZOR_PROPERTY_PREUN,
ali@418
  1177
						 ai->package))
ali@418
  1178
			barrier_needed = 1;
ali@418
  1179
		else if (ai->action == RAZOR_INSTALL_ACTION_REMOVE &&
ali@418
  1180
			 aj->action == RAZOR_INSTALL_ACTION_REMOVE &&
ali@418
  1181
			 package_script_requires(set, ai->package,
ali@418
  1182
						 RAZOR_PROPERTY_POSTUN,
ali@418
  1183
						 aj->package))
ali@418
  1184
			barrier_needed = 1;
ali@418
  1185
		else
ali@418
  1186
			barrier_needed = 0;
ali@418
  1187
		if (barrier_needed) {
ali@418
  1188
			an = array_add(&ii->actions, sizeof *an);
ali@418
  1189
			actions = ii->actions.data;
ali@418
  1190
			an->package = NULL;
ali@418
  1191
			an->action = RAZOR_INSTALL_ACTION_COMMIT;
ali@418
  1192
			deque_unshift(ii->order, an - actions);
ali@418
  1193
		}
ali@418
  1194
		deque_unshift(ii->order, j);
ali@418
  1195
	}
ali@418
  1196
	deque_free(order);
ali@418
  1197
ali@403
  1198
	ii->left = deque_dup(ii->order);
ali@367
  1199
	graph_release(&follows);
krh@254
  1200
krh@316
  1201
	return ii;
krh@254
  1202
}
krh@254
  1203
krh@316
  1204
RAZOR_EXPORT int
krh@316
  1205
razor_install_iterator_next(struct razor_install_iterator *ii,
krh@316
  1206
			    struct razor_package **package,
krh@316
  1207
			    enum razor_install_action *action,
krh@316
  1208
			    int *count)
krh@254
  1209
{
ali@367
  1210
	struct install_action *a;
ali@382
  1211
	struct razor_package_iterator *pi;
ali@382
  1212
	struct razor_package *pkg;
ali@382
  1213
	const char *removing, *name;
ali@382
  1214
ali@403
  1215
	if (deque_empty(ii->left))
krh@316
  1216
		return 0;
krh@254
  1217
ali@403
  1218
	a = (struct install_action *)ii->actions.data + deque_pop(ii->left);
ali@367
  1219
	*package = a->package;
ali@367
  1220
	*action = a->action;
krh@316
  1221
	*count = 0;
krh@254
  1222
ali@382
  1223
	if (a->action == RAZOR_INSTALL_ACTION_REMOVE) {
ali@382
  1224
		razor_package_get_details(ii->set, a->package,
ali@382
  1225
					  RAZOR_DETAIL_NAME, &removing,
ali@382
  1226
					  RAZOR_DETAIL_LAST);
ali@382
  1227
ali@382
  1228
		pi = razor_package_iterator_create(ii->next);
ali@382
  1229
		while (razor_package_iterator_next(pi, &pkg,
ali@382
  1230
						   RAZOR_DETAIL_NAME, &name,
ali@382
  1231
						   RAZOR_DETAIL_LAST)) {
ali@382
  1232
			if (!strcmp(name, removing))
ali@382
  1233
				(*count)++;
ali@382
  1234
		}
ali@382
  1235
		razor_package_iterator_destroy(pi);
ali@418
  1236
	} else if (a->action == RAZOR_INSTALL_ACTION_ADD)
ali@403
  1237
		*count = 1;
ali@382
  1238
krh@316
  1239
	return 1;
krh@316
  1240
}
krh@254
  1241
ali@418
  1242
static int
ali@418
  1243
action_is_included(struct razor_install_iterator *ii, struct deque *done,
ali@418
  1244
		   struct razor_package *package,
ali@418
  1245
		   enum razor_install_action action)
ali@418
  1246
{
ali@418
  1247
	struct deque *t;
ali@418
  1248
	struct install_action *a;
ali@418
  1249
	int retval;
ali@418
  1250
ali@418
  1251
	t = deque_dup(done);
ali@418
  1252
ali@418
  1253
	while(!deque_empty(t)) {
ali@418
  1254
		a = (struct install_action *)ii->actions.data + deque_pop(t);
ali@418
  1255
		if (a->package == package && a->action == action)
ali@418
  1256
			break;
ali@418
  1257
	}
ali@418
  1258
	retval = !deque_empty(t);
ali@418
  1259
ali@418
  1260
	deque_free(t);
ali@418
  1261
ali@418
  1262
	return retval;
ali@418
  1263
}
ali@418
  1264
ali@418
  1265
RAZOR_EXPORT struct razor_set *
ali@418
  1266
razor_install_iterator_commit_set(struct razor_install_iterator *ii)
ali@418
  1267
{
ali@418
  1268
	struct razor_merger *merger;
ali@418
  1269
	struct razor_set *set;
ali@442
  1270
	struct razor_package *n, *nend, *s, *send;
ali@418
  1271
	struct deque *done;
ali@418
  1272
	size_t pos;
ali@418
  1273
	char *npool, *spool;
ali@418
  1274
	int cmp;
ali@418
  1275
ali@418
  1276
	done = deque_dup(ii->order);
ali@418
  1277
	pos = razor_install_iterator_tell(ii);
ali@418
  1278
	while(deque_length(done) > pos)
ali@418
  1279
		deque_shift(done);
ali@418
  1280
ali@418
  1281
	s = ii->set->packages.data;
ali@418
  1282
	send = ii->set->packages.data + ii->set->packages.size;
ali@418
  1283
	spool = ii->set->string_pool.data;
ali@418
  1284
ali@418
  1285
	n = ii->next->packages.data;
ali@418
  1286
	nend = ii->next->packages.data + ii->next->packages.size;
ali@418
  1287
	npool = ii->next->string_pool.data;
ali@418
  1288
ali@418
  1289
	merger = razor_merger_create(ii->set, ii->next);
ali@418
  1290
	while (s < send || n < nend) {
ali@418
  1291
		if (s < send && n < nend)
ali@418
  1292
			cmp = strcmp(&spool[s->name], &npool[n->name]);
ali@418
  1293
		else if (s < send)
ali@418
  1294
			cmp = -1;
ali@418
  1295
		else
ali@418
  1296
			cmp = 1;
ali@418
  1297
ali@418
  1298
		if (cmp < 0) {
ali@418
  1299
			if (!action_is_included(ii, done, s,
ali@418
  1300
						RAZOR_INSTALL_ACTION_REMOVE))
ali@418
  1301
				razor_merger_add_package(merger, s);
ali@418
  1302
			s++;
ali@418
  1303
		} else if (cmp == 0) {
ali@418
  1304
			if (!action_is_included(ii, done, s,
ali@418
  1305
						RAZOR_INSTALL_ACTION_REMOVE))
ali@418
  1306
				razor_merger_add_package(merger, s);
ali@418
  1307
			if (action_is_included(ii, done, n,
ali@418
  1308
					       RAZOR_INSTALL_ACTION_ADD))
ali@418
  1309
				razor_merger_add_package(merger, n);
ali@418
  1310
ali@418
  1311
			s++;
ali@418
  1312
			n++;
ali@418
  1313
		} else {
ali@418
  1314
			if (action_is_included(ii, done, n,
ali@418
  1315
					       RAZOR_INSTALL_ACTION_ADD))
ali@418
  1316
				razor_merger_add_package(merger, n);
ali@418
  1317
			n++;
ali@418
  1318
		}
ali@418
  1319
	}
ali@418
  1320
ali@418
  1321
	set = razor_merger_commit(merger);
ali@418
  1322
	razor_merger_destroy(merger);
ali@458
  1323
	deque_free(done);
ali@418
  1324
ali@418
  1325
	return set;
ali@418
  1326
}
ali@418
  1327
krh@316
  1328
RAZOR_EXPORT void
ali@403
  1329
razor_install_iterator_rewind(struct razor_install_iterator *ii)
ali@403
  1330
{
ali@403
  1331
	deque_free(ii->left);
ali@403
  1332
	ii->left=deque_dup(ii->order);
ali@403
  1333
}
ali@403
  1334
ali@418
  1335
RAZOR_EXPORT size_t
ali@418
  1336
razor_install_iterator_tell(struct razor_install_iterator *ii)
ali@418
  1337
{
ali@418
  1338
	return deque_length(ii->order) - deque_length(ii->left);
ali@418
  1339
}
ali@418
  1340
ali@418
  1341
RAZOR_EXPORT size_t
ali@418
  1342
razor_install_iterator_seek(struct razor_install_iterator *ii, size_t pos)
ali@418
  1343
{
ali@418
  1344
	size_t current_pos;
ali@418
  1345
ali@418
  1346
	if (pos > deque_length(ii->order))
ali@418
  1347
		pos = deque_length(ii->order);
ali@418
  1348
ali@418
  1349
	current_pos = razor_install_iterator_tell(ii);
ali@418
  1350
ali@418
  1351
	if (pos < current_pos) {
ali@418
  1352
		razor_install_iterator_rewind(ii);
ali@418
  1353
		current_pos = 0;
ali@418
  1354
	}
ali@418
  1355
ali@418
  1356
	while(current_pos < pos) {
ali@418
  1357
		(void) deque_pop(ii->left);
ali@418
  1358
		current_pos++;
ali@418
  1359
	}
ali@418
  1360
ali@418
  1361
	return current_pos;
ali@418
  1362
}
ali@418
  1363
ali@403
  1364
RAZOR_EXPORT void
krh@316
  1365
razor_install_iterator_destroy(struct razor_install_iterator *ii)
krh@316
  1366
{
krh@316
  1367
	array_release(&ii->actions);
ali@367
  1368
	deque_free(ii->order);
ali@403
  1369
	deque_free(ii->left);
krh@316
  1370
	free(ii);
krh@254
  1371
}