From d1796d60e9a3eac8f4aa98ec98eb06e0f611a273 Mon Sep 17 00:00:00 2001
From: Nik <njunger@uwaterloo.ca>
Date: Thu, 5 Feb 2015 19:09:31 -0500
Subject: [PATCH] Removed POSIX requirement; support Win,BSD,Darwin

---
 INSTALL              |   7 ++
 README.md            |   4 +-
 doc.go               |  10 ++-
 element_fmt.go       |  10 +--
 memstream.h          |  35 ++++++++++
 memstream_bsdlike.go | 150 +++++++++++++++++++++++++++++++++++++++++++
 memstream_other.go   |  70 ++++++++++++++++++++
 memstream_posix.go   |  57 ++++++++++++++++
 params.go            |  10 +--
 9 files changed, 337 insertions(+), 16 deletions(-)
 create mode 100644 memstream.h
 create mode 100644 memstream_bsdlike.go
 create mode 100644 memstream_other.go
 create mode 100644 memstream_posix.go

diff --git a/INSTALL b/INSTALL
index a0630de..50ced3e 100644
--- a/INSTALL
+++ b/INSTALL
@@ -38,3 +38,10 @@ System:
 After installing, you may need to rebuild the search path for libraries:
 
 	sudo ldconfig
+
+It is possible to install the package on Windows through the use of MinGW
+and MSYS. MSYS is required for installing PBC, while GMP can be installed
+through a package. Based on your MinGW installation, you may need to add
+"-I/usr/local/include" to CPPFLAGS and "-L/usr/local/lib" to LDFLAGS when
+building PBC. Likewise, you may need to add these options to CGO_CPPFLAGS
+and CGO_LDFLAGS when installing this package.
\ No newline at end of file
diff --git a/README.md b/README.md
index 904d5cb..9bd9322 100644
--- a/README.md
+++ b/README.md
@@ -38,9 +38,7 @@ cryptosystems.
 This package must be compiled using cgo. It also requires the installation
 of GMP and PBC. During the build process, this package will attempt to
 include `gmp.h` and `pbc/pbc.h`, and then dynamically link to GMP and PBC.
-It also expects a POSIX-like environment for several C functions. For this
-reason, this package cannot be used in Windows without a POSIX compatibility
-layer and a gcc compiler.
+Installation on Windows requires the use of MinGW.
 
 ## Documentation
 For additional installation instructions and documentation, see
diff --git a/doc.go b/doc.go
index 93002b6..c61311e 100644
--- a/doc.go
+++ b/doc.go
@@ -71,9 +71,6 @@
 	This package must be compiled using cgo. It also requires the installation
 	of GMP and PBC. During the build process, this package will attempt to
 	include <gmp.h> and <pbc/pbc.h>, and then dynamically link to GMP and PBC.
-	It also expects a POSIX-like environment for several C functions. For this
-	reason, this package cannot be used in Windows without a POSIX compatibility
-	layer and a gcc compiler.
 
 	Most systems include a package for GMP. To install GMP in Debian / Ubuntu:
 
@@ -109,6 +106,13 @@
 
 		sudo ldconfig
 
+	It is possible to install the package on Windows through the use of MinGW
+	and MSYS. MSYS is required for installing PBC, while GMP can be installed
+	through a package. Based on your MinGW installation, you may need to add
+	"-I/usr/local/include" to CPPFLAGS and "-L/usr/local/lib" to LDFLAGS when
+	building PBC. Likewise, you may need to add these options to CGO_CPPFLAGS
+	and CGO_LDFLAGS when installing this package.
+
 	License
 
 	This package is free software: you can redistribute it and/or modify it
diff --git a/element_fmt.go b/element_fmt.go
index ba88b80..6897602 100644
--- a/element_fmt.go
+++ b/element_fmt.go
@@ -23,13 +23,13 @@ package pbc
 
 /*
 #include <pbc/pbc.h>
+#include "memstream.h"
 
 int element_out_str_wrapper(char** bufp, size_t* sizep, int base, element_t e) {
-	FILE* handle = open_memstream(bufp, sizep);
-	if (!handle) return 0;
-	element_out_str(handle, base, e);
-	fclose(handle);
-	return 1;
+	memstream_t* stream = pbc_open_memstream();
+	if (stream == NULL) return 0;
+	element_out_str(pbc_memstream_to_fd(stream), base, e);
+	return pbc_close_memstream(stream, bufp, sizep);
 }
 */
 import "C"
