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