<commit_before>/*
 * Copyright (c) 2015-2019 Dubalu LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include "config.h"              // for HAVE_PWRITE, HAVE_FSYNC

#include <atomic>                // for std::atomic_bool
#include <errno.h>               // for errno, EINTR
#include <fcntl.h>               // for fchmod, open, O_RDONLY
#include <stddef.h>              // for size_t
#include <stdlib.h>              // for mkdtemp
#include <sys/stat.h>            // for fstat
#include <sys/socket.h>          // for send
#include <unistd.h>              // for off_t, ssize_t, close, lseek, unlink
#include <type_traits>           // for std::forward

#ifdef XAPIAND_RANDOM_ERRORS
#include "random.hh"                // for random_real
#include "opts.h"                   // for opts
#define RANDOM_ERRORS_IO_ERRNO_RETURN(errnum) \
	if (opts.random_errors_io) { \
		auto prob = random_real(0, 1); \
		if (prob < opts.random_errors_io) { \
			errno = errnum; \
			return -1; \
		} \
	}
#define RANDOM_ERRORS_NET_ERRNO_RETURN(errnum, sock) \
	if (opts.random_errors_net) { \
		auto prob = random_real(0, 1); \
		if (prob < opts.random_errors_net) { \
			if (sock) { \
				::shutdown(sock, SHUT_RDWR); \
			} \
			errno = errnum; \
			return -1; \
		} \
	}
#else
#define RANDOM_ERRORS_IO_ERRNO_RETURN(...)
#define RANDOM_ERRORS_NET_ERRNO_RETURN(...)
#endif

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-statement-expression"

// Do not accept any file descriptor less than this value, in order to avoid
// opening database file using file descriptors that are commonly used for
// standard input, output, and error.
#ifndef XAPIAND_MINIMUM_FILE_DESCRIPTOR
#define XAPIAND_MINIMUM_FILE_DESCRIPTOR STDERR_FILENO + 1
#endif

namespace io {

#ifdef XAPIAND_CHECK_IO_FDES
#define OPENED 1
#define SOCKET 2
#define CLOSED 4
int check(const char* msg, int fd, int check_set, int check_unset, int set, const char* function, const char* filename, int line);
#define CHECK_OPEN(fd) io::check("while opening as file", fd, 0, OPENED | CLOSED, OPENED, __func__, __FILE__, __LINE__)
#define CHECK_OPEN_SOCKET(fd) io::check("while opening as socket", fd, 0, OPENED | SOCKET | CLOSED, OPENED | SOCKET, __func__, __FILE__, __LINE__)
#define CHECK_CLOSING(fd) io::check("while closing", fd, OPENED, 0, 0, __func__, __FILE__, __LINE__)
#define CHECK_CLOSE(fd) io::check("while closing", fd, 0, CLOSED, CLOSED, __func__, __FILE__, __LINE__)
#define CHECK_OPENED(msg, fd) io::check(msg, fd, OPENED, CLOSED, 0, __func__, __FILE__, __LINE__)
#define CHECK_OPENED_SOCKET(msg, fd) io::check(msg, fd, OPENED | SOCKET, CLOSED, 0, __func__, __FILE__, __LINE__)
#else
#define CHECK_OPEN(fd)
#define CHECK_OPEN_SOCKET(fd)
#define CHECK_CLOSING(fd)
#define CHECK_CLOSE(fd)
#define CHECK_OPENED(msg, fd)
#define CHECK_OPENED_SOCKET(msg, fd)
#endif

#if defined HAVE_FDATASYNC
#define __io_fsync ::fdatasync
#elif defined HAVE_FSYNC
#define __io_fsync ::fsync
#else
inline int __noop(int /*unused*/) { return 0; }
#define __io_fsync io::__noop
#endif

std::atomic_bool& ignore_eintr();

inline bool ignored_errno(int e, bool again, bool tcp, bool udp) {
	switch(e) {
		case EINTR:
			return ignore_eintr().load();  //  Always ignore error
		case EAGAIN:
#if EAGAIN != EWOULDBLOCK
		case EWOULDBLOCK:
#endif
			return again;  //  Ignore not-available error
		case EPIPE:
		case EINPROGRESS:
			return tcp;  //  Ignore error on TCP sockets

		case ENETDOWN:
		case EPROTO:
		case ENOPROTOOPT:
		case EHOSTDOWN:
#ifdef ENONET  // Linux-specific
		case ENONET:
#endif
		case EHOSTUNREACH:
		case EOPNOTSUPP:
		case ENETUNREACH:
		case ECONNRESET:
			return udp;  //  Ignore error on UDP sockets

		default:
			return false;  // Do not ignore error
	}
}

template <typename Fun, typename... Args>
inline auto RetryAfterSignal(const Fun &F, const Args &... As) -> decltype(F(As...)) {
	decltype(F(As...)) _err;
	do {
		errno = 0;
		_err = F(As...);
	} while (_err == -1 && errno == EINTR && ignore_eintr().load());
	return _err;
}

int open(const char* path, int oflag=O_RDONLY, int mode=0644);
int close(int fd);

ssize_t write(int fd, const void* buf, size_t nbyte);
ssize_t pwrite(int fd, const void* buf, size_t nbyte, off_t offset);

ssize_t read(int fd, void* buf, size_t nbyte);
ssize_t pread(int fd, void* buf, size_t nbyte, off_t offset);

inline int mkstemp(char* template_) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	int fd = ::mkstemp(template_);
	CHECK_OPEN(fd);
	return fd;
}

inline char* mkdtemp(char* template_) {
	return ::mkdtemp(template_);
}

inline int unlink(const char* path) {
	return ::unlink(path);
}

inline off_t lseek(int fd, off_t offset, int whence) {
	CHECK_OPENED("during lseek()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::lseek(fd, offset, whence);
}

template <typename... Args>
inline int unchecked_fcntl(int fd, int cmd, Args&&... args) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return RetryAfterSignal(::fcntl, fd, cmd, std::forward<Args>(args)...);
}

