<commit_before>/**
 * pty.js
 * Copyright (c) 2012, Christopher Jeffrey (MIT License)
 *
 * pty.cc:
 *   This file is responsible for starting processes
 *   with pseudo-terminal file descriptors.
 *
 * See:
 *   man pty
 *   man tty_ioctl
 *   man termios
 *   man forkpty
 */

#include <v8.h>
#include <node.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>

/* forkpty */
/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */
#if defined(__GLIBC__) || defined(__CYGWIN__)
#include <pty.h>
#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <util.h>
#elif defined(__FreeBSD__)
#include <libutil.h>
#elif defined(__sun)
#include <stropts.h> /* for I_PUSH */
#else
#include <pty.h>
#endif

#include <termios.h> /* tcgetattr, tty_ioctl */

/* environ for execvpe */
/* node/src/node_child_process.cc */
#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif

/* for pty_getproc */
#if defined(__linux__)
#include <stdio.h>
#include <stdint.h>
#elif defined(__APPLE__)
#include <sys/sysctl.h>
#include <libproc.h>
#endif

using namespace std;
using namespace node;
using namespace v8;

static Handle<Value>
PtyFork(const Arguments&);

static Handle<Value>
PtyOpen(const Arguments&);

static Handle<Value>
PtyResize(const Arguments&);

static Handle<Value>
PtyGetProc(const Arguments&);

static int
pty_execvpe(const char *, char **, char **);

static int
pty_nonblock(int);

static char *
pty_getproc(int, char *);

static int
pty_openpty(int *, int *, char *,
            const struct termios *,
            const struct winsize *);

static pid_t
pty_forkpty(int *, char *,
            const struct termios *,
            const struct winsize *);

extern "C" void
init(Handle<Object>);

/**
 * PtyFork
 */

static Handle<Value>
PtyFork(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 6) {
    return ThrowException(Exception::Error(
      String::New("Not enough arguments.")));
  }

  if (!args[0]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("file must be a string.")));
  }

  if (!args[1]->IsArray()) {
    return ThrowException(Exception::Error(
      String::New("args must be an array.")));
  }

  if (!args[2]->IsArray()) {
    return ThrowException(Exception::Error(
      String::New("env must be an array.")));
  }

  if (!args[3]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("cwd must be a string.")));
  }

  if (!args[4]->IsNumber() || !args[5]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("cols and rows must be numbers.")));
  }

  // node/src/node_child_process.cc

  // file
  String::Utf8Value file(args[0]->ToString());

  // args
  int i = 0;
  Local<Array> argv_ = Local<Array>::Cast(args[1]);
  int argc = argv_->Length();
  int argl = argc + 1 + 1;
  char **argv = new char*[argl];
  argv[0] = strdup(*file);
  argv[argl-1] = NULL;
  for (; i < argc; i++) {
    String::Utf8Value arg(argv_->Get(Integer::New(i))->ToString());
    argv[i+1] = strdup(*arg);
  }

  // env
  i = 0;
  Local<Array> env_ = Local<Array>::Cast(args[2]);
  int envc = env_->Length();
  char **env = new char*[envc+1];
  env[envc] = NULL;
  for (; i < envc; i++) {
    String::Utf8Value pair(env_->Get(Integer::New(i))->ToString());
    env[i] = strdup(*pair);
  }

  // cwd
  String::Utf8Value cwd_(args[3]->ToString());
  char *cwd = strdup(*cwd_);

  // size
  struct winsize winp;
  winp.ws_col = args[4]->IntegerValue();
  winp.ws_row = args[5]->IntegerValue();
  winp.ws_xpixel = 0;
  winp.ws_ypixel = 0;

  // fork the pty
  int master;
  char name[40];
  pid_t pid = pty_forkpty(&master, name, NULL, &winp);

  if (pid) {
    for (i = 0; i < argl; i++) free(argv[i]);
    delete[] argv;
    for (i = 0; i < envc; i++) free(env[i]);
    delete[] env;
    free(cwd);
  }

  switch (pid) {
    case -1:
      return ThrowException(Exception::Error(
        String::New("forkpty failed.")));
    case 0:
      if (strlen(cwd)) chdir(cwd);

      pty_execvpe(argv[0], argv, env);

      perror("execvp failed");
      _exit(1);
    default:
      // nonblocking
      if (pty_nonblock(master) == -1) {
        return ThrowException(Exception::Error(
          String::New("Could not set master fd to nonblocking.")));
      }

      Local<Object> obj = Object::New();
      obj->Set(String::New("fd"), Number::New(master));
      obj->Set(String::New("pid"), Number::New(pid));
      obj->Set(String::New("pty"), String::New(name));

      return scope.Close(obj);
  }

  return Undefined();
}

/**
 * PtyOpen
 */

static Handle<Value>
PtyOpen(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 2
      || !args[0]->IsNumber()
      || !args[1]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("Bad arguments.")));
  }

  // size
  struct winsize winp;
  winp.ws_col = args[0]->IntegerValue();
  winp.ws_row = args[1]->IntegerValue();
  winp.ws_xpixel = 0;
  winp.ws_ypixel = 0;

  // pty
  int master, slave;
  char name[40];
  int ret = pty_openpty(&master, &slave, name, NULL, &winp);

  if (ret == -1) {
    return ThrowException(Exception::Error(
      String::New("openpty failed.")));
  }

  if (pty_nonblock(master) == -1) {
    return ThrowException(Exception::Error(
      String::New("Could not set master fd to nonblocking.")));
  }

  if (pty_nonblock(slave) == -1) {
    return ThrowException(Exception::Error(
      String::New("Could not set slave fd to nonblocking.")));
  }

  Local<Object> obj = Object::New();
  obj->Set(String::New("master"), Number::New(master));
  obj->Set(String::New("slave"), Number::New(slave));
  obj->Set(String::New("pty"), String::New(name));

  return scope.Close(obj);
}

/**
 * Resize Functionality
 */

static Handle<Value>
PtyResize(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 3
      || !args[0]->IsNumber()
      || !args[1]->IsNumber()
      || !args[2]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("Bad arguments.")));
  }

  int fd = args[0]->IntegerValue();

  struct winsize winp;
  winp.ws_col = args[1]->IntegerValue();
  winp.ws_row = args[2]->IntegerValue();
  winp.ws_xpixel = 0;
  winp.ws_ypixel = 0;

  if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
    return ThrowException(Exception::Error(
      String::New("ioctl failed.")));
  }

  return Undefined();
}

/**
 * Foreground Process Name
 */

static Handle<Value>
PtyGetProc(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 2
      || !args[0]->IsNumber()
      || !args[1]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("Bad arguments.")));
  }

  int fd = args[0]->IntegerValue();

  String::Utf8Value tty_(args[1]->ToString());
  char *tty = strdup(*tty_);
  char *name = pty_getproc(fd, tty);
  free(tty);

  if (name == NULL) {
    return Undefined();
  }

  Local<String> name_ = String::New(name);
  free(name);
  return scope.Close(name_);
}

/**
 * execvpe
 */

// execvpe(3) is not portable.
// http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html
static int
pty_execvpe(const char *file, char **argv, char **envp) {
  char **old = environ;
  environ = envp;
  int ret = execvp(file, argv);
  environ = old;
  return ret;
}

/**
 * Nonblocking FD
 */

