aboutsummaryrefslogblamecommitdiffstats
path: root/libskrift_open_font___.c
blob: 3668cb3efa9042968e02c4dd217dcef3a44efecb (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                                                             



                        






























































































































































































                                                                                                                    



                                                 










                                                                            

                                      

                                  
                                                                  



                                

                                         

         

                                                

                              


                                      










                                                   






                                            

                 
/* See LICENSE file for copyright and license details. */
#include "common.h"

#ifdef GUNZIP_PATH
# define IS_GZIP() (size >= 18 && mem8[0] == 0x1f && mem8[1] == 0x8b)
#endif

#define IS_PSF_V1() (size >= 4 && mem8[0] == 0x36 && mem8[1] == 0x04)
#define IS_PSF_V2() (size >= 32 && mem8[0] == 0x72 && mem8[1] == 0xb5 && mem8[2] == 0x4a && mem8[3] == 0x86) 

#if defined(GUNZIP_PATH)
# define WITH_DECOMPRESS
#endif

#ifdef WITH_DECOMPRESS
static int
decompress(const char *path, const void *mem, size_t size, void **memp, size_t *sizep)
{
	const char *mem_in = mem;
	char *mem_out = *memp, *new;
	size_t off_in = 0, off_out = 0;
	int i, n, mem_fds[2][2], err_fds[2], saved_errno;
	struct pollfd pfds[2];
	nfds_t npfds;
	int status;
	pid_t pid;
	ssize_t r;

	if (pipe(mem_fds[0]))
		return -1;

	if (pipe(mem_fds[1])) {
		saved_errno = errno;
		close(mem_fds[0][0]);
		close(mem_fds[0][1]);
		errno = saved_errno;
		return -1;
	}

	if (pipe2(err_fds, O_CLOEXEC)) {
		saved_errno = errno;
		close(mem_fds[0][0]);
		close(mem_fds[0][1]);
		close(mem_fds[1][0]);
		close(mem_fds[1][1]);
		errno = saved_errno;
		return -1;
	}

	switch ((pid = fork())) {
	case -1:
		saved_errno = errno;
		close(mem_fds[0][0]);
		close(mem_fds[0][1]);
		close(mem_fds[1][0]);
		close(mem_fds[1][1]);
		close(err_fds[0]);
		close(err_fds[1]);
		errno = saved_errno;
		return -1;

	case 0:
		close(err_fds[0]);
		for (i = 0; i < 2; i++) {
			if (mem_fds[i][i] != i) {
				close(i);
			dup2_again:
				if (dup2(mem_fds[i][i], i) != i) {
					if (errno == EINTR)
						goto dup2_again;
					write(err_fds[1], &errno, sizeof(errno));
					return 255;
				}
				close(mem_fds[i][i]);
			}
			close(mem_fds[i][i ^ 1]);
		}
		execl(path, path, NULL);
		write(err_fds[1], &errno, sizeof(errno));
		return 255;

	default:
		break;
	}

	close(err_fds[1]);
	close(mem_fds[0][0]);
	close(mem_fds[1][1]);

	memset(pfds, 0, sizeof(pfds));
	pfds[0].fd = mem_fds[1][0];
	pfds[1].fd = mem_fds[0][1];
	pfds[0].events = POLLIN;
	pfds[1].events = POLLOUT;
	npfds = 2;
	for (;;) {
		n = poll(pfds, npfds, -1);
		if (n <= 0) {
			if (!n)
				continue;
		poll_loop_fail:
			saved_errno = errno;
			close(err_fds[1]);
			if (mem_fds[0][1] >= 0)
				close(mem_fds[0][1]);
			close(mem_fds[1][0]);
			kill(pid, SIGKILL);
			waitpid(pid, NULL, 0);
			free(mem_out);
			errno = saved_errno;
			return -1;
		}
		if (pfds[0].revents) {
			if (*sizep == off_out) {
				if (*sizep > SIZE_MAX - 4096) {
					errno = ENOMEM;
					goto poll_loop_fail;
				}
				new = realloc(mem_out, *sizep += 4096);
				if (!new)
					goto poll_loop_fail;
				mem_out = new;
			}
		read_again:
			r = read(pfds[0].fd, &mem_out[off_out], *sizep - off_out);
			if (r <= 0) {
				if (!r)
					break;
				if (errno == EINTR)
					goto read_again;
				goto poll_loop_fail;
			}
			off_out += (size_t)r;
		}
		if (npfds > 1 && pfds[1].revents) {
			if (off_in == size) {
				npfds -= 1;
				close(mem_fds[0][1]);
				mem_fds[0][1] = -1;
				continue;
			}
write_again:
			r = write(pfds[1].fd, &mem_in[off_in], MIN(size - off_in, (size_t)1 << 16));
			if (r <= 0) {
				if (r < 0 && errno == EINTR)
					goto write_again;
				goto poll_loop_fail;
			}
			off_in += (size_t)r;
		}
	}

	if (mem_fds[0][1] >= 0)
		close(mem_fds[0][1]);
	close(mem_fds[1][0]);

waitpid_again:
	if (waitpid(pid, &status, 0) != pid) {
		if (errno == EINTR)
			goto waitpid_again;
		saved_errno = errno;
		close(err_fds[0]);
		free(mem_out);
		errno = saved_errno;
	}

	if (WIFEXITED(status) && WEXITSTATUS(status) == 255) {
		if (read(err_fds[1], &errno, sizeof(errno)) == sizeof(errno)) {
			goto child_failed;
		} else {
			errno = EIO;
			goto child_failed;
		}
	} else if (status) {
		errno = WIFEXITED(status) ? EBFONT : EPIPE;
		goto child_failed;
	} else if (off_in < size) {
		errno = EPIPE;
	child_failed:
		saved_errno = errno;
		close(err_fds[0]);
		free(mem_out);
		errno = saved_errno;
		return -1;
	}

	close(err_fds[0]);

	if (*sizep > off_out) {
		new = realloc(mem_out, off_out);
		if (new)
			mem_out = new;
	}

	*memp = mem_out;
	*sizep = off_out;
	return 0;
}
#endif

int
libskrift_open_font___(LIBSKRIFT_FONT **fontp, const void *mem_static, void *mem_free, void *mem_unmap, size_t size)
{
	const void *mem = mem_static ? mem_static : mem_free ? mem_free : mem_unmap;
	const uint8_t *mem8 = mem;
	void *new_mem = NULL;
#ifdef WITH_DECOMPRESS
	void *free_on_failure = NULL;
	void *old_free = NULL, *old_unmap = NULL;
	size_t new_size = 0, old_size = size;
#endif

	*fontp = calloc(1, sizeof(**fontp));
	if (!*fontp)
		return -1;
	(*fontp)->refcount = 1;

#if defined(GUNZIP_PATH)
	if (IS_GZIP()) {
		if (decompress(GUNZIP_PATH, mem, size, &new_mem, &new_size))
			return -1;
		old_free = mem_free;
		old_unmap = mem_unmap;
		mem_static = NULL;
		mem_unmap = NULL;
		mem8 = mem = free_on_failure = mem_free = new_mem;
		size = new_size;
	}
#endif

	if (IS_PSF_V1() || IS_PSF_V2()) {
		/* TODO Convert to TTF */
	}

	(*fontp)->font = sft_loadmem(mem, size);
	if (!(*fontp)->font) {
		free(*fontp);
		*fontp = NULL;
#ifdef WITH_DECOMPRESS
		free(free_on_failure);
#endif
		return -1;
	}

	if (mem_free) {
		(*fontp)->memory_free = mem_free;
		(*fontp)->memory_size = size;
	} else if (mem_unmap) {
		(*fontp)->memory_unmap = mem_unmap;
		(*fontp)->memory_size = size;
	}

#ifdef WITH_DECOMPRESS
	if (old_free)
		free(old_free);
	else if (old_unmap)
		munmap(old_unmap, old_size);
#endif

	return 0;
}