template <typename... Args>
inline int fcntl(int fd, int cmd, Args&&... args) {
	CHECK_OPENED("during fcntl()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return io::unchecked_fcntl(fd, cmd, std::forward<Args>(args)...);
}

inline int fstat(int fd, struct stat* buf) {
	CHECK_OPENED("during fstat()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::fstat(fd, buf);
}

inline int dup(int fd) {
	CHECK_OPENED("during dup()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::dup(fd);
}

inline int dup2(int fd, int fd2) {
	CHECK_OPENED("during dup2()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::dup2(fd, fd2);  // RetryAfterSignal?
}

inline int shutdown(int socket, int how) {
	CHECK_OPENED_SOCKET("during shutdown()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, 0);

	return ::shutdown(socket, how);
}

inline ssize_t send(int socket, const void* buffer, size_t length, int flags) {
	CHECK_OPENED_SOCKET("during send()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::send, socket, buffer, length, flags);
}

inline ssize_t sendto(int socket, const void* buffer, size_t length, int flags, const struct sockaddr* dest_addr, socklen_t dest_len) {
	CHECK_OPENED_SOCKET("during sendto()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::sendto, socket, buffer, length, flags, dest_addr, dest_len);
}

inline ssize_t recv(int socket, void* buffer, size_t length, int flags) {
	CHECK_OPENED_SOCKET("during recv()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::recv, socket, buffer, length, flags);
}

inline ssize_t recvfrom(int socket, void* buffer, size_t length, int flags, struct sockaddr* address, socklen_t* address_len) {
	CHECK_OPENED_SOCKET("during recvfrom()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::recvfrom, socket, buffer, length, flags, address, address_len);
}

inline int socket(int domain, int type, int protocol) {
	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	int socket = ::socket(domain, type, protocol);
	CHECK_OPEN_SOCKET(socket);
	return socket;
}

inline int getsockopt(int socket, int level, int option_name, void* option_value, socklen_t* option_len){
	CHECK_OPENED_SOCKET("during getsockopt()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(EOPNOTSUPP, 0);

	return ::getsockopt(socket, level, option_name, option_value, option_len);
}

inline int setsockopt(int socket, int level, int option_name, const void* option_value, socklen_t option_len){
	CHECK_OPENED_SOCKET("during setsockopt()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(EOPNOTSUPP, 0);

	return ::setsockopt(socket, level, option_name, option_value, option_len);
}

inline int listen(int socket, int backlog) {
	CHECK_OPENED_SOCKET("during listen()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	return ::listen(socket, backlog);
}

inline int accept(int socket, struct sockaddr* address, socklen_t* address_len) {
	CHECK_OPENED_SOCKET("during accept()", socket);
	int new_socket = RetryAfterSignal(::accept, socket, address, address_len);
	CHECK_OPEN_SOCKET(new_socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	return new_socket;
}

inline int bind(int socket, const struct sockaddr *address, socklen_t address_len) {
	CHECK_OPENED_SOCKET("during bind()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(EOPNOTSUPP, 0);

	return ::bind(socket, address, address_len);
}

inline int connect(int socket, const struct sockaddr* address, socklen_t address_len) {
	CHECK_OPENED_SOCKET("during connect()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	return RetryAfterSignal(::connect, socket, address, address_len);
}

inline int unchecked_fsync(int fd) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return RetryAfterSignal(__io_fsync, fd);
}

inline int fsync(int fd) {
	CHECK_OPENED("during fsync()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return io::unchecked_fsync(fd);
}

inline int unchecked_full_fsync(int fd) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

#ifdef F_FULLFSYNC
	return RetryAfterSignal(::fcntl, fd, F_FULLFSYNC, 0);
#else
	return RetryAfterSignal(__io_fsync, fd);
#endif
}

inline int full_fsync(int fd) {
	CHECK_OPENED("during full_fsync()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return io::unchecked_full_fsync(fd);
}

#ifdef HAVE_FALLOCATE
inline int fallocate(int fd, int mode, off_t offset, off_t len) {
	CHECK_OPENED("during fallocate()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return RetryAfterSignal(::fallocate, fd, mode, offset, len);
}
#else
int fallocate(int fd, int mode, off_t offset, off_t len);
#endif

#ifdef HAVE_POSIX_FADVISE
inline int fadvise(int fd, off_t offset, off_t len, int advice) {
	CHECK_OPENED("during fadvise()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::posix_fadvise(fd, offset, len, advice) == 0;
}
#else
#define POSIX_FADV_NORMAL     0
#define POSIX_FADV_SEQUENTIAL 1
#define POSIX_FADV_RANDOM     2
#define POSIX_FADV_WILLNEED   3
#define POSIX_FADV_DONTNEED   4
#define POSIX_FADV_NOREUSE    5

inline int fadvise([[maybe_unused]] int fd, off_t, off_t, int) {
	CHECK_OPENED("during fadvise()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return 0;
}
#endif

} /* namespace io */

#pragma clang diagnostic pop
<commit_msg>IO: EPIPE should not be ignored<commit_after>/*
 * Copyright (c) 2015-2019 Dubalu LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include "config.h"              // for HAVE_PWRITE, HAVE_FSYNC

#include <atomic>                // for std::atomic_bool
#include <errno.h>               // for errno, EINTR
#include <fcntl.h>               // for fchmod, open, O_RDONLY
#include <stddef.h>              // for size_t
#include <stdlib.h>              // for mkdtemp
#include <sys/stat.h>            // for fstat
#include <sys/socket.h>          // for send
#include <unistd.h>              // for off_t, ssize_t, close, lseek, unlink
#include <type_traits>           // for std::forward

#ifdef XAPIAND_RANDOM_ERRORS
#include "random.hh"                // for random_real
#include "opts.h"                   // for opts
#define RANDOM_ERRORS_IO_ERRNO_RETURN(errnum) \
	if (opts.random_errors_io) { \
		auto prob = random_real(0, 1); \
		if (prob < opts.random_errors_io) { \
			errno = errnum; \
			return -1; \
		} \
	}
#define RANDOM_ERRORS_NET_ERRNO_RETURN(errnum, sock) \
	if (opts.random_errors_net) { \
		auto prob = random_real(0, 1); \
		if (prob < opts.random_errors_net) { \
			if (sock) { \
				::shutdown(sock, SHUT_RDWR); \
			} \
			errno = errnum; \
			return -1; \
		} \
	}
#else
#define RANDOM_ERRORS_IO_ERRNO_RETURN(...)
#define RANDOM_ERRORS_NET_ERRNO_RETURN(...)
#endif

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-statement-expression"

// Do not accept any file descriptor less than this value, in order to avoid
// opening database file using file descriptors that are commonly used for
// standard input, output, and error.
#ifndef XAPIAND_MINIMUM_FILE_DESCRIPTOR
#define XAPIAND_MINIMUM_FILE_DESCRIPTOR STDERR_FILENO + 1
#endif

namespace io {

#ifdef XAPIAND_CHECK_IO_FDES
#define OPENED 1
#define SOCKET 2
#define CLOSED 4
int check(const char* msg, int fd, int check_set, int check_unset, int set, const char* function, const char* filename, int line);
#define CHECK_OPEN(fd) io::check("while opening as file", fd, 0, OPENED | CLOSED, OPENED, __func__, __FILE__, __LINE__)
#define CHECK_OPEN_SOCKET(fd) io::check("while opening as socket", fd, 0, OPENED | SOCKET | CLOSED, OPENED | SOCKET, __func__, __FILE__, __LINE__)
#define CHECK_CLOSING(fd) io::check("while closing", fd, OPENED, 0, 0, __func__, __FILE__, __LINE__)
#define CHECK_CLOSE(fd) io::check("while closing", fd, 0, CLOSED, CLOSED, __func__, __FILE__, __LINE__)
#define CHECK_OPENED(msg, fd) io::check(msg, fd, OPENED, CLOSED, 0, __func__, __FILE__, __LINE__)
#define CHECK_OPENED_SOCKET(msg, fd) io::check(msg, fd, OPENED | SOCKET, CLOSED, 0, __func__, __FILE__, __LINE__)
#else
#define CHECK_OPEN(fd)
#define CHECK_OPEN_SOCKET(fd)
#define CHECK_CLOSING(fd)
#define CHECK_CLOSE(fd)
#define CHECK_OPENED(msg, fd)
#define CHECK_OPENED_SOCKET(msg, fd)
#endif

#if defined HAVE_FDATASYNC
#define __io_fsync ::fdatasync
#elif defined HAVE_FSYNC
#define __io_fsync ::fsync
#else
inline int __noop(int /*unused*/) { return 0; }
#define __io_fsync io::__noop
#endif

std::atomic_bool& ignore_eintr();

inline bool ignored_errno(int e, bool again, bool tcp, bool udp) {
	switch(e) {
		case EINTR:
			return ignore_eintr().load();  //  Always ignore error
		case EAGAIN:
#if EAGAIN != EWOULDBLOCK
		case EWOULDBLOCK:
#endif
			return again;  //  Ignore not-available error
		case EINPROGRESS:
			return tcp;  //  Ignore error on TCP sockets

		case ENETDOWN:
		case EPROTO:
		case ENOPROTOOPT:
		case EHOSTDOWN:
#ifdef ENONET  // Linux-specific
		case ENONET:
#endif
		case EHOSTUNREACH:
		case EOPNOTSUPP:
		case ENETUNREACH:
		case ECONNRESET:
			return udp;  //  Ignore error on UDP sockets

		default:
			return false;  // Do not ignore error
	}
}

template <typename Fun, typename... Args>
inline auto RetryAfterSignal(const Fun &F, const Args &... As) -> decltype(F(As...)) {
	decltype(F(As...)) _err;
	do {
		errno = 0;
		_err = F(As...);
	} while (_err == -1 && errno == EINTR && ignore_eintr().load());
	return _err;
}

int open(const char* path, int oflag=O_RDONLY, int mode=0644);
int close(int fd);

ssize_t write(int fd, const void* buf, size_t nbyte);
ssize_t pwrite(int fd, const void* buf, size_t nbyte, off_t offset);

ssize_t read(int fd, void* buf, size_t nbyte);
ssize_t pread(int fd, void* buf, size_t nbyte, off_t offset);

inline int mkstemp(char* template_) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	int fd = ::mkstemp(template_);
	CHECK_OPEN(fd);
	return fd;
}

inline char* mkdtemp(char* template_) {
	return ::mkdtemp(template_);
}

inline int unlink(const char* path) {
	return ::unlink(path);
}

inline off_t lseek(int fd, off_t offset, int whence) {
	CHECK_OPENED("during lseek()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::lseek(fd, offset, whence);
}

template <typename... Args>
inline int unchecked_fcntl(int fd, int cmd, Args&&... args) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return RetryAfterSignal(::fcntl, fd, cmd, std::forward<Args>(args)...);
}

template <typename... Args>
inline int fcntl(int fd, int cmd, Args&&... args) {
	CHECK_OPENED("during fcntl()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return io::unchecked_fcntl(fd, cmd, std::forward<Args>(args)...);
}

inline int fstat(int fd, struct stat* buf) {
	CHECK_OPENED("during fstat()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::fstat(fd, buf);
}

inline int dup(int fd) {
	CHECK_OPENED("during dup()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::dup(fd);
}

inline int dup2(int fd, int fd2) {
	CHECK_OPENED("during dup2()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::dup2(fd, fd2);  // RetryAfterSignal?
}

inline int shutdown(int socket, int how) {
	CHECK_OPENED_SOCKET("during shutdown()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, 0);

	return ::shutdown(socket, how);
}

inline ssize_t send(int socket, const void* buffer, size_t length, int flags) {
	CHECK_OPENED_SOCKET("during send()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::send, socket, buffer, length, flags);
}

inline ssize_t sendto(int socket, const void* buffer, size_t length, int flags, const struct sockaddr* dest_addr, socklen_t dest_len) {
	CHECK_OPENED_SOCKET("during sendto()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::sendto, socket, buffer, length, flags, dest_addr, dest_len);
}

inline ssize_t recv(int socket, void* buffer, size_t length, int flags) {
	CHECK_OPENED_SOCKET("during recv()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::recv, socket, buffer, length, flags);
}

inline ssize_t recvfrom(int socket, void* buffer, size_t length, int flags, struct sockaddr* address, socklen_t* address_len) {
	CHECK_OPENED_SOCKET("during recvfrom()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ECONNABORTED, socket);

	return RetryAfterSignal(::recvfrom, socket, buffer, length, flags, address, address_len);
}

inline int socket(int domain, int type, int protocol) {
	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	int socket = ::socket(domain, type, protocol);
	CHECK_OPEN_SOCKET(socket);
	return socket;
}

inline int getsockopt(int socket, int level, int option_name, void* option_value, socklen_t* option_len){
	CHECK_OPENED_SOCKET("during getsockopt()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(EOPNOTSUPP, 0);

	return ::getsockopt(socket, level, option_name, option_value, option_len);
}

inline int setsockopt(int socket, int level, int option_name, const void* option_value, socklen_t option_len){
	CHECK_OPENED_SOCKET("during setsockopt()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(EOPNOTSUPP, 0);

	return ::setsockopt(socket, level, option_name, option_value, option_len);
}

inline int listen(int socket, int backlog) {
	CHECK_OPENED_SOCKET("during listen()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	return ::listen(socket, backlog);
}

inline int accept(int socket, struct sockaddr* address, socklen_t* address_len) {
	CHECK_OPENED_SOCKET("during accept()", socket);
	int new_socket = RetryAfterSignal(::accept, socket, address, address_len);
	CHECK_OPEN_SOCKET(new_socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	return new_socket;
}

inline int bind(int socket, const struct sockaddr *address, socklen_t address_len) {
	CHECK_OPENED_SOCKET("during bind()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(EOPNOTSUPP, 0);

	return ::bind(socket, address, address_len);
}

inline int connect(int socket, const struct sockaddr* address, socklen_t address_len) {
	CHECK_OPENED_SOCKET("during connect()", socket);

	RANDOM_ERRORS_NET_ERRNO_RETURN(ENETDOWN, 0);

	return RetryAfterSignal(::connect, socket, address, address_len);
}

inline int unchecked_fsync(int fd) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return RetryAfterSignal(__io_fsync, fd);
}

inline int fsync(int fd) {
	CHECK_OPENED("during fsync()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return io::unchecked_fsync(fd);
}

inline int unchecked_full_fsync(int fd) {
	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

#ifdef F_FULLFSYNC
	return RetryAfterSignal(::fcntl, fd, F_FULLFSYNC, 0);
#else
	return RetryAfterSignal(__io_fsync, fd);
#endif
}

inline int full_fsync(int fd) {
	CHECK_OPENED("during full_fsync()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return io::unchecked_full_fsync(fd);
}

#ifdef HAVE_FALLOCATE
inline int fallocate(int fd, int mode, off_t offset, off_t len) {
	CHECK_OPENED("during fallocate()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return RetryAfterSignal(::fallocate, fd, mode, offset, len);
}
#else
int fallocate(int fd, int mode, off_t offset, off_t len);
#endif

#ifdef HAVE_POSIX_FADVISE
inline int fadvise(int fd, off_t offset, off_t len, int advice) {
	CHECK_OPENED("during fadvise()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return ::posix_fadvise(fd, offset, len, advice) == 0;
}
#else
#define POSIX_FADV_NORMAL     0
#define POSIX_FADV_SEQUENTIAL 1
#define POSIX_FADV_RANDOM     2
#define POSIX_FADV_WILLNEED   3
#define POSIX_FADV_DONTNEED   4
#define POSIX_FADV_NOREUSE    5

inline int fadvise([[maybe_unused]] int fd, off_t, off_t, int) {
	CHECK_OPENED("during fadvise()", fd);

	RANDOM_ERRORS_IO_ERRNO_RETURN(EIO);

	return 0;
}
#endif

} /* namespace io */

#pragma clang diagnostic pop
<|endoftext|>


<commit_before>#include "vm.hpp"
#include "vm/object_utils.hpp"
#include "objectmemory.hpp"

#include "builtin/class.hpp"
#include "builtin/module.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/lookuptable.hpp"
#include "builtin/methodtable.hpp"
#include "builtin/symbol.hpp"
#include "builtin/string.hpp"
#include "builtin/system.hpp"
#include "builtin/array.hpp"
#include "builtin/compactlookuptable.hpp"

#include "global_cache.hpp"

namespace rubinius {

  void Module::bootstrap_methods(STATE) {
    System::attach_primitive(state,
                             G(module), false,
                             state->symbol("const_set"),
                             state->symbol("module_const_set"));
  }

  Module* Module::create(STATE) {
    Module* mod = state->om->new_object_enduring<Module>(G(module));

    mod->name(state, (Symbol*)Qnil);
    mod->superclass(state, (Module*)Qnil);

    mod->setup(state);

    return mod;
  }

  Module* Module::allocate(STATE, Object* self) {
    Module* module = Module::create(state);

    module->klass(state, (Class*)self);

    return module;
  }

  void Module::setup(STATE) {
    constants(state, LookupTable::create(state));
    method_table(state, MethodTable::create(state));
  }

  void Module::setup(STATE, const char* str, Module* under) {
    setup(state, state->symbol(str), under);

    if(under && under != G(object)) {
      std::ostringstream ss;
      ss << under->name()->c_str(state) << "::" << str;
      this->name(state, state->symbol(ss.str().c_str()));
    }
  }

  void Module::setup(STATE, Symbol* name, Module* under) {
    if(!under) under = G(object);

    this->constants(state, LookupTable::create(state));
    this->method_table(state, MethodTable::create(state));
    this->name(state, name);
    under->set_const(state, name, this);
  }

  Object* Module::case_compare(STATE, Object* obj) {
    return obj->kind_of_p(state, this) ? Qtrue : Qfalse;
  }

  void Module::set_name(STATE, Module* under, Symbol* name) {
    if(under == G(object)) {
      this->name(state, name);
    } else {
      String* cur = under->name()->to_str(state);
      String* str_name = cur->add(state, "::")->append(state,
          name->to_str(state));

      this->name(state, str_name->to_sym(state));
    }
  }

  Object* Module::const_set(STATE, Object* name, Object* value) {
    set_const(state, name, value);
    return value;
  }

  void Module::set_const(STATE, Object* sym, Object* val) {
    constants_->store(state, sym, val);
    state->shared.inc_global_serial();
  }

  void Module::del_const(STATE, Symbol* sym) {
    constants_->remove(state, sym);
    state->shared.inc_global_serial();
  }

  void Module::set_const(STATE, const char* name, Object* val) {
    set_const(state, state->symbol(name), val);
  }

  Object* Module::get_const(STATE, Symbol* sym, bool* found, bool check_super) {
    Module* mod = this;

    while(!mod->nil_p()) {
      Object* obj = mod->constants()->fetch(state, sym, found);

      if(*found) return obj;

      if(!check_super) break;

      mod = mod->superclass();
    }

    return Qnil;
  }

  Object* Module::get_const(STATE, Symbol* sym) {
    bool found;
    return get_const(state, sym, &found);
  }

  Object* Module::get_const(STATE, const char* sym) {
    return get_const(state, state->symbol(sym));
  }

  void Module::add_method(STATE, Symbol* name, Executable* exec, Symbol* vis) {
    if(!vis) vis = G(sym_public);
    method_table_->store(state, name, exec, vis);
    state->global_cache()->clear(this, name);
  }

  Executable* Module::find_method(Symbol* name, Module** defined_in) {
    MethodTableBucket* buk;
    Module* mod = this;

    do {
      buk = mod->method_table()->find_entry(name);
      if(buk) {
        if(defined_in) *defined_in = mod;
        return buk->method();
      }
      mod = mod->superclass();
    } while(!mod->nil_p());

    return NULL;
  }

  Array* Module::class_variables(STATE) {
    Array* ary = Array::create(state, 2);
    Module* mod = this;
    Module* mod_to_query;

    class cvar_match : public ObjectMatcher {
    public:
      virtual bool match_p(STATE, Object* obj) {
        if(Symbol* sym = try_as<Symbol>(obj)) {
          return sym->is_cvar_p(state)->true_p();
        }

        return false;
      };
    } match;

    while(!mod->nil_p()) {
      if(MetaClass* mc = try_as<MetaClass>(mod)) {
        mod_to_query = as<Module>(mc->attached_instance());
      } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
        mod_to_query = im->module();
      } else {
        mod_to_query = mod;
      }

      Object* ivars = mod_to_query->ivars();

      if(CompactLookupTable* tbl = try_as<CompactLookupTable>(ivars)) {
        ary->concat(state, tbl->filtered_keys(state, match));
      } else if(LookupTable* tbl = try_as<LookupTable>(ivars)) {
        ary->concat(state, tbl->filtered_keys(state, match));
      }

      mod = mod->superclass();
    }

    return ary;
  }

  Object* Module::cvar_defined(STATE, Symbol* name) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      if(MetaClass* mc = try_as<MetaClass>(mod)) {
        mod_to_query = as<Module>(mc->attached_instance());
      } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
        mod_to_query = im->module();
      } else {
        mod_to_query = mod;
      }

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) return Qtrue;

      mod = mod->superclass();
    }

    return Qfalse;
  }

  Object* Module::cvar_get(STATE, Symbol* name) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      if(MetaClass* mc = try_as<MetaClass>(mod)) {
        mod_to_query = as<Module>(mc->attached_instance());
      } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
        mod_to_query = im->module();
      } else {
        mod_to_query = mod;
      }

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) {
        return mod_to_query->get_table_ivar(state, name);
      }

      mod = mod->superclass();
    }

    mod = this;
    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod = as<Module>(mc->attached_instance());
    }

    std::stringstream ss;
    ss << "uninitialized class variable ";
    ss << name->c_str(state);
    ss << " in module ";
    if(mod->name()->nil_p()) {
      if(kind_of<Class>(mod)) {
        ss << "#<Class>";
      } else {
        ss << "#<Module>";
      }
    } else {
      ss << mod->name()->c_str(state);
    }

    RubyException::raise(
        Exception::make_exception(state,
          as<Class>(G(object)->get_const(state, "NameError")),
          ss.str().c_str()));

    return NULL;
  }

  Object* Module::cvar_set(STATE, Symbol* name, Object* value) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    if(RTEST(frozen_p(state))) {
      Exception::type_error(state, "unable to change frozen object");
    }

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      if(MetaClass* mc = try_as<MetaClass>(mod)) {
        mod_to_query = as<Module>(mc->attached_instance());
      } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
        mod_to_query = im->module();
      } else {
        mod_to_query = mod;
      }

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) {
        mod_to_query->set_table_ivar(state, name, value);
        return value;
      }

      mod = mod->superclass();
    }

    mod = this;
    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod_to_query = as<Module>(mc->attached_instance());
    } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
      mod_to_query = im->module();
    } else {
      mod_to_query = mod;
    }

    mod_to_query->set_ivar(state, name, value);
    return value;
  }

  Object* Module::cvar_get_or_set(STATE, Symbol* name, Object* value) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      if(MetaClass* mc = try_as<MetaClass>(mod)) {
        mod_to_query = as<Module>(mc->attached_instance());
      } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
        mod_to_query = im->module();
      } else {
        mod_to_query = mod;
      }

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) {
        return mod_to_query->get_table_ivar(state, name);
      }

      mod = mod->superclass();
    }

    mod = this;
    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod_to_query = as<Module>(mc->attached_instance());
    } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
      mod_to_query = im->module();
    } else {
      mod_to_query = mod;
    }

    mod_to_query->set_table_ivar(state, name, value);
    return value;
  }

  Object* Module::cvar_remove(STATE, Symbol* name) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod_to_query = as<Module>(mc->attached_instance());
    } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
      mod_to_query = im->module();
    } else {
      mod_to_query = mod;
    }

    bool removed = false;
    Object* value = mod_to_query->del_table_ivar(state, name, &removed);
    if(removed) return value;

    std::stringstream ss;
    mod = this;
    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod = as<Module>(mc->attached_instance());
    }

    if (this->cvar_defined(state, name) == Qtrue) {
      ss << "cannot remove ";
      ss << name->c_str(state);
      ss << " for ";
    } else {
      ss << "uninitialized class variable ";
      ss << name->c_str(state);
      ss << " in module ";
    }

    if(mod->name()->nil_p()) {
      if(kind_of<Class>(mod)) {
        ss << "#<Class>";
      } else {
        ss << "#<Module>";
      }
    } else {
      ss << mod->name()->c_str(state);
    }

    RubyException::raise(
        Exception::make_exception(state,
          as<Class>(G(object)->get_const(state, "NameError")),
          ss.str().c_str()));

    return NULL;
  }

  void Module::Info::show(STATE, Object* self, int level) {
    Module* mod = as<Module>(self);

    class_header(state, self);
    indent_attribute(++level, "name"); mod->name()->show(state, level);
    indent_attribute(level, "superclass"); class_info(state, mod->superclass(), true);
    indent_attribute(level, "constants"); mod->constants()->show(state, level);
    indent_attribute(level, "method_table"); mod->method_table()->show(state, level);
    close_body(level);
  }

  IncludedModule* IncludedModule::create(STATE) {
    IncludedModule* imod;
    imod = state->om->new_object_enduring<IncludedModule>(G(included_module));

    imod->name(state, state->symbol("<included module>"));
    imod->superclass(state, (Module*)Qnil);

    return imod;
  }

  IncludedModule* IncludedModule::allocate(STATE, Object* self) {
    IncludedModule* imod = IncludedModule::create(state);

    imod->klass(state, (Class*)self);

    return imod;
  }

}
<commit_msg>Fixed class variable with metaclass. Closes #411.<commit_after>#include "vm.hpp"
#include "vm/object_utils.hpp"
#include "objectmemory.hpp"

#include "builtin/class.hpp"
#include "builtin/module.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/lookuptable.hpp"
#include "builtin/methodtable.hpp"
#include "builtin/symbol.hpp"
#include "builtin/string.hpp"
#include "builtin/system.hpp"
#include "builtin/array.hpp"
#include "builtin/compactlookuptable.hpp"

#include "global_cache.hpp"

namespace rubinius {

  void Module::bootstrap_methods(STATE) {
    System::attach_primitive(state,
                             G(module), false,
                             state->symbol("const_set"),
                             state->symbol("module_const_set"));
  }

  Module* Module::create(STATE) {
    Module* mod = state->om->new_object_enduring<Module>(G(module));

    mod->name(state, (Symbol*)Qnil);
    mod->superclass(state, (Module*)Qnil);

    mod->setup(state);

    return mod;
  }

  Module* Module::allocate(STATE, Object* self) {
    Module* module = Module::create(state);

    module->klass(state, (Class*)self);

    return module;
  }

  void Module::setup(STATE) {
    constants(state, LookupTable::create(state));
    method_table(state, MethodTable::create(state));
  }

  void Module::setup(STATE, const char* str, Module* under) {
    setup(state, state->symbol(str), under);

    if(under && under != G(object)) {
      std::ostringstream ss;
      ss << under->name()->c_str(state) << "::" << str;
      this->name(state, state->symbol(ss.str().c_str()));
    }
  }

  void Module::setup(STATE, Symbol* name, Module* under) {
    if(!under) under = G(object);

    this->constants(state, LookupTable::create(state));
    this->method_table(state, MethodTable::create(state));
    this->name(state, name);
    under->set_const(state, name, this);
  }

  Object* Module::case_compare(STATE, Object* obj) {
    return obj->kind_of_p(state, this) ? Qtrue : Qfalse;
  }

  void Module::set_name(STATE, Module* under, Symbol* name) {
    if(under == G(object)) {
      this->name(state, name);
    } else {
      String* cur = under->name()->to_str(state);
      String* str_name = cur->add(state, "::")->append(state,
          name->to_str(state));

      this->name(state, str_name->to_sym(state));
    }
  }

  Object* Module::const_set(STATE, Object* name, Object* value) {
    set_const(state, name, value);
    return value;
  }

  void Module::set_const(STATE, Object* sym, Object* val) {
    constants_->store(state, sym, val);
    state->shared.inc_global_serial();
  }

  void Module::del_const(STATE, Symbol* sym) {
    constants_->remove(state, sym);
    state->shared.inc_global_serial();
  }

  void Module::set_const(STATE, const char* name, Object* val) {
    set_const(state, state->symbol(name), val);
  }

  Object* Module::get_const(STATE, Symbol* sym, bool* found, bool check_super) {
    Module* mod = this;

    while(!mod->nil_p()) {
      Object* obj = mod->constants()->fetch(state, sym, found);

      if(*found) return obj;

      if(!check_super) break;

      mod = mod->superclass();
    }

    return Qnil;
  }

  Object* Module::get_const(STATE, Symbol* sym) {
    bool found;
    return get_const(state, sym, &found);
  }

  Object* Module::get_const(STATE, const char* sym) {
    return get_const(state, state->symbol(sym));
  }

  void Module::add_method(STATE, Symbol* name, Executable* exec, Symbol* vis) {
    if(!vis) vis = G(sym_public);
    method_table_->store(state, name, exec, vis);
    state->global_cache()->clear(this, name);
  }

  Executable* Module::find_method(Symbol* name, Module** defined_in) {
    MethodTableBucket* buk;
    Module* mod = this;

    do {
      buk = mod->method_table()->find_entry(name);
      if(buk) {
        if(defined_in) *defined_in = mod;
        return buk->method();
      }
      mod = mod->superclass();
    } while(!mod->nil_p());

    return NULL;
  }

  static Module* get_module_to_query(Module* mod) {
    Module* mod_to_query = 0;

    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod_to_query = try_as<Module>(mc->attached_instance());
      if(!mod_to_query) mod_to_query = mod;
    } else if(IncludedModule* im = try_as<IncludedModule>(mod)) {
      mod_to_query = im->module();
    } else {
      mod_to_query = mod;
    }

    return mod_to_query;
  }

  Array* Module::class_variables(STATE) {
    Array* ary = Array::create(state, 2);
    Module* mod = this;
    Module* mod_to_query;

    class cvar_match : public ObjectMatcher {
    public:
      virtual bool match_p(STATE, Object* obj) {
        if(Symbol* sym = try_as<Symbol>(obj)) {
          return sym->is_cvar_p(state)->true_p();
        }

        return false;
      };
    } match;

    while(!mod->nil_p()) {
      mod_to_query = get_module_to_query(mod);

      Object* ivars = mod_to_query->ivars();

      if(CompactLookupTable* tbl = try_as<CompactLookupTable>(ivars)) {
        ary->concat(state, tbl->filtered_keys(state, match));
      } else if(LookupTable* tbl = try_as<LookupTable>(ivars)) {
        ary->concat(state, tbl->filtered_keys(state, match));
      }

      mod = mod->superclass();
    }

    return ary;
  }

  Object* Module::cvar_defined(STATE, Symbol* name) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      mod_to_query = get_module_to_query(mod);

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) return Qtrue;

      mod = mod->superclass();
    }

    return Qfalse;
  }

  Object* Module::cvar_get(STATE, Symbol* name) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      mod_to_query = get_module_to_query(mod);

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) {
        return mod_to_query->get_table_ivar(state, name);
      }

      mod = mod->superclass();
    }

    mod = this;
    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod = as<Module>(mc->attached_instance());
    }

    std::stringstream ss;
    ss << "uninitialized class variable ";
    ss << name->c_str(state);
    ss << " in module ";
    if(mod->name()->nil_p()) {
      if(kind_of<Class>(mod)) {
        ss << "#<Class>";
      } else {
        ss << "#<Module>";
      }
    } else {
      ss << mod->name()->c_str(state);
    }

    RubyException::raise(
        Exception::make_exception(state,
          as<Class>(G(object)->get_const(state, "NameError")),
          ss.str().c_str()));

    return NULL;
  }

  Object* Module::cvar_set(STATE, Symbol* name, Object* value) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    if(RTEST(frozen_p(state))) {
      Exception::type_error(state, "unable to change frozen object");
    }

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      mod_to_query = get_module_to_query(mod);

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) {
        mod_to_query->set_table_ivar(state, name, value);
        return value;
      }

      mod = mod->superclass();
    }

    mod = this;
    mod_to_query = get_module_to_query(mod);

    mod_to_query->set_ivar(state, name, value);
    return value;
  }

  Object* Module::cvar_get_or_set(STATE, Symbol* name, Object* value) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    while(!mod->nil_p()) {
      mod_to_query = get_module_to_query(mod);

      if(mod_to_query->table_ivar_defined(state, name)->true_p()) {
        return mod_to_query->get_table_ivar(state, name);
      }

      mod = mod->superclass();
    }

    mod = this;
    mod_to_query = get_module_to_query(mod);

    mod_to_query->set_table_ivar(state, name, value);
    return value;
  }

  Object* Module::cvar_remove(STATE, Symbol* name) {
    if(!name->is_cvar_p(state)->true_p()) return Primitives::failure();

    Module* mod = this;
    Module* mod_to_query;

    mod_to_query = get_module_to_query(mod);

    bool removed = false;
    Object* value = mod_to_query->del_table_ivar(state, name, &removed);
    if(removed) return value;

    std::stringstream ss;
    mod = this;
    if(MetaClass* mc = try_as<MetaClass>(mod)) {
      mod = as<Module>(mc->attached_instance());
    }

    if (this->cvar_defined(state, name) == Qtrue) {
      ss << "cannot remove ";
      ss << name->c_str(state);
      ss << " for ";
    } else {
      ss << "uninitialized class variable ";
      ss << name->c_str(state);
      ss << " in module ";
    }

    if(mod->name()->nil_p()) {
      if(kind_of<Class>(mod)) {
        ss << "#<Class>";
      } else {
        ss << "#<Module>";
      }
    } else {
      ss << mod->name()->c_str(state);
    }

    RubyException::raise(
        Exception::make_exception(state,
          as<Class>(G(object)->get_const(state, "NameError")),
          ss.str().c_str()));

    return NULL;
  }

  void Module::Info::show(STATE, Object* self, int level) {
    Module* mod = as<Module>(self);

    class_header(state, self);
    indent_attribute(++level, "name"); mod->name()->show(state, level);
    indent_attribute(level, "superclass"); class_info(state, mod->superclass(), true);
    indent_attribute(level, "constants"); mod->constants()->show(state, level);
    indent_attribute(level, "method_table"); mod->method_table()->show(state, level);
    close_body(level);
  }

  IncludedModule* IncludedModule::create(STATE) {
    IncludedModule* imod;
    imod = state->om->new_object_enduring<IncludedModule>(G(included_module));

    imod->name(state, state->symbol("<included module>"));
    imod->superclass(state, (Module*)Qnil);

    return imod;
  }

  IncludedModule* IncludedModule::allocate(STATE, Object* self) {
    IncludedModule* imod = IncludedModule::create(state);

    imod->klass(state, (Class*)self);

    return imod;
  }

}
<|endoftext|>


