librazor/uri-io.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jul 08 17:52:02 2016 +0100 (2016-07-08)
changeset 485 5e309e37906e
parent 483 8087224f30c4
child 487 a5837882a252
permissions -rw-r--r--
Fix bug setting length in razor_uri_get_contents
ali@475
     1
/*
ali@475
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
ali@475
     3
 * Copyright (C) 2008  Red Hat, Inc
ali@475
     4
 * Copyright (C) 2009, 2011, 2012, 2014, 2016  J. Ali Harlow <ali@juiblex.co.uk>
ali@475
     5
 *
ali@475
     6
 * This program is free software; you can redistribute it and/or modify
ali@475
     7
 * it under the terms of the GNU General Public License as published by
ali@475
     8
 * the Free Software Foundation; either version 2 of the License, or
ali@475
     9
 * (at your option) any later version.
ali@475
    10
 *
ali@475
    11
 * This program is distributed in the hope that it will be useful,
ali@475
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@475
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@475
    14
 * GNU General Public License for more details.
ali@475
    15
 *
ali@475
    16
 * You should have received a copy of the GNU General Public License along
ali@475
    17
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@475
    18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@475
    19
 */
ali@475
    20
ali@475
    21
#include "config.h"
ali@475
    22
ali@475
    23
#include <limits.h>
ali@475
    24
#include <string.h>
ali@475
    25
#include <sys/types.h>
ali@475
    26
#include <sys/stat.h>
ali@475
    27
#include <stdlib.h>
ali@475
    28
#include <stdio.h>
ali@475
    29
#include <stdint.h>
ali@475
    30
#include <errno.h>
ali@475
    31
#include <unistd.h>
ali@475
    32
#include <fcntl.h>
ali@475
    33
#include <dirent.h>
ali@475
    34
#ifdef MSWIN_API
ali@475
    35
#include <windows.h>
ali@475
    36
#include <direct.h>
ali@475
    37
#endif
ali@475
    38
#if HAVE_SYS_MMAN_H
ali@475
    39
#include <sys/mman.h>
ali@475
    40
#endif
ali@476
    41
#if HAVE_LIBARCHIVE
ali@476
    42
#include <archive.h>
ali@476
    43
#include <archive_entry.h>
ali@476
    44
#endif
ali@475
    45
#include <assert.h>
ali@475
    46
ali@475
    47
#include "razor.h"
ali@475
    48
#include "types/types.h"
ali@475
    49
#include "razor-internal.h"
ali@475
    50
ali@475
    51
#ifndef O_BINARY
ali@475
    52
#define O_BINARY	0
ali@475
    53
#endif
ali@475
    54
ali@475
    55
#define strcmp0(s1, s2)		((s1) && (s2) ? strcmp(s1, s2) : \
ali@475
    56
				 (s1) ? 1 : (s2) ? -1 : 0)
ali@475
    57
ali@476
    58
static void *
ali@476
    59
razor_archive_get_file_contents(const char *filename, int fd, const char *path,
ali@476
    60
				size_t *length, struct razor_error **error)
ali@476
    61
{
ali@483
    62
#if HAVE_LIBARCHIVE
ali@476
    63
	int r;
ali@483
    64
	const char *errmsg;
ali@476
    65
	void *addr;
ali@476
    66
	const void *buf;
ali@485
    67
	size_t size, total_size;
ali@476
    68
	off_t offset;
ali@476
    69
	struct archive *a;
ali@476
    70
	struct archive_entry *entry;
ali@476
    71
ali@476
    72
	a = archive_read_new();
ali@476
    73
	archive_read_support_compression_all(a);
ali@476
    74
	archive_read_support_format_all(a);
ali@476
    75
ali@476
    76
	r = archive_read_open_fd(a, fd, 10240);
ali@476
    77
ali@476
    78
	if (r) {
ali@483
    79
		errmsg = archive_error_string(a);
ali@483
    80
		if (!strcmp(errmsg, "Unrecognized archive format"))
ali@483
    81
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@483
    82
					RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
ali@483
    83
					filename, errmsg);
ali@483
    84
		else
ali@483
    85
			razor_set_error(error, RAZOR_POSIX_ERROR,
ali@483
    86
					archive_errno(a), filename, errmsg);
ali@476
    87
		archive_read_finish(a);
ali@476
    88
		return NULL;
ali@476
    89
	}
ali@476
    90
ali@476
    91
	for (;;) {
ali@476
    92
		r = archive_read_next_header(a, &entry);
ali@476
    93
		if (r == ARCHIVE_EOF)
ali@476
    94
			break;
ali@476
    95
		else if (r < ARCHIVE_WARN) {
ali@476
    96
			razor_set_error(error, RAZOR_POSIX_ERROR,
ali@476
    97
					archive_errno(a), filename,
ali@476
    98
					archive_error_string(a));
ali@476
    99
			archive_read_close(a);
ali@476
   100
			archive_read_finish(a);
ali@476
   101
			return NULL;
ali@476
   102
		}
ali@476
   103
ali@476
   104
		/*
ali@476
   105
		 * TODO: Unicode support. Might need to wait for libarchive v4.
ali@476
   106
		 */
ali@476
   107
		if (!strcmp(archive_entry_pathname(entry), path)) {
ali@485
   108
			total_size = archive_entry_size(entry);
ali@485
   109
			addr = malloc(total_size);
ali@476
   110
			if (!addr) {
ali@476
   111
				archive_read_close(a);
ali@476
   112
				archive_read_finish(a);
ali@476
   113
				razor_set_error(error, RAZOR_POSIX_ERROR,
ali@476
   114
						ENOMEM, NULL,
ali@476
   115
						"Not enough memory");
ali@476
   116
				return NULL;
ali@476
   117
			}
ali@476
   118
ali@476
   119
			for(;;) {
ali@476
   120
				r = archive_read_data_block(a, &buf, &size,
ali@476
   121
							    &offset);
ali@476
   122
				if (r == ARCHIVE_EOF)
ali@476
   123
					break;
ali@476
   124
				if (r < ARCHIVE_OK) {
ali@476
   125
					razor_set_error(error, RAZOR_POSIX_ERROR,
ali@476
   126
							archive_errno(a), path,
ali@476
   127
							archive_error_string(a));
ali@476
   128
					free(addr);
ali@476
   129
					addr = NULL;
ali@476
   130
					break;
ali@476
   131
				}
ali@476
   132
				memcpy((char *)addr + offset, buf, size);
ali@476
   133
			}
ali@476
   134
ali@476
   135
			archive_read_close(a);
ali@476
   136
			archive_read_finish(a);
ali@476
   137
ali@485
   138
			*length = total_size;
ali@485
   139
ali@476
   140
			return addr;
ali@476
   141
		}
ali@476
   142
	}
ali@476
   143
ali@476
   144
	archive_read_close(a);
ali@476
   145
	archive_read_finish(a);
ali@476
   146
ali@476
   147
	razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, path,
ali@476
   148
			"No such file or directory in archive");
ali@476
   149
ali@476
   150
	return NULL;
ali@483
   151
#else
ali@483
   152
	razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@483
   153
			RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
ali@483
   154
			filename, "Archives are not supported in this build");
