librazor/uri-io.c
changeset 475 008c75a5e08d
child 476 48e45439fd9a
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/librazor/uri-io.c	Mon Jul 04 10:48:18 2016 +0100
     1.3 @@ -0,0 +1,1177 @@
     1.4 +/*
     1.5 + * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     1.6 + * Copyright (C) 2008  Red Hat, Inc
     1.7 + * Copyright (C) 2009, 2011, 2012, 2014, 2016  J. Ali Harlow <ali@juiblex.co.uk>
     1.8 + *
     1.9 + * This program is free software; you can redistribute it and/or modify
    1.10 + * it under the terms of the GNU General Public License as published by
    1.11 + * the Free Software Foundation; either version 2 of the License, or
    1.12 + * (at your option) any later version.
    1.13 + *
    1.14 + * This program is distributed in the hope that it will be useful,
    1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.17 + * GNU General Public License for more details.
    1.18 + *
    1.19 + * You should have received a copy of the GNU General Public License along
    1.20 + * with this program; if not, write to the Free Software Foundation, Inc.,
    1.21 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    1.22 + */
    1.23 +
    1.24 +#include "config.h"
    1.25 +
    1.26 +#include <limits.h>
    1.27 +#include <string.h>
    1.28 +#include <sys/types.h>
    1.29 +#include <sys/stat.h>
    1.30 +#include <stdlib.h>
    1.31 +#include <stdio.h>
    1.32 +#include <stdint.h>
    1.33 +#include <errno.h>
    1.34 +#include <unistd.h>
    1.35 +#include <fcntl.h>
    1.36 +#include <dirent.h>
    1.37 +#ifdef MSWIN_API
    1.38 +#include <windows.h>
    1.39 +#include <direct.h>
    1.40 +#endif
    1.41 +#if HAVE_SYS_MMAN_H
    1.42 +#include <sys/mman.h>
    1.43 +#endif
    1.44 +#include <assert.h>
    1.45 +
    1.46 +#include "razor.h"
    1.47 +#include "types/types.h"
    1.48 +#include "razor-internal.h"
    1.49 +
    1.50 +#ifndef O_BINARY
    1.51 +#define O_BINARY	0
    1.52 +#endif
    1.53 +
    1.54 +#define strcmp0(s1, s2)		((s1) && (s2) ? strcmp(s1, s2) : \
    1.55 +				 (s1) ? 1 : (s2) ? -1 : 0)
    1.56 +
    1.57 +#define OPEN_FILE_USED		(1U<<0)
    1.58 +#define OPEN_FILE_MMAPPED	(1U<<1)
    1.59 +
    1.60 +struct open_file {
    1.61 +	void *addr;
    1.62 +	size_t length;
    1.63 +	uint32_t flags;
    1.64 +};
    1.65 +
    1.66 +struct array open_files = { 0, };
    1.67 +
    1.68 +void *razor_file_get_contents(const char *filename, size_t *length, int private,
    1.69 +			      struct razor_error **error)
    1.70 +{
    1.71 +	int fd;
    1.72 +	struct stat st;
    1.73 +	void *addr = NULL;
    1.74 +	size_t nb;
    1.75 +	ssize_t res;
    1.76 +	struct open_file *of, *ofend;
    1.77 +
    1.78 +	fd = open(filename, O_RDONLY | O_BINARY);
    1.79 +	if (fd < 0) {
    1.80 +		razor_set_error_posix(error, filename);
    1.81 +		return NULL;
    1.82 +	}
    1.83 +
    1.84 +	if (fstat(fd, &st) < 0) {
    1.85 +		razor_set_error_posix(error, filename);
    1.86 +		close(fd);
    1.87 +		return NULL;
    1.88 +	}
    1.89 +
    1.90 +	*length = st.st_size;
    1.91 +
    1.92 +	ofend = open_files.data + open_files.size;
    1.93 +	for (of = open_files.data; of < ofend; of++)
    1.94 +		if (!(of->flags & OPEN_FILE_USED))
    1.95 +			break;
    1.96 +	if (of == ofend) {
    1.97 +		of = array_add(&open_files, sizeof *of);
    1.98 +		of->flags = 0;
    1.99 +	}
   1.100 +
   1.101 +#if HAVE_SYS_MMAN_H
   1.102 +	if (!private) {
   1.103 +		addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   1.104 +		if (addr == MAP_FAILED)
   1.105 +			addr = NULL;
   1.106 +		else
   1.107 +			of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED;
   1.108 +	}
   1.109 +#endif	/* HAVE_SYS_MMAN_H */
   1.110 +	if (!addr) {
   1.111 +		addr = malloc(st.st_size);
   1.112 +		if (addr) {
   1.113 +			of->flags = OPEN_FILE_USED;
   1.114 +			nb = 0;
   1.115 +			while(nb < st.st_size) {
   1.116 +				res = read(fd, addr + nb, st.st_size - nb);
   1.117 +				if (res <= 0) {
   1.118 +					razor_set_error_posix(error, filename);
   1.119 +					free(addr);
   1.120 +					addr = NULL;
   1.121 +					break;
   1.122 +				}
   1.123 +				nb += res;
   1.124 +			}
   1.125 +		} else
   1.126 +			razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
   1.127 +					"Not enough memory");
   1.128 +	}
   1.129 +	close(fd);
   1.130 +
   1.131 +	of->addr = addr;
   1.132 +	of->length = st.st_size;
   1.133 +
   1.134 +	return addr;
   1.135 +}
   1.136 +
   1.137 +int razor_file_free_contents(void *addr, size_t length)
   1.138 +{
   1.139 +#if HAVE_SYS_MMAN_H
   1.140 +	int mmapped;
   1.141 +#endif
   1.142 +	struct open_file *of, *ofend;
   1.143 +
   1.144 +	ofend = open_files.data + open_files.size;
   1.145 +	for (of = open_files.data; of < ofend; of++)
   1.146 +		if ((of->flags & OPEN_FILE_USED) && of->addr == addr)
   1.147 +			break;
   1.148 +
   1.149 +	if (of == ofend)
   1.150 +		return -2;
   1.151 +
   1.152 +	length = of->length;
   1.153 +#if HAVE_SYS_MMAN_H
   1.154 +	mmapped = of->flags & OPEN_FILE_MMAPPED;
   1.155 +#endif
   1.156 +	of->flags &= ~OPEN_FILE_USED;
   1.157 +
   1.158 +	for (of = open_files.data; of < ofend; of++)
   1.159 +		if (of->flags & OPEN_FILE_USED)
   1.160 +			break;
   1.161 +
   1.162 +	if (of == ofend) {
   1.163 +		array_release(&open_files);
   1.164 +		array_init(&open_files);
   1.165 +	}
   1.166 +
   1.167 +#if HAVE_SYS_MMAN_H
   1.168 +	if (mmapped)
   1.169 +		return munmap(addr, length);
   1.170 +#endif
   1.171 +
   1.172 +	free(addr);
   1.173 +	return 0;
   1.174 +}
   1.175 +
   1.176 +int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error)
   1.177 +{
   1.178 +	int retval, code;
   1.179 +	struct stat buf;
   1.180 +
   1.181 +	retval = mkdir(path, mode);
   1.182 +
   1.183 +	if (retval) {
   1.184 +		/*
   1.185 +		 * Ignore the case of a pre-existing directory and give
   1.186 +		 * an explicit error message if there is something other
   1.187 +		 * than a directory already at path.
   1.188 +		 */
   1.189 +		code = errno;
   1.190 +		if (code != EEXIST || stat(path, &buf))
   1.191 +			razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
   1.192 +					strerror(code));
   1.193 +		else if (!S_ISDIR(buf.st_mode))
   1.194 +			razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
   1.195 +					"Not a directory");
   1.196 +	}
   1.197 +
   1.198 +	return retval;
   1.199 +}
   1.200 +
   1.201 +int razor_file_unlink(const char *path, struct razor_error **error)
   1.202 +{
   1.203 +	int retval;
   1.204 +
   1.205 +	retval = unlink(path);
   1.206 +
   1.207 +	if (retval)
   1.208 +		razor_set_error_posix(error, path);
   1.209 +
   1.210 +	return retval;
   1.211 +}
   1.212 +
   1.213 +int razor_file_open(const char *path, int flags, mode_t mode,
   1.214 +		    struct razor_error **error)
   1.215 +{
   1.216 +	int retval;
   1.217 +
   1.218 +	retval = open(path, flags, mode);
   1.219 +
   1.220 +	if (retval < 0)
   1.221 +		razor_set_error_posix(error, path);
   1.222 +
   1.223 +	return retval;
   1.224 +}
   1.225 +
   1.226 +int razor_file_move(const char *path, const char *dest,
   1.227 +		    struct razor_error **error)
   1.228 +{
   1.229 +	int retval = 0;
   1.230 +
   1.231 +#ifdef MSWIN_API
   1.232 +	wchar_t *oldbuf, *newbuf;
   1.233 +	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   1.234 +
   1.235 +	newbuf = razor_utf8_to_utf16(dest, -1);
   1.236 +	oldbuf = razor_utf8_to_utf16(path, -1);
   1.237 +
   1.238 +	/*
   1.239 +	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
   1.240 +	 * cover every case we care about _except_ replacing an empty
   1.241 +	 * directory with a file. Calling RemoveDirectory() will deal
   1.242 +	 * with this case while having no effect in all other cases.
   1.243 +	 */
   1.244 +	(void)RemoveDirectoryW(newbuf);
   1.245 +
   1.246 +	if (!MoveFileExW(oldbuf, newbuf, flags)) {
   1.247 +		razor_set_error_mswin(error, newbuf, GetLastError());
   1.248 +		retval = -1;
   1.249 +	}
   1.250 +
   1.251 +	free(newbuf);
   1.252 +	free(oldbuf);
   1.253 +#else
   1.254 +	int code;
   1.255 +	const char *object;
   1.256 +
   1.257 +	if (rename(path, dest)) {
   1.258 +		if (error) {
   1.259 +			code = errno;
   1.260 +			if (access(path, F_OK) < 0)
   1.261 +				object = path;
   1.262 +			else
   1.263 +				object = dest;
   1.264 +			razor_set_error(error, RAZOR_POSIX_ERROR, code, object, 
   1.265 +					strerror(code));
   1.266 +		}
   1.267 +		retval = -1;
   1.268 +	}
   1.269 +#endif
   1.270 +
   1.271 +	return retval;
   1.272 +}
   1.273 +
   1.274 +#ifndef MSWIN_API
   1.275 +static char *absolute_path(const char *path)
   1.276 +{
   1.277 +	int len;
   1.278 +	char *result, *subpath, *p, *s, *t;
   1.279 +
   1.280 +	result = realpath(path, NULL);
   1.281 +
   1.282 +	if (!result && errno == ENOENT) {
   1.283 +		p = strdup(path);
   1.284 +		s = strrchr(p, '/');
   1.285 +
   1.286 +		while (s) {
   1.287 +			if (s == p) {
   1.288 +				result = strdup("/");
   1.289 +				break;
   1.290 +			}
   1.291 +
   1.292 +			*s = '\0';
   1.293 +			subpath = realpath(p, NULL);
   1.294 +
   1.295 +			if (subpath) {
   1.296 +				*s = '/';
   1.297 +				len = strlen(subpath);
   1.298 +				result = malloc(len + strlen(s) + 1);
   1.299 +				memcpy(result, subpath, len);
   1.300 +				strcpy(result + len, s);
   1.301 +				free(subpath);
   1.302 +				break;
   1.303 +			} else if (errno != ENOENT)
   1.304 +				break;
   1.305 +
   1.306 +			t = strrchr(p, '/');
   1.307 +			*s = '/';
   1.308 +			s = t;
   1.309 +		}
   1.310 +
   1.311 +		if (!s)
   1.312 +			result = realpath(".", NULL);
   1.313 +
   1.314 +		free(p);
   1.315 +	}
   1.316 +
   1.317 +	return result;
   1.318 +}
   1.319 +#endif
   1.320 +
   1.321 +int razor_file_is_directory(const char *path, struct razor_error **error)
   1.322 +{
   1.323 +	struct stat st;
   1.324 +
   1.325 +	if (stat(path, &st) < 0) {
   1.326 +		razor_set_error_posix(error, path);
   1.327 +		return -1;
   1.328 +	}
   1.329 +
   1.330 +	return !!S_ISDIR(st.st_mode);
   1.331 +}
   1.332 +
   1.333 +char *razor_file_mkdtemp_near(const char *path, const char *template,
   1.334 +			      struct razor_error **error)
   1.335 +{
   1.336 +	char *retval;
   1.337 +
   1.338 +#ifdef MSWIN_API
   1.339 +	if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
   1.340 +	    && strchr(path+3,'\\')) {
   1.341 +		/* We have a UNC path: \\servername\sharename... */
   1.342 +		const char *sharename, *root;
   1.343 +		int disklen;
   1.344 +
   1.345 +		sharename = strchr(path+3,'\\')+1;
   1.346 +		root = strchr(sharename,'\\');
   1.347 +		if (root)
   1.348 +		    disklen = root - path;
   1.349 +		else
   1.350 +		    disklen = strlen(path);
   1.351 +
   1.352 +		retval = malloc(disklen + 1 + strlen(template) + 1);
   1.353 +		memcpy(retval, path, disklen);
   1.354 +		retval[disklen] = '\\';
   1.355 +		strcpy(retval + disklen + 1, template);
   1.356 +	} else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
   1.357 +		    path[1]==':') {
   1.358 +		retval = malloc(3 + strlen(template) + 1);
   1.359 +		retval[0] = path[0];
   1.360 +		retval[1] = ':';
   1.361 +		retval[2] = '\\';
   1.362 +		strcpy(retval + 3, template);
   1.363 +	} else {
   1.364 +		DWORD n;
   1.365 +		wchar_t *buf;
   1.366 +		char *dir;
   1.367 +
   1.368 +		n = GetCurrentDirectoryW(0, NULL);
   1.369 +		buf = malloc(n * sizeof(wchar_t));
   1.370 +
   1.371 +		if (GetCurrentDirectoryW(n, buf)) {
   1.372 +			dir = razor_utf16_to_utf8(buf, n - 1);
   1.373 +			free(buf);
   1.374 +			retval = razor_file_mkdtemp_near(dir, template, error);
   1.375 +			free(dir);
   1.376 +			return retval;
   1.377 +		} else {
   1.378 +			retval = malloc(3 + strlen(template) + 1);
   1.379 +			retval[0] = 'C';
   1.380 +			retval[1] = ':';
   1.381 +			retval[2] = '\\';
   1.382 +			strcpy(retval + 3, template);
   1.383 +		}
   1.384 +
   1.385 +		free(buf);
   1.386 +	}
   1.387 +#else
   1.388 +	/*
   1.389 +	 * Find the mount point (assuming we can write to the
   1.390 +	 * whole filesystem). Otherwise stop at the first
   1.391 +	 * unwritable directory and take one step back.
   1.392 +	 */
   1.393 +	char *s, *abspath, saved;
   1.394 +	int len, can_step_back = 0;
   1.395 +	dev_t filesystem;
   1.396 +	struct stat buf;
   1.397 +
   1.398 +	abspath = absolute_path(path);
   1.399 +	if (!abspath) {
   1.400 +		razor_set_error_posix(error, path);
   1.401 +		return NULL;
   1.402 +	}
   1.403 +
   1.404 +	if (stat(abspath, &buf) < 0) {
   1.405 +		if (errno == ENOENT)
   1.406 +			filesystem = 0;
   1.407 +		else {
   1.408 +			razor_set_error_posix(error, abspath);
   1.409 +			free(abspath);
   1.410 +			return NULL;
   1.411 +		}
   1.412 +	} else
   1.413 +		filesystem = buf.st_dev;
   1.414 +
   1.415 +	len = strlen(abspath);
   1.416 +	while(len > 1 && (s = strrchr(abspath, '/'))) {
   1.417 +		if (s == abspath) {
   1.418 +			saved = s[1];
   1.419 +			s[1] = '\0';
   1.420 +			len = s + 1 - abspath;
   1.421 +		} else {
   1.422 +			s[0] = '\0';
   1.423 +			len = s - abspath;
   1.424 +		}
   1.425 +
   1.426 +		if (stat(abspath, &buf) < 0) {
   1.427 +			if (errno == ENOENT)
   1.428 +				continue;
   1.429 +			else {
   1.430 +			    razor_set_error_posix(error, abspath);
   1.431 +			    free(abspath);
   1.432 +			    return NULL;
   1.433 +			}
   1.434 +		} else if (!filesystem)
   1.435 +			filesystem = buf.st_dev;
   1.436 +
   1.437 +		if (buf.st_dev != filesystem || access(abspath, W_OK)) {
   1.438 +			if (can_step_back) {
   1.439 +				if (s == abspath)
   1.440 +					s[1] = saved;
   1.441 +				else
   1.442 +					s[0] = '/';
   1.443 +			}
   1.444 +			len = strlen(abspath);
   1.445 +			break;
   1.446 +		} else
   1.447 +			can_step_back = 1;
   1.448 +	}
   1.449 +
   1.450 +	if (len == 1)
   1.451 +		len = 0;	/* Avoid an unslightly double slash. */
   1.452 +	retval = malloc(len + 1 + strlen(template) + 1);
   1.453 +	memcpy(retval, abspath, len);
   1.454 +	retval[len] = '/';
   1.455 +	strcpy(retval + len + 1, template);
   1.456 +
   1.457 +	free(abspath);
   1.458 +#endif
   1.459 +
   1.460 +	if (!mkdtemp(retval)) {
   1.461 +		int err = errno;
   1.462 +
   1.463 +#ifdef EACCES
   1.464 +		if (err == EACCES) {
   1.465 +			char *s = strdup(template);
   1.466 +
   1.467 +#ifndef MSWIN_API
   1.468 +			if (stat(".", &buf) < 0) {
   1.469 +				razor_set_error_posix(error, ".");
   1.470 +				free(s);
   1.471 +				free(retval);
   1.472 +				return NULL;
   1.473 +			}
   1.474 +			if (buf.st_dev != filesystem)
   1.475 +				/*
   1.476 +				 * Don't use a different filesystem. It will
   1.477 +				 * only fail later on (in rename) and cause
   1.478 +				 * an unhelpful error message (EXDEV).
   1.479 +				 */
   1.480 +				free(s);
   1.481 +			else
   1.482 +#endif
   1.483 +			if (mkdtemp(s)) {
   1.484 +				free(retval);
   1.485 +				retval = s;
   1.486 +				return retval;
   1.487 +			} else
   1.488 +				free(s);
   1.489 +		}
   1.490 +#endif
   1.491 +
   1.492 +		razor_set_error(error, RAZOR_POSIX_ERROR, err, retval,
   1.493 +				strerror(err));
   1.494 +
   1.495 +		free(retval);
   1.496 +		retval = NULL;
   1.497 +	}
   1.498 +
   1.499 +	return retval;
   1.500 +}
   1.501 +
   1.502 +struct open_dir {
   1.503 +	uint32_t flags;
   1.504 +#ifdef MSWIN_API
   1.505 +	_WDIR *dp;
   1.506 +	wchar_t *path2;
   1.507 +#else
   1.508 +	DIR *dp;
   1.509 +	char *path;
   1.510 +#endif
   1.511 +};
   1.512 +
   1.513 +#define OPEN_DIR_USED		(1U<<0)
   1.514 +
   1.515 +struct array open_dirs = { 0, };
   1.516 +
   1.517 +void *razor_file_opendir(const char *path, struct razor_error **error)
   1.518 +{
   1.519 +	struct open_dir *od, *odend;
   1.520 +
   1.521 +	odend = open_dirs.data + open_dirs.size;
   1.522 +	for (od = open_dirs.data; od < odend; od++)
   1.523 +		if (!(od->flags & OPEN_DIR_USED))
   1.524 +			break;
   1.525 +	if (od == odend) {
   1.526 +		od = array_add(&open_dirs, sizeof *od);
   1.527 +		od->flags = 0;
   1.528 +	}
   1.529 +
   1.530 +#ifdef MSWIN_API
   1.531 +	od->path2 = razor_utf8_to_utf16(path, -1);
   1.532 +	od->dp = _wopendir(od->path2);
   1.533 +#else
   1.534 +	od->path = strdup(path);
   1.535 +	od->dp = opendir(od->path);
   1.536 +#endif
   1.537 +
   1.538 +	if (!od->dp) {
   1.539 +#ifdef MSWIN_API
   1.540 +		razor_set_error_mswin(error, od->path2, GetLastError());
   1.541 +		free(od->path2);
   1.542 +#else
   1.543 +		razor_set_error_posix(error, od->path);
   1.544 +		free(od->path);
   1.545 +#endif
   1.546 +		return NULL;
   1.547 +	}
   1.548 +
   1.549 +	od->flags = OPEN_DIR_USED;
   1.550 +	return od;
   1.551 +}
   1.552 +
   1.553 +char *razor_file_readdir(void *dp, struct razor_error **error)
   1.554 +{
   1.555 +	struct open_dir *od = dp, *odend;
   1.556 +#ifdef MSWIN_API
   1.557 +	struct _wdirent *dirp;
   1.558 +	char *path;
   1.559 +#else
   1.560 +	struct dirent *dirp;
   1.561 +#endif
   1.562 +
   1.563 +	odend = open_dirs.data + open_dirs.size;
   1.564 +	if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
   1.565 +		return (char *)-1;
   1.566 +
   1.567 +	errno = 0;
   1.568 +
   1.569 +#ifdef MSWIN_API
   1.570 +	while((dirp = _wreaddir(od->dp))) {
   1.571 +		path = razor_utf16_to_utf8(dirp->d_name, -1);
   1.572 +		if (strcmp(path, ".") && strcmp(path, ".."))
   1.573 +			return path;
   1.574 +		else
   1.575 +			free(path);
   1.576 +	}
   1.577 +#else
   1.578 +	while((dirp = readdir(od->dp)))
   1.579 +		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, ".."))
   1.580 +			return strdup(dirp->d_name);
   1.581 +#endif
   1.582 +
   1.583 +	if (errno) {
   1.584 +#ifdef MSWIN_API
   1.585 +		razor_set_error_mswin(error, od->path2, GetLastError());
   1.586 +#else
   1.587 +		razor_set_error_posix(error, od->path);
   1.588 +#endif
   1.589 +	}
   1.590 +
   1.591 +	return NULL;
   1.592 +}
   1.593 +
   1.594 +int razor_file_closedir(void *dp, struct razor_error **error)
   1.595 +{
   1.596 +	struct open_dir *od = dp, *odend;
   1.597 +	int retval;
   1.598 +
   1.599 +	odend = open_dirs.data + open_dirs.size;
   1.600 +	if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
   1.601 +		return -2;
   1.602 +
   1.603 +#ifdef MSWIN_API
   1.604 +	/*
   1.605 +	 * I can't find documentation to state that _wclosedir()
   1.606 +	 * returns -1 on failure, so be paranoid.
   1.607 +	 */
   1.608 +	retval = _wclosedir(od->dp) ? -1 : 0;
   1.609 +#else
   1.610 +	retval = closedir(od->dp);
   1.611 +#endif
   1.612 +
   1.613 +	if (retval) {
   1.614 +#ifdef MSWIN_API
   1.615 +		razor_set_error_mswin(error, od->path2, GetLastError());
   1.616 +#else
   1.617 +		razor_set_error_posix(error, od->path);
   1.618 +#endif
   1.619 +	}
   1.620 +
   1.621 +#ifdef MSWIN_API
   1.622 +	free(od->path2);
   1.623 +#else
   1.624 +	free(od->path);
   1.625 +#endif
   1.626 +
   1.627 +	od->flags &= ~OPEN_DIR_USED;
   1.628 +
   1.629 +	for (od = open_dirs.data; od < odend; od++)
   1.630 +		if (od->flags & OPEN_DIR_USED)
   1.631 +			break;
   1.632 +
   1.633 +	if (od == odend) {
   1.634 +		array_release(&open_dirs);
   1.635 +		array_init(&open_dirs);
   1.636 +	}
   1.637 +
   1.638 +	return retval;
   1.639 +}
   1.640 +
   1.641 +struct razor_uri_vtable_entry {
   1.642 +	struct razor_uri_vtable vtable;
   1.643 +	char *scheme;
   1.644 +	struct array open_files, open_directories;
   1.645 +};
   1.646 +
   1.647 +static struct array razor_uri_vtable_entries;
   1.648 +
   1.649 +static struct razor_uri_vtable_entry *
   1.650 +  razor_uri_get_vtable_entry(const char *scheme)
   1.651 +{
   1.652 +	struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL;
   1.653 +
   1.654 +	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
   1.655 +	for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
   1.656 +		if (!strcmp0(entry->scheme, scheme))
   1.657 +			return entry;
   1.658 +		else if (!entry->scheme)
   1.659 +			fallback = entry;
   1.660 +	}
   1.661 +
   1.662 +	return fallback;
   1.663 +}
   1.664 +
   1.665 +int razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error)
   1.666 +{
   1.667 +	int retval;
   1.668 +	char *path;
   1.669 +	struct razor_uri ru;
   1.670 +	struct razor_uri_vtable_entry *entry;
   1.671 +	struct razor_error *tmp_error = NULL;
   1.672 +
   1.673 +	if (razor_uri_parse(&ru, uri, error))
   1.674 +		return -1;
   1.675 +
   1.676 +	path = razor_path_from_parsed_uri(&ru, &tmp_error);
   1.677 +
   1.678 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.679 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.680 +		razor_error_free(tmp_error);
   1.681 +	else if (!path) {
   1.682 +		razor_propagate_error(error, tmp_error, NULL);
   1.683 +		razor_uri_destroy(&ru);
   1.684 +		return -1;
   1.685 +	}
   1.686 +
   1.687 +	if (path) {
   1.688 +		razor_uri_destroy(&ru);
   1.689 +		retval = razor_file_mkdir(path, mode, error);
   1.690 +		free(path);
   1.691 +	} else {
   1.692 +		entry = razor_uri_get_vtable_entry(ru.scheme);
   1.693 +		razor_uri_destroy(&ru);
   1.694 +		if (!entry || !entry->vtable.mkdir) {
   1.695 +			retval = -1;
   1.696 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   1.697 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
   1.698 +					uri, "No URI handler installed");
   1.699 +		} else
   1.700 +			retval = entry->vtable.mkdir(uri, mode, error);
   1.701 +	}
   1.702 +
   1.703 +	return retval;
   1.704 +}
   1.705 +
   1.706 +int razor_uri_unlink(const char *uri, struct razor_error **error)
   1.707 +{
   1.708 +	int retval;
   1.709 +	char *path;
   1.710 +	struct razor_uri ru;
   1.711 +	struct razor_uri_vtable_entry *entry;
   1.712 +	struct razor_error *tmp_error = NULL;
   1.713 +
   1.714 +	if (razor_uri_parse(&ru, uri, error))
   1.715 +		return -1;
   1.716 +
   1.717 +	path = razor_path_from_parsed_uri(&ru, &tmp_error);
   1.718 +
   1.719 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.720 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.721 +		razor_error_free(tmp_error);
   1.722 +	else if (!path) {
   1.723 +		razor_propagate_error(error, tmp_error, NULL);
   1.724 +		razor_uri_destroy(&ru);
   1.725 +		return -1;
   1.726 +	}
   1.727 +
   1.728 +	if (path) {
   1.729 +		razor_uri_destroy(&ru);
   1.730 +		retval = razor_file_unlink(path, error);
   1.731 +		free(path);
   1.732 +	} else {
   1.733 +		entry = razor_uri_get_vtable_entry(ru.scheme);
   1.734 +		razor_uri_destroy(&ru);
   1.735 +		if (!entry || !entry->vtable.unlink) {
   1.736 +			retval = -1;
   1.737 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   1.738 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
   1.739 +					uri, "No URI handler installed");
   1.740 +		} else
   1.741 +			retval = entry->vtable.unlink(uri, error);
   1.742 +	}
   1.743 +
   1.744 +	return retval;
   1.745 +}
   1.746 +
   1.747 +int razor_uri_open(const char *uri, int flags, mode_t mode,
   1.748 +		   struct razor_error **error)
   1.749 +{
   1.750 +	int retval;
   1.751 +	char *path;
   1.752 +	struct razor_uri ru;
   1.753 +	struct razor_uri_vtable_entry *entry;
   1.754 +	struct razor_error *tmp_error = NULL;
   1.755 +
   1.756 +	if (razor_uri_parse(&ru, uri, error))
   1.757 +		return -1;
   1.758 +
   1.759 +	path = razor_path_from_parsed_uri(&ru, &tmp_error);
   1.760 +
   1.761 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.762 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.763 +		razor_error_free(tmp_error);
   1.764 +	else if (!path) {
   1.765 +		razor_propagate_error(error, tmp_error, NULL);
   1.766 +		razor_uri_destroy(&ru);
   1.767 +		return -1;
   1.768 +	}
   1.769 +
   1.770 +	if (path) {
   1.771 +		razor_uri_destroy(&ru);
   1.772 +		retval = razor_file_open(path, flags, mode, error);
   1.773 +		free(path);
   1.774 +	} else {
   1.775 +		entry = razor_uri_get_vtable_entry(ru.scheme);
   1.776 +		razor_uri_destroy(&ru);
   1.777 +		if (!entry || !entry->vtable.open) {
   1.778 +			retval = -1;
   1.779 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   1.780 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
   1.781 +					uri, "No URI handler installed");
   1.782 +		} else
   1.783 +			retval = entry->vtable.open(uri, flags, mode, error);
   1.784 +	}
   1.785 +
   1.786 +	return retval;
   1.787 +}
   1.788 +
   1.789 +int razor_uri_move(const char *src_uri, const char *dst_uri,
   1.790 +		   struct razor_error **error)
   1.791 +{
   1.792 +	int retval;
   1.793 +	char *src_path, *dst_path;
   1.794 +	struct razor_uri src_ru, dst_ru;
   1.795 +	struct razor_uri_vtable_entry *entry;
   1.796 +	struct razor_error *tmp_error = NULL;
   1.797 +
   1.798 +	if (razor_uri_parse(&src_ru, src_uri, error) ||
   1.799 +	    razor_uri_parse(&dst_ru, dst_uri, error))
   1.800 +		return -1;
   1.801 +
   1.802 +	src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error);
   1.803 +
   1.804 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.805 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.806 +		razor_error_free(tmp_error);
   1.807 +	else if (!src_path) {
   1.808 +		razor_propagate_error(error, tmp_error, NULL);
   1.809 +		razor_uri_destroy(&src_ru);
   1.810 +		return -1;
   1.811 +	}
   1.812 +
   1.813 +	dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error);
   1.814 +
   1.815 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.816 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.817 +		razor_error_free(tmp_error);
   1.818 +	else if (!dst_path) {
   1.819 +		razor_propagate_error(error, tmp_error, NULL);
   1.820 +		razor_uri_destroy(&dst_ru);
   1.821 +		razor_uri_destroy(&src_ru);
   1.822 +		free(src_path);
   1.823 +		return -1;
   1.824 +	}
   1.825 +
   1.826 +	if (src_path && dst_path)
   1.827 +		retval = razor_file_move(src_path, dst_path, error);
   1.828 +	else {
   1.829 +		if (!strcmp(src_ru.scheme, dst_ru.scheme))
   1.830 +			entry = razor_uri_get_vtable_entry(src_ru.scheme);
   1.831 +		else
   1.832 +			entry = NULL;
   1.833 +		if (entry && entry->vtable.move)
   1.834 +			retval = entry->vtable.move(src_uri, dst_uri, error);
   1.835 +		else if (strcmp(src_ru.scheme, dst_ru.scheme)) {
   1.836 +			retval = -1;
   1.837 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   1.838 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
   1.839 +					dst_uri, "Cross-scheme URI move");
   1.840 +		} else {
   1.841 +			retval = -1;
   1.842 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   1.843 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
   1.844 +					src_path ? dst_uri : src_uri,
   1.845 +					"No URI handler installed");
   1.846 +		}
   1.847 +	}
   1.848 +
   1.849 +	razor_uri_destroy(&src_ru);
   1.850 +	razor_uri_destroy(&dst_ru);
   1.851 +	free(src_path);
   1.852 +	free(dst_path);
   1.853 +
   1.854 +	return retval;
   1.855 +}
   1.856 +
   1.857 +void *razor_uri_get_contents(const char *uri, size_t *length, int private,
   1.858 +			     struct razor_error **error)
   1.859 +{
   1.860 +	void *retval;
   1.861 +	char *path;
   1.862 +	struct razor_uri ru;
   1.863 +	struct razor_uri_vtable_entry *entry;
   1.864 +	struct razor_error *tmp_error = NULL;
   1.865 +
   1.866 +	if (razor_uri_parse(&ru, uri, error))
   1.867 +		return NULL;
   1.868 +
   1.869 +	path = razor_path_from_parsed_uri(&ru, &tmp_error);
   1.870 +
   1.871 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.872 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.873 +		razor_error_free(tmp_error);
   1.874 +	else if (!path) {
   1.875 +		razor_propagate_error(error, tmp_error, NULL);
   1.876 +		razor_uri_destroy(&ru);
   1.877 +		return NULL;
   1.878 +	}
   1.879 +
   1.880 +	if (path) {
   1.881 +		razor_uri_destroy(&ru);
   1.882 +		retval = razor_file_get_contents(path, length, private, error);
   1.883 +		free(path);
   1.884 +	} else {
   1.885 +		entry = razor_uri_get_vtable_entry(ru.scheme);
   1.886 +		razor_uri_destroy(&ru);
   1.887 +		if (!entry || !entry->vtable.get_contents) {
   1.888 +			retval = NULL;
   1.889 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   1.890 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
   1.891 +					uri, "No URI handler installed");
   1.892 +		} else {
   1.893 +			retval = entry->vtable.get_contents(uri, length,
   1.894 +							    private, error);
   1.895 +			if (retval)
   1.896 +				ptr_array_add(&entry->open_files, retval);
   1.897 +		}
   1.898 +	}
   1.899 +
   1.900 +	return retval;
   1.901 +}
   1.902 +
   1.903 +int razor_uri_free_contents(void *addr, size_t length)
   1.904 +{
   1.905 +	int retval, of;
   1.906 +	struct razor_uri_vtable_entry *entry, *eend;
   1.907 +
   1.908 +	if (!addr)
   1.909 +		return 0;
   1.910 +
   1.911 +	retval = razor_file_free_contents(addr, length);
   1.912 +
   1.913 +	if (retval != -2)
   1.914 +		return retval;
   1.915 +
   1.916 +	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
   1.917 +	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
   1.918 +		of = ptr_array_find(&entry->open_files, addr);
   1.919 +		if (of >= 0) {
   1.920 +			if (entry->vtable.free_contents)
   1.921 +				retval = entry->vtable.free_contents(addr,
   1.922 +								     length);
   1.923 +			ptr_array_remove_index(&entry->open_files, of);
   1.924 +			break;
   1.925 +		}
   1.926 +	}
   1.927 +
   1.928 +	return retval;
   1.929 +}
   1.930 +
   1.931 +int razor_uri_is_directory(const char *uri, struct razor_error **error)
   1.932 +{
   1.933 +	int retval;
   1.934 +	char *path;
   1.935 +	struct razor_uri ru;
   1.936 +	struct razor_uri_vtable_entry *entry;
   1.937 +	struct razor_error *tmp_error = NULL;
   1.938 +
   1.939 +	if (razor_uri_parse(&ru, uri, error))
   1.940 +		return -1;
   1.941 +
   1.942 +	path = razor_path_from_parsed_uri(&ru, &tmp_error);
   1.943 +
   1.944 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.945 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.946 +		razor_error_free(tmp_error);
   1.947 +	else if (!path) {
   1.948 +		razor_propagate_error(error, tmp_error, NULL);
   1.949 +		razor_uri_destroy(&ru);
   1.950 +		return -1;
   1.951 +	}
   1.952 +
   1.953 +	if (path) {
   1.954 +		razor_uri_destroy(&ru);
   1.955 +		retval = razor_file_is_directory(path, error);
   1.956 +		free(path);
   1.957 +	} else {
   1.958 +		entry = razor_uri_get_vtable_entry(ru.scheme);
   1.959 +		razor_uri_destroy(&ru);
   1.960 +		if (!entry || !entry->vtable.is_directory) {
   1.961 +			retval = -1;
   1.962 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   1.963 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
   1.964 +					uri, "No URI handler installed");
   1.965 +		} else
   1.966 +			retval = entry->vtable.is_directory(uri, error);
   1.967 +	}
   1.968 +
   1.969 +	return retval;
   1.970 +}
   1.971 +
   1.972 +char *razor_uri_mkdtemp_near(const char *uri, const char *template,
   1.973 +			     struct razor_error **error)
   1.974 +{
   1.975 +	char *retval, *s;
   1.976 +	char *path;
   1.977 +	struct razor_uri ru;
   1.978 +	struct razor_uri_vtable_entry *entry;
   1.979 +	struct razor_error *tmp_error = NULL;
   1.980 +
   1.981 +	if (razor_uri_parse(&ru, uri, error))
   1.982 +		return NULL;
   1.983 +
   1.984 +	path = razor_path_from_parsed_uri(&ru, &tmp_error);
   1.985 +
   1.986 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
   1.987 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
   1.988 +		razor_error_free(tmp_error);
   1.989 +	else if (!path) {
   1.990 +		razor_propagate_error(error, tmp_error, NULL);
   1.991 +		razor_uri_destroy(&ru);
   1.992 +		return NULL;
   1.993 +	}
   1.994 +
   1.995 +	if (path) {
   1.996 +		razor_uri_destroy(&ru);
   1.997 +		s = razor_file_mkdtemp_near(path, template, error);
   1.998 +		retval = razor_path_to_uri(s);
   1.999 +		free(s);
  1.1000 +		free(path);
  1.1001 +	} else {
  1.1002 +		entry = razor_uri_get_vtable_entry(ru.scheme);
  1.1003 +		razor_uri_destroy(&ru);
  1.1004 +		if (!entry || !entry->vtable.mkdtemp_near) {
  1.1005 +			retval = NULL;
  1.1006 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
  1.1007 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
  1.1008 +					uri, "No URI handler installed");
  1.1009 +		} else
  1.1010 +			retval = entry->vtable.mkdtemp_near(uri, template,
  1.1011 +							    error);
  1.1012 +	}
  1.1013 +
  1.1014 +	return retval;
  1.1015 +}
  1.1016 +
  1.1017 +void *razor_uri_opendir(const char *uri, struct razor_error **error)
  1.1018 +{
  1.1019 +	void *retval;
  1.1020 +	char *path;
  1.1021 +	struct razor_uri ru;
  1.1022 +	struct razor_uri_vtable_entry *entry;
  1.1023 +	struct razor_error *tmp_error = NULL;
  1.1024 +
  1.1025 +	if (razor_uri_parse(&ru, uri, error))
  1.1026 +		return NULL;
  1.1027 +
  1.1028 +	path = razor_path_from_parsed_uri(&ru, &tmp_error);
  1.1029 +
  1.1030 +	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
  1.1031 +	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
  1.1032 +		razor_error_free(tmp_error);
  1.1033 +	else if (!path) {
  1.1034 +		razor_propagate_error(error, tmp_error, NULL);
  1.1035 +		razor_uri_destroy(&ru);
  1.1036 +		return NULL;
  1.1037 +	}
  1.1038 +
  1.1039 +	if (path) {
  1.1040 +		razor_uri_destroy(&ru);
  1.1041 +		retval = razor_file_opendir(path, error);
  1.1042 +		free(path);
  1.1043 +	} else {
  1.1044 +		entry = razor_uri_get_vtable_entry(ru.scheme);
  1.1045 +		razor_uri_destroy(&ru);
  1.1046 +		if (!entry || !entry->vtable.opendir) {
  1.1047 +			retval = NULL;
  1.1048 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
  1.1049 +					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
  1.1050 +					uri, "No URI handler installed");
  1.1051 +		} else {
  1.1052 +			retval = entry->vtable.opendir(uri, error);
  1.1053 +			if (retval)
  1.1054 +				ptr_array_add(&entry->open_directories, retval);
  1.1055 +		}
  1.1056 +	}
  1.1057 +
  1.1058 +	return retval;
  1.1059 +}
  1.1060 +
  1.1061 +char *razor_uri_readdir(void *dir, struct razor_error **error)
  1.1062 +{
  1.1063 +	int od;
  1.1064 +	char *retval;
  1.1065 +	struct razor_uri_vtable_entry *entry, *eend;
  1.1066 +
  1.1067 +	retval = razor_file_readdir(dir, error);
  1.1068 +
  1.1069 +	if (retval != (char *)-1)
  1.1070 +		return retval;
  1.1071 +
  1.1072 +	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
  1.1073 +	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
  1.1074 +		od = ptr_array_find(&entry->open_directories, dir);
  1.1075 +		if (od >= 0) {
  1.1076 +			if (entry->vtable.readdir)
  1.1077 +				retval = entry->vtable.readdir(dir, error);
  1.1078 +			break;
  1.1079 +		}
  1.1080 +	}
  1.1081 +
  1.1082 +	if (retval == (char *)-1) {
  1.1083 +		retval = NULL;
  1.1084 +		razor_set_error(error, RAZOR_GENERAL_ERROR,
  1.1085 +				RAZOR_GENERAL_ERROR_FAILED, NULL,
  1.1086 +				"Invalid directory handle");
  1.1087 +	}
  1.1088 +
  1.1089 +	return retval;
  1.1090 +}
  1.1091 +
  1.1092 +int razor_uri_closedir(void *dir, struct razor_error **error)
  1.1093 +{
  1.1094 +	int od;
  1.1095 +	int retval;
  1.1096 +	struct razor_uri_vtable_entry *entry, *eend;
  1.1097 +
  1.1098 +	retval = razor_file_closedir(dir, error);
  1.1099 +
  1.1100 +	if (retval != -2)
  1.1101 +		return retval;
  1.1102 +
  1.1103 +	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
  1.1104 +	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
  1.1105 +		od = ptr_array_find(&entry->open_directories, dir);
  1.1106 +		if (od >= 0) {
  1.1107 +			if (entry->vtable.closedir)
  1.1108 +				retval = entry->vtable.closedir(dir, error);
  1.1109 +			break;
  1.1110 +		}
  1.1111 +	}
  1.1112 +
  1.1113 +	if (retval == -2)
  1.1114 +		razor_set_error(error, RAZOR_GENERAL_ERROR,
  1.1115 +				RAZOR_GENERAL_ERROR_FAILED, NULL,
  1.1116 +				"Invalid directory handle");
  1.1117 +
  1.1118 +	return retval;
  1.1119 +}
  1.1120 +
  1.1121 +RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme,
  1.1122 +  struct razor_uri_vtable *vtable, struct razor_error **error)
  1.1123 +{
  1.1124 +	struct razor_uri_vtable_entry *entry, *eend;
  1.1125 +
  1.1126 +	if (!strcmp0(scheme, "file")) {
  1.1127 +		/*
  1.1128 +		 * There's no fundamental reason why we couldn't support
  1.1129 +		 * this, but it's a lot of work without any obvious need.
  1.1130 +		 */
  1.1131 +		razor_set_error(error, RAZOR_GENERAL_ERROR,
  1.1132 +				RAZOR_GENERAL_ERROR_FAILED, scheme,
  1.1133 +				"Can't override file scheme handler");
  1.1134 +		return -1;
  1.1135 +	}
  1.1136 +
  1.1137 +	if (vtable->structure_size < sizeof(struct razor_uri_vtable)) {
  1.1138 +		/*
  1.1139 +		 * A future version of the API might add vfuncs to the
  1.1140 +		 * table (which we ignore), but won't remove any.
  1.1141 +		 */
  1.1142 +		razor_set_error(error, RAZOR_GENERAL_ERROR,
  1.1143 +				RAZOR_GENERAL_ERROR_FAILED, scheme,
  1.1144 +				"Invalid vtable structure size");
  1.1145 +		return -1;
  1.1146 +	}
  1.1147 +
  1.1148 +	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
  1.1149 +	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
  1.1150 +		if (!strcmp0(entry->scheme, scheme))
  1.1151 +			break;
  1.1152 +	}
  1.1153 +
  1.1154 +	if (entry == eend) {
  1.1155 +		if (!vtable)
  1.1156 +			return 0;
  1.1157 +		entry = array_add(&razor_uri_vtable_entries, sizeof *entry);
  1.1158 +		entry->scheme = scheme ? strdup(scheme) : NULL;
  1.1159 +		array_init(&entry->open_files);
  1.1160 +		array_init(&entry->open_directories);
  1.1161 +	} else if (entry->open_files.size || entry->open_directories.size) {
  1.1162 +		razor_set_error(error, RAZOR_GENERAL_ERROR,
  1.1163 +				RAZOR_GENERAL_ERROR_FAILED, scheme,
  1.1164 +				"URI handler busy");
  1.1165 +		return -1;
  1.1166 +	}
  1.1167 +
  1.1168 +	if (vtable) {
  1.1169 +		entry->vtable = *vtable;
  1.1170 +		entry->vtable.structure_size = sizeof(struct razor_uri_vtable);
  1.1171 +	} else {
  1.1172 +		free(entry->scheme);
  1.1173 +		if (entry + 1 < eend)
  1.1174 +			memmove(entry, entry + 1, eend - (entry + 1));
  1.1175 +		array_set_size(&razor_uri_vtable_entries,
  1.1176 +			       razor_uri_vtable_entries.size - sizeof *entry);
  1.1177 +	}
  1.1178 +
  1.1179 +	return 0;
  1.1180 +}