<commit_before>// Copyright 2004-present Facebook. All Rights Reserved.

#include <string>
#include <iomanip>

#include <stdlib.h>

#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>

#include <boost/algorithm/string/trim.hpp>

#include <glog/logging.h>

#include <osquery/core.h>
#include <osquery/tables.h>
#include "osquery/tables/networking/utils.h"

namespace osquery {
namespace tables {

#define MAX_NETLINK_SIZE 8192

std::string getNetlinkIP(int family, const char* buffer) {
  char dst[INET6_ADDRSTRLEN];
  memset(dst, 0, INET6_ADDRSTRLEN);

  inet_ntop(family, buffer, dst, INET6_ADDRSTRLEN);
  std::string address(dst);
  boost::trim(address);

  return address;
}

Status readNetlink(int socket_fd, int seq, char* output, size_t* size) {
  struct nlmsghdr* nl_hdr;

  size_t message_size = 0;
  do {
    int bytes = 0;
    while (bytes == 0) {
      bytes = recv(socket_fd, output, MAX_NETLINK_SIZE - message_size, 0);
      if (bytes < 0) {
        return Status(1, "Could not read from NETLINK.");
      } else if (bytes == 0) {
        ::usleep(20);
      }
    }

    // Assure valid header response, and not an error type.
    nl_hdr = (struct nlmsghdr*)output;
    if (NLMSG_OK(nl_hdr, bytes) == 0 || nl_hdr->nlmsg_type == NLMSG_ERROR) {
      return Status(1, "Read invalid NETLINK message.");
    }

    if (nl_hdr->nlmsg_type == NLMSG_DONE) {
      break;
    }

    // Move the buffer pointer
    output += bytes;
    message_size += bytes;
    if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) {
      break;
    }
  } while (nl_hdr->nlmsg_seq != seq || nl_hdr->nlmsg_pid != getpid());