ali@483
   155
ali@483
   156
	return NULL;
ali@483
   157
#endif	/* HAVE_LIBARCHIVE */
ali@476
   158
}
ali@476
   159
ali@476
   160
static void *
ali@476
   161
razor_file_get_contents_archive(const char *filename, size_t *length,
ali@476
   162
				struct razor_error **error)
ali@476
   163
{
ali@476
   164
	int fd;
ali@476
   165
	char *path, *slash, *s;
ali@476
   166
	void *addr;
ali@483
   167
	struct razor_error *tmp_error = NULL;
ali@476
   168
ali@476
   169
	path = strdup(filename);
ali@476
   170
	slash = strrchr(path, '/');
ali@476
   171
ali@476
   172
	while (slash) {
ali@476
   173
		*slash = '\0';
ali@476
   174
		fd = open(path, O_RDONLY | O_BINARY);
ali@476
   175
		if (fd >= 0) {
ali@476
   176
			addr = razor_archive_get_file_contents(path, fd,
ali@476
   177
							       slash + 1,
ali@483
   178
							       length,
ali@483
   179
							       error);
ali@476
   180
			free(path);
ali@476
   181
			close(fd);
ali@476
   182
			return addr;
ali@481
   183
#ifdef MSWIN_API
ali@481
   184
		} else if (errno != ENOTDIR && errno != ENOENT) {
ali@481
   185
#else
ali@476
   186
		} else if (errno != ENOTDIR) {
ali@481
   187
#endif
ali@476
   188
			free(path);
ali@476
   189
			razor_set_error_posix(error, filename);
ali@476
   190
			return NULL;
ali@476
   191
		}
ali@476
   192
		s = strrchr(path, '/');
ali@476
   193
		*slash = '/';
ali@476
   194
		slash = s;
ali@476
   195
	}
ali@476
   196
ali@476
   197
	free(path);
ali@481
   198
#ifdef MSWIN_API
ali@481
   199
	razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, filename,
ali@481
   200
			strerror(ENOENT));
ali@481
   201
#else
ali@476
   202
	razor_set_error(error, RAZOR_POSIX_ERROR, ENOTDIR, filename,
ali@476
   203
			strerror(ENOTDIR));
ali@481
   204
#endif
ali@476
   205
	return NULL;
ali@476
   206
}
ali@476
   207
ali@475
   208
#define OPEN_FILE_USED		(1U<<0)
ali@475
   209
#define OPEN_FILE_MMAPPED	(1U<<1)
ali@475
   210
ali@475
   211
struct open_file {
ali@475
   212
	void *addr;
ali@475
   213
	size_t length;
ali@475
   214
	uint32_t flags;
ali@475
   215
};
ali@475
   216
ali@475
   217
struct array open_files = { 0, };
ali@475
   218
ali@475
   219
void *razor_file_get_contents(const char *filename, size_t *length, int private,
ali@475
   220
			      struct razor_error **error)
ali@475
   221
{
ali@475
   222
	int fd;
ali@475
   223
	struct stat st;
ali@475
   224
	void *addr = NULL;
ali@476
   225
	size_t nb, size;
ali@475
   226
	ssize_t res;
ali@475
   227
	struct open_file *of, *ofend;
ali@475
   228
ali@475
   229
	fd = open(filename, O_RDONLY | O_BINARY);
ali@475
   230
	if (fd < 0) {
ali@481
   231
#ifdef MSWIN_API
ali@481
   232
		if (errno != ENOTDIR && errno != ENOENT) {
ali@481
   233
#else
ali@476
   234
		if (errno != ENOTDIR) {
ali@481
   235
#endif
ali@476
   236
			razor_set_error_posix(error, filename);
ali@476
   237
			return NULL;
ali@476
   238
		}
ali@476
   239
		addr = razor_file_get_contents_archive(filename, &size, error);
ali@476
   240
		if (!addr)
ali@476
   241
			return NULL;
ali@476
   242
	} else {
ali@476
   243
		if (fstat(fd, &st) < 0) {
ali@476
   244
			razor_set_error_posix(error, filename);
ali@476
   245
			close(fd);
ali@476
   246
			return NULL;
ali@476
   247
		}
ali@476
   248
		size = st.st_size;
ali@475
   249
	}
ali@475
   250
ali@475
   251
	ofend = open_files.data + open_files.size;
ali@475
   252
	for (of = open_files.data; of < ofend; of++)
ali@475
   253
		if (!(of->flags & OPEN_FILE_USED))
ali@475
   254
			break;
ali@475
   255
	if (of == ofend) {
ali@475
   256
		of = array_add(&open_files, sizeof *of);
ali@475
   257
		of->flags = 0;
ali@475
   258
	}
ali@475
   259
ali@475
   260
#if HAVE_SYS_MMAN_H
ali@476
   261
	if (!addr && !private) {
ali@476
   262
		addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
ali@475
   263
		if (addr == MAP_FAILED)
ali@475
   264
			addr = NULL;
ali@475
   265
		else
ali@475
   266
			of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED;
ali@475
   267
	}
ali@475
   268
#endif	/* HAVE_SYS_MMAN_H */
ali@476
   269
ali@475
   270
	if (!addr) {
ali@476
   271
		addr = malloc(size);
ali@475
   272
		if (addr) {
ali@475
   273
			nb = 0;
ali@476
   274
			while(nb < size) {
ali@476
   275
				res = read(fd, addr + nb, size - nb);
ali@475
   276
				if (res <= 0) {
ali@475
   277
					razor_set_error_posix(error, filename);
ali@475
   278
					free(addr);
ali@475
   279
					addr = NULL;
ali@475
   280
					break;
ali@475
   281
				}
ali@475
   282
				nb += res;
ali@475
   283
			}
ali@476
   284
			if (addr)
ali@476
   285
				of->flags = OPEN_FILE_USED;
ali@475
   286
		} else
ali@475
   287
			razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
ali@475
   288
					"Not enough memory");
ali@475
   289
	}
ali@476
   290
ali@476
   291
	if (fd >= 0)
ali@476
   292
		close(fd);
ali@475
   293
ali@475
   294
	of->addr = addr;
ali@476
   295
	of->length = size;
ali@476
   296
ali@476
   297
	if (addr)
ali@476
   298
		*length = size;
ali@475
   299
ali@475
   300
	return addr;
ali@475
   301
}
ali@475
   302
ali@475
   303