diff --git a/memstream.h b/memstream.h
new file mode 100644
index 0000000..20191a2
--- /dev/null
+++ b/memstream.h
@@ -0,0 +1,35 @@
+// Copyright © 2015 Nik Unger
+//
+// This file is part of The PBC Go Wrapper.
+//
+// The PBC Go Wrapper is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or (at your
+// option) any later version.
+//
+// The PBC Go Wrapper is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+// License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with The PBC Go Wrapper. If not, see <http://www.gnu.org/licenses/>.
+//
+// The PBC Go Wrapper makes use of The PBC library. The PBC Library and its use
+// are covered under the terms of the GNU Lesser General Public License
+// version 3, or (at your option) any later version.
+
+#include <stdio.h>
+
+// memstream_s is a structure that provides platform-independent conversion from
+// file descriptor writes to strings
+typedef struct memstream_s memstream_t;
+
+// pbc_open_memstream returns a memstream that can be used for writing data
+memstream_t* pbc_open_memstream();
+
+// pbc_memstream_to_fd retrieves the file descriptor for a memstream
+FILE* pbc_memstream_to_fd(memstream_t* m);
+
+// pbc_close_memstream closes the memstream and returns the written data
+int pbc_close_memstream(memstream_t* m, char** bufp, size_t* sizep);
diff --git a/memstream_bsdlike.go b/memstream_bsdlike.go
new file mode 100644
index 0000000..d8f0704
--- /dev/null
+++ b/memstream_bsdlike.go
@@ -0,0 +1,150 @@
+// Copyright © 2015 Nik Unger
+//
+// This file is part of The PBC Go Wrapper.
+//
+// The PBC Go Wrapper is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or (at your
+// option) any later version.
+//
+// The PBC Go Wrapper is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+// License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with The PBC Go Wrapper. If not, see <http://www.gnu.org/licenses/>.
+//
+// The PBC Go Wrapper makes use of The PBC library. The PBC Library and its use
+// are covered under the terms of the GNU Lesser General Public License
+// version 3, or (at your option) any later version.
+//
+
+// +build darwin freebsd
+
+package pbc
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include "memstream.h"
+
+struct memstream_s {
+	char*  buffer;
+	size_t cap;
+	size_t len;
+	fpos_t cursor;
+	FILE*  fd;
+	int    closed;
+};
+
+static void memstream_realloc(memstream_t* m, size_t size) {
+	m->cap = size;
+	m->buffer = realloc(m->buffer, size);
+	if (size < m->len) {
+		m->len = size;
+		m->cursor = m->len;
+	}
+}
+
+static int memstream_read(void* cookie, char* buf, int nbytes) {
+	memstream_t* m = (memstream_t*)cookie;
+	if (m->closed) { errno = EBADF; return -1; }
+	if (m->cursor >= m->len) return 0;
+	size_t toRead = (m->len - m->cursor);
+	if (toRead > (size_t)nbytes) toRead = (size_t)nbytes;
+	memcpy(buf, &m->buffer[m->cursor], toRead);
+	m->cursor += toRead;
+	return toRead;
+}
+
+static int memstream_write(void* cookie, const char* buf, int nbytes) {
+	memstream_t* m = (memstream_t*)cookie;
+	if (m->closed) { errno = EBADF; return -1; }
+	size_t zeros = m->cursor - m->len;
+	size_t dataLen = (size_t)nbytes;
+	size_t neededSpace = zeros + dataLen;
+	if (neededSpace < dataLen) {
+		errno = EFBIG;
+		return -1;
+	}
+	size_t newLen = m->len + neededSpace;
+	if (newLen < m->len) {
+		errno = EFBIG;
+		return -1;
+	}
+	if (newLen > m->cap) {
+		size_t newCap = m->cap;
+		do {
+			if (SIZE_MAX - newCap < newCap) {
+				newCap = SIZE_MAX;
+			} else {
+				newCap <<= 1;
+			}
+		} while (newLen > newCap);
+		memstream_realloc(m, newCap);
+	}
+	if (zeros > 0) {
+		memset(&m->buffer[m->len], 0, zeros);
+	}
+	memcpy(&m->buffer[m->cursor], buf, dataLen);
+	m->cursor += dataLen;
+	m->len = newLen;
+	return neededSpace;
+}
+
+static fpos_t memstream_seek(void* cookie, fpos_t offset, int whence) {
+	memstream_t* m = (memstream_t*)cookie;
+	if (m->closed) { errno = EBADF; return -1; }
+	fpos_t base = 0;
+	switch (whence) {
+		case SEEK_SET: base = 0; break;
+		case SEEK_CUR: base = m->cursor; break;
+		case SEEK_END: base = m->len; break;
+		// SEEK_HOLE and SEEK_DATA are not supported on darwin
+		default: errno = EINVAL; return -1;
+	}
+	fpos_t desired = base + offset;
+	if (desired < 0) {
+		errno = EINVAL;
+		return -1;
+	}
+	if (offset > 0 && desired < base) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+	return (m->cursor = desired);
+}
+
+static int memstream_close(void* cookie) {
+	memstream_t* m = (memstream_t*)cookie;
+	m->closed = 1;
+	return 0;
+}
+
+memstream_t* pbc_open_memstream() {
+	memstream_t* m = malloc(sizeof(memstream_t));
+	m->buffer = NULL;
+	memstream_realloc(m, 1024);
+	m->len = 0;
+	m->cursor = 0;
+	m->closed = 0;
+	m->fd = funopen(m, memstream_read, memstream_write, memstream_seek, memstream_close);
+	return m;
+}
+
+FILE* pbc_memstream_to_fd(memstream_t* m) { return m->fd; }
+
+int pbc_close_memstream(memstream_t* m, char** bufp, size_t* sizep) {
+	fclose(m->fd);
+	*bufp = m->buffer;
+	*sizep = m->len;
+	free(m);
+	return 1;
+}
+*/
+import "C"
diff --git a/memstream_other.go b/memstream_other.go
new file mode 100644
index 0000000..91093c6
--- /dev/null
+++ b/memstream_other.go
@@ -0,0 +1,70 @@
+// Copyright © 2015 Nik Unger
+//
+// This file is part of The PBC Go Wrapper.
+//
+// The PBC Go Wrapper is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or (at your
+// option) any later version.
+//
+// The PBC Go Wrapper is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+// License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with The PBC Go Wrapper. If not, see <http://www.gnu.org/licenses/>.
+//
+// The PBC Go Wrapper makes use of The PBC library. The PBC Library and its use
+// are covered under the terms of the GNU Lesser General Public License
+// version 3, or (at your option) any later version.
+
+// +build !linux,!darwin,!freebsd
+
+package pbc
+
+/*
+#include <stdlib.h>
+#include "memstream.h"
+
+struct memstream_s {
+	FILE* fd;
+};
+
+memstream_t* pbc_open_memstream() {
+	FILE* fd = tmpfile();
+	if (fd == NULL) return NULL;
+	memstream_t* result = malloc(sizeof(memstream_t));
+	result->fd = fd;
+	return result;
+}
+
+FILE* pbc_memstream_to_fd(memstream_t* m) { return m->fd; }
+
+int pbc_close_memstream(memstream_t* m, char** bufp, size_t* sizep) {
+	*bufp = NULL;
+	*sizep = 0;
+
+	FILE* fd = m->fd;
+	m->fd = NULL;
+	free(m);
+	m = NULL;
+
+	if (!ferror(fd)) {
+		fseek(fd, 0, SEEK_END);
+		*sizep = (size_t)ftell(fd);
+		rewind(fd);
+		*bufp = malloc(*sizep + 1);
+		size_t readBytes = fread(*bufp, 1, *sizep, fd);
+		if (readBytes < *sizep || ferror(fd)) {
+			free(*bufp);
+			*bufp = NULL;
+			*sizep = 0;
+		}
+		bufp[*sizep] = '\0';
+	}
+	fclose(fd);
+	return (*bufp != NULL);
+}
+*/
+import "C"
diff --git a/memstream_posix.go b/memstream_posix.go
new file mode 100644
index 0000000..19375a4
--- /dev/null
+++ b/memstream_posix.go
@@ -0,0 +1,57 @@
+// Copyright © 2015 Nik Unger
+//
+// This file is part of The PBC Go Wrapper.
+//
+// The PBC Go Wrapper is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or (at your
+// option) any later version.
+//
+// The PBC Go Wrapper is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+// License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with The PBC Go Wrapper. If not, see <http://www.gnu.org/licenses/>.
+//
+// The PBC Go Wrapper makes use of The PBC library. The PBC Library and its use
+// are covered under the terms of the GNU Lesser General Public License
+// version 3, or (at your option) any later version.
+
+// +build linux
+
+package pbc
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+#include "memstream.h"
+
+struct memstream_s {
+	char*  buf;
+	size_t size;
+	FILE*  fd;
+};
+
+memstream_t* pbc_open_memstream() {
+	memstream_t* result = malloc(sizeof(memstream_t));
+	result->fd = open_memstream(&result->buf, &result->size);
+	if (result->fd == NULL) {
+		free(result);
+		result = NULL;
+	}
+	return result;
+}
+
+FILE* pbc_memstream_to_fd(memstream_t* m) { return m->fd; }
+
+int pbc_close_memstream(memstream_t* m, char** bufp, size_t* sizep) {
+	fclose(m->fd);
+	*bufp = m->buf;
+	*sizep = m->size;
+	free(m);
+	return 1;
+}
+*/
+import "C"
diff --git a/params.go b/params.go
index c729348..314d31c 100644
--- a/params.go
+++ b/params.go
@@ -23,13 +23,13 @@ package pbc
 
 /*
 #include <pbc/pbc.h>
+#include "memstream.h"
 
 int param_out_str_wrapper(char** bufp, size_t* sizep, pbc_param_t p) {
-	FILE* handle = open_memstream(bufp, sizep);
-	if (!handle) return 0;
-	pbc_param_out_str(handle, p);
-	fclose(handle);
-	return 1;
+	memstream_t* stream = pbc_open_memstream();
+	if (stream == NULL) return 0;
+	pbc_param_out_str(pbc_memstream_to_fd(stream), p);
+	return pbc_close_memstream(stream, bufp, sizep);
 }
 */
 import "C"
-- 
GitLab