  *size = message_size;
  return Status(0, "OK");
}

void genNetlinkRoutes(const struct nlmsghdr* netlink_msg, QueryData& results) {
  std::string address;
  int mask = 0;
  char interface[IF_NAMESIZE];

  struct rtmsg* message = (struct rtmsg*)NLMSG_DATA(netlink_msg);
  struct rtattr* attr = (struct rtattr*)RTM_RTA(message);
  int attr_size = RTM_PAYLOAD(netlink_msg);

  Row r;

  // Iterate over each route in the netlink message
  bool has_destination = false;
  r["metric"] = "0";
  while (RTA_OK(attr, attr_size)) {
    switch (attr->rta_type) {
    case RTA_OIF:
      if_indextoname(*(int*)RTA_DATA(attr), interface);
      r["interface"] = std::string(interface);
      break;
    case RTA_GATEWAY:
      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
      r["gateway"] = address;
      break;
    case RTA_PREFSRC:
      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
      r["source"] = address;
      break;
    case RTA_DST:
      if (message->rtm_dst_len != 32 && message->rtm_dst_len != 128) {
        mask = (int)message->rtm_dst_len;
      }
      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
      r["destination"] = address;
      has_destination = true;
      break;
    case RTA_PRIORITY:
      r["metric"] = INTEGER(*(int*)RTA_DATA(attr));
      break;
    }
    attr = RTA_NEXT(attr, attr_size);
  }

  if (!has_destination) {
    r["destination"] = "0.0.0.0";
    if (message->rtm_dst_len) {
      mask = (int)message->rtm_dst_len;
    }
  }

  // Route type determination
  if (message->rtm_type == RTN_UNICAST) {
    r["type"] = "gateway";
  } else if (message->rtm_type == RTN_LOCAL) {
    r["type"] = "local";
  } else if (message->rtm_type == RTN_BROADCAST) {
    r["type"] = "broadcast";
  } else if (message->rtm_type == RTN_ANYCAST) {
    r["type"] = "anycast";
  } else {
    r["type"] = "other";
  }

  r["flags"] = INTEGER(message->rtm_flags);

  // This is the cidr-formatted mask
  r["netmask"] = INTEGER(mask);

  // Fields not supported by Linux routes:
  r["mtu"] = "0";
  results.push_back(r);
}

QueryData genRoutes(QueryContext& context) {
  QueryData results;

  int socket_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
  if (socket_fd < 0) {
    LOG(ERROR) << "Cannot open NETLINK socket.";
    return results;
  }

  // Create netlink message header
  void* netlink_buffer = malloc(MAX_NETLINK_SIZE);
  struct nlmsghdr* netlink_msg = (struct nlmsghdr*)netlink_buffer;
  if (netlink_msg == nullptr) {
    close(socket_fd);
    return results;
  }

  netlink_msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  netlink_msg->nlmsg_type = RTM_GETROUTE; // routes from kernel routing table
  netlink_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
  netlink_msg->nlmsg_seq = 0;
  netlink_msg->nlmsg_pid = getpid();

  // Send the netlink request to the kernel
  if (send(socket_fd, netlink_msg, netlink_msg->nlmsg_len, 0) < 0) {
    LOG(ERROR) << "Cannot write NETLINK request header to socket.";
    goto cleanup;
  }

  // Wrap the read socket to support multi-netlink messages
  size_t size;
  if (!readNetlink(socket_fd, 1, (char*)netlink_msg, &size).ok()) {
    LOG(ERROR) << "Cannot read NETLINK response from socket.";
    goto cleanup;
  }

  // Treat the netlink response as route information
  while (NLMSG_OK(netlink_msg, size)) {
    genNetlinkRoutes(netlink_msg, results);
    netlink_msg = NLMSG_NEXT(netlink_msg, size);
  }

cleanup:
  close(socket_fd);
  free(netlink_buffer);

  return results;
}
}
}
<commit_msg>Add timeout to netlink socket read<commit_after>// Copyright 2004-present Facebook. All Rights Reserved.