int razor_file_free_contents(void *addr, size_t length)
ali@475
   304
{
ali@475
   305
#if HAVE_SYS_MMAN_H
ali@475
   306
	int mmapped;
ali@475
   307
#endif
ali@475
   308
	struct open_file *of, *ofend;
ali@475
   309
ali@475
   310
	ofend = open_files.data + open_files.size;
ali@475
   311
	for (of = open_files.data; of < ofend; of++)
ali@475
   312
		if ((of->flags & OPEN_FILE_USED) && of->addr == addr)
ali@475
   313
			break;
ali@475
   314
ali@475
   315
	if (of == ofend)
ali@475
   316
		return -2;
ali@475
   317
ali@475
   318
	length = of->length;
ali@475
   319
#if HAVE_SYS_MMAN_H
ali@475
   320
	mmapped = of->flags & OPEN_FILE_MMAPPED;
ali@475
   321
#endif
ali@475
   322
	of->flags &= ~OPEN_FILE_USED;
ali@475
   323
ali@475
   324
	for (of = open_files.data; of < ofend; of++)
ali@475
   325
		if (of->flags & OPEN_FILE_USED)
ali@475
   326
			break;
ali@475
   327
ali@475
   328
	if (of == ofend) {
ali@475
   329
		array_release(&open_files);
ali@475
   330
		array_init(&open_files);
ali@475
   331
	}
ali@475
   332
ali@475
   333
#if HAVE_SYS_MMAN_H
ali@475
   334
	if (mmapped)
ali@475
   335
		return munmap(addr, length);
ali@475
   336
#endif
ali@475
   337
ali@475
   338
	free(addr);
ali@475
   339
	return 0;
ali@475
   340
}
ali@475
   341
ali@475
   342
int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error)
ali@475
   343
{
ali@475
   344
	int retval, code;
ali@475
   345
	struct stat buf;
ali@475
   346
ali@475
   347
	retval = mkdir(path, mode);
ali@475
   348
ali@475
   349
	if (retval) {
ali@475
   350
		/*
ali@475
   351
		 * Ignore the case of a pre-existing directory and give
ali@475
   352
		 * an explicit error message if there is something other
ali@475
   353
		 * than a directory already at path.
ali@475
   354
		 */
ali@475
   355
		code = errno;
ali@475
   356
		if (code != EEXIST || stat(path, &buf))
ali@475
   357
			razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
ali@475
   358
					strerror(code));
ali@475
   359
		else if (!S_ISDIR(buf.st_mode))
ali@475
   360
			razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
ali@475
   361
					"Not a directory");
ali@475
   362
	}
ali@475
   363
ali@475
   364
	return retval;
ali@475
   365
}
ali@475
   366
ali@475
   367
int razor_file_unlink(const char *path, struct razor_error **error)
ali@475
   368
{
ali@475
   369
	int retval;
ali@475
   370
ali@475
   371
	retval = unlink(path);
ali@475
   372
ali@475
   373
	if (retval)
ali@475
   374
		razor_set_error_posix(error, path);
ali@475
   375
ali@475
   376
	return retval;
ali@475
   377
}
ali@475
   378
ali@475
   379
int razor_file_open(const char *path, int flags, mode_t mode,
ali@475
   380
		    struct razor_error **error)
ali@475
   381
{
ali@475
   382
	int retval;
ali@475
   383
ali@475
   384
	retval = open(path, flags, mode);
ali@475
   385
ali@475
   386
	if (retval < 0)
ali@475
   387
		razor_set_error_posix(error, path);
ali@475
   388
ali@475
   389
	return retval;
ali@475
   390
}
ali@475
   391
ali@475
   392
int razor_file_move(const char *path, const char *dest,
ali@475
   393
		    struct razor_error **error)
ali@475
   394
{
ali@475
   395
	int retval = 0;
ali@475
   396
ali@475
   397
#ifdef MSWIN_API
ali@475
   398
	wchar_t *oldbuf, *newbuf;
ali@475
   399
	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
ali@475
   400
ali@475
   401
	newbuf = razor_utf8_to_utf16(dest, -1);
ali@475
   402
	oldbuf = razor_utf8_to_utf16(path, -1);
ali@475
   403
ali@475
   404
	/*
ali@475
   405
	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
ali@475
   406
	 * cover every case we care about _except_ replacing an empty
ali@475
   407
	 * directory with a file. Calling RemoveDirectory() will deal
ali@475
   408
	 * with this case while having no effect in all other cases.
ali@475
   409
	 */
ali@475
   410
	(void)RemoveDirectoryW(newbuf);
ali@475
   411
ali@475
   412
	if (!MoveFileExW(oldbuf, newbuf, flags)) {
ali@475
   413
		razor_set_error_mswin(error, newbuf, GetLastError());
ali@475
   414
		retval = -1;
ali@475
   415
	}
ali@475
   416
ali@475
   417
	free(newbuf);
ali@475
   418
	free(oldbuf);
ali@475
   419
#else
ali@475
   420
	int code;
ali@475
   421
	const char *object;
ali@475
   422
ali@475
   423
	if (rename(path, dest)) {
ali@475
   424
		if (error) {
ali@475
   425
			code = errno;
ali@475
   426
			if (access(path, F_OK) < 0)
ali@475
   427
				object = path;
ali@475
   428
			else
ali@475
   429
				object = dest;
ali@475
   430
			razor_set_error(error, RAZOR_POSIX_ERROR, code, object, 
ali@475
   431
					strerror(code));
ali@475
   432
		}
ali@475
   433
		retval = -1;
ali@475
   434
	}
ali@475
   435
#endif
ali@475
   436
ali@475
   437
	return retval;
ali@475
   438
}
ali@475
   439
ali@475
   440
#ifndef MSWIN_API
ali@475
   441
static char *absolute_path(const char *path)
ali@475
   442
{
ali@475
   443
	int len;
ali@475
   444
	char *result, *subpath, *p, *s, *t;
ali@475
   445
ali@475
   446
	result = realpath(path, NULL);
ali@475
   447
ali@475
   448
	if (!result && errno == ENOENT) {
ali@475
   449
		p = strdup(path);
ali@475
   450
		s = strrchr(p, '/');
ali@475
   451
ali@475
   452
		while (s) {
ali@475
   453
			if (s == p) {
ali@475
   454
				result = strdup("/");
ali@475
   455
				break;
ali@475
   456
			}
ali@475
   457
ali@475
   458
			*s = '\0';
ali@475
   459
			subpath = realpath(p, NULL);
ali@475
   460
ali@475
   461
			if (subpath) {
ali@475
   462
				*s = '/';
ali@475
   463
				len = strlen(subpath);
ali@475
   464
				result = malloc(len + strlen(s) + 1);
ali@475
   465
				memcpy(result, subpath, len);
ali@475
   466
				strcpy(result + len, s);
ali@475
   467
				free(subpath);
ali@475
   468
				break;
ali@475
   469
			} else if (errno != ENOENT)
ali@475
   470
				break;
ali@475
   471
ali@475
   472
			t = strrchr(p, '/');
ali@475
   473
			*s = '/';
ali@475
   474
			s = t;
ali@475
   475
		}
ali@475
   476
ali@475
   477
		if (!s)
ali@475
   478
			result = realpath(".", NULL);
ali@475
   479
ali@475
   480
		free(p);
ali@475
   481
	}
ali@475
   482
ali@475
   483
	return result;
ali@475
   484
}
ali@475
   485
#endif
ali@475
   486
ali@475
   487
int razor_file_is_directory(const char *path, struct razor_error **error)
ali@475
   488
{
ali@475
   489
	struct stat st;
ali@475
   490
ali@475
   491
	if (stat(path, &st) < 0) {
ali@475
   492
		razor_set_error_posix(error, path);
ali@475
   493
		return -1;
ali@475
   494
	}
ali@475
   495
ali@475
   496
	return !!S_ISDIR(st.st_mode);
ali@475
   497
}
ali@475
   498
ali@475
   499
