/* Copyright (C) 2010,2013 G.P. Halkes
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3, as
   published by the Free Software Foundation.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef COMMON_H
#define COMMON_H

#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>

#include "replay.h"

/* This should be automatically determined from the repository
   [and is, by the mkdist.sh script] */
#define VERSION_STRING "2.2"

#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

/* Define a td_bool type */
typedef enum { td_false, td_true } td_bool;

/* Exit program with message, in response to an error. */
#ifdef __GNUC__
void fatal(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
#else
void fatal(const char *fmt, ...);
#endif
/* Write to file descriptor, retrying if interrupted by a signal. */
void safe_write(int fd, const void *buffer, size_t size);
/* Read from file descriptor, retrying if interrupted by a signal. */
ssize_t safe_read(int fd, char *buffer, size_t size);
/* Echo characters to screen, interpreting control characters as ^X. */
void write_echo(const unsigned char *buffer, size_t size);
/* Copy the attributes from a file descriptor (pseudo tty) to the actual terminal. */
void copy_attrs_to_terminal(int from);
void set_non_block(int fd, const char *descr, td_bool value);
/* Open the output file. If *name == NULL, a file is created in directory (if != NULL) or in the cwd. */
FILE *open_output(char **name, const char *directory);
void install_signal_handler(int sig, void (*handler)(int), const char *descr);
/* Calculate the difference between two timeval structs in miliseconds. */
int timevaldiff(struct timeval origin, struct timeval now);

/* Values for bytes sent over the signal_pipe. */
enum {
	SIGNAL_CHILD,
	SIGNAL_WINCH
};

/* Give the pipe fd's names. */
enum {
	PIPE_READ,
	PIPE_WRITE
};

extern int signal_pipe[2];
void sigwinch_handler(int param);
void sigchld_handler(int param);

void save_tty(void);
void reset_tty(void);

#define HANDLE_SIG_BYTES() { char signal_byte; \
	while (safe_read(signal_pipe[PIPE_READ], &signal_byte, 1) == 1) { \
		switch (signal_byte) {
#define END_HANDLE default: break; }}}

/* Directions for copying bytes to/from terminal. NONE is used only for keeping track of the last
   direction output to file. */
typedef enum {
	TYPE_NONE,
	TYPE_INPUT,
	TYPE_OUTPUT
} Type;

void init_timestamp(void);
/* Write a set of bytes to the output file. */
void write_record(FILE *output, Type dir, int delay, int min_delay, unsigned char *buffer, ssize_t length);
/* Copy bytes between tty and pseudo tty, recording the events to output. */
void copy_fd(int from, int to, Type dir, FILE *output);
/* Main loop for interactive operation of the child program. */
int interact_loop(int master, pid_t pid, FILE *output, int timeout);
/* Write a string to out, using escaping to ensure that it can be read back. */
void write_escaped_string(FILE *out, const char *string, size_t length);
/* Check if the current terminal is capable of resizing initiated by a program. */
td_bool detect_resize_capable(void);
/* Start the child program. */
pid_t start_client(ExpNode *client, int fd, FILE *log_file);
/* getcwd which allocates a large enough buffer. */
char *getcwd_wrapper(void);

#define CURRENT_SCRIPT_VERSION 2
void check_script(const char *file_name, ExpNode *script);

/* Portable implementation wrappers for openpty, login_tty, forkpty, poll and strdup.
   Note that these only provide the arguments actually used in the termdebug programs. */
#ifdef HAVE_OPENPTY
#if defined(OPENPTY_IN_UTIL_H)
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <util.h>
#elif defined(OPENPTY_IN_LIBUTIL_H)
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <libutil.h>
#else
#include <pty.h>
#endif
#define port_openpty(_a, _b, _c) openpty((_a), (_b), NULL, NULL, (_c))
#else
#include <sys/ioctl.h>
#include <termios.h>
int port_openpty(int *master, int *slave, struct winsize *wsz);
#endif

#ifdef HAVE_LOGIN_TTY
#if defined(LOGIN_TTY_IN_UTIL_H)
#include <termios.h>
#include <util.h>
#elif defined(LOGIN_TTY_IN_LIBUTIL_H)
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <libutil.h>
#else
#include <utmp.h>
#endif
#define port_login_tty(_a) login_tty((_a))
#else
int port_login_tty(int fd);
#endif

#ifdef HAVE_FORKPTY
#if defined(FORKPTY_IN_UTIL_H)
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <util.h>
#elif defined(FORKPTY_IN_LIBUTIL_H)
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <libutil.h>
#else
#include <pty.h>
#endif
#define port_forkpty(_a, _b) forkpty((_a), NULL, NULL, (_b))
#else
#include <sys/ioctl.h>
#include <termios.h>
pid_t port_forkpty(int *master, struct winsize *wsz);
#endif

#ifdef HAVE_POLL
#include <poll.h>
typedef struct pollfd port_pollfd;
typedef nfds_t port_nfds_t;
#define PORT_POLLIN POLLIN
#define PORT_POLLOUT POLLOUT
#define PORT_POLLERR POLLERR
#define port_poll(_a, _b, _c) poll((_a), (_b), (_c))
#else
typedef struct {
	int fd;
	short events, revents;
} port_pollfd;
typedef size_t port_nfds_t;
#define PORT_POLLIN (1<<0)
#define PORT_POLLOUT (1<<1)
#define PORT_POLLERR (1<<2)
int port_poll(port_pollfd *fds, port_nfds_t nfds, int timeout);
#endif

#ifdef HAVE_STRDUP
#include <string.h>
#define port_strdup(_a) strdup(_a)
#else
char *port_strdup(const char *s);
#endif

/* Retrieve an environment variable from a script. */
const char *get_script_env(const ExpNode *script, const char *name);
td_bool check_fmt(const char *str);

/* Unfortunately, the system(3) call has been marked as warn_unused_result in
   some distributions. In our case, we don't care about whether the program we
   are trying to start actually succeeded, or whether the fork failed. There is
   nothing we can do about it, and we don't have a good fall back.

   The only way to stop GCC whining about ignoring the result, is by assigning
   to a variable, or using the result in some other way. However, if we simply
   assign the variable, GCC will start whining about the fact that the assigned
   value is never used. Sigh. So we explicitly tell it to ignore that, by
   casting it to a void.
*/
#ifdef __GNUC__
#define ignore_result(x) do { __typeof__(x) _ignore = x; (void) _ignore; } while (0)
#else
#define ignore_result(x) x
#endif

#endif