#include <string>
#include <iomanip>

#include <stdlib.h>

#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>

#include <boost/algorithm/string/trim.hpp>

#include <glog/logging.h>

#include <osquery/core.h>
#include <osquery/tables.h>
#include "osquery/tables/networking/utils.h"

namespace osquery {
namespace tables {

#define MAX_NETLINK_SIZE 8192
#define MAX_NETLINK_LATENCY 2000

std::string getNetlinkIP(int family, const char* buffer) {
  char dst[INET6_ADDRSTRLEN];
  memset(dst, 0, INET6_ADDRSTRLEN);

  inet_ntop(family, buffer, dst, INET6_ADDRSTRLEN);
  std::string address(dst);
  boost::trim(address);

  return address;
}

Status readNetlink(int socket_fd, int seq, char* output, size_t* size) {
  struct nlmsghdr* nl_hdr;

  size_t message_size = 0;
  do {
    int latency = 0;
    int bytes = 0;
    while (bytes == 0) {
      bytes = recv(socket_fd, output, MAX_NETLINK_SIZE - message_size, 0);
      if (bytes < 0) {
        return Status(1, "Could not read from NETLINK.");
      } else if (latency >= MAX_NETLINK_LATENCY) {
        return Status(1, "Netlink timeout.");
      } else if (bytes == 0) {
        ::usleep(20);
        latency += 20;
      }
    }

    // Assure valid header response, and not an error type.
    nl_hdr = (struct nlmsghdr*)output;
    if (NLMSG_OK(nl_hdr, bytes) == 0 || nl_hdr->nlmsg_type == NLMSG_ERROR) {
      return Status(1, "Read invalid NETLINK message.");
    }

    if (nl_hdr->nlmsg_type == NLMSG_DONE) {
      break;
    }

    // Move the buffer pointer
    output += bytes;
    message_size += bytes;
    if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) {
      break;
    }
  } while (nl_hdr->nlmsg_seq != seq || nl_hdr->nlmsg_pid != getpid());

  *size = message_size;
  return Status(0, "OK");
}

void genNetlinkRoutes(const struct nlmsghdr* netlink_msg, QueryData& results) {
  std::string address;
  int mask = 0;
  char interface[IF_NAMESIZE];

  struct rtmsg* message = (struct rtmsg*)NLMSG_DATA(netlink_msg);
  struct rtattr* attr = (struct rtattr*)RTM_RTA(message);
  int attr_size = RTM_PAYLOAD(netlink_msg);

  Row r;

  // Iterate over each route in the netlink message
  bool has_destination = false;
  r["metric"] = "0";
  while (RTA_OK(attr, attr_size)) {
    switch (attr->rta_type) {
    case RTA_OIF:
      if_indextoname(*(int*)RTA_DATA(attr), interface);
      r["interface"] = std::string(interface);
      break;
    case RTA_GATEWAY:
      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
      r["gateway"] = address;
      break;
    case RTA_PREFSRC:
      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
      r["source"] = address;
      break;
    case RTA_DST:
      if (message->rtm_dst_len != 32 && message->rtm_dst_len != 128) {
        mask = (int)message->rtm_dst_len;
      }
      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
      r["destination"] = address;
      has_destination = true;
      break;
    case RTA_PRIORITY:
      r["metric"] = INTEGER(*(int*)RTA_DATA(attr));
      break;
    }
    attr = RTA_NEXT(attr, attr_size);
  }

  if (!has_destination) {
    r["destination"] = "0.0.0.0";
    if (message->rtm_dst_len) {
      mask = (int)message->rtm_dst_len;
    }
  }

  // Route type determination
  if (message->rtm_type == RTN_UNICAST) {
    r["type"] = "gateway";
  } else if (message->rtm_type == RTN_LOCAL) {
    r["type"] = "local";
  } else if (message->rtm_type == RTN_BROADCAST) {
    r["type"] = "broadcast";
  } else if (message->rtm_type == RTN_ANYCAST) {
    r["type"] = "anycast";
  } else {
    r["type"] = "other";
  }

  r["flags"] = INTEGER(message->rtm_flags);

  // This is the cidr-formatted mask
  r["netmask"] = INTEGER(mask);

  // Fields not supported by Linux routes:
  r["mtu"] = "0";
  results.push_back(r);
}

QueryData genRoutes(QueryContext& context) {
  QueryData results;

  int socket_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
  if (socket_fd < 0) {
    LOG(ERROR) << "Cannot open NETLINK socket.";
    return results;
  }

  // Create netlink message header
  void* netlink_buffer = malloc(MAX_NETLINK_SIZE);
  struct nlmsghdr* netlink_msg = (struct nlmsghdr*)netlink_buffer;
  if (netlink_msg == nullptr) {
    close(socket_fd);
    return results;
  }

  netlink_msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  netlink_msg->nlmsg_type = RTM_GETROUTE; // routes from kernel routing table
  netlink_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
  netlink_msg->nlmsg_seq = 0;
  netlink_msg->nlmsg_pid = getpid();

  // Send the netlink request to the kernel
  if (send(socket_fd, netlink_msg, netlink_msg->nlmsg_len, 0) < 0) {
    LOG(ERROR) << "Cannot write NETLINK request header to socket.";
    goto cleanup;
  }

  // Wrap the read socket to support multi-netlink messages
  size_t size;
  if (!readNetlink(socket_fd, 1, (char*)netlink_msg, &size).ok()) {
    LOG(ERROR) << "Cannot read NETLINK response from socket.";
    goto cleanup;
  }

  // Treat the netlink response as route information
  while (NLMSG_OK(netlink_msg, size)) {
    genNetlinkRoutes(netlink_msg, results);
    netlink_msg = NLMSG_NEXT(netlink_msg, size);
  }

cleanup:
  close(socket_fd);
  free(netlink_buffer);

  return results;
}
}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/atom_resource_dispatcher_host_delegate.h"

#include "atom/browser/login_handler.h"
#include "atom/browser/web_contents_permission_helper.h"
#include "atom/browser/web_contents_preferences.h"
#include "atom/common/atom_constants.h"
#include "atom/common/platform_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/stream_info.h"
#include "net/base/escape.h"
#include "net/ssl/client_cert_store.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"

#if defined(USE_NSS_CERTS)
#include "net/ssl/client_cert_store_nss.h"
#elif defined(OS_WIN)
#include "net/ssl/client_cert_store_win.h"
#elif defined(OS_MACOSX)
#include "net/ssl/client_cert_store_mac.h"
#endif

using content::BrowserThread;