char *razor_file_mkdtemp_near(const char *path, const char *template,
ali@475
   500
			      struct razor_error **error)
ali@475
   501
{
ali@475
   502
	char *retval;
ali@475
   503
ali@475
   504
#ifdef MSWIN_API
ali@475
   505
	if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
ali@475
   506
	    && strchr(path+3,'\\')) {
ali@475
   507
		/* We have a UNC path: \\servername\sharename... */
ali@475
   508
		const char *sharename, *root;
ali@475
   509
		int disklen;
ali@475
   510
ali@475
   511
		sharename = strchr(path+3,'\\')+1;
ali@475
   512
		root = strchr(sharename,'\\');
ali@475
   513
		if (root)
ali@475
   514
		    disklen = root - path;
ali@475
   515
		else
ali@475
   516
		    disklen = strlen(path);
ali@475
   517
ali@475
   518
		retval = malloc(disklen + 1 + strlen(template) + 1);
ali@475
   519
		memcpy(retval, path, disklen);
ali@475
   520
		retval[disklen] = '\\';
ali@475
   521
		strcpy(retval + disklen + 1, template);
ali@475
   522
	} else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
ali@475
   523
		    path[1]==':') {
ali@475
   524
		retval = malloc(3 + strlen(template) + 1);
ali@475
   525
		retval[0] = path[0];
ali@475
   526
		retval[1] = ':';
ali@475
   527
		retval[2] = '\\';
ali@475
   528
		strcpy(retval + 3, template);
ali@475
   529
	} else {
ali@475
   530
		DWORD n;
ali@475
   531
		wchar_t *buf;
ali@475
   532
		char *dir;
ali@475
   533
ali@475
   534
		n = GetCurrentDirectoryW(0, NULL);
ali@475
   535
		buf = malloc(n * sizeof(wchar_t));
ali@475
   536
ali@475
   537
		if (GetCurrentDirectoryW(n, buf)) {
ali@475
   538
			dir = razor_utf16_to_utf8(buf, n - 1);
ali@475
   539
			free(buf);
ali@475
   540
			retval = razor_file_mkdtemp_near(dir, template, error);
ali@475
   541
			free(dir);
ali@475
   542
			return retval;
ali@475
   543
		} else {
ali@475
   544
			retval = malloc(3 + strlen(template) + 1);
ali@475
   545
			retval[0] = 'C';
ali@475
   546
			retval[1] = ':';
ali@475
   547
			retval[2] = '\\';
ali@475
   548
			strcpy(retval + 3, template);
ali@475
   549
		}
ali@475
   550
ali@475
   551
		free(buf);
ali@475
   552
	}
ali@475
   553
#else
ali@475
   554
	/*
ali@475
   555
	 * Find the mount point (assuming we can write to the
ali@475
   556
	 * whole filesystem). Otherwise stop at the first
ali@475
   557
	 * unwritable directory and take one step back.
ali@475
   558
	 */
ali@475
   559
	char *s, *abspath, saved;
ali@475
   560
	int len, can_step_back = 0;
ali@475
   561
	dev_t filesystem;
ali@475
   562
	struct stat buf;
ali@475
   563
ali@475
   564
	abspath = absolute_path(path);
ali@475
   565
	if (!abspath) {
ali@475
   566
		razor_set_error_posix(error, path);
ali@475
   567
		return NULL;
ali@475
   568
	}
ali@475
   569
ali@475
   570
	if (stat(abspath, &buf) < 0) {
ali@475
   571
		if (errno == ENOENT)
ali@475
   572
			filesystem = 0;
ali@475
   573
		else {
ali@475
   574
			razor_set_error_posix(error, abspath);
ali@475
   575
			free(abspath);
ali@475
   576
			return NULL;
ali@475
   577
		}
ali@475
   578
	} else
ali@475
   579
		filesystem = buf.st_dev;
ali@475
   580
ali@475
   581
	len = strlen(abspath);
ali@475
   582
	while(len > 1 && (s = strrchr(abspath, '/'))) {
ali@475
   583
		if (s == abspath) {
ali@475
   584
			saved = s[1];
ali@475
   585
			s[1] = '\0';
ali@475
   586
			len = s + 1 - abspath;
ali@475
   587
		} else {
ali@475
   588
			s[0] = '\0';
ali@475
   589
			len = s - abspath;
ali@475
   590
		}
ali@475
   591
ali@475
   592
		if (stat(abspath, &buf) < 0) {
ali@475
   593
			if (errno == ENOENT)
ali@475
   594
				continue;
ali@475
   595
			else {
ali@475
   596
			    razor_set_error_posix(error, abspath);
ali@475
   597
			    free(abspath);
ali@475
   598
			    return NULL;
ali@475
   599
			}
ali@475
   600
		} else if (!filesystem)
ali@475
   601
			filesystem = buf.st_dev;
ali@475
   602
ali@475
   603
		if (buf.st_dev != filesystem || access(abspath, W_OK)) {
ali@475
   604
			if (can_step_back) {
ali@475
   605
				if (s == abspath)
ali@475
   606
					s[1] = saved;
ali@475
   607
				else
ali@475
   608
					s[0] = '/';
ali@475
   609
			}
ali@475
   610
			len = strlen(abspath);
ali@475
   611
			break;
ali@475
   612
		} else
ali@475
   613
			can_step_back = 1;
ali@475
   614
	}
ali@475
   615
ali@475
   616
	if (len == 1)
ali@475
   617
		len = 0;	/* Avoid an unslightly double slash. */
ali@475
   618
	retval = malloc(len + 1 + strlen(template) + 1);
ali@475
   619
	memcpy(retval, abspath, len);
ali@475
   620
	retval[len] = '/';
ali@475
   621
	strcpy(retval + len + 1, template);
ali@475
   622
ali@475
   623
	free(abspath);
ali@475
   624
#endif
ali@475
   625
ali@475
   626
	if (!mkdtemp(retval)) {
ali@475
   627
		int err = errno;
ali@475
   628
ali@475
   629
#ifdef EACCES
ali@475
   630
		if (err == EACCES) {
ali@475
   631
			char *s = strdup(template);
ali@475
   632
ali@475
   633
#ifndef MSWIN_API
ali@475
   634
			if (stat(".", &buf) < 0) {
ali@475
   635
				razor_set_error_posix(error, ".");
ali@475
   636
				free(s);
ali@475
   637
				free(retval);
ali@475
   638
				return NULL;
ali@475
   639
			}
ali@475
   640
			if (buf.st_dev != filesystem)
ali@475
   641
				/*
ali@475
   642
				 * Don't use a different filesystem. It will
ali@475
   643
				 * only fail later on (in rename) and cause
ali@475
   644
				 * an unhelpful error message (EXDEV).
ali@475
   645
				 */
ali@475
   646
				free(s);
ali@475
   647
			else
ali@475
   648
#endif
ali@475
   649
			if (mkdtemp(s)) {
ali@475
   650
				free(retval);
ali@475
   651
				retval = s;
ali@475
   652
				return retval;
ali@475
   653
			} else
ali@475
   654
				free(s);
ali@475
   655
		}
ali@475
   656
#endif
ali@475
   657
ali@475
   658
		razor_set_error(error, RAZOR_POSIX_ERROR, err, retval,
ali@475
   659
				strerror(err));
ali@475
   660
ali@475
   661
		free(retval);
ali@475
   662
		retval = NULL;
ali@475
   663
	}