static int
pty_nonblock(int fd) {
  int flags = fcntl(fd, F_GETFL, 0);
  if (flags == -1) return -1;
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

/**
 * pty_getproc
 * Taken from tmux.
 */

// Taken from: tmux (http://tmux.sourceforge.net/)
// Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
// Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#if defined(__linux__)

static char *
pty_getproc(int fd, char *tty) {
  FILE *f;
  char *path, *buf;
  size_t len;
  int ch;
  pid_t pgrp;
  int r;

  if ((pgrp = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp);
  if (r == -1 || path == NULL) return NULL;

  if ((f = fopen(path, "r")) == NULL) {
    free(path);
    return NULL;
  }

  free(path);

  len = 0;
  buf = NULL;
  while ((ch = fgetc(f)) != EOF) {
    if (ch == '\0') break;
    buf = (char *)realloc(buf, len + 2);
    if (buf == NULL) return NULL;
    buf[len++] = ch;
  }

  if (buf != NULL) {
    buf[len] = '\0';
  }

  fclose(f);
  return buf;
}

#elif defined(__APPLE__)

static char *
pty_getproc(int fd, char *tty) {
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
  size_t size;
  struct kinfo_proc kp;

  if ((mib[3] = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  size = sizeof kp;
  if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
    return NULL;
  }

  if (*kp.kp_proc.p_comm == '\0') {
    return NULL;
  }

  return strdup(kp.kp_proc.p_comm);
}

#else

static char *
pty_getproc(int fd, char *tty) {
  return NULL;
}

#endif

/**
 * openpty(3) / forkpty(3)
 */

static int
pty_openpty(int *amaster, int *aslave, char *name,
            const struct termios *termp,
            const struct winsize *winp) {
#if defined(__sun)
  int master = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if (master == -1) return -1;
  if (amaster) *amaster = master;

  if (grantpt(master) == -1) goto err;
  if (unlockpt(master) == -1) goto err;

  char *slave_name = ptsname(master);
  if (slave_name == NULL) goto err;
  if (name) strcpy(name, slave_name);

  int slave = open(slave_name, O_RDWR | O_NOCTTY);
  if (slave == -1) goto err;
  if (aslave) *aslave = slave;

  if (termp) tcsetattr(slave, TCSAFLUSH, termp);
  if (winp) ioctl(slave, TIOCSWINSZ, winp);

  ioctl(slave, I_PUSH, "ptem");
  ioctl(slave, I_PUSH, "ldterm");
  ioctl(slave, I_PUSH, "ttcompat");

  return 0;

err:
  close(master);
  return -1;
#else
  return openpty(amaster, aslave, name, termp, winp);
#endif
}

static pid_t
pty_forkpty(int *amaster, char *name,
            const struct termios *termp,
            const struct winsize *winp) {
#if defined(__sun)
  int master, slave;

  int ret = pty_openpty(&master, &slave, name, termp, winp);
  if (ret == -1) return -1;
  if (amaster) *amaster = master;

  pid_t pid = fork();

  switch (pid) {
    case -1:
      close(master);
      close(slave);
      return -1;
    case 0:
      close(master);

      setsid();

#if defined(TIOCSCTTY)
      // glibc does this
      if (ioctl(slave, TIOCSCTTY, NULL) == -1) _exit(1);
#endif

      dup2(slave, 0);
      dup2(slave, 1);
      dup2(slave, 2);

      if (slave > 2) close(slave);

      return 0;
    default:
      close(slave);
      return pid;
  }

  return -1;
#else
  return forkpty(amaster, name, termp, winp);
#endif
}

/**
 * Init
 */

extern "C" void
init(Handle<Object> target) {
  HandleScope scope;
  NODE_SET_METHOD(target, "fork", PtyFork);
  NODE_SET_METHOD(target, "open", PtyOpen);
  NODE_SET_METHOD(target, "resize", PtyResize);
  NODE_SET_METHOD(target, "process", PtyGetProc);
}
<commit_msg>refactor arguments and type checking<commit_after>/**
 * pty.js
 * Copyright (c) 2012, Christopher Jeffrey (MIT License)
 *
 * pty.cc:
 *   This file is responsible for starting processes
 *   with pseudo-terminal file descriptors.
 *
 * See:
 *   man pty
 *   man tty_ioctl
 *   man termios
 *   man forkpty
 */

#include <v8.h>
#include <node.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>

/* forkpty */
/* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */
#if defined(__GLIBC__) || defined(__CYGWIN__)
#include <pty.h>
#elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <util.h>
#elif defined(__FreeBSD__)
#include <libutil.h>
#elif defined(__sun)
#include <stropts.h> /* for I_PUSH */
#else
#include <pty.h>
#endif

#include <termios.h> /* tcgetattr, tty_ioctl */

/* environ for execvpe */
/* node/src/node_child_process.cc */
#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif

/* for pty_getproc */
#if defined(__linux__)
#include <stdio.h>
#include <stdint.h>
#elif defined(__APPLE__)
#include <sys/sysctl.h>
#include <libproc.h>
#endif

using namespace std;
using namespace node;
using namespace v8;

static Handle<Value>
PtyFork(const Arguments&);

static Handle<Value>
PtyOpen(const Arguments&);

static Handle<Value>
PtyResize(const Arguments&);

static Handle<Value>
PtyGetProc(const Arguments&);

static int
pty_execvpe(const char *, char **, char **);

static int
pty_nonblock(int);

static char *
pty_getproc(int, char *);

static int
pty_openpty(int *, int *, char *,
            const struct termios *,
            const struct winsize *);

static pid_t
pty_forkpty(int *, char *,
            const struct termios *,
            const struct winsize *);

extern "C" void
init(Handle<Object>);

/**
 * PtyFork
 * pty.fork(file, args, env, cwd, cols, rows)
 */

static Handle<Value>
PtyFork(const Arguments& args) {
  HandleScope scope;

  if (args.Length() != 6
      || !args[0]->IsString()
      || !args[1]->IsArray()
      || !args[2]->IsArray()
      || !args[3]->IsString()
      || !args[4]->IsNumber()
      || !args[5]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("Usage: pty.fork(file, args, env, cwd, cols, rows)")));
  }

  // node/src/node_child_process.cc

  // file
  String::Utf8Value file(args[0]->ToString());

  // args
  int i = 0;
  Local<Array> argv_ = Local<Array>::Cast(args[1]);
  int argc = argv_->Length();
  int argl = argc + 1 + 1;
  char **argv = new char*[argl];
  argv[0] = strdup(*file);
  argv[argl-1] = NULL;
  for (; i < argc; i++) {
    String::Utf8Value arg(argv_->Get(Integer::New(i))->ToString());
    argv[i+1] = strdup(*arg);
  }

  // env
  i = 0;
  Local<Array> env_ = Local<Array>::Cast(args[2]);
  int envc = env_->Length();
  char **env = new char*[envc+1];
  env[envc] = NULL;
  for (; i < envc; i++) {
    String::Utf8Value pair(env_->Get(Integer::New(i))->ToString());
    env[i] = strdup(*pair);
  }

  // cwd
  String::Utf8Value cwd_(args[3]->ToString());
  char *cwd = strdup(*cwd_);

  // size
  struct winsize winp;
  winp.ws_col = args[4]->IntegerValue();
  winp.ws_row = args[5]->IntegerValue();
  winp.ws_xpixel = 0;
  winp.ws_ypixel = 0;

  // fork the pty
  int master;
  char name[40];
  pid_t pid = pty_forkpty(&master, name, NULL, &winp);

  if (pid) {
    for (i = 0; i < argl; i++) free(argv[i]);
    delete[] argv;
    for (i = 0; i < envc; i++) free(env[i]);
    delete[] env;
    free(cwd);
  }

  switch (pid) {
    case -1:
      return ThrowException(Exception::Error(
        String::New("forkpty(3) failed.")));
    case 0:
      if (strlen(cwd)) chdir(cwd);

      pty_execvpe(argv[0], argv, env);

      perror("execvp(3) failed.");
      _exit(1);
    default:
      if (pty_nonblock(master) == -1) {
        return ThrowException(Exception::Error(
          String::New("Could not set master fd to nonblocking.")));
      }

      Local<Object> obj = Object::New();
      obj->Set(String::New("fd"), Number::New(master));
      obj->Set(String::New("pid"), Number::New(pid));
      obj->Set(String::New("pty"), String::New(name));

      return scope.Close(obj);
  }

  return Undefined();
}

/**
 * PtyOpen
 * pty.open(cols, rows)
 */

static Handle<Value>
PtyOpen(const Arguments& args) {
  HandleScope scope;

  if (args.Length() != 2
      || !args[0]->IsNumber()
      || !args[1]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("Usage: pty.open(cols, rows)")));
  }

  // size
  struct winsize winp;
  winp.ws_col = args[0]->IntegerValue();
  winp.ws_row = args[1]->IntegerValue();
  winp.ws_xpixel = 0;
  winp.ws_ypixel = 0;

  // pty
  int master, slave;
  char name[40];
  int ret = pty_openpty(&master, &slave, name, NULL, &winp);

  if (ret == -1) {
    return ThrowException(Exception::Error(
      String::New("openpty(3) failed.")));
  }

  if (pty_nonblock(master) == -1) {
    return ThrowException(Exception::Error(
      String::New("Could not set master fd to nonblocking.")));
  }

  if (pty_nonblock(slave) == -1) {
    return ThrowException(Exception::Error(
      String::New("Could not set slave fd to nonblocking.")));
  }

  Local<Object> obj = Object::New();
  obj->Set(String::New("master"), Number::New(master));
  obj->Set(String::New("slave"), Number::New(slave));
  obj->Set(String::New("pty"), String::New(name));

  return scope.Close(obj);
}

/**
 * Resize Functionality
 * pty.resize(fd, cols, rows)
 */

static Handle<Value>
PtyResize(const Arguments& args) {
  HandleScope scope;

  if (args.Length() != 3
      || !args[0]->IsNumber()
      || !args[1]->IsNumber()
      || !args[2]->IsNumber()) {
    return ThrowException(Exception::Error(
      String::New("Usage: pty.resize(fd, cols, rows)")));
  }

  int fd = args[0]->IntegerValue();

  struct winsize winp;
  winp.ws_col = args[1]->IntegerValue();
  winp.ws_row = args[2]->IntegerValue();
  winp.ws_xpixel = 0;
  winp.ws_ypixel = 0;

  if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
    return ThrowException(Exception::Error(
      String::New("ioctl(2) failed.")));
  }

  return Undefined();
}

/**
 * PtyGetProc
 * Foreground Process Name
 * pty.process(fd, tty)
 */

static Handle<Value>
PtyGetProc(const Arguments& args) {
  HandleScope scope;

  if (args.Length() != 2
      || !args[0]->IsNumber()
      || !args[1]->IsString()) {
    return ThrowException(Exception::Error(
      String::New("Usage: pty.process(fd, tty)")));
  }

  int fd = args[0]->IntegerValue();

  String::Utf8Value tty_(args[1]->ToString());
  char *tty = strdup(*tty_);
  char *name = pty_getproc(fd, tty);
  free(tty);

  if (name == NULL) {
    return Undefined();
  }

  Local<String> name_ = String::New(name);
  free(name);
  return scope.Close(name_);
}

/**
 * execvpe
 */

// execvpe(3) is not portable.
// http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html
static int
pty_execvpe(const char *file, char **argv, char **envp) {
  char **old = environ;
  environ = envp;
  int ret = execvp(file, argv);
  environ = old;
  return ret;
}

/**
 * Nonblocking FD
 */