namespace atom {

namespace {

void OnOpenExternal(const GURL& escaped_url,
                    bool allowed) {
  if (allowed)
    platform_util::OpenExternal(
#if defined(OS_WIN)
        base::UTF8ToUTF16(escaped_url.spec()),
#else
        escaped_url,
#endif
        true);
}

void HandleExternalProtocolInUI(
    const GURL& url,
    const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
    bool has_user_gesture) {
  content::WebContents* web_contents = web_contents_getter.Run();
  if (!web_contents)
    return;

  auto permission_helper =
      WebContentsPermissionHelper::FromWebContents(web_contents);
  if (!permission_helper)
    return;

  GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
  auto callback = base::Bind(&OnOpenExternal, escaped_url);
  permission_helper->RequestOpenExternalPermission(callback, has_user_gesture);
}

void OnPdfResourceIntercepted(
    const GURL& original_url,
    const content::ResourceRequestInfo::WebContentsGetter&
        web_contents_getter) {
  content::WebContents* web_contents = web_contents_getter.Run();
  if (!web_contents)
    return;

  // The URL passes the original pdf resource url, that will be requested
  // by the webui page.
  // chrome://pdf-viewer/index.html?src=https://somepage/123.pdf
  content::NavigationController::LoadURLParams params(
      GURL(base::StringPrintf("%sindex.html?%s=%s", kPdfViewerUIOrigin,
                              kPdfPluginSrc, original_url.spec().c_str())));
  web_contents->GetController().LoadURLWithParams(params);
}

}  // namespace

AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {
}

bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
    const GURL& url,
    content::ResourceRequestInfo* info) {
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                          base::Bind(&HandleExternalProtocolInUI,
                                     url,
                                     info->GetWebContentsGetterForRequest(),
                                     info->HasUserGesture()));
  return true;
}

content::ResourceDispatcherHostLoginDelegate*
AtomResourceDispatcherHostDelegate::CreateLoginDelegate(
    net::AuthChallengeInfo* auth_info,
    net::URLRequest* request) {
  return new LoginHandler(auth_info, request);
}

std::unique_ptr<net::ClientCertStore>
AtomResourceDispatcherHostDelegate::CreateClientCertStore(
    content::ResourceContext* resource_context) {
  #if defined(USE_NSS_CERTS)
    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
        net::ClientCertStoreNSS::PasswordDelegateFactory()));
  #elif defined(OS_WIN)
    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
  #elif defined(OS_MACOSX)
    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
  #elif defined(USE_OPENSSL)
    return std::unique_ptr<net::ClientCertStore>();
  #endif
}

bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
    net::URLRequest* request,
    const base::FilePath& plugin_path,
    const std::string& mime_type,
    GURL* origin,
    std::string* payload) {
  const content::ResourceRequestInfo* info =
      content::ResourceRequestInfo::ForRequest(request);
  content::WebContents* web_contents =
      info->GetWebContentsGetterForRequest().Run();
  if (mime_type == "application/pdf" && info->IsMainFrame() &&
      WebContentsPreferences::IsPluginsEnabled(web_contents)) {
    *origin = GURL(kPdfViewerUIOrigin);
    content::BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        base::Bind(&OnPdfResourceIntercepted, request->url(),
                   info->GetWebContentsGetterForRequest()));
    return true;
  }
  return false;
}

}  // namespace atom
<commit_msg>Move preferences check to OnPdfResourceIntercepted<commit_after>// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/atom_resource_dispatcher_host_delegate.h"

#include "atom/browser/atom_browser_context.h"
#include "atom/browser/login_handler.h"
#include "atom/browser/web_contents_permission_helper.h"
#include "atom/browser/web_contents_preferences.h"
#include "atom/common/atom_constants.h"
#include "atom/common/platform_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/stream_info.h"
#include "net/base/escape.h"
#include "net/ssl/client_cert_store.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"

#if defined(USE_NSS_CERTS)
#include "net/ssl/client_cert_store_nss.h"
#elif defined(OS_WIN)
#include "net/ssl/client_cert_store_win.h"
#elif defined(OS_MACOSX)
#include "net/ssl/client_cert_store_mac.h"
#endif

using content::BrowserThread;

namespace atom {

namespace {

void OnOpenExternal(const GURL& escaped_url,
                    bool allowed) {
  if (allowed)
    platform_util::OpenExternal(
#if defined(OS_WIN)
        base::UTF8ToUTF16(escaped_url.spec()),
#else
        escaped_url,
#endif
        true);
}

void HandleExternalProtocolInUI(
    const GURL& url,
    const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
    bool has_user_gesture) {
  content::WebContents* web_contents = web_contents_getter.Run();
  if (!web_contents)
    return;

  auto permission_helper =
      WebContentsPermissionHelper::FromWebContents(web_contents);
  if (!permission_helper)
    return;

  GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
  auto callback = base::Bind(&OnOpenExternal, escaped_url);
  permission_helper->RequestOpenExternalPermission(callback, has_user_gesture);
}

void OnPdfResourceIntercepted(
    const GURL& original_url,
    const content::ResourceRequestInfo::WebContentsGetter&
        web_contents_getter) {
  content::WebContents* web_contents = web_contents_getter.Run();
  if (!web_contents)
    return;

  if (!WebContentsPreferences::IsPluginsEnabled(web_contents)) {
    auto browser_context = web_contents->GetBrowserContext();
    auto download_manager =
      content::BrowserContext::GetDownloadManager(browser_context);

    download_manager->DownloadUrl(
        content::DownloadUrlParameters::CreateForWebContentsMainFrame(
            web_contents, original_url));
    return;
  }

  // The URL passes the original pdf resource url, that will be requested
  // by the webui page.
  // chrome://pdf-viewer/index.html?src=https://somepage/123.pdf
  content::NavigationController::LoadURLParams params(
      GURL(base::StringPrintf("%sindex.html?%s=%s", kPdfViewerUIOrigin,
                              kPdfPluginSrc, original_url.spec().c_str())));
  web_contents->GetController().LoadURLWithParams(params);
}

}  // namespace

AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {
}

bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
    const GURL& url,
    content::ResourceRequestInfo* info) {
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                          base::Bind(&HandleExternalProtocolInUI,
                                     url,
                                     info->GetWebContentsGetterForRequest(),
                                     info->HasUserGesture()));
  return true;
}

content::ResourceDispatcherHostLoginDelegate*
AtomResourceDispatcherHostDelegate::CreateLoginDelegate(
    net::AuthChallengeInfo* auth_info,
    net::URLRequest* request) {
  return new LoginHandler(auth_info, request);
}

std::unique_ptr<net::ClientCertStore>
AtomResourceDispatcherHostDelegate::CreateClientCertStore(
    content::ResourceContext* resource_context) {
  #if defined(USE_NSS_CERTS)
    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
        net::ClientCertStoreNSS::PasswordDelegateFactory()));
  #elif defined(OS_WIN)
    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
  #elif defined(OS_MACOSX)
    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
  #elif defined(USE_OPENSSL)
    return std::unique_ptr<net::ClientCertStore>();
  #endif
}

bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
    net::URLRequest* request,
    const base::FilePath& plugin_path,
    const std::string& mime_type,
    GURL* origin,
    std::string* payload) {
  const content::ResourceRequestInfo* info =
      content::ResourceRequestInfo::ForRequest(request);
  if (mime_type == "application/pdf" && info->IsMainFrame()) {
    *origin = GURL(kPdfViewerUIOrigin);
    content::BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE,
        base::Bind(&OnPdfResourceIntercepted, request->url(),
                   info->GetWebContentsGetterForRequest()));
    return true;
  }
  return false;
}

}  // namespace atom
<|endoftext|>


<commit_before>#include "wallsprojectparser.h"

#include <iostream>

#include <QDir>
#include <QDebug>
#include "signum.h"

#include "segment.h"
#include "segmentparseexception.h"