ali@475
   664
ali@475
   665
	return retval;
ali@475
   666
}
ali@475
   667
ali@475
   668
struct open_dir {
ali@475
   669
	uint32_t flags;
ali@475
   670
#ifdef MSWIN_API
ali@475
   671
	_WDIR *dp;
ali@475
   672
	wchar_t *path2;
ali@475
   673
#else
ali@475
   674
	DIR *dp;
ali@475
   675
	char *path;
ali@475
   676
#endif
ali@475
   677
};
ali@475
   678
ali@475
   679
#define OPEN_DIR_USED		(1U<<0)
ali@475
   680
ali@475
   681
struct array open_dirs = { 0, };
ali@475
   682
ali@475
   683
void *razor_file_opendir(const char *path, struct razor_error **error)
ali@475
   684
{
ali@475
   685
	struct open_dir *od, *odend;
ali@475
   686
ali@475
   687
	odend = open_dirs.data + open_dirs.size;
ali@475
   688
	for (od = open_dirs.data; od < odend; od++)
ali@475
   689
		if (!(od->flags & OPEN_DIR_USED))
ali@475
   690
			break;
ali@475
   691
	if (od == odend) {
ali@475
   692
		od = array_add(&open_dirs, sizeof *od);
ali@475
   693
		od->flags = 0;
ali@475
   694
	}
ali@475
   695
ali@475
   696
#ifdef MSWIN_API
ali@475
   697
	od->path2 = razor_utf8_to_utf16(path, -1);
ali@475
   698
	od->dp = _wopendir(od->path2);
ali@475
   699
#else
ali@475
   700
	od->path = strdup(path);
ali@475
   701
	od->dp = opendir(od->path);
ali@475
   702
#endif
ali@475
   703
ali@475
   704
	if (!od->dp) {
ali@475
   705
#ifdef MSWIN_API
ali@475
   706
		razor_set_error_mswin(error, od->path2, GetLastError());
ali@475
   707
		free(od->path2);
ali@475
   708
#else
ali@475
   709
		razor_set_error_posix(error, od->path);
ali@475
   710
		free(od->path);
ali@475
   711
#endif
ali@475
   712
		return NULL;
ali@475
   713
	}
ali@475
   714
ali@475
   715
	od->flags = OPEN_DIR_USED;
ali@475
   716
	return od;
ali@475
   717
}
ali@475
   718
ali@475
   719
char *razor_file_readdir(void *dp, struct razor_error **error)
ali@475
   720
{
ali@475
   721
	struct open_dir *od = dp, *odend;
ali@475
   722
#ifdef MSWIN_API
ali@475
   723
	struct _wdirent *dirp;
ali@475
   724
	char *path;
ali@475
   725
#else
ali@475
   726
	struct dirent *dirp;
ali@475
   727
#endif
ali@475
   728
ali@475
   729
	odend = open_dirs.data + open_dirs.size;
ali@475
   730
	if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
ali@475
   731
		return (char *)-1;
ali@475
   732
ali@475
   733
	errno = 0;
ali@475
   734
ali@475
   735
#ifdef MSWIN_API
ali@475
   736
	while((dirp = _wreaddir(od->dp))) {
ali@475
   737
		path = razor_utf16_to_utf8(dirp->d_name, -1);
ali@475
   738
		if (strcmp(path, ".") && strcmp(path, ".."))
ali@475
   739
			return path;
ali@475
   740
		else
ali@475
   741
			free(path);
ali@475
   742
	}
ali@475
   743
#else
ali@475
   744
	while((dirp = readdir(od->dp)))
ali@475
   745
		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, ".."))
ali@475
   746
			return strdup(dirp->d_name);
ali@475
   747
#endif
ali@475
   748
ali@475
   749
	if (errno) {
ali@475
   750
#ifdef MSWIN_API
ali@475
   751
		razor_set_error_mswin(error, od->path2, GetLastError());
ali@475
   752
#else
ali@475
   753
		razor_set_error_posix(error, od->path);
ali@475
   754
#endif
ali@475
   755
	}
ali@475
   756
ali@475
   757
	return NULL;
ali@475
   758
}
ali@475
   759
ali@475
   760
int razor_file_closedir(void *dp, struct razor_error **error)
ali@475
   761
{
ali@475
   762
	struct open_dir *od = dp, *odend;
ali@475
   763
	int retval;
ali@475
   764
ali@475
   765
	odend = open_dirs.data + open_dirs.size;
ali@475
   766
	if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
ali@475
   767
		return -2;
ali@475
   768
ali@475
   769
#ifdef MSWIN_API
ali@475
   770
	/*
ali@475
   771
	 * I can't find documentation to state that _wclosedir()
ali@475
   772
	 * returns -1 on failure, so be paranoid.
ali@475
   773
	 */
ali@475
   774
	retval = _wclosedir(od->dp) ? -1 : 0;
ali@475
   775
#else
ali@475
   776
	retval = closedir(od->dp);
ali@475
   777
#endif
ali@475
   778
ali@475
   779
	if (retval) {
ali@475
   780
#ifdef MSWIN_API
ali@475
   781
		razor_set_error_mswin(error, od->path2, GetLastError());
ali@475
   782
#else
ali@475
   783
		razor_set_error_posix(error, od->path);
ali@475
   784
#endif
ali@475
   785
	}
ali@475
   786
ali@475
   787
#ifdef MSWIN_API
ali@475
   788
	free(od->path2);
ali@475
   789
#else
ali@475
   790
	free(od->path);
ali@475
   791
#endif
ali@475
   792
ali@475
   793
	od->flags &= ~OPEN_DIR_USED;
ali@475
   794
ali@475
   795
	for (od = open_dirs.data; od < odend; od++)
ali@475
   796
		if (od->flags & OPEN_DIR_USED)
ali@475
   797
			break;
ali@475
   798
ali@475
   799
	if (od == odend) {
ali@475
   800
		array_release(&open_dirs);
ali@475
   801
		array_init(&open_dirs);
ali@475
   802
	}
ali@475
   803
ali@475
   804
	return retval;
ali@475
   805
}
ali@475
   806
ali@475
   807
struct razor_uri_vtable_entry {
ali@475
   808
	struct razor_uri_vtable vtable;
ali@475
   809
	char *scheme;
ali@475
   810
	struct array open_files, open_directories;
ali@475
   811
};
ali@475
   812
ali@475
   813
static struct array razor_uri_vtable_entries;
ali@475
   814
ali@475
   815
static struct razor_uri_vtable_entry *
ali@475
   816
  razor_uri_get_vtable_entry(const char *scheme)
ali@475
   817
{
ali@475
   818
	struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL;
ali@475
   819
ali@475
   820
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
   821
	for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
   822
		if (!strcmp0(entry->scheme, scheme))
ali@475
   823
			return entry;
ali@475
   824
		else if (!entry->scheme)
ali@475
   825
			fallback = entry;
ali@475
   826
	}
ali@475
   827