static int
pty_nonblock(int fd) {
  int flags = fcntl(fd, F_GETFL, 0);
  if (flags == -1) return -1;
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

/**
 * pty_getproc
 * Taken from tmux.
 */

// Taken from: tmux (http://tmux.sourceforge.net/)
// Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
// Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#if defined(__linux__)

static char *
pty_getproc(int fd, char *tty) {
  FILE *f;
  char *path, *buf;
  size_t len;
  int ch;
  pid_t pgrp;
  int r;

  if ((pgrp = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  r = asprintf(&path, "/proc/%lld/cmdline", (long long)pgrp);
  if (r == -1 || path == NULL) return NULL;

  if ((f = fopen(path, "r")) == NULL) {
    free(path);
    return NULL;
  }

  free(path);

  len = 0;
  buf = NULL;
  while ((ch = fgetc(f)) != EOF) {
    if (ch == '\0') break;
    buf = (char *)realloc(buf, len + 2);
    if (buf == NULL) return NULL;
    buf[len++] = ch;
  }

  if (buf != NULL) {
    buf[len] = '\0';
  }

  fclose(f);
  return buf;
}

#elif defined(__APPLE__)

static char *
pty_getproc(int fd, char *tty) {
  int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
  size_t size;
  struct kinfo_proc kp;

  if ((mib[3] = tcgetpgrp(fd)) == -1) {
    return NULL;
  }

  size = sizeof kp;
  if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
    return NULL;
  }

  if (*kp.kp_proc.p_comm == '\0') {
    return NULL;
  }

  return strdup(kp.kp_proc.p_comm);
}

#else

static char *
pty_getproc(int fd, char *tty) {
  return NULL;
}

#endif

/**
 * openpty(3) / forkpty(3)
 */

static int
pty_openpty(int *amaster, int *aslave, char *name,
            const struct termios *termp,
            const struct winsize *winp) {
#if defined(__sun)
  int master = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if (master == -1) return -1;
  if (amaster) *amaster = master;

  if (grantpt(master) == -1) goto err;
  if (unlockpt(master) == -1) goto err;

  char *slave_name = ptsname(master);
  if (slave_name == NULL) goto err;
  if (name) strcpy(name, slave_name);

  int slave = open(slave_name, O_RDWR | O_NOCTTY);
  if (slave == -1) goto err;
  if (aslave) *aslave = slave;

  if (termp) tcsetattr(slave, TCSAFLUSH, termp);
  if (winp) ioctl(slave, TIOCSWINSZ, winp);

  ioctl(slave, I_PUSH, "ptem");
  ioctl(slave, I_PUSH, "ldterm");
  ioctl(slave, I_PUSH, "ttcompat");

  return 0;

err:
  close(master);
  return -1;
#else
  return openpty(amaster, aslave, name, termp, winp);
#endif
}

static pid_t
pty_forkpty(int *amaster, char *name,
            const struct termios *termp,
            const struct winsize *winp) {
#if defined(__sun)
  int master, slave;

  int ret = pty_openpty(&master, &slave, name, termp, winp);
  if (ret == -1) return -1;
  if (amaster) *amaster = master;

  pid_t pid = fork();

  switch (pid) {
    case -1:
      close(master);
      close(slave);
      return -1;
    case 0:
      close(master);

      setsid();

#if defined(TIOCSCTTY)
      // glibc does this
      if (ioctl(slave, TIOCSCTTY, NULL) == -1) _exit(1);
#endif

      dup2(slave, 0);
      dup2(slave, 1);
      dup2(slave, 2);

      if (slave > 2) close(slave);

      return 0;
    default:
      close(slave);
      return pid;
  }

  return -1;
#else
  return forkpty(amaster, name, termp, winp);
#endif
}

/**
 * Init
 */

extern "C" void
init(Handle<Object> target) {
  HandleScope scope;
  NODE_SET_METHOD(target, "fork", PtyFork);
  NODE_SET_METHOD(target, "open", PtyOpen);
  NODE_SET_METHOD(target, "resize", PtyResize);
  NODE_SET_METHOD(target, "process", PtyGetProc);
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include <string>
#include <node.h>
#include <node_buffer.h>
#include <v8.h>
#include <qrencode.h>
#include <png.h>

using namespace v8;

const unsigned int QRC_MAX_SIZE[] = { 2938, 2319, 1655, 1268 };
const int WHITE = 16777216;

struct Qrc_Params {
  const char* data;
  QRecLevel ec_level;
  int dot_size;
  int margin;
  int foreground_color;
  int background_color;
  int version;

  Qrc_Params(std::string p_data, QRecLevel p_ec_level = QR_ECLEVEL_L,
          int p_version = 0,
          int p_dot_size = 3, int p_margin = 4,
          int p_foreground_color = 0x0, int p_background_color = 0xffffff) {
    data = new char[p_data.length() + 1];
    strcpy((char*)data, p_data.c_str());
    ec_level = p_ec_level;
    version = p_version;
    dot_size = p_dot_size;
    margin = p_margin;
    foreground_color = p_foreground_color;
    background_color = p_background_color;
  }

  ~Qrc_Params() {
    delete data;
  }
};

struct Qrc_Png_Buffer {
  char *data;
  size_t size;
  Qrc_Png_Buffer() {
    data = NULL;
    size = 0;
  }
  ~Qrc_Png_Buffer() {
    if (data) {
      free(data);
    }
  }
};

Qrc_Params* ValidateArgs(const Arguments& args) {
  struct Qrc_Params* params;

  if (args.Length() < 1 || !args[0]->IsString()) {
    ThrowException(Exception::TypeError(String::New("No source string given")));
    return NULL;
  }
  std::string data(*v8::String::Utf8Value(args[0]));
  if (data.length() < 1 || data.length() > QRC_MAX_SIZE[0]) {
    ThrowException(Exception::RangeError(String::New("Source string length out of range")));
    return NULL;
  }
  params = new Qrc_Params(data);

  if (args.Length() > 1) {
    if (!args[1]->IsObject()) {
      ThrowException(Exception::TypeError(String::New("Second argument must be an object")));
      delete params;
      return NULL;
    }
    Local<Object> paramsObj = Local<Object>::Cast(args[1]);
    Local<Value> paramsEcLevel = paramsObj->Get(String::New("ecLevel"));
    if (!paramsEcLevel->IsUndefined()) {
      if (!paramsEcLevel->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for EC level")));
        delete params;
        return NULL;
      } else if (paramsEcLevel->IntegerValue() < QR_ECLEVEL_L || paramsEcLevel->IntegerValue() > QR_ECLEVEL_H) {
        ThrowException(Exception::RangeError(String::New("EC level out of range")));
        delete params;
        return NULL;
      } else {
        params->ec_level = (QRecLevel) paramsEcLevel->IntegerValue();
        if (data.length() > QRC_MAX_SIZE[params->ec_level]) {
          ThrowException(Exception::RangeError(String::New("Source string length out of range")));
          delete params;
          return NULL;
        }
      }
    }
    Local<Value> paramsDotSize = paramsObj->Get(String::New("dotSize"));
    if (!paramsDotSize->IsUndefined()) {
      if (!paramsDotSize->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for dot size")));
        delete params;
        return NULL;
      } else if (paramsDotSize->IntegerValue() < 1 || paramsDotSize->IntegerValue() > 50) {
        ThrowException(Exception::RangeError(String::New("Dot size out of range")));
        delete params;
        return NULL;
      } else {
        params->dot_size = paramsDotSize->IntegerValue();
      }
    }
    Local<Value> paramsMargin = paramsObj->Get(String::New("margin"));
    if (!paramsMargin->IsUndefined()) {
      if (!paramsMargin->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for margin size")));
        delete params;
        return NULL;
      } else if (paramsMargin->IntegerValue() < 0 || paramsMargin->IntegerValue() > 10) {
        ThrowException(Exception::RangeError(String::New("Margin size out of range")));
        delete params;
        return NULL;
      } else {
        params->margin = paramsMargin->IntegerValue();
      }
    }
    Local<Value> paramsFgColor = paramsObj->Get(String::New("foregroundColor"));
    if (!paramsFgColor->IsUndefined()) {
      if (!paramsFgColor->IsUint32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for foreground color")));
        delete params;
        return NULL;
      } else if (paramsFgColor->IntegerValue() < 0 || paramsFgColor->IntegerValue() >= WHITE) {
        ThrowException(Exception::RangeError(String::New("Foreground color out of range")));
        delete params;
        return NULL;
      } else {
        params->foreground_color = paramsFgColor->IntegerValue();
      }
    }
    Local<Value> paramsBgColor = paramsObj->Get(String::New("backgroundColor"));
    if (!paramsBgColor->IsUndefined()) {
      if (!paramsBgColor->IsUint32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for background color")));
        delete params;
        return NULL;
      } else if (paramsBgColor->IntegerValue() < 0 || paramsBgColor->IntegerValue() >= WHITE) {
        ThrowException(Exception::RangeError(String::New("Background color out of range")));
        delete params;
        return NULL;
      } else {
        params->background_color = paramsBgColor->IntegerValue();
      }
    }
    Local<Value> paramsVersion = paramsObj->Get(String::New("version"));
    if (!paramsVersion->IsUndefined()) {
      if (!paramsVersion->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for version")));
        delete params;
        return NULL;
      } else if (paramsVersion->IntegerValue() < 1 || paramsVersion->IntegerValue() > QRSPEC_VERSION_MAX) {
        ThrowException(Exception::RangeError(String::New("Version out of range")));
        delete params;
        return NULL;
      } else {
        params->version = paramsVersion->IntegerValue();
      }
    }
  }

  return params;
}

QRcode* Encode(Qrc_Params* params) {
  QRcode* code;
  code = QRcode_encodeString8bit((const char*)params->data, params->version, params->ec_level);
  return code;
}

Handle<Value> EncodeBuf(const Arguments& args) {
  HandleScope scope;
  Local<Object> codeObj = Object::New();

  Qrc_Params* params = ValidateArgs(args);
  if (!params) {
    return scope.Close(codeObj);
  }

  QRcode* code = Encode(params);
  delete params;
  if (code != NULL) {
    Local<node::Buffer> buffer = node::Buffer::New((const char*)code->data, code->width * code->width);
    codeObj->Set(String::NewSymbol("width"), Integer::New(code->width));
    codeObj->Set(String::NewSymbol("version"), Integer::New(code->version));
    codeObj->Set(String::NewSymbol("data"), buffer->handle_);
    QRcode_free(code);
  }
  return scope.Close(codeObj);
}

void Qrc_png_write_buffer(png_structp png_ptr, png_bytep data, png_size_t length) {
  Qrc_Png_Buffer* b = (Qrc_Png_Buffer *)png_get_io_ptr(png_ptr);
  size_t nsize = b->size + length;

  if (b->data) {
    b->data = (char *)realloc(b->data, nsize);
  } else {
    b->data = (char *)malloc(nsize);
  }

  if (!b->data) {
    png_error(png_ptr, "Write Error");
  }

  memcpy(b->data + b->size, data, length);
  b->size += length;
}

Handle<Value> EncodePNG(const Arguments& args) {
  HandleScope scope;
  Local<Object> obj = Object::New();

  Qrc_Params* params = ValidateArgs(args);
  if (!params) {
    return scope.Close(obj);
  }

  QRcode* code = Encode(params);

  if (code != NULL) {
    Qrc_Png_Buffer* bp = new Qrc_Png_Buffer();

    png_structp png_ptr;
    png_infop info_ptr;
    png_colorp png_plte;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
        NULL, NULL, NULL);
    if (!png_ptr) {
      delete params;
      return scope.Close(obj);
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
      png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
      delete params;
      return scope.Close(obj);
    }

    if (setjmp(png_jmpbuf(png_ptr))) {
      png_destroy_write_struct(&png_ptr, &info_ptr);
      delete params;
      return scope.Close(obj);
    }

    png_set_write_fn(png_ptr, bp, Qrc_png_write_buffer, NULL);

    png_plte = (png_colorp) malloc(sizeof(png_color) * 2);
    png_plte[0].red = params->background_color >> 16 & 0xFF;
    png_plte[0].green = params->background_color >> 8 & 0xFF;
    png_plte[0].blue = params->background_color & 0xFF;
    png_plte[1].red = params->foreground_color >> 16 & 0xFF;
    png_plte[1].green = params->foreground_color >> 8 & 0xFF;
    png_plte[1].blue = params->foreground_color & 0xFF;

    png_set_PLTE(png_ptr, info_ptr, png_plte, 2);

    png_set_IHDR(png_ptr, info_ptr, (code->width + params->margin * 2) * params->dot_size, (code->width + params->margin * 2) * params->dot_size, 1,
        PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
        PNG_FILTER_TYPE_DEFAULT);

    png_write_info(png_ptr, info_ptr);

    png_set_packing(png_ptr);

    unsigned char* row = new unsigned char[(code->width + params->margin * 2) * params->dot_size];

    for (int y = -(params->margin); y < code->width + params->margin; y++) {
      for (int x = -(params->margin * params->dot_size); x < (code->width + params->margin) * params->dot_size; x += params->dot_size) {
        for (int d = 0; d < params->dot_size; d++) {
          if (y < 0 || y > code->width - 1 || x < 0 || x > ((code->width - 1) * params->dot_size)) {
            row[x + params->margin * params->dot_size + d] = 0;
          } else {
            row[x + params->margin * params->dot_size + d] = code->data[y * code->width + x/params->dot_size] << 7;
          }
        }
      }
      for (int d = 0; d < params->dot_size; d++) {
        png_write_row(png_ptr, row);
      }
    }

    png_write_end(png_ptr, info_ptr);
    png_destroy_write_struct(&png_ptr, &info_ptr);

    delete[] row;
    free(png_plte);

    Local<node::Buffer> buffer = node::Buffer::New(bp->data, bp->size);
    obj->Set(String::NewSymbol("width"), Integer::New(code->width));
    obj->Set(String::NewSymbol("version"), Integer::New(code->version));
    obj->Set(String::NewSymbol("data"), buffer->handle_);
    QRcode_free(code);
    delete bp;
  }
  delete params;
  return scope.Close(obj);
}

void init(Handle<Object> exports) {
  exports->Set(String::NewSymbol("encode"),
      FunctionTemplate::New(EncodeBuf)->GetFunction());
  exports->Set(String::NewSymbol("encodePng"),
      FunctionTemplate::New(EncodePNG)->GetFunction());
}

NODE_MODULE(qrc, init)
<commit_msg>Reorder validation code<commit_after>#include <cstdlib>
#include <string>
#include <node.h>
#include <node_buffer.h>
#include <v8.h>
#include <qrencode.h>
#include <png.h>

using namespace v8;

const unsigned int QRC_MAX_SIZE[] = { 2938, 2319, 1655, 1268 };
const int WHITE = 16777216;

struct Qrc_Params {
  const char* data;
  QRecLevel ec_level;
  int dot_size;
  int margin;
  int foreground_color;
  int background_color;
  int version;

  Qrc_Params(std::string p_data, QRecLevel p_ec_level = QR_ECLEVEL_L,
          int p_version = 0,
          int p_dot_size = 3, int p_margin = 4,
          int p_foreground_color = 0x0, int p_background_color = 0xffffff) {
    data = new char[p_data.length() + 1];
    strcpy((char*)data, p_data.c_str());
    ec_level = p_ec_level;
    version = p_version;
    dot_size = p_dot_size;
    margin = p_margin;
    foreground_color = p_foreground_color;
    background_color = p_background_color;
  }

  ~Qrc_Params() {
    delete data;
  }
};

struct Qrc_Png_Buffer {
  char *data;
  size_t size;
  Qrc_Png_Buffer() {
    data = NULL;
    size = 0;
  }
  ~Qrc_Png_Buffer() {
    if (data) {
      free(data);
    }
  }
};

Qrc_Params* ValidateArgs(const Arguments& args) {
  struct Qrc_Params* params;

  if (args.Length() < 1 || !args[0]->IsString()) {
    ThrowException(Exception::TypeError(String::New("No source string given")));
    return NULL;
  }
  std::string data(*v8::String::Utf8Value(args[0]));
  if (data.length() < 1 || data.length() > QRC_MAX_SIZE[0]) {
    ThrowException(Exception::RangeError(String::New("Source string length out of range")));
    return NULL;
  }
  params = new Qrc_Params(data);

  if (args.Length() > 1) {
    if (!args[1]->IsObject()) {
      ThrowException(Exception::TypeError(String::New("Second argument must be an object")));
      delete params;
      return NULL;
    }
    Local<Object> paramsObj = Local<Object>::Cast(args[1]);
    Local<Value> paramsVersion = paramsObj->Get(String::New("version"));
    if (!paramsVersion->IsUndefined()) {
      if (!paramsVersion->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for version")));
        delete params;
        return NULL;
      } else if (paramsVersion->IntegerValue() < 1 || paramsVersion->IntegerValue() > QRSPEC_VERSION_MAX) {
        ThrowException(Exception::RangeError(String::New("Version out of range")));
        delete params;
        return NULL;
      } else {
        params->version = paramsVersion->IntegerValue();
      }
    }
    Local<Value> paramsEcLevel = paramsObj->Get(String::New("ecLevel"));
    if (!paramsEcLevel->IsUndefined()) {
      if (!paramsEcLevel->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for EC level")));
        delete params;
        return NULL;
      } else if (paramsEcLevel->IntegerValue() < QR_ECLEVEL_L || paramsEcLevel->IntegerValue() > QR_ECLEVEL_H) {
        ThrowException(Exception::RangeError(String::New("EC level out of range")));
        delete params;
        return NULL;
      } else {
        params->ec_level = (QRecLevel) paramsEcLevel->IntegerValue();
        if (data.length() > QRC_MAX_SIZE[params->ec_level]) {
          ThrowException(Exception::RangeError(String::New("Source string length out of range")));
          delete params;
          return NULL;
        }
      }
    }
    Local<Value> paramsDotSize = paramsObj->Get(String::New("dotSize"));
    if (!paramsDotSize->IsUndefined()) {
      if (!paramsDotSize->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for dot size")));
        delete params;
        return NULL;
      } else if (paramsDotSize->IntegerValue() < 1 || paramsDotSize->IntegerValue() > 50) {
        ThrowException(Exception::RangeError(String::New("Dot size out of range")));
        delete params;
        return NULL;
      } else {
        params->dot_size = paramsDotSize->IntegerValue();
      }
    }
    Local<Value> paramsMargin = paramsObj->Get(String::New("margin"));
    if (!paramsMargin->IsUndefined()) {
      if (!paramsMargin->IsInt32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for margin size")));
        delete params;
        return NULL;
      } else if (paramsMargin->IntegerValue() < 0 || paramsMargin->IntegerValue() > 10) {
        ThrowException(Exception::RangeError(String::New("Margin size out of range")));
        delete params;
        return NULL;
      } else {
        params->margin = paramsMargin->IntegerValue();
      }
    }
    Local<Value> paramsFgColor = paramsObj->Get(String::New("foregroundColor"));
    if (!paramsFgColor->IsUndefined()) {
      if (!paramsFgColor->IsUint32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for foreground color")));
        delete params;
        return NULL;
      } else if (paramsFgColor->IntegerValue() < 0 || paramsFgColor->IntegerValue() >= WHITE) {
        ThrowException(Exception::RangeError(String::New("Foreground color out of range")));
        delete params;
        return NULL;
      } else {
        params->foreground_color = paramsFgColor->IntegerValue();
      }
    }
    Local<Value> paramsBgColor = paramsObj->Get(String::New("backgroundColor"));
    if (!paramsBgColor->IsUndefined()) {
      if (!paramsBgColor->IsUint32()) {
        ThrowException(Exception::TypeError(String::New("Wrong type for background color")));
        delete params;
        return NULL;
      } else if (paramsBgColor->IntegerValue() < 0 || paramsBgColor->IntegerValue() >= WHITE) {
        ThrowException(Exception::RangeError(String::New("Background color out of range")));
        delete params;
        return NULL;
      } else {
        params->background_color = paramsBgColor->IntegerValue();
      }
    }
  }

  return params;
}

QRcode* Encode(Qrc_Params* params) {
  QRcode* code;
  code = QRcode_encodeString8bit((const char*)params->data, params->version, params->ec_level);
  return code;
}

Handle<Value> EncodeBuf(const Arguments& args) {
  HandleScope scope;
  Local<Object> codeObj = Object::New();

  Qrc_Params* params = ValidateArgs(args);
  if (!params) {
    return scope.Close(codeObj);
  }

  QRcode* code = Encode(params);
  delete params;
  if (code != NULL) {
    Local<node::Buffer> buffer = node::Buffer::New((const char*)code->data, code->width * code->width);
    codeObj->Set(String::NewSymbol("width"), Integer::New(code->width));
    codeObj->Set(String::NewSymbol("version"), Integer::New(code->version));
    codeObj->Set(String::NewSymbol("data"), buffer->handle_);
    QRcode_free(code);
  }
  return scope.Close(codeObj);
}

void Qrc_png_write_buffer(png_structp png_ptr, png_bytep data, png_size_t length) {
  Qrc_Png_Buffer* b = (Qrc_Png_Buffer *)png_get_io_ptr(png_ptr);
  size_t nsize = b->size + length;

  if (b->data) {
    b->data = (char *)realloc(b->data, nsize);
  } else {
    b->data = (char *)malloc(nsize);
  }

  if (!b->data) {
    png_error(png_ptr, "Write Error");
  }

  memcpy(b->data + b->size, data, length);
  b->size += length;
}

Handle<Value> EncodePNG(const Arguments& args) {
  HandleScope scope;
  Local<Object> obj = Object::New();

  Qrc_Params* params = ValidateArgs(args);
  if (!params) {
    return scope.Close(obj);
  }

  QRcode* code = Encode(params);

  if (code != NULL) {
    Qrc_Png_Buffer* bp = new Qrc_Png_Buffer();

    png_structp png_ptr;
    png_infop info_ptr;
    png_colorp png_plte;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
        NULL, NULL, NULL);
    if (!png_ptr) {
      delete params;
      return scope.Close(obj);
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
      png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
      delete params;
      return scope.Close(obj);
    }

    if (setjmp(png_jmpbuf(png_ptr))) {
      png_destroy_write_struct(&png_ptr, &info_ptr);
      delete params;
      return scope.Close(obj);
    }

    png_set_write_fn(png_ptr, bp, Qrc_png_write_buffer, NULL);

    png_plte = (png_colorp) malloc(sizeof(png_color) * 2);
    png_plte[0].red = params->background_color >> 16 & 0xFF;
    png_plte[0].green = params->background_color >> 8 & 0xFF;
    png_plte[0].blue = params->background_color & 0xFF;
    png_plte[1].red = params->foreground_color >> 16 & 0xFF;
    png_plte[1].green = params->foreground_color >> 8 & 0xFF;
    png_plte[1].blue = params->foreground_color & 0xFF;

    png_set_PLTE(png_ptr, info_ptr, png_plte, 2);

    png_set_IHDR(png_ptr, info_ptr, (code->width + params->margin * 2) * params->dot_size, (code->width + params->margin * 2) * params->dot_size, 1,
        PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
        PNG_FILTER_TYPE_DEFAULT);

    png_write_info(png_ptr, info_ptr);

    png_set_packing(png_ptr);

    unsigned char* row = new unsigned char[(code->width + params->margin * 2) * params->dot_size];

    for (int y = -(params->margin); y < code->width + params->margin; y++) {
      for (int x = -(params->margin * params->dot_size); x < (code->width + params->margin) * params->dot_size; x += params->dot_size) {
        for (int d = 0; d < params->dot_size; d++) {
          if (y < 0 || y > code->width - 1 || x < 0 || x > ((code->width - 1) * params->dot_size)) {
            row[x + params->margin * params->dot_size + d] = 0;
          } else {
            row[x + params->margin * params->dot_size + d] = code->data[y * code->width + x/params->dot_size] << 7;
          }
        }
      }
      for (int d = 0; d < params->dot_size; d++) {
        png_write_row(png_ptr, row);
      }
    }

    png_write_end(png_ptr, info_ptr);
    png_destroy_write_struct(&png_ptr, &info_ptr);

    delete[] row;
    free(png_plte);

    Local<node::Buffer> buffer = node::Buffer::New(bp->data, bp->size);
    obj->Set(String::NewSymbol("width"), Integer::New(code->width));
    obj->Set(String::NewSymbol("version"), Integer::New(code->version));
    obj->Set(String::NewSymbol("data"), buffer->handle_);
    QRcode_free(code);
    delete bp;
  }
  delete params;
  return scope.Close(obj);
}

void init(Handle<Object> exports) {
  exports->Set(String::NewSymbol("encode"),
      FunctionTemplate::New(EncodeBuf)->GetFunction());
  exports->Set(String::NewSymbol("encodePng"),
      FunctionTemplate::New(EncodePNG)->GetFunction());
}

NODE_MODULE(qrc, init)
<|endoftext|>


<commit_before>
#include "vm.hpp"

#define JUMP(pos) loc = codes + pos
#define SKIP ++loc; if( Compiler::hasOperand(*loc) ) loc += _C_OP_SIZE
#define RUN loc = codes + length + 1
#define LOC loc - codes
#define LLOC loc - sloc
#define RLOC sloc - codes
#define CODE (*loc)
#define OP _C_OP_UNPACKA(loc, 1)
#define HADOP loc += _C_OP_SIZE;

#define _EPUSH(v) this->stack.push_back(v)
#define _EPOP this->stack.pop_back()
#define _ETOP this->stack.back()
#define _ETOP1 this->stack.backOffset(1)

#define _DPUSH(v) this->dataStack.push_back(v)
#define _DPOP this->dataStack.pop_back()
#define _DTOP this->dataStack.back()
#define _DSEARCH(v) searchStack(&this->dataStack, v)
#define _DSEARCHNR(v) searchStackNR(&this->dataStack, v)

#ifdef DEBUG
#define DBGHEAD printf("%03ld: %03ld: %-8s\t", LOC, LLOC, Compiler::opcodeName(*loc))
#define DBG(...) printf(__VA_ARGS__)
#define DBGFOOT printf("\n");
#else
#define DBGHEAD
#define DBG(...)
#define DBGFOOT
#endif

namespace mustache {

std::string * VM::execute(uint8_t * codes, size_t length, Data * data)
{
  std::string * output = new std::string;
  execute(codes, length, data, output);
  return output;
}

void VM::execute(std::vector<uint8_t> * codes, Data * data, std::string * output)
{
#ifdef MUSTACHE_HAVE_CXX11
  execute(codes->data(), codes->size(), data, output);
#else
  uint8_t * codes_buf = NULL;
  size_t codes_length = 0;
  Compiler::vectorToBuffer(codes, &codes_buf, &codes_length);
  execute(codes_buf, codes_length, data, output);
  free(codes_buf);
#endif
}

void VM::execute(uint8_t * codes, size_t length, Data * data, std::string * output)
{
  register uint8_t * loc = codes;
  register uint8_t * end = codes + length;
  register uint8_t * sloc = loc;
  register uint32_t * symbols = NULL;

  // Clear the output buffer
  outputBuffer.clear();

  // Push the initial data onto the data stack
  _DPUSH(data);

  // Read the symbol table
  {
    uint32_t nSymbols = _UNPACK4A(loc, 0);
    uint32_t i;
    loc += 4;
    symbols = (uint32_t *) malloc(sizeof(uint32_t) * nSymbols);
    for( i = 0; i < nSymbols; i++ ) {
      symbols[i] = _UNPACK4A(loc, 0);
      DBG("Symbol %d @ %u\n", i, symbols[i]);
      loc += 4;
    }
  }

  // Jump to main
  sloc = loc = codes + symbols[0] + 1 + _C_OP_SIZE;

  // First thing to put on stack is code past the end of length so that the VM
  // will terminate when returning from main
  _EPUSH(LOC);
  _EPUSH(length + 1);

  for( ; loc < end; loc++ ) {
    DBGHEAD;
    switch( CODE ) {
      /** Basic operations -------------------------------------------------- */
      case opcodes::PRINTL:
        DBG("Jump: %u", OP);
        outputBuffer.append((const char *) (codes + OP));
        HADOP;
        break;
      case opcodes::PRINTSYM:
        DBG("Symbol: %u, Jump: %u", OP, symbols[OP]);
        outputBuffer.append((const char *) (codes + symbols[OP] + 1 + _C_OP_SIZE)); // size of code + operand
        HADOP;
        break;
      case opcodes::CALL:
        DBG("Push: %ld, Jump: %u", LOC, OP);
        _EPUSH(RLOC);
        _EPUSH(LOC + _C_OP_SIZE);
        JUMP(OP - 1);  // Need -1 so increment on loop will cancel out
        // Don't need HADOP because JUMP
        sloc = loc + 1;
        break;
      case opcodes::CALLSYM:
        DBG("Symbol: %d, Push: %ld, Jump: %u", OP, LOC, symbols[OP]);
        _EPUSH(RLOC);
        _EPUSH(LOC + _C_OP_SIZE);
        JUMP(symbols[OP] + 1 + _C_OP_SIZE - 1);  // Need -1 so increment on loop will cancel out, size of code + operand
        // Don't need HADOP because JUMP
        sloc = loc + 1;
        break;
      case opcodes::RETURN:
        DBG("Jump: %lu", _ETOP);
        JUMP(_EPOP);
        sloc = codes + _EPOP;
        break;
      case opcodes::JUMP:
        DBG("Jump: %u", OP);
        JUMP(OP - 1); // -1 to cancel out the next loop increment
        // Don't need HADOP because JUMP
        break;
      case opcodes::JUMPL:
        DBG("Jump: %u", (RLOC) + OP);
        JUMP((RLOC) + OP - 1); // -1 to cancel out the next loop increment
        // Don't need HADOP because JUMP
        break;
      case opcodes::PUSH:
        DBG("Push: %u", OP);
        _EPUSH(OP);
        HADOP;
        break;
      case opcodes::POP:
        DBG("Top: %lu", _ETOP);
        _EPOP;
        break;
      case opcodes::INCR:
        DBG("%lu+1", _ETOP);
        _EPUSH(_EPOP + 1);
        break;
      case opcodes::IF_GE:
        DBG("%ld >= %ld", _ETOP, _ETOP1);
        if( _ETOP >= _ETOP1 ) {
          DBG(", GE");
        } else {
          DBG(", !GE");
          SKIP;
        }
        break;

      /** Data operations --------------------------------------------------- */
//      case opcodes::DPUSH:
//        DBG("Top %p", (Data *) _ETOP);
//        _DPUSH((Data *) _ETOP);
//        break;
      case opcodes::DPOP:
        DBG("DTop: %p", (Data *) _DTOP);
        _DPOP;
        break;
      case opcodes::DLOOKUP: {
        DBG("Symbol: %u", OP);
        const char * cstr = (const char *) (codes + OP);
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCH(&lookupstr));
        _DPUSH(_DSEARCH(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPSYM: {
        DBG("Symbol: %u, Jump: %d", OP, symbols[OP]);
        const char * cstr = (const char *) (codes + symbols[OP] + 1 + _C_OP_SIZE); // size of code + operand
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCH(&lookupstr));
        _DPUSH(_DSEARCH(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPNR: {
        DBG("Symbol: %u", *(1 + loc));
        char * cstr = (/*unsigned*/ char *) (codes + OP);
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCHNR(&lookupstr));
        _DPUSH(_DSEARCHNR(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPNRSYM: {
        DBG("Symbol: %u, Jump: %d", OP, symbols[OP]);
        const char * cstr = (const char *) (codes + symbols[OP] + 1 + _C_OP_SIZE); // size of code + operand
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCHNR(&lookupstr));
        _DPUSH(_DSEARCHNR(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPA: {
        DBG("Index: %lu, DPush: %p", _ETOP, _DTOP->array[_ETOP]);
        _DPUSH(_DTOP->array[_ETOP]);
        break;
      }
      case opcodes::DARRSIZE:
        if( _DTOP != NULL && _DTOP->type == Data::TypeArray ) {
          _EPUSH(_DTOP->length);
        } else {
          _EPUSH(0);
        }
        DBG("Size: %lu", _ETOP);
        break;
      case opcodes::DPRINT:
        DBG("DTop: %p", _DTOP);
        if( _DTOP != NULL && _DTOP->type == Data::TypeString ) {
          outputBuffer.append(*(_DTOP->val));
        }
        break;
      case opcodes::DPRINTE:
        DBG("DTop: %p", _DTOP);
        if( _DTOP != NULL && _DTOP->type == Data::TypeString ) {
          htmlspecialchars_append((_DTOP->val), &outputBuffer);
        }
        break;

      /** Data conditionals ------------------------------------------------- */
      case opcodes::DIF_EMPTY:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->isEmpty() ) {
          DBG(", empty");
        } else {
          DBG(", not empty");
          SKIP;
        }
        break;
      case opcodes::DIF_NOTEMPTY:
        DBG("DTop %p", _DTOP);
        if( _DTOP != NULL && !_DTOP->isEmpty() ) {
          DBG(", not empty");
        } else {
          DBG(", empty");
          SKIP;
        }
        break;
      case opcodes::DIF_HASH:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeMap ) {
          DBG(", not hash");
          SKIP;
        } else {
          DBG(", hash");
        }
        break;
      case opcodes::DIF_NOTHASH:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeMap ) {
          DBG(", not hash");
        } else {
          DBG(", hash");
          SKIP;
        }
        break;
      case opcodes::DIF_ARRAY:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeArray ) {
          DBG(", not array");
          SKIP;
        } else {
          DBG(", array");
        }
        break;
      case opcodes::DIF_NOTARRAY:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeArray ) {
          DBG(", not array");
        } else {
          DBG(", array");
          SKIP;
        }
        break;

      /** Unknown ----------------------------------------------------------- */
      case opcodes::CALLEXT:
        DBG("NOT IMPLEMENTED %d\n", OP);
        HADOP;
        break;
      case opcodes::NOOP:
        DBG("Jump: %d", length + 1);
        JUMP(length + 1);
        break;
      default:
        DBG("UNKNOWN 0x%02x\n", CODE);
        break;
    }
    DBGFOOT;
  }

  // Copy the internal output buffer to the output
  output->assign(outputBuffer);

  // Free
  free(symbols);
}

Data * VM::search(std::string * key)
{
  searchStack(&this->dataStack, key);
}

Data * VM::searchnr(std::string * key)
{
  searchStackNR(&this->dataStack, key);
}

}
<commit_msg>Fix stack size exceeded when VM not reinitialized<commit_after>
#include "vm.hpp"

#define JUMP(pos) loc = codes + pos
#define SKIP ++loc; if( Compiler::hasOperand(*loc) ) loc += _C_OP_SIZE
#define RUN loc = codes + length + 1
#define LOC loc - codes
#define LLOC loc - sloc
#define RLOC sloc - codes
#define CODE (*loc)
#define OP _C_OP_UNPACKA(loc, 1)
#define HADOP loc += _C_OP_SIZE;

#define _EPUSH(v) this->stack.push_back(v)
#define _EPOP this->stack.pop_back()
#define _ETOP this->stack.back()
#define _ETOP1 this->stack.backOffset(1)

#define _DPUSH(v) this->dataStack.push_back(v)
#define _DPOP this->dataStack.pop_back()
#define _DTOP this->dataStack.back()
#define _DSEARCH(v) searchStack(&this->dataStack, v)
#define _DSEARCHNR(v) searchStackNR(&this->dataStack, v)

#ifdef DEBUG
#define DBGHEAD printf("%03ld: %03ld: %-8s\t", LOC, LLOC, Compiler::opcodeName(*loc))
#define DBG(...) printf(__VA_ARGS__)
#define DBGFOOT printf("\n");
#else
#define DBGHEAD
#define DBG(...)
#define DBGFOOT
#endif

namespace mustache {

std::string * VM::execute(uint8_t * codes, size_t length, Data * data)
{
  std::string * output = new std::string;
  execute(codes, length, data, output);
  return output;
}

void VM::execute(std::vector<uint8_t> * codes, Data * data, std::string * output)
{
#ifdef MUSTACHE_HAVE_CXX11
  execute(codes->data(), codes->size(), data, output);
#else
  uint8_t * codes_buf = NULL;
  size_t codes_length = 0;
  Compiler::vectorToBuffer(codes, &codes_buf, &codes_length);
  execute(codes_buf, codes_length, data, output);
  free(codes_buf);
#endif
}

void VM::execute(uint8_t * codes, size_t length, Data * data, std::string * output)
{
  register uint8_t * loc = codes;
  register uint8_t * end = codes + length;
  register uint8_t * sloc = loc;
  register uint32_t * symbols = NULL;

  // Clear the output buffer
  outputBuffer.clear();

  // Clear the stack and push the initial data onto the data stack
  this->dataStack.clear();
  _DPUSH(data);

  // Read the symbol table
  {
    uint32_t nSymbols = _UNPACK4A(loc, 0);
    uint32_t i;
    loc += 4;
    symbols = (uint32_t *) malloc(sizeof(uint32_t) * nSymbols);
    for( i = 0; i < nSymbols; i++ ) {
      symbols[i] = _UNPACK4A(loc, 0);
      DBG("Symbol %d @ %u\n", i, symbols[i]);
      loc += 4;
    }
  }

  // Jump to main
  sloc = loc = codes + symbols[0] + 1 + _C_OP_SIZE;

  // First thing to put on stack is code past the end of length so that the VM
  // will terminate when returning from main
  _EPUSH(LOC);
  _EPUSH(length + 1);

  for( ; loc < end; loc++ ) {
    DBGHEAD;
    switch( CODE ) {
      /** Basic operations -------------------------------------------------- */
      case opcodes::PRINTL:
        DBG("Jump: %u", OP);
        outputBuffer.append((const char *) (codes + OP));
        HADOP;
        break;
      case opcodes::PRINTSYM:
        DBG("Symbol: %u, Jump: %u", OP, symbols[OP]);
        outputBuffer.append((const char *) (codes + symbols[OP] + 1 + _C_OP_SIZE)); // size of code + operand
        HADOP;
        break;
      case opcodes::CALL:
        DBG("Push: %ld, Jump: %u", LOC, OP);
        _EPUSH(RLOC);
        _EPUSH(LOC + _C_OP_SIZE);
        JUMP(OP - 1);  // Need -1 so increment on loop will cancel out
        // Don't need HADOP because JUMP
        sloc = loc + 1;
        break;
      case opcodes::CALLSYM:
        DBG("Symbol: %d, Push: %ld, Jump: %u", OP, LOC, symbols[OP]);
        _EPUSH(RLOC);
        _EPUSH(LOC + _C_OP_SIZE);
        JUMP(symbols[OP] + 1 + _C_OP_SIZE - 1);  // Need -1 so increment on loop will cancel out, size of code + operand
        // Don't need HADOP because JUMP
        sloc = loc + 1;
        break;
      case opcodes::RETURN:
        DBG("Jump: %lu", _ETOP);
        JUMP(_EPOP);
        sloc = codes + _EPOP;
        break;
      case opcodes::JUMP:
        DBG("Jump: %u", OP);
        JUMP(OP - 1); // -1 to cancel out the next loop increment
        // Don't need HADOP because JUMP
        break;
      case opcodes::JUMPL:
        DBG("Jump: %u", (RLOC) + OP);
        JUMP((RLOC) + OP - 1); // -1 to cancel out the next loop increment
        // Don't need HADOP because JUMP
        break;
      case opcodes::PUSH:
        DBG("Push: %u", OP);
        _EPUSH(OP);
        HADOP;
        break;
      case opcodes::POP:
        DBG("Top: %lu", _ETOP);
        _EPOP;
        break;
      case opcodes::INCR:
        DBG("%lu+1", _ETOP);
        _EPUSH(_EPOP + 1);
        break;
      case opcodes::IF_GE:
        DBG("%ld >= %ld", _ETOP, _ETOP1);
        if( _ETOP >= _ETOP1 ) {
          DBG(", GE");
        } else {
          DBG(", !GE");
          SKIP;
        }
        break;

      /** Data operations --------------------------------------------------- */
//      case opcodes::DPUSH:
//        DBG("Top %p", (Data *) _ETOP);
//        _DPUSH((Data *) _ETOP);
//        break;
      case opcodes::DPOP:
        DBG("DTop: %p", (Data *) _DTOP);
        _DPOP;
        break;
      case opcodes::DLOOKUP: {
        DBG("Symbol: %u", OP);
        const char * cstr = (const char *) (codes + OP);
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCH(&lookupstr));
        _DPUSH(_DSEARCH(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPSYM: {
        DBG("Symbol: %u, Jump: %d", OP, symbols[OP]);
        const char * cstr = (const char *) (codes + symbols[OP] + 1 + _C_OP_SIZE); // size of code + operand
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCH(&lookupstr));
        _DPUSH(_DSEARCH(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPNR: {
        DBG("Symbol: %u", *(1 + loc));
        char * cstr = (/*unsigned*/ char *) (codes + OP);
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCHNR(&lookupstr));
        _DPUSH(_DSEARCHNR(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPNRSYM: {
        DBG("Symbol: %u, Jump: %d", OP, symbols[OP]);
        const char * cstr = (const char *) (codes + symbols[OP] + 1 + _C_OP_SIZE); // size of code + operand
        DBG(", String: %s", cstr);
        lookupstr.assign(cstr);
        DBG(", DPush: %p", _DSEARCHNR(&lookupstr));
        _DPUSH(_DSEARCHNR(&lookupstr));
        HADOP;
        break;
      }
      case opcodes::DLOOKUPA: {
        DBG("Index: %lu, DPush: %p", _ETOP, _DTOP->array[_ETOP]);
        _DPUSH(_DTOP->array[_ETOP]);
        break;
      }
      case opcodes::DARRSIZE:
        if( _DTOP != NULL && _DTOP->type == Data::TypeArray ) {
          _EPUSH(_DTOP->length);
        } else {
          _EPUSH(0);
        }
        DBG("Size: %lu", _ETOP);
        break;
      case opcodes::DPRINT:
        DBG("DTop: %p", _DTOP);
        if( _DTOP != NULL && _DTOP->type == Data::TypeString ) {
          outputBuffer.append(*(_DTOP->val));
        }
        break;
      case opcodes::DPRINTE:
        DBG("DTop: %p", _DTOP);
        if( _DTOP != NULL && _DTOP->type == Data::TypeString ) {
          htmlspecialchars_append((_DTOP->val), &outputBuffer);
        }
        break;

      /** Data conditionals ------------------------------------------------- */
      case opcodes::DIF_EMPTY:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->isEmpty() ) {
          DBG(", empty");
        } else {
          DBG(", not empty");
          SKIP;
        }
        break;
      case opcodes::DIF_NOTEMPTY:
        DBG("DTop %p", _DTOP);
        if( _DTOP != NULL && !_DTOP->isEmpty() ) {
          DBG(", not empty");
        } else {
          DBG(", empty");
          SKIP;
        }
        break;
      case opcodes::DIF_HASH:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeMap ) {
          DBG(", not hash");
          SKIP;
        } else {
          DBG(", hash");
        }
        break;
      case opcodes::DIF_NOTHASH:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeMap ) {
          DBG(", not hash");
        } else {
          DBG(", hash");
          SKIP;
        }
        break;
      case opcodes::DIF_ARRAY:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeArray ) {
          DBG(", not array");
          SKIP;
        } else {
          DBG(", array");
        }
        break;
      case opcodes::DIF_NOTARRAY:
        DBG("DTop %p", _DTOP);
        if( _DTOP == NULL || _DTOP->type != Data::TypeArray ) {
          DBG(", not array");
        } else {
          DBG(", array");
          SKIP;
        }
        break;

      /** Unknown ----------------------------------------------------------- */
      case opcodes::CALLEXT:
        DBG("NOT IMPLEMENTED %d\n", OP);
        HADOP;
        break;
      case opcodes::NOOP:
        DBG("Jump: %d", length + 1);
        JUMP(length + 1);
        break;
      default:
        DBG("UNKNOWN 0x%02x\n", CODE);
        break;
    }
    DBGFOOT;
  }

  // Copy the internal output buffer to the output
  output->assign(outputBuffer);

  // Free
  free(symbols);
}

Data * VM::search(std::string * key)
{
  searchStack(&this->dataStack, key);
}

Data * VM::searchnr(std::string * key)
{
  searchStackNR(&this->dataStack, key);
}

}
<|endoftext|>


<commit_before>#ifndef SILICIUM_FILE_DESCRIPTOR_HPP
#define SILICIUM_FILE_DESCRIPTOR_HPP

#ifdef __linux__
#	include <silicium/linux/file_descriptor.hpp>
#endif

#ifdef _WIN32
#	include <silicium/win32/file_descriptor.hpp>
#endif

#include <silicium/exchange.hpp>

namespace Si
{
	struct file_descriptor //: private boost::noncopyable
	{
		native_file_handle handle = no_file_handle;

		file_descriptor() BOOST_NOEXCEPT
		{
		}

		file_descriptor(file_descriptor &&other) BOOST_NOEXCEPT
		{
			swap(other);
		}

		explicit file_descriptor(native_file_handle handle) BOOST_NOEXCEPT
			: handle(handle)
		{
		}

		file_descriptor &operator = (file_descriptor &&other) BOOST_NOEXCEPT
		{
			swap(other);
			return *this;
		}

		void swap(file_descriptor &other) BOOST_NOEXCEPT
		{
			using std::swap;
			swap(handle, other.handle);
		}

		void close() BOOST_NOEXCEPT
		{
			file_descriptor().swap(*this);
		}

		native_file_handle release() BOOST_NOEXCEPT
		{
			return Si::exchange(handle, no_file_handle);
		}

		~file_descriptor() BOOST_NOEXCEPT
		{
			if (handle != no_file_handle)
			{
				terminating_close(handle);
			}
		}
	};
}

#endif
<commit_msg>make file_descriptor explicitly non-copyable<commit_after>#ifndef SILICIUM_FILE_DESCRIPTOR_HPP
#define SILICIUM_FILE_DESCRIPTOR_HPP

#ifdef __linux__
#	include <silicium/linux/file_descriptor.hpp>
#endif

#ifdef _WIN32
#	include <silicium/win32/file_descriptor.hpp>
#endif

#include <silicium/exchange.hpp>

namespace Si
{
	struct file_descriptor : private boost::noncopyable
	{
		native_file_handle handle = no_file_handle;

		file_descriptor() BOOST_NOEXCEPT
		{
		}

		file_descriptor(file_descriptor &&other) BOOST_NOEXCEPT
		{
			swap(other);
		}

		explicit file_descriptor(native_file_handle handle) BOOST_NOEXCEPT
			: handle(handle)
		{
		}

		file_descriptor &operator = (file_descriptor &&other) BOOST_NOEXCEPT
		{
			swap(other);
			return *this;
		}

		void swap(file_descriptor &other) BOOST_NOEXCEPT
		{
			using std::swap;
			swap(handle, other.handle);
		}

		void close() BOOST_NOEXCEPT
		{
			file_descriptor().swap(*this);
		}

		native_file_handle release() BOOST_NOEXCEPT
		{
			return Si::exchange(handle, no_file_handle);
		}

		~file_descriptor() BOOST_NOEXCEPT
		{
			if (handle != no_file_handle)
			{
				terminating_close(handle);
			}
		}
	};
}

#endif
<|endoftext|>


<commit_before>#include "BrainStdAfx.h"
#include "HAL.h"
#include "Comms.h"

#include <sys/time.h>
#include <sys/resource.h>

#include <boost/program_options.hpp>
#include <thread>
#include <iostream>

size_t s_test = 0;
bool s_exit = false;
boost::asio::io_service s_async_io_service;

namespace boost
{
	void throw_exception(std::exception const & e)
	{
        QLOGE("boost::exception {}", e.what());
		throw e;
    }
}

// Define the function to be called when ctrl-c (SIGINT) signal is sent to process
void signal_handler(int signum)
{
    if (s_exit)
    {
        QLOGI("Forcing an exit due to signal {}", signum);
        abort();
    }
    s_exit = true;
    QLOGI("Exitting due to signal {}", signum);
}

void out_of_memory_handler()
{
    QLOGE("Out of memory");
    std::abort();
}

int main(int argc, char const* argv[])
{
    signal(SIGINT, signal_handler); // Trap basic signals (exit cleanly)
    signal(SIGKILL, signal_handler);
    signal(SIGUSR1, signal_handler);
    signal(SIGQUIT, signal_handler);
//    signal(SIGABRT, signal_handler);
    signal(SIGTERM, signal_handler);

    //set the new_handler
    std::set_new_handler(out_of_memory_handler);

    q::logging::add_logger(q::logging::Logger_uptr(new q::logging::Console_Logger()));
    q::logging::set_decorations(q::logging::Decorations(q::logging::Decoration::TIMESTAMP, q::logging::Decoration::LEVEL, q::logging::Decoration::TOPIC));

    QLOG_TOPIC("silk");

//    q::util::Rand rnd;
//    while (true)
//    {
//        math::vec3f target(rnd.get_float() * 40.0, rnd.get_float() * 40.0, rnd.get_float() * 10.0);
//        test_mm(target, 0.01);
//        test_mm(target, 0.1);
//        test_mm(target, 0.5);
//    }

    namespace po = boost::program_options;

	po::options_description desc("Options");
	desc.add_options()
		("help", "produce help message")
        ("blind", "no camera")
        ("test", po::value<size_t>(), "test");

	po::variables_map vm;
	po::store(po::parse_command_line(argc, argv, desc), vm);
	po::notify(vm);

	if (vm.count("help"))
	{
        std::cout << desc << "\n";
		return 1;
	}

    s_test = vm.count("test") ? vm["test"].as<size_t>() : size_t(0);
    //bool blind = vm.count("blind") != 0;

    QLOGI("Creating io_service thread");

    boost::shared_ptr<boost::asio::io_service::work> async_work(new boost::asio::io_service::work(s_async_io_service));
    auto async_thread = std::thread([]() { s_async_io_service.run(); });

#if defined RASPBERRY_PI
    {
        int policy = SCHED_FIFO;
        struct sched_param param;
        param.sched_priority = sched_get_priority_max(policy);
        if (pthread_setschedparam(pthread_self(), policy, &param) != 0)
        {
            perror("main sched_setscheduler");
            exit(EXIT_FAILURE);
        }
        policy = SCHED_IDLE;
        param.sched_priority = sched_get_priority_min(policy);
        if (pthread_setschedparam(async_thread.native_handle(), policy, &param) != 0)
        {
            perror("async_thread sched_setscheduler");
            exit(EXIT_FAILURE);
        }
    }
#endif

    try
    {
        silk::HAL hal;
        silk::Comms comms(hal);

        if (!hal.init(comms))
        {
            QLOGE("Hardware failure! Aborting");
            goto exit;
        }

#if defined RASPBERRY_PI
        if (!comms.start_rfmon("mon0", 5))
        {
            QLOGE("Cannot start communication channel! Aborting");
            goto exit;
        }
#else
        if (!comms.start_udp(8000, 8001))
        {
            QLOGE("Cannot start communication channel! Aborting");
            goto exit;
        }
#endif

//        while (!s_exit)
//        {
//            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
//            QLOGI("Waiting for comms to connect...");
//            if (comms.is_connected())
//            {
//                break;
//            }
//        }

        QLOGI("All systems up. Ready to fly...");

        {
            constexpr std::chrono::microseconds PERIOD(5000);
            auto last = q::Clock::now();
            while (!s_exit)
            {
                auto start = q::Clock::now();
                auto dt = start - last;
                last = start;
                if (dt > std::chrono::milliseconds(10))
                {
                    QLOGW("Process Latency of {}!!!!!", dt);
                }
                comms.process();
                hal.process();

                //No sleeping here!!! process as fast as possible as the nodes are not always in the ideal order
                // and out of order nodes will be processes next 'frame'. So the quicker the frames, the smaller the lag between nodes
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
            }
        }

exit:
        QLOGI("Stopping everything");

        //stop threads
        async_work.reset();
        s_async_io_service.stop();
        if (async_thread.joinable())
        {
            std::this_thread::yield();
            async_thread.join();
        }
        hal.shutdown();
    }
    catch (std::exception const& e)
    {
        QLOGE("exception: {}", e.what());
        abort();
    }

    QLOGI("Closing");
}

<commit_msg>removed unneded sleep and reduced latency alarm duration<commit_after>#include "BrainStdAfx.h"
#include "HAL.h"
#include "Comms.h"

#include <sys/time.h>
#include <sys/resource.h>

#include <boost/program_options.hpp>
#include <thread>
#include <iostream>

size_t s_test = 0;
bool s_exit = false;
boost::asio::io_service s_async_io_service;

namespace boost
{
	void throw_exception(std::exception const & e)
	{
        QLOGE("boost::exception {}", e.what());
		throw e;
    }
}

// Define the function to be called when ctrl-c (SIGINT) signal is sent to process
void signal_handler(int signum)
{
    if (s_exit)
    {
        QLOGI("Forcing an exit due to signal {}", signum);
        abort();
    }
    s_exit = true;
    QLOGI("Exitting due to signal {}", signum);
}

void out_of_memory_handler()
{
    QLOGE("Out of memory");
    std::abort();
}

int main(int argc, char const* argv[])
{
    signal(SIGINT, signal_handler); // Trap basic signals (exit cleanly)
    signal(SIGKILL, signal_handler);
    signal(SIGUSR1, signal_handler);
    signal(SIGQUIT, signal_handler);
//    signal(SIGABRT, signal_handler);
    signal(SIGTERM, signal_handler);

    //set the new_handler
    std::set_new_handler(out_of_memory_handler);

    q::logging::add_logger(q::logging::Logger_uptr(new q::logging::Console_Logger()));
    q::logging::set_decorations(q::logging::Decorations(q::logging::Decoration::TIMESTAMP, q::logging::Decoration::LEVEL, q::logging::Decoration::TOPIC));

    QLOG_TOPIC("silk");

//    q::util::Rand rnd;
//    while (true)
//    {
//        math::vec3f target(rnd.get_float() * 40.0, rnd.get_float() * 40.0, rnd.get_float() * 10.0);
//        test_mm(target, 0.01);
//        test_mm(target, 0.1);
//        test_mm(target, 0.5);
//    }

    namespace po = boost::program_options;

	po::options_description desc("Options");
	desc.add_options()
		("help", "produce help message")
        ("blind", "no camera")
        ("test", po::value<size_t>(), "test");

	po::variables_map vm;
	po::store(po::parse_command_line(argc, argv, desc), vm);
	po::notify(vm);

	if (vm.count("help"))
	{
        std::cout << desc << "\n";
		return 1;
	}

    s_test = vm.count("test") ? vm["test"].as<size_t>() : size_t(0);
    //bool blind = vm.count("blind") != 0;

    QLOGI("Creating io_service thread");

    boost::shared_ptr<boost::asio::io_service::work> async_work(new boost::asio::io_service::work(s_async_io_service));
    auto async_thread = std::thread([]() { s_async_io_service.run(); });

#if defined RASPBERRY_PI
    {
        int policy = SCHED_FIFO;
        struct sched_param param;
        param.sched_priority = sched_get_priority_max(policy);
        if (pthread_setschedparam(pthread_self(), policy, &param) != 0)
        {
            perror("main sched_setscheduler");
            exit(EXIT_FAILURE);
        }
        policy = SCHED_IDLE;
        param.sched_priority = sched_get_priority_min(policy);
        if (pthread_setschedparam(async_thread.native_handle(), policy, &param) != 0)
        {
            perror("async_thread sched_setscheduler");
            exit(EXIT_FAILURE);
        }
    }
#endif

    try
    {
        silk::HAL hal;
        silk::Comms comms(hal);

        if (!hal.init(comms))
        {
            QLOGE("Hardware failure! Aborting");
            goto exit;
        }

#if defined RASPBERRY_PI
        if (!comms.start_rfmon("mon0", 5))
        {
            QLOGE("Cannot start communication channel! Aborting");
            goto exit;
        }
#else
        if (!comms.start_udp(8000, 8001))
        {
            QLOGE("Cannot start communication channel! Aborting");
            goto exit;
        }
#endif

//        while (!s_exit)
//        {
//            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
//            QLOGI("Waiting for comms to connect...");
//            if (comms.is_connected())
//            {
//                break;
//            }
//        }

        QLOGI("All systems up. Ready to fly...");

        {
            constexpr std::chrono::microseconds PERIOD(5000);
            auto last = q::Clock::now();
            while (!s_exit)
            {
                auto start = q::Clock::now();
                auto dt = start - last;
                last = start;
                if (dt > std::chrono::milliseconds(5))
                {
                    QLOGW("Process Latency of {}!!!!!", dt);
                }
                comms.process();
                hal.process();

                //No sleeping here!!! process as fast as possible as the nodes are not always in the ideal order
                // and out of order nodes will be processes next 'frame'. So the quicker the frames, the smaller the lag between nodes
                //std::this_thread::sleep_for(std::chrono::milliseconds(1));
            }
        }

exit:
        QLOGI("Stopping everything");

        //stop threads
        async_work.reset();
        s_async_io_service.stop();
        if (async_thread.joinable())
        {
            std::this_thread::yield();
            async_thread.join();
        }
        hal.shutdown();
    }
    catch (std::exception const& e)
    {
        QLOGE("exception: {}", e.what());
        abort();
    }

    QLOGI("Closing");
}

<|endoftext|>


<commit_before>/**************************************************************************
 * newbucket.hxx -- new bucket routines for better world modeling
 *
 * Written by Curtis L. Olson, started February 1999.
 *
 * Copyright (C) 1999  Curtis L. Olson - curt@flightgear.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 * $Id$
 **************************************************************************/

#ifndef _NEWBUCKET_HXX
#define _NEWBUCKET_HXX

#include <simgear/compiler.h>
#include <simgear/constants.h>

#ifdef FG_HAVE_STD_INCLUDES
#  include <cmath>
#  include <cstdio> // sprintf()
#  include <iostream>
#else
#  include <math.h>
#  include <stdio.h> // sprintf()
#  include <iostream.h>
#endif

// I don't understand ... <math.h> or <cmath> should be included
// already depending on how you defined FG_HAVE_STD_INCLUDES, but I
// can go ahead and add this -- CLO
#ifdef __MWERKS__
FG_USING_STD(sprintf);
FG_USING_STD(fabs);
#endif

#include STL_STRING

FG_USING_STD(string);

#if ! defined( FG_HAVE_NATIVE_SGI_COMPILERS )
FG_USING_STD(ostream);
#endif

#define SG_BUCKET_SPAN      0.125   // 1/8 of a degree
#define SG_HALF_BUCKET_SPAN 0.0625  // 1/2 of 1/8 of a degree = 1/16 = 0.0625

class SGBucket;
ostream& operator<< ( ostream&, const SGBucket& );
bool operator== ( const SGBucket&, const SGBucket& );

// return the horizontal tile span factor based on latitude
inline double sg_bucket_span( double l ) {
    if ( l >= 89.0 ) {
	return 360.0;
    } else if ( l >= 88.0 ) {
	return 8.0;
    } else if ( l >= 86.0 ) {
	return 4.0;
    } else if ( l >= 83.0 ) {
	return 2.0;
    } else if ( l >= 76.0 ) {
	return 1.0;
    } else if ( l >= 62.0 ) {
	return 0.5;
    } else if ( l >= 22.0 ) {
	return 0.25;
    } else if ( l >= -22.0 ) {
	return 0.125;
    } else if ( l >= -62.0 ) {
	return 0.25;
    } else if ( l >= -76.0 ) {
	return 0.5;
    } else if ( l >= -83.0 ) {
	return 1.0;
    } else if ( l >= -86.0 ) {
	return 2.0;
    } else if ( l >= -88.0 ) {
	return 4.0;
    } else if ( l >= -89.0 ) {
	return 8.0;
    } else {
	return 360.0;
    }
}

class SGBucket {

private:
    double cx, cy;  // centerpoint (lon, lat) in degrees of bucket
    int lon;        // longitude index (-180 to 179)
    int lat;        // latitude index (-90 to 89)
    int x;          // x subdivision (0 to 7)
    int y;          // y subdivision (0 to 7)

public:

    // default constructor
    SGBucket();

    // constructor for specified location
    SGBucket(const double dlon, const double dlat);

    // create an impossible bucket if false
    SGBucket(const bool is_good);

    // Parse a unique scenery tile index and find the lon, lat, x, and y
    SGBucket(const long int bindex);

    // default destructor
    ~SGBucket();

    // Set the bucket params for the specified lat and lon
    void set_bucket( double dlon, double dlat );
    void set_bucket( double *lonlat );

    // create an impossible bucket
    inline void make_bad( void ) {
	set_bucket(0.0, 0.0);
	lon = -1000;
    }

    // Generate the unique scenery tile index for this bucket
    //
    // The index is constructed as follows:
    //
    // 9 bits - to represent 360 degrees of longitude (-180 to 179)
    // 8 bits - to represent 180 degrees of latitude (-90 to 89)
    //
    // Each 1 degree by 1 degree tile is further broken down into an 8x8
    // grid.  So we also need:
    //
    // 3 bits - to represent x (0 to 7)
    // 3 bits - to represent y (0 to 7)

    inline long int gen_index() const {
	return ((lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x;
    }

    inline string gen_index_str() const {
	char tmp[20];
	sprintf(tmp, "%ld",
		(((long)lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x);
	return (string)tmp;
    }

    // Build the path name for this bucket
    string gen_base_path() const;

    // return the center lon of a tile
    inline double get_center_lon() const {
	double span = sg_bucket_span( lat + y / 8.0 + SG_HALF_BUCKET_SPAN );

	if ( span >= 1.0 ) {
	    return lon + span / 2.0;
	} else {
	    return lon + x * span + span / 2.0;
	}
    }

    // return the center lat of a tile
    inline double get_center_lat() const {
	return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
    }

    // return width of the tile in degrees
    double get_width() const;
    // return height of the tile in degrees
    double get_height() const;

    // return width of the tile in meters
    double get_width_m() const;
    // return height of the tile in meters
    double get_height_m() const;

    // Informational methods
    inline int get_lon() const { return lon; }
    inline int get_lat() const { return lat; }
    inline int get_x() const { return x; }
    inline int get_y() const { return y; }

    // friends
    friend ostream& operator<< ( ostream&, const SGBucket& );
    friend bool operator== ( const SGBucket&, const SGBucket& );
};

// offset a bucket specified by dlon, dlat by the specified tile units
// in the X & Y direction
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );

// calculate the offset between two buckets
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy );

inline ostream&
operator<< ( ostream& out, const SGBucket& b )
{
    return out << b.lon << ":" << b.x << ", " << b.lat << ":" << b.y;
}

inline bool
operator== ( const SGBucket& b1, const SGBucket& b2 )
{
    return ( b1.lon == b2.lon &&
	     b1.lat == b2.lat &&
	     b1.x == b2.x &&
	     b1.y == b2.y );
}

#endif // _NEWBUCKET_HXX

<commit_msg>Added doxygen comments.<commit_after>/**************************************************************************
 * newbucket.hxx -- new bucket routines for better world modeling
 *
 * Written by Curtis L. Olson, started February 1999.
 *
 * Copyright (C) 1999  Curtis L. Olson - curt@flightgear.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 * $Id$
 **************************************************************************/

/** \file newbucket.hxx
 * A class and associated utiltity functions to manage world scenery tiling.
 */

#ifndef _NEWBUCKET_HXX
#define _NEWBUCKET_HXX

#include <simgear/compiler.h>
#include <simgear/constants.h>

#ifdef FG_HAVE_STD_INCLUDES
#  include <cmath>
#  include <cstdio> // sprintf()
#else
#  include <math.h>
#  include <stdio.h> // sprintf()
#endif

#include STL_IOSTREAM

// I don't understand ... <math.h> or <cmath> should be included
// already depending on how you defined FG_HAVE_STD_INCLUDES, but I
// can go ahead and add this -- CLO
#ifdef __MWERKS__
FG_USING_STD(sprintf);
FG_USING_STD(fabs);
#endif

#include STL_STRING

FG_USING_STD(string);

#if ! defined( FG_HAVE_NATIVE_SGI_COMPILERS )
FG_USING_STD(ostream);
#endif

/**
 * standard size of a bucket in degrees (1/8 of a degree)
 */
#define SG_BUCKET_SPAN      0.125

/**
 * half of a standard SG_BUCKET_SPAN
 */
#define SG_HALF_BUCKET_SPAN ( 0.5 * SG_BUCKET_SPAN )

// return the horizontal tile span factor based on latitude
static double sg_bucket_span( double l ) {
    if ( l >= 89.0 ) {
	return 360.0;
    } else if ( l >= 88.0 ) {
	return 8.0;
    } else if ( l >= 86.0 ) {
	return 4.0;
    } else if ( l >= 83.0 ) {
	return 2.0;
    } else if ( l >= 76.0 ) {
	return 1.0;
    } else if ( l >= 62.0 ) {
	return 0.5;
    } else if ( l >= 22.0 ) {
	return 0.25;
    } else if ( l >= -22.0 ) {
	return 0.125;
    } else if ( l >= -62.0 ) {
	return 0.25;
    } else if ( l >= -76.0 ) {
	return 0.5;
    } else if ( l >= -83.0 ) {
	return 1.0;
    } else if ( l >= -86.0 ) {
	return 2.0;
    } else if ( l >= -88.0 ) {
	return 4.0;
    } else if ( l >= -89.0 ) {
	return 8.0;
    } else {
	return 360.0;
    }
}

/**
 * A class to manage world scenery tiling.
 * This class encapsulates the world tiling scheme.  It provides ways
 * to calculate a unique tile index from a lat/lon, and it can provide
 * information such as the dimensions of a given tile.
 */

class SGBucket {

private:
    double cx, cy;  // centerpoint (lon, lat) in degrees of bucket
    int lon;        // longitude index (-180 to 179)
    int lat;        // latitude index (-90 to 89)
    int x;          // x subdivision (0 to 7)
    int y;          // y subdivision (0 to 7)

public:

    /**
     * Default constructor.
     */
    SGBucket();

    /**
     * Construct a bucket given a specific location.
     * @param dlon longitude specified in degrees
     * @param dlat latitude specified in degrees
     */
    SGBucket(const double dlon, const double dlat);

    /** Construct a bucket.
     *  @param is_good if false, create an invalid bucket.  This is
     *  useful * if you are comparing cur_bucket to last_bucket and
     *  you want to * make sure last_bucket starts out as something
     *  impossible.
     */
    SGBucket(const bool is_good);

    /** Construct a bucket given a unique bucket index number.
     * @param bindex unique bucket index
     */
    SGBucket(const long int bindex);

    /**
     * Default destructor.
     */
    ~SGBucket();

    /**
     * Reset a bucket to represent a new lat and lon
     * @param dlon longitude specified in degrees
     * @param dlat latitude specified in degrees
     */
    void set_bucket( double dlon, double dlat );

    /**
     * Reset a bucket to represent a new lat and lon
     * @param lonlat an array of double[2] holding lon and lat
     * (specified) in degrees
     */
    void set_bucket( double *lonlat );

    /**
     * Create an impossible bucket.
     * This is useful if you are comparing cur_bucket to last_bucket
     * and you want to make sure last_bucket starts out as something
     * impossible.
     */
    inline void make_bad() {
	set_bucket(0.0, 0.0);
	lon = -1000;
    }

    /**
     * Generate the unique scenery tile index for this bucket
     *
     * The index is constructed as follows:
     *
     * 9 bits - to represent 360 degrees of longitude (-180 to 179)
     * 8 bits - to represent 180 degrees of latitude (-90 to 89)
     *
     * Each 1 degree by 1 degree tile is further broken down into an 8x8
     * grid.  So we also need:
     *
     * 3 bits - to represent x (0 to 7)
     * 3 bits - to represent y (0 to 7)
     */
    inline long int gen_index() const {
	return ((lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x;
    }

    /**
     * Generate the unique scenery tile index for this bucket in ascii
     * string form.
     */
    inline string gen_index_str() const {
	char tmp[20];
	sprintf(tmp, "%ld",
                (((long)lon + 180) << 14) + ((lat + 90) << 6) + (y << 3) + x);
	return (string)tmp;
    }

    /**
     * Build the base path name for this bucket.
     */
    string gen_base_path() const;

    /**
     * Return the center lon of a tile.
     */
    inline double get_center_lon() const {
	double span = sg_bucket_span( lat + y / 8.0 + SG_HALF_BUCKET_SPAN );

	if ( span >= 1.0 ) {
	    return lon + span / 2.0;
	} else {
	    return lon + x * span + span / 2.0;
	}
    }

    /**
     * Return the center lat of a tile.
     */
    inline double get_center_lat() const {
	return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
    }

    /**
     * Return width of the tile in degrees.
     */
    double get_width() const;

    /**
     * Return height of the tile in degrees.
     */
    double get_height() const;

    /**
     * Return width of the tile in meters.
     */
    double get_width_m() const;

    /**
     * Return height of the tile in meters.
     */
    double get_height_m() const;

    // Informational methods.

    /**
     * Return the lon of the lower left corner of this tile.
     */
    inline int get_lon() const { return lon; }

    /**
     * Return the lat of the lower left corner of this tile.
     */
    inline int get_lat() const { return lat; }

    /**
     * Return the x coord within the 1x1 degree chunk this tile.
     */
    inline int get_x() const { return x; }

    /**
     * Return the y coord within the 1x1 degree chunk this tile.
     */
    inline int get_y() const { return y; }

    // friends

    friend ostream& operator<< ( ostream&, const SGBucket& );
    friend bool operator== ( const SGBucket&, const SGBucket& );
};

/**
 * Return the bucket which is offset from the specified dlon, dlat by
 * the specified tile units in the X & Y direction.
 * @param dlon starting lon in degrees
 * @param dlat starting lat in degrees
 * @param x number of bucket units to offset in x (lon) direction
 * @param y number of bucket units to offset in y (lat) direction
 */
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );

/**
 * Calculate the offset between two buckets (in quantity of buckets).
 * @param b1 bucket 1
 * @param b2 bucket 2
 * @param dx offset distance (lon) in tile units
 * @param dy offset distance (lat) in tile units
 */
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy );

/**
 * Write the bucket lon, lat, x, and y to the output stream.
 * @param out output stream
 * @param b bucket
 */
inline ostream&
operator<< ( ostream& out, const SGBucket& b )
{
    return out << b.lon << ":" << b.x << ", " << b.lat << ":" << b.y;
}

/**
 * Compare two bucket structures for equality.
 * @param b1 bucket 1
 * @param b2 bucket 2
 */
inline bool
operator== ( const SGBucket& b1, const SGBucket& b2 )
{
    return ( b1.lon == b2.lon &&
	     b1.lat == b2.lat &&
	     b1.x == b2.x &&
	     b1.y == b2.y );
}

#endif // _NEWBUCKET_HXX

<|endoftext|>