namespace dewalls {

typedef UnitizedDouble<Length> ULength;
typedef UnitizedDouble<Angle> UAngle;

// STATUS BITS
//   2^0 : Type = Book
//   2^1 : ?
//   2^2 : ?
//   2^3 : Name defines segment
//   2^4 : 1 = Feet, 0 = Meters
//   2^5 : ?
//   2^6 : reference unspecified
//   2^7 : ?
// Derive decl from date:
//   2^8 : 1 = no
//   2^9 : 1 = yes
// UTM/GPS Grid-relative:
//   2^10: 1 = no
//   2^11: 1 = yes
// Preserve vertical shot orientation:
//   2^12: 1 = no
//   2^13: 1 = yes
// Preserve vertical shot length:
//   2^14: 1 = no
//   2^15: 1 = yes
// Other type
//   2^16: 1 = type is other
//   2^17: edit on launch
//   2^18: open on launch
// Default view after compilation (bits 21-19):
//      1: North or East
// 	   10: North or West
//     11: North
//    100: East
//    101: West

// REFERENCE FIELDS
//  	northing	easting	   zn conv   el ? d  m  s  lat d  m  s long index/name
// .REF	2308552.729 324501.432 16 -0.601 27 6 20 52 14.229 88 41 13.085 0 "Adindan"

WallsProjectParser::WallsProjectParser(QObject* parent)
    : QObject(parent), LineParser()
{

}

WpjEntry::WpjEntry(WpjBookPtr parent, QString title)
    : Parent(parent), Title(title), Name(), Path(), Status(0), Options(), Reference()
{

}

WpjBook::WpjBook(WpjBookPtr parent, QString title)
    : WpjEntry(parent, title)
{
}

const int WpjEntry::NameDefinesSegmentBit = 1 << 3;
const int WpjEntry::FeetBit = 1 << 4;
const int WpjEntry::ReferenceUnspecifiedBit = 1 << 6;
const int WpjEntry::DontDeriveDeclBit = 1 << 8;
const int WpjEntry::DeriveDeclBit = 1 << 9;
const int WpjEntry::NotGridRelativeBit = 1 << 10;
const int WpjEntry::GridRelativeBit = 1 << 11;
const int WpjEntry::DontPreserveVertShotOrientationBit = 1 << 12;
const int WpjEntry::PreserveVertShotOrientationBit = 1 << 13;
const int WpjEntry::DontPreserveVertShotLengthBit = 1 << 14;
const int WpjEntry::PreserveVertShotLengthBit = 1 << 15;
const int WpjEntry::OtherTypeBit = 1 << 16;
const int WpjEntry::EditOnLaunchBit = 1 << 17;
const int WpjEntry::OpenOnLaunchBit = 1 << 18;
const int WpjEntry::DefaultViewAfterCompilationMask = 7 << 19;
const int WpjEntry::NorthOrEastViewBits = 1 << 19;
const int WpjEntry::NorthOrWestViewBits = 2 << 19;
const int WpjEntry::NorthViewBits = 3 << 19;
const int WpjEntry::EastViewBits = 4 << 19;
const int WpjEntry::WestViewBits = 5 << 19;

bool WpjEntry::isOther() const {
    return Status & OtherTypeBit;
}

bool WpjEntry::isSurvey() const {
    return !isBook() && !isOther();
}

bool WpjEntry::nameDefinesSegment() const {
    return Status & NameDefinesSegmentBit;
}

GeoReferencePtr WpjEntry::reference() const {
    if (Status & ReferenceUnspecifiedBit) {
        return GeoReferencePtr();
    }
    if (!Reference.isNull()) {
        return Reference;
    }
    if (!Parent.isNull()) {
        return Parent->reference();
    }
    return GeoReferencePtr();
}

WpjEntry::ReviewUnits WpjEntry::reviewUnits() const {
    return Status & FeetBit ? Feet : Meters;
}

bool WpjEntry::deriveDeclFromDate() const {
    if (Status & DontDeriveDeclBit) {
        return false;
    }
    if (Status & DeriveDeclBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->deriveDeclFromDate();
    }
    return false;
}

bool WpjEntry::gridRelative() const {
    if (Status & NotGridRelativeBit) {
        return false;
    }
    if (Status & GridRelativeBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->gridRelative();
    }
    return false;
}

bool WpjEntry::preserveVertShotOrientation() const {
    if (Status & DontPreserveVertShotOrientationBit) {
        return false;
    }
    if (Status & PreserveVertShotOrientationBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->preserveVertShotOrientation();
    }
    return false;
}

bool WpjEntry::preserveVertShotLength() const {
    if (Status & DontPreserveVertShotLengthBit) {
        return false;
    }
    if (Status & PreserveVertShotLengthBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->preserveVertShotLength();
    }
    return false;
}

WpjEntry::LaunchOptions WpjEntry::launchOptions() const {
    if (Status & EditOnLaunchBit) {
        return Edit;
    }
    if (Status & OpenOnLaunchBit) {
        return Open;
    }
    return Properties;
}

WpjEntry::View WpjEntry::defaultViewAfterCompilation() const {
    switch (Status & DefaultViewAfterCompilationMask) {
    case NorthOrEastViewBits:
        return NorthOrEast;
    case NorthOrWestViewBits:
        return NorthOrWest;
    case NorthViewBits:
        return North;
    case EastViewBits:
        return East;
    case WestViewBits:
        return West;
    default:
        if (!Parent.isNull()) {
            return Parent->defaultViewAfterCompilation();
        }
        return NorthOrEast;
    }
}

QDir WpjEntry::dir() const {
    if (Parent.isNull()) {
        return QDir(Path);
    }
    QDir dir = Parent->dir();
    dir.cd(Path);
    return dir;
}

QString WpjEntry::absolutePath() const {
    if (isBook()) {
        return QDir::cleanPath(dir().absolutePath());
    }
    else {
        QString name = Name.value();
        if (name.isEmpty()) {
            return QString();
        }
        if (isSurvey() && !name.endsWith(".SRV", Qt::CaseInsensitive)) {
            if (QFileInfo(QDir::cleanPath(dir().absoluteFilePath(name + ".SRV"))).exists()) {
                name += ".SRV";
            }
            else if (QFileInfo(QDir::cleanPath(dir().absoluteFilePath(name + ".srv"))).exists()) {
                name += ".srv";
            }
            else {
                name += ".SRV";
            }
        }
        return QDir::cleanPath(dir().absoluteFilePath(name));
    }
}

QList<Segment> WpjEntry::allOptions() const {
    QList<Segment> options;
    if (!Parent.isNull()) {
        options = Parent->allOptions();
    }
    if (!Options.isEmpty()) {
        options << Options;
    }
    return options;
}

void WallsProjectParser::parseLine(QString line) {
    parseLine(Segment(line));
}

void WallsProjectParser::parseLine(Segment line) {
    reset(line);

    maybeWhitespace();

    if (ProjectRoot.isNull()) {
        oneOf([&]() { this->bookLine(); },
        [&]() { this->commentLine(); },
        [&]() { this->emptyLine(); });
    }
    else if (CurrentEntry.isNull()) {
        oneOf([&]() { this->commentLine(); },
        [&]() { this->emptyLine(); });
    }
    else {
        oneOf([&]() { this->bookLine(); },
        [&]() { this->endbookLine(); },
        [&]() { this->surveyLine(); },
        [&]() { this->nameLine(); },
        [&]() { this->pathLine(); },
        [&]() { this->refLine(); },
        [&]() { this->optionsLine(); },
        [&]() { this->statusLine(); },
        [&]() { this->commentLine(); },
        [&]() { this->emptyLine(); });
    }
}

void WallsProjectParser::emptyLine() {
    maybeWhitespace();
    endOfLine();
}

void WallsProjectParser::bookLine() {
    expect(".BOOK", Qt::CaseInsensitive);
    whitespace();
    QString title = remaining().value();
    WpjBookPtr parent = currentBook();
    WpjBookPtr newEntry(new WpjBook(parent, title));
    if (!parent.isNull()) {
        parent->Children << newEntry;
    }
    else {
        ProjectRoot = newEntry;
    }
    CurrentEntry = newEntry;
}

void WallsProjectParser::endbookLine() {
    expect(".ENDBOOK", Qt::CaseInsensitive);
    maybeWhitespace();
    endOfLine();
    CurrentEntry = currentBook()->Parent;
}

void WallsProjectParser::nameLine() {
    expect(".NAME", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Name = remaining();
}

void WallsProjectParser::pathLine() {
    expect(".PATH", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Path = remaining().value();
}

void WallsProjectParser::surveyLine() {
    expect(".SURVEY", Qt::CaseInsensitive);
    whitespace();
    QString title = remaining().value();
    WpjBookPtr book = currentBook();
    WpjEntryPtr newEntry(new WpjEntry(book, title));
    book->Children << newEntry;
    CurrentEntry = newEntry;
}

void WallsProjectParser::statusLine() {
    expect(".STATUS", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Status = unsignedIntLiteral();
}

void WallsProjectParser::optionsLine() {
    expect(".OPTIONS", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Options = remaining();
}

void WallsProjectParser::commentLine() {
    expect(";");
    remaining();
}

void WallsProjectParser::refLine() {
    expect(".REF", Qt::CaseInsensitive);
    whitespace();
    GeoReference ref;
    ref.northing = ULength(doubleLiteral(), Length::meters());
    whitespace();
    ref.easting = ULength(doubleLiteral(), Length::meters());
    whitespace();
    ref.zone = intLiteral();
    whitespace();
    ref.gridConvergence = UAngle(doubleLiteral(), Angle::degrees());
    whitespace();
    ref.elevation = ULength(doubleLiteral(), Length::meters());
    whitespace();
    doubleLiteral(); // don't know what this field represents
    whitespace();

    double degrees, minutes, seconds;

    degrees = intLiteral();
    whitespace();
    minutes = unsignedIntLiteral();
    whitespace();
    seconds = unsignedDoubleLiteral();
    whitespace();
    ref.latitude = UAngle(degrees + ((minutes + seconds / 60.0) / 60.0) * signum(degrees), Angle::degrees());

    degrees = intLiteral();
    whitespace();
    minutes = unsignedIntLiteral();
    whitespace();
    seconds = unsignedDoubleLiteral();
    whitespace();
    ref.longitude = UAngle(degrees + ((minutes + seconds / 60.0) / 60.0) * signum(degrees), Angle::degrees());

    ref.wallsDatumIndex = unsignedIntLiteral();
    whitespace();
    expect('"');
    ref.datumName = expect(QRegExp("[^\"]+"), {"<DATUM_NAME>"}).value();
    expect('"');
    maybeWhitespace();
    endOfLine();
    CurrentEntry->Reference = GeoReferencePtr(new GeoReference);
    *(CurrentEntry->Reference) = ref;
}

WpjBookPtr WallsProjectParser::parseFile(QString fileName) {
    QFile file(fileName);

    if (!file.open(QFile::ReadOnly))
    {
        QString msg = QString("I couldn't open %1").arg(fileName);
        emit message(WallsMessage("error", msg));
        return WpjBookPtr();
    }

    int lineNumber = 0;
    while (!file.atEnd())
    {
        QString line = file.readLine();
        line = line.trimmed();
        if (file.error() != QFile::NoError)
        {
            emit message(WallsMessage("error",
                                      QString("failed to read from file: %1").arg(file.errorString()),
                                      fileName,
                                      lineNumber));
            return WpjBookPtr();
        }

        try {
            parseLine(Segment(line, fileName, lineNumber, 0));
        }
        catch (const SegmentParseException& ex) {
            emit message(WallsMessage(ex));
            return WpjBookPtr();
        }

        lineNumber++;
    }

    file.close();

    if (!ProjectRoot.isNull()) {
        ProjectRoot->Path = QFileInfo(fileName).dir().canonicalPath();
    }

    return ProjectRoot;
}

} // namespace dewalls

<commit_msg>handle absolute node paths correctly<commit_after>#include "wallsprojectparser.h"

#include <iostream>

#include <QDir>
#include <QDebug>
#include "signum.h"

#include "segment.h"
#include "segmentparseexception.h"

namespace dewalls {

typedef UnitizedDouble<Length> ULength;
typedef UnitizedDouble<Angle> UAngle;

// STATUS BITS
//   2^0 : Type = Book
//   2^1 : ?
//   2^2 : ?
//   2^3 : Name defines segment
//   2^4 : 1 = Feet, 0 = Meters
//   2^5 : ?
//   2^6 : reference unspecified
//   2^7 : ?
// Derive decl from date:
//   2^8 : 1 = no
//   2^9 : 1 = yes
// UTM/GPS Grid-relative:
//   2^10: 1 = no
//   2^11: 1 = yes
// Preserve vertical shot orientation:
//   2^12: 1 = no
//   2^13: 1 = yes
// Preserve vertical shot length:
//   2^14: 1 = no
//   2^15: 1 = yes
// Other type
//   2^16: 1 = type is other
//   2^17: edit on launch
//   2^18: open on launch
// Default view after compilation (bits 21-19):
//      1: North or East
// 	   10: North or West
//     11: North
//    100: East
//    101: West

// REFERENCE FIELDS
//  	northing	easting	   zn conv   el ? d  m  s  lat d  m  s long index/name
// .REF	2308552.729 324501.432 16 -0.601 27 6 20 52 14.229 88 41 13.085 0 "Adindan"

WallsProjectParser::WallsProjectParser(QObject* parent)
    : QObject(parent), LineParser()
{

}

WpjEntry::WpjEntry(WpjBookPtr parent, QString title)
    : Parent(parent), Title(title), Name(), Path(), Status(0), Options(), Reference()
{

}

WpjBook::WpjBook(WpjBookPtr parent, QString title)
    : WpjEntry(parent, title)
{
}

const int WpjEntry::NameDefinesSegmentBit = 1 << 3;
const int WpjEntry::FeetBit = 1 << 4;
const int WpjEntry::ReferenceUnspecifiedBit = 1 << 6;
const int WpjEntry::DontDeriveDeclBit = 1 << 8;
const int WpjEntry::DeriveDeclBit = 1 << 9;
const int WpjEntry::NotGridRelativeBit = 1 << 10;
const int WpjEntry::GridRelativeBit = 1 << 11;
const int WpjEntry::DontPreserveVertShotOrientationBit = 1 << 12;
const int WpjEntry::PreserveVertShotOrientationBit = 1 << 13;
const int WpjEntry::DontPreserveVertShotLengthBit = 1 << 14;
const int WpjEntry::PreserveVertShotLengthBit = 1 << 15;
const int WpjEntry::OtherTypeBit = 1 << 16;
const int WpjEntry::EditOnLaunchBit = 1 << 17;
const int WpjEntry::OpenOnLaunchBit = 1 << 18;
const int WpjEntry::DefaultViewAfterCompilationMask = 7 << 19;
const int WpjEntry::NorthOrEastViewBits = 1 << 19;
const int WpjEntry::NorthOrWestViewBits = 2 << 19;
const int WpjEntry::NorthViewBits = 3 << 19;
const int WpjEntry::EastViewBits = 4 << 19;
const int WpjEntry::WestViewBits = 5 << 19;

bool WpjEntry::isOther() const {
    return Status & OtherTypeBit;
}

bool WpjEntry::isSurvey() const {
    return !isBook() && !isOther();
}

bool WpjEntry::nameDefinesSegment() const {
    return Status & NameDefinesSegmentBit;
}

GeoReferencePtr WpjEntry::reference() const {
    if (Status & ReferenceUnspecifiedBit) {
        return GeoReferencePtr();
    }
    if (!Reference.isNull()) {
        return Reference;
    }
    if (!Parent.isNull()) {
        return Parent->reference();
    }
    return GeoReferencePtr();
}

WpjEntry::ReviewUnits WpjEntry::reviewUnits() const {
    return Status & FeetBit ? Feet : Meters;
}

bool WpjEntry::deriveDeclFromDate() const {
    if (Status & DontDeriveDeclBit) {
        return false;
    }
    if (Status & DeriveDeclBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->deriveDeclFromDate();
    }
    return false;
}

bool WpjEntry::gridRelative() const {
    if (Status & NotGridRelativeBit) {
        return false;
    }
    if (Status & GridRelativeBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->gridRelative();
    }
    return false;
}

bool WpjEntry::preserveVertShotOrientation() const {
    if (Status & DontPreserveVertShotOrientationBit) {
        return false;
    }
    if (Status & PreserveVertShotOrientationBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->preserveVertShotOrientation();
    }
    return false;
}

bool WpjEntry::preserveVertShotLength() const {
    if (Status & DontPreserveVertShotLengthBit) {
        return false;
    }
    if (Status & PreserveVertShotLengthBit) {
        return true;
    }
    if (!Parent.isNull()) {
        return Parent->preserveVertShotLength();
    }
    return false;
}

WpjEntry::LaunchOptions WpjEntry::launchOptions() const {
    if (Status & EditOnLaunchBit) {
        return Edit;
    }
    if (Status & OpenOnLaunchBit) {
        return Open;
    }
    return Properties;
}

WpjEntry::View WpjEntry::defaultViewAfterCompilation() const {
    switch (Status & DefaultViewAfterCompilationMask) {
    case NorthOrEastViewBits:
        return NorthOrEast;
    case NorthOrWestViewBits:
        return NorthOrWest;
    case NorthViewBits:
        return North;
    case EastViewBits:
        return East;
    case WestViewBits:
        return West;
    default:
        if (!Parent.isNull()) {
            return Parent->defaultViewAfterCompilation();
        }
        return NorthOrEast;
    }
}

QDir WpjEntry::dir() const {
    if (Parent.isNull() || QDir::isAbsolutePath(Path)) {
        return QDir(Path);
    }
    QDir dir = Parent->dir();
    dir.cd(Path);
    return dir;
}

QString WpjEntry::absolutePath() const {
    if (isBook()) {
        return QDir::cleanPath(dir().absolutePath());
    }
    else {
        QString name = Name.value();
        if (name.isEmpty()) {
            return QString();
        }
        if (isSurvey() && !name.endsWith(".SRV", Qt::CaseInsensitive)) {
            if (QFileInfo(QDir::cleanPath(dir().absoluteFilePath(name + ".SRV"))).exists()) {
                name += ".SRV";
            }
            else if (QFileInfo(QDir::cleanPath(dir().absoluteFilePath(name + ".srv"))).exists()) {
                name += ".srv";
            }
            else {
                name += ".SRV";
            }
        }
        return QDir::cleanPath(dir().absoluteFilePath(name));
    }
}

QList<Segment> WpjEntry::allOptions() const {
    QList<Segment> options;
    if (!Parent.isNull()) {
        options = Parent->allOptions();
    }
    if (!Options.isEmpty()) {
        options << Options;
    }
    return options;
}

void WallsProjectParser::parseLine(QString line) {
    parseLine(Segment(line));
}

void WallsProjectParser::parseLine(Segment line) {
    reset(line);

    maybeWhitespace();

    if (ProjectRoot.isNull()) {
        oneOf([&]() { this->bookLine(); },
        [&]() { this->commentLine(); },
        [&]() { this->emptyLine(); });
    }
    else if (CurrentEntry.isNull()) {
        oneOf([&]() { this->commentLine(); },
        [&]() { this->emptyLine(); });
    }
    else {
        oneOf([&]() { this->bookLine(); },
        [&]() { this->endbookLine(); },
        [&]() { this->surveyLine(); },
        [&]() { this->nameLine(); },
        [&]() { this->pathLine(); },
        [&]() { this->refLine(); },
        [&]() { this->optionsLine(); },
        [&]() { this->statusLine(); },
        [&]() { this->commentLine(); },
        [&]() { this->emptyLine(); });
    }
}

void WallsProjectParser::emptyLine() {
    maybeWhitespace();
    endOfLine();
}

void WallsProjectParser::bookLine() {
    expect(".BOOK", Qt::CaseInsensitive);
    whitespace();
    QString title = remaining().value();
    WpjBookPtr parent = currentBook();
    WpjBookPtr newEntry(new WpjBook(parent, title));
    if (!parent.isNull()) {
        parent->Children << newEntry;
    }
    else {
        ProjectRoot = newEntry;
    }
    CurrentEntry = newEntry;
}

void WallsProjectParser::endbookLine() {
    expect(".ENDBOOK", Qt::CaseInsensitive);
    maybeWhitespace();
    endOfLine();
    CurrentEntry = currentBook()->Parent;
}

void WallsProjectParser::nameLine() {
    expect(".NAME", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Name = remaining();
}

void WallsProjectParser::pathLine() {
    expect(".PATH", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Path = remaining().value();
}

void WallsProjectParser::surveyLine() {
    expect(".SURVEY", Qt::CaseInsensitive);
    whitespace();
    QString title = remaining().value();
    WpjBookPtr book = currentBook();
    WpjEntryPtr newEntry(new WpjEntry(book, title));
    book->Children << newEntry;
    CurrentEntry = newEntry;
}

void WallsProjectParser::statusLine() {
    expect(".STATUS", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Status = unsignedIntLiteral();
}

void WallsProjectParser::optionsLine() {
    expect(".OPTIONS", Qt::CaseInsensitive);
    whitespace();
    CurrentEntry->Options = remaining();
}

void WallsProjectParser::commentLine() {
    expect(";");
    remaining();
}

void WallsProjectParser::refLine() {
    expect(".REF", Qt::CaseInsensitive);
    whitespace();
    GeoReference ref;
    ref.northing = ULength(doubleLiteral(), Length::meters());
    whitespace();
    ref.easting = ULength(doubleLiteral(), Length::meters());
    whitespace();
    ref.zone = intLiteral();
    whitespace();
    ref.gridConvergence = UAngle(doubleLiteral(), Angle::degrees());
    whitespace();
    ref.elevation = ULength(doubleLiteral(), Length::meters());
    whitespace();
    doubleLiteral(); // don't know what this field represents
    whitespace();

    double degrees, minutes, seconds;

    degrees = intLiteral();
    whitespace();
    minutes = unsignedIntLiteral();
    whitespace();
    seconds = unsignedDoubleLiteral();
    whitespace();
    ref.latitude = UAngle(degrees + ((minutes + seconds / 60.0) / 60.0) * signum(degrees), Angle::degrees());

    degrees = intLiteral();
    whitespace();
    minutes = unsignedIntLiteral();
    whitespace();
    seconds = unsignedDoubleLiteral();
    whitespace();
    ref.longitude = UAngle(degrees + ((minutes + seconds / 60.0) / 60.0) * signum(degrees), Angle::degrees());

    ref.wallsDatumIndex = unsignedIntLiteral();
    whitespace();
    expect('"');
    ref.datumName = expect(QRegExp("[^\"]+"), {"<DATUM_NAME>"}).value();
    expect('"');
    maybeWhitespace();
    endOfLine();
    CurrentEntry->Reference = GeoReferencePtr(new GeoReference);
    *(CurrentEntry->Reference) = ref;
}

WpjBookPtr WallsProjectParser::parseFile(QString fileName) {
    QFile file(fileName);

    if (!file.open(QFile::ReadOnly))
    {
        QString msg = QString("I couldn't open %1").arg(fileName);
        emit message(WallsMessage("error", msg));
        return WpjBookPtr();
    }

    int lineNumber = 0;
    while (!file.atEnd())
    {
        QString line = file.readLine();
        line = line.trimmed();
        if (file.error() != QFile::NoError)
        {
            emit message(WallsMessage("error",
                                      QString("failed to read from file: %1").arg(file.errorString()),
                                      fileName,
                                      lineNumber));
            return WpjBookPtr();
        }

        try {
            parseLine(Segment(line, fileName, lineNumber, 0));
        }
        catch (const SegmentParseException& ex) {
            emit message(WallsMessage(ex));
            return WpjBookPtr();
        }

        lineNumber++;
    }

    file.close();

    if (!ProjectRoot.isNull()) {
        ProjectRoot->Path = QFileInfo(fileName).dir().canonicalPath();
    }

    return ProjectRoot;
}

} // namespace dewalls

<|endoftext|>