ali@475
   828
	return fallback;
ali@475
   829
}
ali@475
   830
ali@476
   831
RAZOR_EXPORT int
ali@476
   832
razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error)
ali@475
   833
{
ali@475
   834
	int retval;
ali@475
   835
	char *path;
ali@475
   836
	struct razor_uri ru;
ali@475
   837
	struct razor_uri_vtable_entry *entry;
ali@475
   838
	struct razor_error *tmp_error = NULL;
ali@475
   839
ali@475
   840
	if (razor_uri_parse(&ru, uri, error))
ali@475
   841
		return -1;
ali@475
   842
ali@475
   843
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
   844
ali@475
   845
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   846
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   847
		razor_error_free(tmp_error);
ali@475
   848
	else if (!path) {
ali@475
   849
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   850
		razor_uri_destroy(&ru);
ali@475
   851
		return -1;
ali@475
   852
	}
ali@475
   853
ali@475
   854
	if (path) {
ali@475
   855
		razor_uri_destroy(&ru);
ali@475
   856
		retval = razor_file_mkdir(path, mode, error);
ali@475
   857
		free(path);
ali@475
   858
	} else {
ali@475
   859
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
   860
		razor_uri_destroy(&ru);
ali@475
   861
		if (!entry || !entry->vtable.mkdir) {
ali@475
   862
			retval = -1;
ali@475
   863
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
   864
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
   865
					uri, "No URI handler installed");
ali@475
   866
		} else
ali@475
   867
			retval = entry->vtable.mkdir(uri, mode, error);
ali@475
   868
	}
ali@475
   869
ali@475
   870
	return retval;
ali@475
   871
}
ali@475
   872
ali@476
   873
RAZOR_EXPORT int
ali@476
   874
razor_uri_unlink(const char *uri, struct razor_error **error)
ali@475
   875
{
ali@475
   876
	int retval;
ali@475
   877
	char *path;
ali@475
   878
	struct razor_uri ru;
ali@475
   879
	struct razor_uri_vtable_entry *entry;
ali@475
   880
	struct razor_error *tmp_error = NULL;
ali@475
   881
ali@475
   882
	if (razor_uri_parse(&ru, uri, error))
ali@475
   883
		return -1;
ali@475
   884
ali@475
   885
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
   886
ali@475
   887
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   888
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   889
		razor_error_free(tmp_error);
ali@475
   890
	else if (!path) {
ali@475
   891
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   892
		razor_uri_destroy(&ru);
ali@475
   893
		return -1;
ali@475
   894
	}
ali@475
   895
ali@475
   896
	if (path) {
ali@475
   897
		razor_uri_destroy(&ru);
ali@475
   898
		retval = razor_file_unlink(path, error);
ali@475
   899
		free(path);
ali@475
   900
	} else {
ali@475
   901
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
   902
		razor_uri_destroy(&ru);
ali@475
   903
		if (!entry || !entry->vtable.unlink) {
ali@475
   904
			retval = -1;
ali@475
   905
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
   906
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
   907
					uri, "No URI handler installed");
ali@475
   908
		} else
ali@475
   909
			retval = entry->vtable.unlink(uri, error);
ali@475
   910
	}
ali@475
   911
ali@475
   912
	return retval;
ali@475
   913
}
ali@475
   914
ali@476
   915
RAZOR_EXPORT int
ali@476
   916
razor_uri_open(const char *uri, int flags, mode_t mode,
ali@476
   917
	       struct razor_error **error)
ali@475
   918
{
ali@475
   919
	int retval;
ali@475
   920
	char *path;
ali@475
   921
	struct razor_uri ru;
ali@475
   922
	struct razor_uri_vtable_entry *entry;
ali@475
   923
	struct razor_error *tmp_error = NULL;
ali@475
   924
ali@475
   925
	if (razor_uri_parse(&ru, uri, error))
ali@475
   926
		return -1;
ali@475
   927
ali@475
   928
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
   929
ali@475
   930
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   931
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   932
		razor_error_free(tmp_error);
ali@475
   933
	else if (!path) {
ali@475
   934
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   935
		razor_uri_destroy(&ru);
ali@475
   936
		return -1;
ali@475
   937
	}
ali@475
   938
ali@475
   939
	if (path) {
ali@475
   940
		razor_uri_destroy(&ru);
ali@475
   941
		retval = razor_file_open(path, flags, mode, error);
ali@475
   942
		free(path);
ali@475
   943
	} else {
ali@475
   944
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
   945
		razor_uri_destroy(&ru);
ali@475
   946
		if (!entry || !entry->vtable.open) {
ali@475
   947
			retval = -1;
ali@475
   948
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
   949
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
   950
					uri, "No URI handler installed");
ali@475
   951
		} else
ali@475
   952
			retval = entry->vtable.open(uri, flags, mode, error);
ali@475
   953
	}
ali@475
   954
ali@475
   955
	return retval;
ali@475
   956
}
ali@475
   957
ali@476
   958
RAZOR_EXPORT int
ali@476
   959
razor_uri_move(const char *src_uri, const char *dst_uri,
ali@476
   960
	       struct razor_error **error)
ali@475
   961
{
ali@475
   962
	int retval;
ali@475
   963
	char *src_path, *dst_path;
ali@475
   964
	struct razor_uri src_ru, dst_ru;
ali@475
   965
	struct razor_uri_vtable_entry *entry;
ali@475
   966
	struct razor_error *tmp_error = NULL;
ali@475
   967
ali@475
   968
	if (razor_uri_parse(&src_ru, src_uri, error) ||
ali@475
   969
	    razor_uri_parse(&dst_ru, dst_uri, error))
ali@475
   970
		return -1;
ali@475
   971
ali@475
   972
	src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error);
ali@475
   973
ali@475
   974
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   975
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   976
		razor_error_free(tmp_error);
ali@475
   977
	else if (!src_path) {
ali@475
   978
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   979
		razor_uri_destroy(&src_ru);
ali@475
   980
		return -1;
ali@475
   981
	}
ali@475
   982
ali@475
   983
	dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error);
ali@475
   984
ali@475
   985
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   986
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   987
		razor_error_free(tmp_error);
ali@475
   988
	else if (!dst_path) {
ali@475
   989
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   990
		razor_uri_destroy(&dst_ru);
ali@475
   991
		razor_uri_destroy(&src_ru);
ali@475
   992
		free(src_path);
ali@475
   993
		return -1;
ali@475
   994
	}
ali@475
   995
ali@475
   996
	if (src_path && dst_path)
ali@475
   997
		retval = razor_file_move(src_path, dst_path, error);
ali@475
   998
	else {
ali@475
   999
		if (!strcmp(src_ru.scheme, dst_ru.scheme))
ali@475
  1000
			entry = razor_uri_get_vtable_entry(src_ru.scheme);
ali@475
  1001
		else
ali@475
  1002
			entry = NULL;
ali@475
  1003
		if (entry && entry->vtable.move)
ali@475
  1004
			retval = entry->vtable.move(src_uri, dst_uri, error);
ali@475
  1005
		else if (strcmp(src_ru.scheme, dst_ru.scheme)) {
ali@475
  1006
			retval = -1;
ali@475
  1007
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1008
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1009
					dst_uri, "Cross-scheme URI move");
ali@475
  1010
		} else {
ali@475
  1011
			retval = -1;
ali@475
  1012
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1013
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1014
					src_path ? dst_uri : src_uri,
ali@475
  1015
					"No URI handler installed");
