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