ali@475
  1016
		}
ali@475
  1017
	}
ali@475
  1018
ali@475
  1019
	razor_uri_destroy(&src_ru);
ali@475
  1020
	razor_uri_destroy(&dst_ru);
ali@475
  1021
	free(src_path);
ali@475
  1022
	free(dst_path);
ali@475
  1023
ali@475
  1024
	return retval;
ali@475
  1025
}
ali@475
  1026
ali@476
  1027
RAZOR_EXPORT void *
ali@476
  1028
razor_uri_get_contents(const char *uri, size_t *length, int private,
ali@476
  1029
		       struct razor_error **error)
ali@475
  1030
{
ali@475
  1031
	void *retval;
ali@475
  1032
	char *path;
ali@475
  1033
	struct razor_uri ru;
ali@475
  1034
	struct razor_uri_vtable_entry *entry;
ali@475
  1035
	struct razor_error *tmp_error = NULL;
ali@475
  1036
ali@475
  1037
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1038
		return NULL;
ali@475
  1039
ali@475
  1040
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1041
ali@475
  1042
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1043
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1044
		razor_error_free(tmp_error);
ali@475
  1045
	else if (!path) {
ali@475
  1046
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1047
		razor_uri_destroy(&ru);
ali@475
  1048
		return NULL;
ali@475
  1049
	}
ali@475
  1050
ali@475
  1051
	if (path) {
ali@475
  1052
		razor_uri_destroy(&ru);
ali@475
  1053
		retval = razor_file_get_contents(path, length, private, error);
ali@475
  1054
		free(path);
ali@475
  1055
	} else {
ali@475
  1056
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1057
		razor_uri_destroy(&ru);
ali@475
  1058
		if (!entry || !entry->vtable.get_contents) {
ali@475
  1059
			retval = NULL;
ali@475
  1060
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1061
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1062
					uri, "No URI handler installed");
ali@475
  1063
		} else {
ali@475
  1064
			retval = entry->vtable.get_contents(uri, length,
ali@475
  1065
							    private, error);
ali@475
  1066
			if (retval)
ali@475
  1067
				ptr_array_add(&entry->open_files, retval);
ali@475
  1068
		}
ali@475
  1069
	}
ali@475
  1070
ali@475
  1071
	return retval;
ali@475
  1072
}
ali@475
  1073
ali@476
  1074
RAZOR_EXPORT int razor_uri_free_contents(void *addr, size_t length)
ali@475
  1075
{
ali@475
  1076
	int retval, of;
ali@475
  1077
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1078
ali@475
  1079
	if (!addr)
ali@475
  1080
		return 0;
ali@475
  1081
ali@475
  1082
	retval = razor_file_free_contents(addr, length);
ali@475
  1083
ali@475
  1084
	if (retval != -2)
ali@475
  1085
		return retval;
ali@475
  1086
ali@475
  1087
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1088
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1089
		of = ptr_array_find(&entry->open_files, addr);
ali@475
  1090
		if (of >= 0) {
ali@475
  1091
			if (entry->vtable.free_contents)
ali@475
  1092
				retval = entry->vtable.free_contents(addr,
ali@475
  1093
								     length);
ali@475
  1094
			ptr_array_remove_index(&entry->open_files, of);
ali@475
  1095
			break;
ali@475
  1096
		}
ali@475
  1097
	}
ali@475
  1098
ali@475
  1099
	return retval;
ali@475
  1100
}
ali@475
  1101
ali@476
  1102
RAZOR_EXPORT int
ali@476
  1103
razor_uri_is_directory(const char *uri, struct razor_error **error)
ali@475
  1104
{
ali@475
  1105
	int retval;
ali@475
  1106
	char *path;
ali@475
  1107
	struct razor_uri ru;
ali@475
  1108
	struct razor_uri_vtable_entry *entry;
ali@475
  1109
	struct razor_error *tmp_error = NULL;
ali@475
  1110
ali@475
  1111
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1112
		return -1;
ali@475
  1113
ali@475
  1114
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1115
ali@475
  1116
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1117
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1118
		razor_error_free(tmp_error);
ali@475
  1119
	else if (!path) {
ali@475
  1120
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1121
		razor_uri_destroy(&ru);
ali@475
  1122
		return -1;
ali@475
  1123
	}
ali@475
  1124
ali@475
  1125
	if (path) {
ali@475
  1126
		razor_uri_destroy(&ru);
ali@475
  1127
		retval = razor_file_is_directory(path, error);
ali@475
  1128
		free(path);
ali@475
  1129
	} else {
ali@475
  1130
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1131
		razor_uri_destroy(&ru);
ali@475
  1132
		if (!entry || !entry->vtable.is_directory) {
ali@475
  1133
			retval = -1;
ali@475
  1134
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1135
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1136
					uri, "No URI handler installed");
ali@475
  1137
		} else
ali@475
  1138
			retval = entry->vtable.is_directory(uri, error);
ali@475
  1139
	}
ali@475
  1140
ali@475
  1141
	return retval;
ali@475
  1142
}
ali@475
  1143
ali@476
  1144
RAZOR_EXPORT char *
ali@476
  1145
razor_uri_mkdtemp_near(const char *uri, const char *template,
ali@476
  1146
		       struct razor_error **error)
ali@475
  1147
{
ali@475
  1148
	char *retval, *s;
ali@475
  1149
	char *path;
ali@475
  1150
	struct razor_uri ru;
ali@475
  1151
	struct razor_uri_vtable_entry *entry;
ali@475
  1152
	struct razor_error *tmp_error = NULL;
ali@475
  1153
ali@475
  1154
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1155
		return NULL;
ali@475
  1156
ali@475
  1157
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1158
ali@475
  1159
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1160
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1161
		razor_error_free(tmp_error);
ali@475
  1162
	else if (!path) {
ali@475
  1163
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1164
		razor_uri_destroy(&ru);
ali@475
  1165
		return NULL;
ali@475
  1166
	}
ali@475
  1167
ali@475
  1168
	if (path) {
ali@475
  1169
		razor_uri_destroy(&ru);
ali@475
  1170
		s = razor_file_mkdtemp_near(path, template, error);
ali@479
  1171
		if (s) {
ali@479
  1172
			retval = razor_path_to_uri(s);
ali@479
  1173
			free(s);
ali@479
  1174
		} else
ali@479
  1175
			retval = NULL;
ali@475
  1176
		free(path);
ali@475
  1177
	} else {
ali@475
  1178
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1179
		razor_uri_destroy(&ru);
ali@475
  1180
		if (!entry || !entry->vtable.mkdtemp_near) {
ali@475
  1181
			retval = NULL;
ali@475
  1182
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1183
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1184
					uri, "No URI handler installed");
ali@475
  1185
		} else
ali@475
  1186
			retval = entry->vtable.mkdtemp_near(uri, template,
ali@475
  1187
							    error);
ali@475
  1188
	}
ali@475
  1189
ali@475
  1190
	return retval;
ali@475
  1191
}
ali@475
  1192
ali@476
  1193
RAZOR_EXPORT void *
ali@476
  1194
razor_uri_opendir(const char *uri, struct razor_error **error)
ali@475
  1195
{
ali@475
  1196
	void *retval;
ali@475
  1197
	char *path;
ali@475
  1198
	struct razor_uri ru;
ali@475
  1199
	struct razor_uri_vtable_entry *entry;
ali@475
  1200
	struct razor_error *tmp_error = NULL;
ali@475
  1201
ali@475
  1202
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1203
		return NULL;
ali@475
  1204
ali@475
  1205
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1206
ali@475
  1207
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1208
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1209
		razor_error_free(tmp_error);
ali@475
  1210
	else if (!path) {
ali@475
  1211
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1212
		razor_uri_destroy(&ru);
ali@475
  1213
		return NULL;
ali@475
  1214
	}
ali@475
  1215
ali@475
  1216
	if (path) {
ali@475
  1217
		razor_uri_destroy(&ru);
ali@475
  1218
		retval = razor_file_opendir(path, error);
ali@475
  1219
		free(path);
ali@475
  1220
	} else {
ali@475
  1221
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1222
		razor_uri_destroy(&ru);
ali@475
  1223
		if (!entry || !entry->vtable.opendir) {
ali@475
  1224
			retval = NULL;
ali@475
  1225
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1226
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1227
					uri, "No URI handler installed");
ali@475
  1228
		} else {
ali@475
  1229
			retval = entry->vtable.opendir(uri, error);
ali@475
  1230
			if (retval)
ali@475
  1231
				ptr_array_add(&entry->open_directories, retval);
ali@475
  1232
		}
ali@475
  1233
	}
ali@475
  1234
ali@475
  1235
	return retval;
ali@475
  1236
}
ali@475
  1237
ali@476
  1238
RAZOR_EXPORT char *razor_uri_readdir(void *dir, struct razor_error **error)
ali@475
  1239
{
ali@475
  1240
	int od;
ali@475
  1241
	char *retval;
ali@475
  1242
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1243
ali@475
  1244
	retval = razor_file_readdir(dir, error);
ali@475
  1245
ali@475
  1246
	if (retval != (char *)-1)
ali@475
  1247
		return retval;
ali@475
  1248
ali@475
  1249
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1250
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1251
		od = ptr_array_find(&entry->open_directories, dir);
ali@475
  1252
		if (od >= 0) {
ali@475
  1253
			if (entry->vtable.readdir)
ali@475
  1254
				retval = entry->vtable.readdir(dir, error);
ali@475
  1255
			break;
ali@475
  1256
		}
ali@475
  1257
	}
ali@475
  1258
ali@475
  1259
	if (retval == (char *)-1) {
ali@475
  1260
		retval = NULL;
ali@475
  1261
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1262
				RAZOR_GENERAL_ERROR_FAILED, NULL,
ali@475
  1263
				"Invalid directory handle");
ali@475
  1264
	}
ali@475
  1265
ali@475
  1266
	return retval;
ali@475
  1267
}
ali@475
  1268
ali@476
  1269
RAZOR_EXPORT int razor_uri_closedir(void *dir, struct razor_error **error)
ali@475
  1270
{
ali@475
  1271
	int od;
ali@475
  1272
	int retval;
ali@475
  1273
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1274
ali@475
  1275
	retval = razor_file_closedir(dir, error);
ali@475
  1276
ali@475
  1277
	if (retval != -2)
ali@475
  1278
		return retval;
ali@475
  1279
ali@475
  1280
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1281
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1282
		od = ptr_array_find(&entry->open_directories, dir);
ali@475
  1283
		if (od >= 0) {
ali@475
  1284
			if (entry->vtable.closedir)
ali@475
  1285
				retval = entry->vtable.closedir(dir, error);
ali@475
  1286
			break;
ali@475
  1287
		}
ali@475
  1288
	}
ali@475
  1289
ali@475
  1290
	if (retval == -2)
ali@475
  1291
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1292
				RAZOR_GENERAL_ERROR_FAILED, NULL,
ali@475
  1293
				"Invalid directory handle");
ali@475
  1294
ali@475
  1295
	return retval;
ali@475
  1296
}
ali@475
  1297
ali@475
  1298
RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme,
ali@475
  1299
  struct razor_uri_vtable *vtable, struct razor_error **error)
ali@475
  1300
{
ali@475
  1301
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1302
ali@475
  1303
	if (!strcmp0(scheme, "file")) {
ali@475
  1304
		/*
ali@475
  1305
		 * There's no fundamental reason why we couldn't support
ali@475
  1306
		 * this, but it's a lot of work without any obvious need.
ali@475
  1307
		 */
ali@475
  1308
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1309
				RAZOR_GENERAL_ERROR_FAILED, scheme,
ali@475
  1310
				"Can't override file scheme handler");
ali@475
  1311
		return -1;
ali@475
  1312
	}
ali@475
  1313
ali@475
  1314
	if (vtable->structure_size < sizeof(struct razor_uri_vtable)) {
ali@475
  1315
		/*
ali@475
  1316
		 * A future version of the API might add vfuncs to the
ali@475
  1317
		 * table (which we ignore), but won't remove any.
ali@475
  1318
		 */
ali@475
  1319
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1320
				RAZOR_GENERAL_ERROR_FAILED, scheme,
ali@475
  1321
				"Invalid vtable structure size");
ali@475
  1322
		return -1;
ali@475
  1323
	}
ali@475
  1324
ali@475
  1325
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1326
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1327
		if (!strcmp0(entry->scheme, scheme))
ali@475
  1328
			break;
ali@475
  1329
	}
ali@475
  1330
ali@475
  1331
	if (entry == eend) {
ali@475
  1332
		if (!vtable)
ali@475
  1333
			return 0;
ali@475
  1334
		entry = array_add(&razor_uri_vtable_entries, sizeof *entry);
ali@475
  1335
		entry->scheme = scheme ? strdup(scheme) : NULL;
ali@475
  1336
		array_init(&entry->open_files);
ali@475
  1337
		array_init(&entry->open_directories);
ali@475
  1338
	} else if (entry->open_files.size || entry->open_directories.size) {
ali@475
  1339
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1340
				RAZOR_GENERAL_ERROR_FAILED, scheme,
ali@475
  1341
				"URI handler busy");
ali@475
  1342
		return -1;
ali@475
  1343
	}
ali@475
  1344
ali@475
  1345
	if (vtable) {
ali@475
  1346
		entry->vtable = *vtable;
ali@475
  1347
		entry->vtable.structure_size = sizeof(struct razor_uri_vtable);
ali@475
  1348
	} else {
ali@475
  1349
		free(entry->scheme);
ali@475
  1350
		if (entry + 1 < eend)
ali@475
  1351
			memmove(entry, entry + 1, eend - (entry + 1));
ali@475
  1352
		array_set_size(&razor_uri_vtable_entries,
ali@475
  1353
			       razor_uri_vtable_entries.size - sizeof *entry);
ali@475
  1354
	}
ali@475
  1355
ali@475
  1356
	return 0;
ali@475
  1357
}