<commit_before>/** @file

    A brief file description

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

# include "ink_config.h"
# include "Diags.h"
# include "ink_cap.h"
# include "ink_thread.h"

#include <grp.h>

#if HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
#endif

#if HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif

// NOTE: Failing to acquire or release privileges is a fatal error. This is because that should never happen
// and if it does, it is likely that some fundamental security assumption has been violated. In that case
// it is dangerous to continue.

# if !TS_USE_POSIX_CAP
ink_mutex ElevateAccess::lock = INK_MUTEX_INIT;
#endif

#define DEBUG_CREDENTIALS(tag)  do { \
  if (is_debug_tag_set(tag)) { \
    uid_t uid = -1, euid = -1, suid = -1; \
    gid_t gid = -1, egid = -1, sgid = -1; \
    getresuid(&uid, &euid, &suid); \
    getresgid(&gid, &egid, &sgid); \
    Debug(tag, "uid=%ld, gid=%ld, euid=%ld, egid=%ld, suid=%ld, sgid=%ld", \
        static_cast<long>(uid), \
        static_cast<long>(gid), \
        static_cast<long>(euid), \
        static_cast<long>(egid), \
        static_cast<long>(suid), \
        static_cast<long>(sgid) ); \
  } \
} while (0)

#if TS_USE_POSIX_CAP

#define DEBUG_PRIVILEGES(tag)  do { \
  if (is_debug_tag_set(tag)) { \
    cap_t caps = cap_get_proc(); \
    char* caps_text = cap_to_text(caps, NULL); \
    Debug(tag, "caps='%s', core=%s, death signal=%d, thread=0x%llx", \
        caps_text, \
        is_dumpable(), \
        death_signal(), \
        (unsigned long long)pthread_self() ); \
    cap_free(caps_text); \
    cap_free(caps); \
  } \
} while (0)

#else /* TS_USE_POSIX_CAP */

#define DEBUG_PRIVILEGES(tag)  do { \
  if (is_debug_tag_set(tag)) { \
    Debug(tag, "caps='', core=%s, death signal=%d, thread=0x%llx", \
        is_dumpable(), \
        death_signal(), \
        (unsigned long long)pthread_self() ); \
  } \
} while(0)

#endif /* TS_USE_POSIX_CAP */

#if !HAVE_GETRESUID
static int
getresuid(uid_t * uid, uid_t * euid, uid_t * suid)
{
  *uid = getuid();
  *euid = geteuid();
  return 0;
}
#endif /* !HAVE_GETRESUID */

#if !HAVE_GETRESGID
static int
getresgid(gid_t * gid, gid_t * egid, gid_t * sgid)
{
  *gid = getgid();
  *egid = getegid();
  return 0;
}
#endif /* !HAVE_GETRESGID */

static unsigned
max_passwd_size()
{
#if defined(_SC_GETPW_R_SIZE_MAX)
  long val = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (val > 0) {
    return (unsigned)val;
  }
#endif

  return 4096;
}

static const char *
is_dumpable()
{
#if defined(PR_GET_DUMPABLE)
  return (prctl(PR_GET_DUMPABLE) != 1) ? "disabled" : "enabled";
#else
  return "unknown";
#endif
}

static int
death_signal()
{
  int signum = -1;

#if defined(PR_GET_PDEATHSIG)
  prctl(PR_GET_PDEATHSIG, &signum, 0, 0, 0);
#endif

  return signum;
}

void
DebugCapabilities(char const* tag)
{
  DEBUG_CREDENTIALS(tag);
  DEBUG_PRIVILEGES(tag);
}

static void
impersonate(const struct passwd * pwd, ImpersonationLevel level)
{
  int deathsig = death_signal();
  bool dumpable = false;

  DEBUG_CREDENTIALS("privileges");
  DEBUG_PRIVILEGES("privileges");

#if defined(PR_GET_DUMPABLE)
  dumpable = (prctl(PR_GET_DUMPABLE) == 1);
#endif

  // Always repopulate the supplementary group list for the new user.
  initgroups(pwd->pw_name, pwd->pw_gid);

  switch (level) {
  case IMPERSONATE_PERMANENT:
    if (setregid(pwd->pw_gid, pwd->pw_gid) != 0) {
      Fatal("switching to user %s, failed to set group ID %ld", pwd->pw_name, (long)pwd->pw_gid);
    }

    if (setreuid(pwd->pw_uid, pwd->pw_uid) != 0) {
      Fatal("switching to user %s, failed to set user ID %ld", pwd->pw_name, (long)pwd->pw_uid);
    }
    break;

  case IMPERSONATE_EFFECTIVE:
    if (setegid(pwd->pw_gid) != 0) {
      Fatal("switching to user %s, failed to set group ID %ld", pwd->pw_name, (long)pwd->pw_gid);
    }

    if (seteuid(pwd->pw_uid) != 0) {
      Fatal("switching to user %s, failed to set effective user ID %ld", pwd->pw_name, (long)pwd->pw_gid);
    }
    break;
  }

  // Reset process flags if necessary. Elevating privilege using capabilities does not reset process
  // flags, so we don't have to bother with this in elevateFileAccess().

  EnableCoreFile(dumpable);

  if (deathsig > 0) {
    EnableDeathSignal(deathsig);
  }

  DEBUG_CREDENTIALS("privileges");
  DEBUG_PRIVILEGES("privileges");
}

void
ImpersonateUserID(uid_t uid, ImpersonationLevel level)
{
  struct passwd * pwd;
  struct passwd   pbuf;
  char            buf[max_passwd_size()];

  if (getpwuid_r(uid, &pbuf, buf, sizeof(buf), &pwd) != 0) {
    Fatal("missing password database entry for UID %ld: %s", (long)uid, strerror(errno));
  }

  if (pwd == NULL) {
    // Password entry not found ...
    Fatal("missing password database entry for UID %ld", (long)uid);
  }

  impersonate(pwd, level);
}

void
ImpersonateUser(const char * user, ImpersonationLevel level)
{
  struct passwd * pwd;
  struct passwd   pbuf;
  char            buf[max_passwd_size()];

  if (*user == '#') {
    // Numeric user notation.
    uid_t uid = (uid_t)atoi(&user[1]);
    if (getpwuid_r(uid, &pbuf, buf, sizeof(buf), &pwd) != 0) {
      Fatal("missing password database entry for UID %ld: %s", (long)uid, strerror(errno));
    }
  } else {
    if (getpwnam_r(user, &pbuf, buf, sizeof(buf), &pwd) != 0) {
      Fatal("missing password database entry for username '%s': %s", user, strerror(errno));
    }
  }

  if (pwd == NULL) {
    // Password entry not found ...
    Fatal("missing password database entry for '%s'", user);
  }

  impersonate(pwd, level);
}

bool
PreserveCapabilities()
{
  int zret = 0;
# if TS_USE_POSIX_CAP
  zret = prctl(PR_SET_KEEPCAPS, 1);
# endif
  Debug("privileges", "[PreserveCapabilities] zret : %d\n", zret);
  return zret == 0;
}

// Adjust the capabilities to only those needed.
bool
RestrictCapabilities()
{
  int zret = 0; // return value.
# if TS_USE_POSIX_CAP
    cap_t caps = cap_init(); // start with nothing.
    // Capabilities we need.
    cap_value_t perm_list[] = { CAP_NET_ADMIN, CAP_NET_BIND_SERVICE, CAP_IPC_LOCK, CAP_DAC_OVERRIDE};
    static int const PERM_CAP_COUNT = sizeof(perm_list)/sizeof(*perm_list);
    cap_value_t eff_list[] = { CAP_NET_ADMIN, CAP_NET_BIND_SERVICE, CAP_IPC_LOCK};
    static int const EFF_CAP_COUNT = sizeof(eff_list)/sizeof(*eff_list);

    cap_set_flag(caps, CAP_PERMITTED, PERM_CAP_COUNT, perm_list, CAP_SET);
    cap_set_flag(caps, CAP_EFFECTIVE, EFF_CAP_COUNT, eff_list, CAP_SET);
    zret = cap_set_proc(caps);
    cap_free(caps);
#  endif
  Debug("privileges", "[RestrictCapabilities] zret : %d\n", zret);
  return zret == 0;
}

bool
EnableCoreFile(bool flag)
{
  int zret = 0;

# if defined(PR_SET_DUMPABLE)
  int state = flag ? 1 : 0;
  if (0 > (zret = prctl(PR_SET_DUMPABLE, state, 0, 0, 0))) {
    Warning("Unable to set PR_DUMPABLE : %s", strerror(errno));
  } else if (state != prctl(PR_GET_DUMPABLE)) {
    zret = ENOSYS; // best guess
    Warning("Call to set PR_DUMPABLE was ineffective");
  }
# endif  // linux check

  Debug("privileges", "[EnableCoreFile] zret : %d\n", zret);
  return zret == 0;
}

void
EnableDeathSignal(int signum)
{
  (void)signum;

#if defined(PR_SET_PDEATHSIG)
  if (prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0) != 0) {
    Debug("privileges", "prctl(PR_SET_PDEATHSIG) failed: %s", strerror(errno));
  }
#endif
}

#if TS_USE_POSIX_CAP
/** Control file access privileges to bypass DAC.
    @parm state Use @c true to enable elevated privileges,
    @c false to disable.
    @return @c true if successful, @c false otherwise.

    @internal After some pondering I decided that the file access
    privilege was worth the effort of restricting. Unlike the network
    privileges this can protect a host system from programming errors
    by not (usually) permitting such errors to access arbitrary
    files. This is particularly true since none of the config files
    current enable this feature so it's not actually called. Still,
    best to program defensively and have it available.
 */
static void
elevateFileAccess(unsigned level, bool state)
{
  Debug("privileges", "[elevateFileAccess] state : %d\n", state);

  cap_t cap_state = cap_get_proc(); // current capabilities

  unsigned cap_count = 0;
  cap_value_t cap_list[2];

  if (level & ElevateAccess::FILE_PRIVILEGE) {
    cap_list[cap_count] = CAP_DAC_OVERRIDE;
    ++cap_count;
  }

  if (level & ElevateAccess::TRACE_PRIVILEGE) {
    cap_list[cap_count] = CAP_SYS_PTRACE;
    ++cap_count;
  }

  ink_release_assert(cap_count <= sizeof(cap_list));

  cap_set_flag(cap_state, CAP_EFFECTIVE, cap_count, cap_list, state ? CAP_SET : CAP_CLEAR);
  if (cap_set_proc(cap_state) != 0) {
    Fatal("failed to %s privileged capabilities: %s", state ? "acquire" : "release", strerror(errno));
  }

  cap_free(cap_state);
}
#endif

ElevateAccess::ElevateAccess(const bool state, unsigned lvl)
  : elevated(false), saved_uid(geteuid()), level(lvl)
{
  // XXX Squash a clang [-Wunused-private-field] warning. The right solution is probably to extract
  // the capabilities into a policy class.
  (void)level;

  if (state == true) {
    elevate();
#if !TS_USE_POSIX_CAP
    DEBUG_CREDENTIALS("privileges");
#endif
    DEBUG_PRIVILEGES("privileges");
  }
}

ElevateAccess::~ElevateAccess()
{
  if (elevated == true) {
    demote();
#if !TS_USE_POSIX_CAP
    DEBUG_CREDENTIALS("privileges");
#endif
    DEBUG_PRIVILEGES("privileges");
  }
}

void
ElevateAccess::elevate()
{
#if TS_USE_POSIX_CAP
  elevateFileAccess(level, true);
#else
  // Since we are setting a process-wide credential, we have to block any other thread
  // attempting to elevate until this one demotes.
  ink_mutex_acquire(&lock);
  ImpersonateUserID(0, IMPERSONATE_EFFECTIVE);
#endif
  elevated = true;
}

void
ElevateAccess::demote()
{
#if TS_USE_POSIX_CAP
  elevateFileAccess(level, false);
#else
  ImpersonateUserID(saved_uid, IMPERSONATE_EFFECTIVE);
  ink_mutex_release(&lock);
#endif
  elevated = false;
}
<commit_msg>Fix a static analyzer warning<commit_after>/** @file

    A brief file description

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

# include "ink_config.h"
# include "Diags.h"
# include "ink_cap.h"
# include "ink_thread.h"

#include <grp.h>

#if HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
#endif

#if HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif

// NOTE: Failing to acquire or release privileges is a fatal error. This is because that should never happen
// and if it does, it is likely that some fundamental security assumption has been violated. In that case
// it is dangerous to continue.

# if !TS_USE_POSIX_CAP
ink_mutex ElevateAccess::lock = INK_MUTEX_INIT;
#endif

#define DEBUG_CREDENTIALS(tag)  do { \
  if (is_debug_tag_set(tag)) { \
    uid_t uid = -1, euid = -1, suid = -1; \
    gid_t gid = -1, egid = -1, sgid = -1; \
    getresuid(&uid, &euid, &suid); \
    getresgid(&gid, &egid, &sgid); \
    Debug(tag, "uid=%ld, gid=%ld, euid=%ld, egid=%ld, suid=%ld, sgid=%ld", \
        static_cast<long>(uid), \
        static_cast<long>(gid), \
        static_cast<long>(euid), \
        static_cast<long>(egid), \
        static_cast<long>(suid), \
        static_cast<long>(sgid) ); \
  } \
} while (0)

#if TS_USE_POSIX_CAP

#define DEBUG_PRIVILEGES(tag)  do { \
  if (is_debug_tag_set(tag)) { \
    cap_t caps = cap_get_proc(); \
    char* caps_text = cap_to_text(caps, NULL); \
    Debug(tag, "caps='%s', core=%s, death signal=%d, thread=0x%llx", \
        caps_text, \
        is_dumpable(), \
        death_signal(), \
        (unsigned long long)pthread_self() ); \
    cap_free(caps_text); \
    cap_free(caps); \
  } \
} while (0)

#else /* TS_USE_POSIX_CAP */

#define DEBUG_PRIVILEGES(tag)  do { \
  if (is_debug_tag_set(tag)) { \
    Debug(tag, "caps='', core=%s, death signal=%d, thread=0x%llx", \
        is_dumpable(), \
        death_signal(), \
        (unsigned long long)pthread_self() ); \
  } \
} while(0)

#endif /* TS_USE_POSIX_CAP */

#if !HAVE_GETRESUID
static int
getresuid(uid_t * uid, uid_t * euid, uid_t * suid)
{
  *uid = getuid();
  *euid = geteuid();
  return 0;
}
#endif /* !HAVE_GETRESUID */

#if !HAVE_GETRESGID
static int
getresgid(gid_t * gid, gid_t * egid, gid_t * sgid)
{
  *gid = getgid();
  *egid = getegid();
  return 0;
}
#endif /* !HAVE_GETRESGID */

static unsigned
max_passwd_size()
{
#if defined(_SC_GETPW_R_SIZE_MAX)
  long val = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (val > 0) {
    return (unsigned)val;
  }
#endif

  return 4096;
}

static const char *
is_dumpable()
{
#if defined(PR_GET_DUMPABLE)
  return (prctl(PR_GET_DUMPABLE) != 1) ? "disabled" : "enabled";
#else
  return "unknown";
#endif
}

static int
death_signal()
{
  int signum = -1;

#if defined(PR_GET_PDEATHSIG)
  prctl(PR_GET_PDEATHSIG, &signum, 0, 0, 0);
#endif

  return signum;
}

void
DebugCapabilities(char const* tag)
{
  DEBUG_CREDENTIALS(tag);
  DEBUG_PRIVILEGES(tag);
}

static void
impersonate(const struct passwd * pwd, ImpersonationLevel level)
{
  int deathsig = death_signal();
  bool dumpable = false;

  DEBUG_CREDENTIALS("privileges");
  DEBUG_PRIVILEGES("privileges");

  ink_release_assert(pwd != NULL);

#if defined(PR_GET_DUMPABLE)
  dumpable = (prctl(PR_GET_DUMPABLE) == 1);
#endif

  // Always repopulate the supplementary group list for the new user.
  initgroups(pwd->pw_name, pwd->pw_gid);

  switch (level) {
  case IMPERSONATE_PERMANENT:
    if (setregid(pwd->pw_gid, pwd->pw_gid) != 0) {
      Fatal("switching to user %s, failed to set group ID %ld", pwd->pw_name, (long)pwd->pw_gid);
    }

    if (setreuid(pwd->pw_uid, pwd->pw_uid) != 0) {
      Fatal("switching to user %s, failed to set user ID %ld", pwd->pw_name, (long)pwd->pw_uid);
    }
    break;

  case IMPERSONATE_EFFECTIVE:
    if (setegid(pwd->pw_gid) != 0) {
      Fatal("switching to user %s, failed to set group ID %ld", pwd->pw_name, (long)pwd->pw_gid);
    }

    if (seteuid(pwd->pw_uid) != 0) {
      Fatal("switching to user %s, failed to set effective user ID %ld", pwd->pw_name, (long)pwd->pw_gid);
    }
    break;
  }

  // Reset process flags if necessary. Elevating privilege using capabilities does not reset process
  // flags, so we don't have to bother with this in elevateFileAccess().

  EnableCoreFile(dumpable);

  if (deathsig > 0) {
    EnableDeathSignal(deathsig);
  }

  DEBUG_CREDENTIALS("privileges");
  DEBUG_PRIVILEGES("privileges");
}

void
ImpersonateUserID(uid_t uid, ImpersonationLevel level)
{
  struct passwd * pwd;
  struct passwd   pbuf;
  char            buf[max_passwd_size()];

  if (getpwuid_r(uid, &pbuf, buf, sizeof(buf), &pwd) != 0) {
    Fatal("missing password database entry for UID %ld: %s", (long)uid, strerror(errno));
  }

  if (pwd == NULL) {
    // Password entry not found ...
    Fatal("missing password database entry for UID %ld", (long)uid);
  }

  impersonate(pwd, level);
}

void
ImpersonateUser(const char * user, ImpersonationLevel level)
{
  struct passwd * pwd;
  struct passwd   pbuf;
  char            buf[max_passwd_size()];

  if (*user == '#') {
    // Numeric user notation.
    uid_t uid = (uid_t)atoi(&user[1]);
    if (getpwuid_r(uid, &pbuf, buf, sizeof(buf), &pwd) != 0) {
      Fatal("missing password database entry for UID %ld: %s", (long)uid, strerror(errno));
    }
  } else {
    if (getpwnam_r(user, &pbuf, buf, sizeof(buf), &pwd) != 0) {
      Fatal("missing password database entry for username '%s': %s", user, strerror(errno));
    }
  }

  if (pwd == NULL) {
    // Password entry not found ...
    Fatal("missing password database entry for '%s'", user);
  }

  impersonate(pwd, level);
}

bool
PreserveCapabilities()
{
  int zret = 0;
# if TS_USE_POSIX_CAP
  zret = prctl(PR_SET_KEEPCAPS, 1);
# endif
  Debug("privileges", "[PreserveCapabilities] zret : %d\n", zret);
  return zret == 0;
}

// Adjust the capabilities to only those needed.
bool
RestrictCapabilities()
{
  int zret = 0; // return value.
# if TS_USE_POSIX_CAP
    cap_t caps = cap_init(); // start with nothing.
    // Capabilities we need.
    cap_value_t perm_list[] = { CAP_NET_ADMIN, CAP_NET_BIND_SERVICE, CAP_IPC_LOCK, CAP_DAC_OVERRIDE};
    static int const PERM_CAP_COUNT = sizeof(perm_list)/sizeof(*perm_list);
    cap_value_t eff_list[] = { CAP_NET_ADMIN, CAP_NET_BIND_SERVICE, CAP_IPC_LOCK};
    static int const EFF_CAP_COUNT = sizeof(eff_list)/sizeof(*eff_list);

    cap_set_flag(caps, CAP_PERMITTED, PERM_CAP_COUNT, perm_list, CAP_SET);
    cap_set_flag(caps, CAP_EFFECTIVE, EFF_CAP_COUNT, eff_list, CAP_SET);
    zret = cap_set_proc(caps);
    cap_free(caps);
#  endif
  Debug("privileges", "[RestrictCapabilities] zret : %d\n", zret);
  return zret == 0;
}

bool
EnableCoreFile(bool flag)
{
  int zret = 0;

# if defined(PR_SET_DUMPABLE)
  int state = flag ? 1 : 0;
  if (0 > (zret = prctl(PR_SET_DUMPABLE, state, 0, 0, 0))) {
    Warning("Unable to set PR_DUMPABLE : %s", strerror(errno));
  } else if (state != prctl(PR_GET_DUMPABLE)) {
    zret = ENOSYS; // best guess
    Warning("Call to set PR_DUMPABLE was ineffective");
  }
# endif  // linux check

  Debug("privileges", "[EnableCoreFile] zret : %d\n", zret);
  return zret == 0;
}

void
EnableDeathSignal(int signum)
{
  (void)signum;

#if defined(PR_SET_PDEATHSIG)
  if (prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0) != 0) {
    Debug("privileges", "prctl(PR_SET_PDEATHSIG) failed: %s", strerror(errno));
  }
#endif
}

#if TS_USE_POSIX_CAP
/** Control file access privileges to bypass DAC.
    @parm state Use @c true to enable elevated privileges,
    @c false to disable.
    @return @c true if successful, @c false otherwise.

    @internal After some pondering I decided that the file access
    privilege was worth the effort of restricting. Unlike the network
    privileges this can protect a host system from programming errors
    by not (usually) permitting such errors to access arbitrary
    files. This is particularly true since none of the config files
    current enable this feature so it's not actually called. Still,
    best to program defensively and have it available.
 */
static void
elevateFileAccess(unsigned level, bool state)
{
  Debug("privileges", "[elevateFileAccess] state : %d\n", state);

  cap_t cap_state = cap_get_proc(); // current capabilities

  unsigned cap_count = 0;
  cap_value_t cap_list[2];

  if (level & ElevateAccess::FILE_PRIVILEGE) {
    cap_list[cap_count] = CAP_DAC_OVERRIDE;
    ++cap_count;
  }

  if (level & ElevateAccess::TRACE_PRIVILEGE) {
    cap_list[cap_count] = CAP_SYS_PTRACE;
    ++cap_count;
  }

  ink_release_assert(cap_count <= sizeof(cap_list));

  cap_set_flag(cap_state, CAP_EFFECTIVE, cap_count, cap_list, state ? CAP_SET : CAP_CLEAR);
  if (cap_set_proc(cap_state) != 0) {
    Fatal("failed to %s privileged capabilities: %s", state ? "acquire" : "release", strerror(errno));
  }

  cap_free(cap_state);
}
#endif

ElevateAccess::ElevateAccess(const bool state, unsigned lvl)
  : elevated(false), saved_uid(geteuid()), level(lvl)
{
  // XXX Squash a clang [-Wunused-private-field] warning. The right solution is probably to extract
  // the capabilities into a policy class.
  (void)level;

  if (state == true) {
    elevate();
#if !TS_USE_POSIX_CAP
    DEBUG_CREDENTIALS("privileges");
#endif
    DEBUG_PRIVILEGES("privileges");
  }
}

ElevateAccess::~ElevateAccess()
{
  if (elevated == true) {
    demote();
#if !TS_USE_POSIX_CAP
    DEBUG_CREDENTIALS("privileges");
#endif
    DEBUG_PRIVILEGES("privileges");
  }
}

void
ElevateAccess::elevate()
{
#if TS_USE_POSIX_CAP
  elevateFileAccess(level, true);
#else
  // Since we are setting a process-wide credential, we have to block any other thread
  // attempting to elevate until this one demotes.
  ink_mutex_acquire(&lock);
  ImpersonateUserID(0, IMPERSONATE_EFFECTIVE);
#endif
  elevated = true;
}

void
ElevateAccess::demote()
{
#if TS_USE_POSIX_CAP
  elevateFileAccess(level, false);
#else
  ImpersonateUserID(saved_uid, IMPERSONATE_EFFECTIVE);
  ink_mutex_release(&lock);
#endif
  elevated = false;
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014 Dmitry Tsvettsikh <https://github.com/reklatsmasters>
 *
 * MIT License <https://github.com/reklatsmasters/node-process-list/blob/master/LICENSE>
 */

#include "tasklist.h"  // NOLINT(build/include)

#define _WIN32_DCOM

#include <windows.h>
#include <comdef.h>
#include <Wbemidl.h>
#include "OAIdl.h"
#include <WbemCli.h>
#include <tchar.h>
#include <atlbase.h>

#include <codecvt>
#include <string>
#include <iostream>

using pl::process;

#pragma comment(lib, "wbemuuid.lib")

#define EPOCH_SINCE_UNIX_NANO 116444736000000000
#define SEC_TO_MS 10000

static std::string ws2s(const std::wstring& wstr) {
  typedef std::codecvt_utf8<wchar_t> convert_typeX;
  std::wstring_convert<convert_typeX, wchar_t> converterX;

  return converterX.to_bytes(wstr);
}

// The SafeRelease Pattern
template <class T>
void SafeRelease(T **ppT) {
  if (*ppT) {
    (*ppT)->Release();
    *ppT = NULL;
  }
}

struct WMI {
  IWbemLocator *pLoc;
  IWbemServices *pSvc;
  IEnumWbemClassObject *pEnumerator;
};

struct WMIEntry {
  WMIEntry() : pClsObj(NULL) {}

  ~WMIEntry() {
    VariantClear(&data);
    SafeRelease(&pClsObj);
  }

  VARIANT data;
  IWbemClassObject *pClsObj;
};

class CoInitializeHelper {
 public:
  CoInitializeHelper() :
    m_hres(CoInitializeEx(NULL, COINIT_MULTITHREADED))
    { }

  ~CoInitializeHelper() {
    if (SUCCEEDED(m_hres)) {
      CoUninitialize();
    }
  }

  operator HRESULT() const {
    return m_hres;
  }

 private:
  HRESULT m_hres;
};

/**
 * open wmi stream
 */
static WMI * wmiopen(const char *query, LONG flags) {
  HRESULT hres;
  WMI *wmi = reinterpret_cast<WMI *>(malloc(sizeof(struct WMI)));

  if (wmi == NULL) {
    throw std::logic_error("Failed to initialize struct WMI");
  }

  memset(wmi, 0, sizeof(struct WMI));

  // Obtain the initial locator to Windows Management
  // on a particular host computer.
  hres = CoCreateInstance(
    CLSID_WbemLocator,
    0,
    CLSCTX_INPROC_SERVER,
    IID_IWbemLocator,
    reinterpret_cast<LPVOID *>(&wmi->pLoc));

  if (FAILED(hres)) {
    free(wmi);
    throw std::logic_error("Failed to create IWbemLocator object.");
  }

  // Connect to the root\cimv2 namespace with the
  // current user and obtain pointer pSvc
  // to make IWbemServices calls.
  hres = wmi->pLoc->ConnectServer(
    _bstr_t(L"ROOT\\CIMV2"),  // WMI namespace
    NULL,                     // User name
    NULL,                     // User password
    0,                        // Locale
    NULL,                     // Security flags
    0,                        // Authority
    0,                        // Context object
    &wmi->pSvc);              // IWbemServices proxy

  if (FAILED(hres)) {
    wmi->pLoc->Release();
    free(wmi);
    throw std::logic_error("Could not connect to root\\cimv2.");
  }

  // Set the IWbemServices proxy so that impersonation
  // of the user (client) occurs.
  hres = CoSetProxyBlanket(
    wmi->pSvc,                    // the proxy to set
    RPC_C_AUTHN_WINNT,            // authentication service
    RPC_C_AUTHZ_NONE,             // authorization service
    NULL,                         // Server principal name
    RPC_C_AUTHN_LEVEL_CALL,       // authentication level
    RPC_C_IMP_LEVEL_IMPERSONATE,  // impersonation level
    NULL,                         // client identity
    EOAC_NONE);                   // proxy capabilities

  if (FAILED(hres)) {
    wmi->pSvc->Release();
    wmi->pLoc->Release();
    free(wmi);
    throw std::logic_error("Could not set proxy blanket.");
  }

  // Use the IWbemServices pointer to make requests of WMI.
  hres = wmi->pSvc->ExecQuery(
    bstr_t("WQL"),
    bstr_t(query),
    flags,
    NULL,
    &wmi->pEnumerator);

  if (FAILED(hres)) {
    wmi->pSvc->Release();
    wmi->pLoc->Release();
    free(wmi);
    throw std::logic_error("Query for processes failed.");
  }

  return wmi;
}

/**
 * close wmi stream
 */
static void wmiclose(WMI *wmi) {
  wmi->pEnumerator->Release();
  wmi->pSvc->Release();
  wmi->pLoc->Release();

  free(wmi);
}

/**
 * read wmi entry
 */
static int wmiread(WMI *wmi, WMIEntry *entry) {
  if (!wmi) {
    return -1;
  }

  if (!wmi->pEnumerator) {
    return -1;
  }

  ULONG ret = 0;
  HRESULT hres = wmi->pEnumerator->Next(
    WBEM_INFINITE,
    1,
    &entry->pClsObj,
    &ret);

  if (ret == 0) {
    return -1;
  }

  return 0;
}

template<typename T>
inline T getter(const VARIANT *) {
  throw new std::logic_error("Specialization not found for this type");
}

/**
 * read `variant` as `std::string`
 */
template<>
inline std::string getter(const VARIANT *prop) {
  return ws2s(prop->bstrVal);
}

/**
 * read `variant` as `BSTR`
 */
template<>
inline BSTR getter(const VARIANT *prop) {
  return prop->bstrVal;
}

/**
 * read `variant` as `uint32_t`
 */
template<>
inline uint32_t getter(const VARIANT *prop) {
  return prop->ulVal;
}

/**
 * read property from wmi entry
 */
template <typename T>
static T wmiprop(WMIEntry *entry, const wchar_t *prop, T defaultValue) {
  if (!entry->pClsObj) {
    return defaultValue;
  }

  HRESULT hres = entry->pClsObj->Get(prop, 0, &entry->data, NULL, NULL);

  if (FAILED(hres) || entry->data.vt == VT_NULL) {
    return defaultValue;
  }

  return getter<T>(&entry->data);
}

/**
 * call wmi method
 */
template <typename T>
static T wmicall(WMI *wmi, WMIEntry *entry,
                 const wchar_t *methodName,
                 const wchar_t *fieldName,
                 T defaultValue) {
  WMIEntry outParams;
  BSTR handlePath = wmiprop<BSTR>(entry, L"__Path", L"");

  if (handlePath == L"") {
    return defaultValue;
  }

  HRESULT hres = wmi->pSvc->ExecMethod(
      handlePath,
      bstr_t(methodName),
      0,
      NULL,
      NULL,
      &outParams.pClsObj,
      NULL);

  if (FAILED(hres)) {
    return defaultValue;
  }

  hres = outParams.pClsObj->Get(
    _bstr_t(fieldName),
    0,
    &outParams.data,
    NULL,
    NULL);

  if (FAILED(hres) || outParams.data.vt == VT_NULL) {
    return defaultValue;
  }

  return getter<T>(&outParams.data);
}

/**
 * special reader for `variant` `datetime` type
 */
static uint64_t wmitime(WMIEntry *entry, const wchar_t *prop) {
  if (!entry->pClsObj) {
    return 0;
  }

  HRESULT hres = entry->pClsObj->Get(prop, 0, &entry->data, NULL, NULL);

  if (FAILED(hres) || entry->data.vt == VT_NULL) {
    return 0;
  }

  BSTR datetime = getter<BSTR>(&entry->data);
  CComPtr<ISWbemDateTime> pSWbemDateTime;

  // create SWbemDateTime instance
  // @link https://msdn.microsoft.com/en-us/library/aa393687(v=vs.85).aspx
  hres = CoCreateInstance(CLSID_SWbemDateTime, NULL, CLSCTX_INPROC_SERVER,
    IID_PPV_ARGS(&pSWbemDateTime));

  if (FAILED(hres)) {
    return 0;
  }

  // set CIM_DATETIME
  // @link https://msdn.microsoft.com/en-us/library/aa387237(v=vs.85).aspx
  hres = pSWbemDateTime->put_Value(datetime);

  if (FAILED(hres)) {
    return 0;
  }

  // get as FILETIME
  // @link https://msdn.microsoft.com/en-us/library/ms724284(v=vs.85).aspx
  BSTR bstrFileTime;
  hres = pSWbemDateTime->GetFileTime(VARIANT_TRUE, &bstrFileTime);

  if (FAILED(hres)) {
    return 0;
  }

  // convert FILETIME to Unix Time
  ULARGE_INTEGER ulargeFileTime, unixEpoch;
  ulargeFileTime.QuadPart = _wtoi64(bstrFileTime);

  unixEpoch.QuadPart = EPOCH_SINCE_UNIX_NANO;
  ulargeFileTime.QuadPart -= unixEpoch.QuadPart;

  SysFreeString(bstrFileTime);
  SysFreeString(datetime);

  return ulargeFileTime.QuadPart / SEC_TO_MS;
}

namespace pl {

  /**
   * main function
   */
  list_t list(const struct process_fields &requested_fields) {
    // Initialize COM.
    CoInitializeHelper co;

    if (FAILED(co)) {
      throw std::logic_error("Failed to initialize COM library");
    }

    list_t proclist;
    LONG flagsOpen = WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY;

    struct WMI *wmi = wmiopen("SELECT * FROM Win32_Process", flagsOpen);

    while (true) {
      struct WMIEntry entry;
      struct process proc;

      int ret = wmiread(wmi, &entry);

      if (ret < 0) {
        break;
      }

      if (requested_fields.pid) {
        proc.pid = wmiprop<uint32_t>(&entry, L"ProcessId", 0);
      }

      if (requested_fields.ppid) {
        proc.ppid = wmiprop<uint32_t>(&entry, L"ParentProcessId", 0);
      }

      if (requested_fields.name) {
        proc.name = wmiprop<std::string>(&entry, L"Name", "");
      }

      if (requested_fields.path) {
        proc.path = wmiprop<std::string>(&entry, L"ExecutablePath", "");
      }

      if (requested_fields.cmdline) {
        proc.cmdline = wmiprop<std::string>(&entry, L"CommandLine", "");
      }

      if (requested_fields.threads) {
        proc.threads = wmiprop<uint32_t>(&entry, L"ThreadCount", 0);
      }

      if (requested_fields.priority) {
        proc.priority = wmiprop<uint32_t>(&entry, L"Priority", 0);
      }

      if (requested_fields.owner) {
        proc.owner = wmicall<std::string>(
          wmi,
          &entry,
          L"GetOwner",
          L"User",
          std::string());
      }

      if (requested_fields.starttime) {
        proc.starttime = wmitime(&entry, L"CreationDate");
      }

      proclist.push_back(proc);
    }

    wmiclose(wmi);
    return proclist;
  }
}  // namespace pl
<commit_msg>win: return `starttime` in utc<commit_after>/**
 * Copyright (c) 2014 Dmitry Tsvettsikh <https://github.com/reklatsmasters>
 *
 * MIT License <https://github.com/reklatsmasters/node-process-list/blob/master/LICENSE>
 */

#include "tasklist.h"  // NOLINT(build/include)

#define _WIN32_DCOM

#include <windows.h>
#include <comdef.h>
#include <Wbemidl.h>
#include "OAIdl.h"
#include <WbemCli.h>
#include <tchar.h>
#include <atlbase.h>

#include <codecvt>
#include <string>
#include <iostream>

using pl::process;

#pragma comment(lib, "wbemuuid.lib")

#define EPOCH_SINCE_UNIX_NANO 116444736000000000
#define SEC_TO_MS 10000

static std::string ws2s(const std::wstring& wstr) {
  typedef std::codecvt_utf8<wchar_t> convert_typeX;
  std::wstring_convert<convert_typeX, wchar_t> converterX;

  return converterX.to_bytes(wstr);
}

// The SafeRelease Pattern
template <class T>
void SafeRelease(T **ppT) {
  if (*ppT) {
    (*ppT)->Release();
    *ppT = NULL;
  }
}

struct WMI {
  IWbemLocator *pLoc;
  IWbemServices *pSvc;
  IEnumWbemClassObject *pEnumerator;
};

struct WMIEntry {
  WMIEntry() : pClsObj(NULL) {}

  ~WMIEntry() {
    VariantClear(&data);
    SafeRelease(&pClsObj);
  }

  VARIANT data;
  IWbemClassObject *pClsObj;
};

class CoInitializeHelper {
 public:
  CoInitializeHelper() :
    m_hres(CoInitializeEx(NULL, COINIT_MULTITHREADED))
    { }

  ~CoInitializeHelper() {
    if (SUCCEEDED(m_hres)) {
      CoUninitialize();
    }
  }

  operator HRESULT() const {
    return m_hres;
  }

 private:
  HRESULT m_hres;
};

/**
 * open wmi stream
 */
static WMI * wmiopen(const char *query, LONG flags) {
  HRESULT hres;
  WMI *wmi = reinterpret_cast<WMI *>(malloc(sizeof(struct WMI)));

  if (wmi == NULL) {
    throw std::logic_error("Failed to initialize struct WMI");
  }

  memset(wmi, 0, sizeof(struct WMI));

  // Obtain the initial locator to Windows Management
  // on a particular host computer.
  hres = CoCreateInstance(
    CLSID_WbemLocator,
    0,
    CLSCTX_INPROC_SERVER,
    IID_IWbemLocator,
    reinterpret_cast<LPVOID *>(&wmi->pLoc));

  if (FAILED(hres)) {
    free(wmi);
    throw std::logic_error("Failed to create IWbemLocator object.");
  }

  // Connect to the root\cimv2 namespace with the
  // current user and obtain pointer pSvc
  // to make IWbemServices calls.
  hres = wmi->pLoc->ConnectServer(
    _bstr_t(L"ROOT\\CIMV2"),  // WMI namespace
    NULL,                     // User name
    NULL,                     // User password
    0,                        // Locale
    NULL,                     // Security flags
    0,                        // Authority
    0,                        // Context object
    &wmi->pSvc);              // IWbemServices proxy

  if (FAILED(hres)) {
    wmi->pLoc->Release();
    free(wmi);
    throw std::logic_error("Could not connect to root\\cimv2.");
  }

  // Set the IWbemServices proxy so that impersonation
  // of the user (client) occurs.
  hres = CoSetProxyBlanket(
    wmi->pSvc,                    // the proxy to set
    RPC_C_AUTHN_WINNT,            // authentication service
    RPC_C_AUTHZ_NONE,             // authorization service
    NULL,                         // Server principal name
    RPC_C_AUTHN_LEVEL_CALL,       // authentication level
    RPC_C_IMP_LEVEL_IMPERSONATE,  // impersonation level
    NULL,                         // client identity
    EOAC_NONE);                   // proxy capabilities

  if (FAILED(hres)) {
    wmi->pSvc->Release();
    wmi->pLoc->Release();
    free(wmi);
    throw std::logic_error("Could not set proxy blanket.");
  }

  // Use the IWbemServices pointer to make requests of WMI.
  hres = wmi->pSvc->ExecQuery(
    bstr_t("WQL"),
    bstr_t(query),
    flags,
    NULL,
    &wmi->pEnumerator);

  if (FAILED(hres)) {
    wmi->pSvc->Release();
    wmi->pLoc->Release();
    free(wmi);
    throw std::logic_error("Query for processes failed.");
  }

  return wmi;
}

/**
 * close wmi stream
 */
static void wmiclose(WMI *wmi) {
  wmi->pEnumerator->Release();
  wmi->pSvc->Release();
  wmi->pLoc->Release();

  free(wmi);
}

/**
 * read wmi entry
 */
static int wmiread(WMI *wmi, WMIEntry *entry) {
  if (!wmi) {
    return -1;
  }

  if (!wmi->pEnumerator) {
    return -1;
  }

  ULONG ret = 0;
  HRESULT hres = wmi->pEnumerator->Next(
    WBEM_INFINITE,
    1,
    &entry->pClsObj,
    &ret);

  if (ret == 0) {
    return -1;
  }

  return 0;
}

template<typename T>
inline T getter(const VARIANT *) {
  throw new std::logic_error("Specialization not found for this type");
}

/**
 * read `variant` as `std::string`
 */
template<>
inline std::string getter(const VARIANT *prop) {
  return ws2s(prop->bstrVal);
}

/**
 * read `variant` as `BSTR`
 */
template<>
inline BSTR getter(const VARIANT *prop) {
  return prop->bstrVal;
}

/**
 * read `variant` as `uint32_t`
 */
template<>
inline uint32_t getter(const VARIANT *prop) {
  return prop->ulVal;
}

/**
 * read property from wmi entry
 */
template <typename T>
static T wmiprop(WMIEntry *entry, const wchar_t *prop, T defaultValue) {
  if (!entry->pClsObj) {
    return defaultValue;
  }

  HRESULT hres = entry->pClsObj->Get(prop, 0, &entry->data, NULL, NULL);

  if (FAILED(hres) || entry->data.vt == VT_NULL) {
    return defaultValue;
  }

  return getter<T>(&entry->data);
}

/**
 * call wmi method
 */
template <typename T>
static T wmicall(WMI *wmi, WMIEntry *entry,
                 const wchar_t *methodName,
                 const wchar_t *fieldName,
                 T defaultValue) {
  WMIEntry outParams;
  BSTR handlePath = wmiprop<BSTR>(entry, L"__Path", L"");

  if (handlePath == L"") {
    return defaultValue;
  }

  HRESULT hres = wmi->pSvc->ExecMethod(
      handlePath,
      bstr_t(methodName),
      0,
      NULL,
      NULL,
      &outParams.pClsObj,
      NULL);

  if (FAILED(hres)) {
    return defaultValue;
  }

  hres = outParams.pClsObj->Get(
    _bstr_t(fieldName),
    0,
    &outParams.data,
    NULL,
    NULL);

  if (FAILED(hres) || outParams.data.vt == VT_NULL) {
    return defaultValue;
  }

  return getter<T>(&outParams.data);
}

/**
 * special reader for `variant` `datetime` type
 */
static uint64_t wmitime(WMIEntry *entry, const wchar_t *prop) {
  if (!entry->pClsObj) {
    return 0;
  }

  HRESULT hres = entry->pClsObj->Get(prop, 0, &entry->data, NULL, NULL);

  if (FAILED(hres) || entry->data.vt == VT_NULL) {
    return 0;
  }

  BSTR datetime = getter<BSTR>(&entry->data);
  CComPtr<ISWbemDateTime> pSWbemDateTime;

  // create SWbemDateTime instance
  // @link https://msdn.microsoft.com/en-us/library/aa393687(v=vs.85).aspx
  hres = CoCreateInstance(CLSID_SWbemDateTime, NULL, CLSCTX_INPROC_SERVER,
    IID_PPV_ARGS(&pSWbemDateTime));

  if (FAILED(hres)) {
    return 0;
  }

  // set CIM_DATETIME
  // @link https://msdn.microsoft.com/en-us/library/aa387237(v=vs.85).aspx
  hres = pSWbemDateTime->put_Value(datetime);

  if (FAILED(hres)) {
    return 0;
  }

  // get as FILETIME
  // @link https://msdn.microsoft.com/en-us/library/ms724284(v=vs.85).aspx
  BSTR bstrFileTime;
  hres = pSWbemDateTime->GetFileTime(VARIANT_FALSE, &bstrFileTime);

  if (FAILED(hres)) {
    return 0;
  }

  // convert FILETIME to Unix Time
  ULARGE_INTEGER ulargeFileTime, unixEpoch;
  ulargeFileTime.QuadPart = _wtoi64(bstrFileTime);

  unixEpoch.QuadPart = EPOCH_SINCE_UNIX_NANO;
  ulargeFileTime.QuadPart -= unixEpoch.QuadPart;

  SysFreeString(bstrFileTime);
  SysFreeString(datetime);

  return ulargeFileTime.QuadPart / SEC_TO_MS;
}

namespace pl {

  /**
   * main function
   */
  list_t list(const struct process_fields &requested_fields) {
    // Initialize COM.
    CoInitializeHelper co;

    if (FAILED(co)) {
      throw std::logic_error("Failed to initialize COM library");
    }

    list_t proclist;
    LONG flagsOpen = WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY;

    struct WMI *wmi = wmiopen("SELECT * FROM Win32_Process", flagsOpen);

    while (true) {
      struct WMIEntry entry;
      struct process proc;

      int ret = wmiread(wmi, &entry);

      if (ret < 0) {
        break;
      }

      if (requested_fields.pid) {
        proc.pid = wmiprop<uint32_t>(&entry, L"ProcessId", 0);
      }

      if (requested_fields.ppid) {
        proc.ppid = wmiprop<uint32_t>(&entry, L"ParentProcessId", 0);
      }

      if (requested_fields.name) {
        proc.name = wmiprop<std::string>(&entry, L"Name", "");
      }

      if (requested_fields.path) {
        proc.path = wmiprop<std::string>(&entry, L"ExecutablePath", "");
      }

      if (requested_fields.cmdline) {
        proc.cmdline = wmiprop<std::string>(&entry, L"CommandLine", "");
      }

      if (requested_fields.threads) {
        proc.threads = wmiprop<uint32_t>(&entry, L"ThreadCount", 0);
      }

      if (requested_fields.priority) {
        proc.priority = wmiprop<uint32_t>(&entry, L"Priority", 0);
      }

      if (requested_fields.owner) {
        proc.owner = wmicall<std::string>(
          wmi,
          &entry,
          L"GetOwner",
          L"User",
          std::string());
      }

      if (requested_fields.starttime) {
        proc.starttime = wmitime(&entry, L"CreationDate");
      }

      proclist.push_back(proc);
    }

    wmiclose(wmi);
    return proclist;
  }
}  // namespace pl
<|endoftext|>


<commit_before>#include "common.th"
#include "errno.th"

#define RANK_0_LOG 4
#define RANK_0_SIZE (1 << RANK_0_LOG)
#define RANKS 3

// NOTE there are some assumptions in the below code that NPER * BPER = 32,
// notably in DIFF()
#define NPERBITS 4
#define BPERBITS 1

#define NPER (1 << NPERBITS)
#define BPER (1 << BPERBITS)

#define TN 0

// TODO size `nodes' appropriately
// TODO implement .lcomm and use it for this
nodes:
    .word 0

// TODO size `pool' appropriately
#define POOL    rel(pool)
pool:
    .word 0

// TODO initialise counts appropriately
counts:
    .word 0, 0, 1

// Type `SZ' is a word-wide unsigned integer (some places used as a boolean).
// Type `NP' is a word-wide struct, where the word is split into a 28-bit word
// index into `nodes` and a 4-bit index into the word indexed.

ilog2:
    B   <- 0
    push(D)
L_ilog2_top:
    C   <- C >> 1
    D   <- C == 0
    jnzrel(D, L_ilog2_done)
    B   <- B + 1
    goto(L_ilog2_top)
L_ilog2_done:
    pop(D)
    ret

// NODE gets SZ index in C, returns NP node in B, clobbers C
#define NODE(B,C)               \
    B   <- C << BPERBITS      ; \
    B   <- B + TN             ; \
    C   <- C & (BPER - 1)     ; \
    B   <- B | C              ; \
    //

// IRANK gets SZ rank in C, returns SZ inverted-rank in B
#define IRANK(B,C)              \
    B   <- - C + (RANKS - 1)  ; \
    //

// DIFF gets NP a in C, NP b in D, returns SZ distance in B
#define DIFF(B,C,D)             \
    B   <- C - D              ; \
    //

// INDEX gets NP n in C, returns SZ distance from TN in B
#define INDEX(B,C)              \
    DIFF(B,C,TN)              ; \
    //

// LLINK gets NP n in C, returns NP left-child in B, clobbers C
#define LLINK(B,C)              \
    INDEX(B,C)                ; \
    C   <- B << 1             ; \
    C   <- C +  1             ; \
    NODE(B,C)                 ; \
    //

// RLINK gets NP n in C, returns NP right-child in B, clobbers C
#define RLINK(B,C)              \
    INDEX(B,C)                ; \
    C   <- B << 1             ; \
    C   <- C +  2             ; \
    NODE(B,C)                 ; \
    //

// ISLEFT gets NP n in C, returns SZ leftness in B
#define ISLEFT(B,C)             \
    INDEX(B,C)                ; \
    B   <- B & 1              ; \
    B   <- B == 1             ; \
    //

// ISRIGHT gets NP n in C, returns SZ rightness in B
#define ISRIGHT(B,C)            \
    INDEX(B,C)                ; \
    B   <- B & 1              ; \
    B   <- B == 0             ; \
    //

// PARENT gets NP n in C, returns NP parent in B, clobbers C
#define PARENT(B,C)             \
    INDEX(B,C)                ; \
    C   <- B -  1             ; \
    C   <- C >> 1             ; \
    NODE(B,C)                 ; \
    //

// SIBLING gets NP n in C, returns NP sibling in B, clobbers C
#define SIBLING(B,C)            \
    INDEX(B,C)                ; \
    ISLEFT(C,B)               ; \
    B   <- B - C              ; \
    C   <- ~ C                ; \
    B   <- B + C              ; \
    //

// NODE2RANK gets NP n in C, returns SZ rank in B
#define NODE2RANK(B,C)          \
    call(NODE2RANK_func)      ; \
    //

NODE2RANK_func:
    push(D)
    D   <- 0
L_NODE2RANK_top:
    INDEX(B,C)
    B   <- B == 0
    jnzrel(B, L_NODE2RANK_done)
    D   <- D + 1
    B   <- C
    PARENT(C,B)
    goto(L_NODE2RANK_top)
L_NODE2RANK_done:
    B   <- - D + (RANKS - 1)
    pop(D)
    ret

#define SIZE2RANK(B,C) \
    call(SIZE2RANK_func)
    //

SIZE2RANK_func:
    B   <- C <> 0
    jzrel(B, L_SIZE2RANK_zero)
    C   <- C - 1
    C   <- C >> RANK_0_LOG
    call(ilog2)
    B   <- B + 1
L_SIZE2RANK_zero:
    ret

// RANK2WORDS gets SZ rank in C, returns SZ word-count in B, clobbers C
#define RANK2WORDS(B,C)         \
    C   <- C + RANK_0_LOG     ; \
    B   <- 1                  ; \
    B   <- B << C             ; \
    //

// GET_COUNT gets SZ rank in C, returns SZ free-count in B
#define GET_COUNT(B,C)          \
    B   <- [C + rel(counts)]  ; \
    //

// SET_COUNT gets SZ rank in C, SZ free-count in D, returns nothing
#define SET_COUNT(C,D)          \
    D   -> [C + rel(counts)]  ; \
    //

// GET_NODE gets NP n in C, returns SZ shifted-word in B, clobbers C
#define GET_NODE(B,C)           \
    B   <- C >> BPERBITS      ; \
    C   <- C & (BPER - 1)     ; \
    B   <- [B]                ; \
    B   <- B >> C             ; \
    //

// GET_LEAF gets NP n in C, returns SZ leafiness in B, clobbers C
#define GET_LEAF(B,C)           \
    GET_NODE(B,C)             ; \
    B   <- B &  1             ; \
    B   <- B == 0             ; \
    //

// GET_FULL gets NP n in C, returns SZ fullness in B, clobbers C
#define GET_FULL(B,C)           \
    GET_NODE(B,C)             ; \
    B   <- B &  2             ; \
    B   <- B == 2             ; \
    //

// GET_VALID gets NP n in C, returns SZ validity in B
#define GET_VALID(B,C,T0)       \
    T0  <- C                  ; \
    INDEX(B,C)                ; \
    PARENT(C,T0)              ; \
    GET_LEAF(T0,C)            ; \
    B   <- B &~ T0            ; \
    //

// TODO SET_LEAF
// TODO SET_FULL
#define SET_FULL(C,D) \
    //
// TODO NODE2ADDR
// ADDR2NODE gets SZ addr in C, returns NP node in B
#define ADDR2NODE(B,C)          \
    call(ADDR2NODE_func)      ; \
    //

// C is key
ADDR2NODE_func:
    pushall(D,E,F,G,H)
    D   <- (RANKS - 1)  // D is current rank
    E   <- POOL         // E is base being built
    F   <- C            // F is a scratch register
    G   <- TN           // G is the node being checked
    H   <- G            // H is another scratch
L_ADDR2NODE_looptop:
    GET_LEAF(B,H)
    jnzrel(B,L_ADDR2NODE_loopdone)
    RANK2WORDS(B,F)
    H   <- C - E        // H is (key - base)
    F   <- H < B        // if true, then left-link
    jnzrel(F,L_ADDR2NODE_left)
    H   <- G
    RLINK(G,H)          // n = RLINK(n)
    E   <- E + B        // base += RANK2WORDS(rank)
    goto(L_ADDR2NODE_loopbottom)
L_ADDR2NODE_left:
    H   <- G
    LLINK(G,H)          // n = LLINK(n)
    // fallthrough

L_ADDR2NODE_loopbottom:
    D   <- D - 1
    goto(L_ADDR2NODE_looptop)

L_ADDR2NODE_loopdone:
    B   <- C == E
    jnzrel(B,L_ADDR2NODE_done)
    call(abort)

L_ADDR2NODE_done:
    popall(D,E,F,G,H)
    ret

.global buddy_malloc
buddy_malloc:
    goto(buddy_alloc)

.global buddy_calloc
buddy_calloc:
    C   <- C * D
    push(C)
    call(buddy_alloc)
    C   <- B
    D   <- 0
    pop(E)
    call(memset)
    ret

.global buddy_free
buddy_free:
    // TODO
    ret

.global buddy_realloc
buddy_realloc:
    // TODO
    ret

// -----------------------------------------------------------------------------

buddy_splitnode:
    // TODO
    ret

buddy_autosplit:
    // TODO
    ret

// TODO check for clobbering
// buddy_alloc gets SZ size in C, returns address or 0 in B
buddy_alloc:
    pushall(D,E,F,G)
    SIZE2RANK(B,C)
    D   <- B < RANKS
    jzrel(D,L_buddy_alloc_error)
    D   <- B
    GET_COUNT(E,D)
    D   <- E == 0
    jnzrel(D,L_buddy_alloc_do_split)
    // here is the main body, where we have a nonzero count of the needed
    // rank. B is rank, D is scratch, E is scratch and currently count
    D   <- B
    IRANK(E,D)
    F   <- 1
    F   <- F << E       // F is loop bound
    D   <- F - 1        // D is first index to check
    F   <- F + D        // F is loop bound adjust for `first'
L_buddy_alloc_loop_top:
    NODE(E,D)           // E is NODE(D)
    GET_VALID(C,E,G)    // C is validity
    jzrel(C,L_buddy_alloc_loop_bottom)
    GET_LEAF(C,E)       // C is leafiness
    jzrel(C,L_buddy_alloc_loop_bottom)
    GET_FULL(C,E)       // C is fullness
    jnzrel(C,L_buddy_alloc_loop_bottom)
    SET_FULL(E,-1)
    GET_COUNT(C,B)
    C   <- C - 1
    SET_COUNT(E,C)
    C   <- E
    ADDR2NODE(B,C)
    goto(L_buddy_alloc_done)

L_buddy_alloc_loop_bottom:
    D   <- D + 1    // D is loop index
    goto(L_buddy_alloc_loop_top)

L_buddy_alloc_do_split:
    D   <- E
    call(buddy_autosplit)
    goto(L_buddy_alloc_done)

L_buddy_alloc_error:
    D   <- ENOMEM
    D   -> errno
    B   <- 0
L_buddy_alloc_done:
    popall(D,E,F,G)
    ret

<commit_msg>SET_NODE and SET_LEAF being thrown together ; compiles<commit_after>#include "common.th"
#include "errno.th"

#define RANK_0_LOG 4
#define RANK_0_SIZE (1 << RANK_0_LOG)
#define RANKS 3

// NOTE there are some assumptions in the below code that NPER * BPER = 32,
// notably in DIFF()
#define NPERBITS 4
#define BPERBITS 1

#define NPER (1 << NPERBITS)
#define BPER (1 << BPERBITS)

#define TN 0

// TODO size `nodes' appropriately
// TODO implement .lcomm and use it for this
nodes:
    .word 0

// TODO size `pool' appropriately
#define POOL    rel(pool)
pool:
    .word 0

// TODO initialise counts appropriately
counts:
    .word 0, 0, 1

// Type `SZ' is a word-wide unsigned integer (some places used as a boolean).
// Type `NP' is a word-wide struct, where the word is split into a 28-bit word
// index into `nodes` and a 4-bit index into the word indexed.

ilog2:
    B   <- 0
    push(D)
L_ilog2_top:
    C   <- C >> 1
    D   <- C == 0
    jnzrel(D, L_ilog2_done)
    B   <- B + 1
    goto(L_ilog2_top)
L_ilog2_done:
    pop(D)
    ret

// NODE gets SZ index in C, returns NP node in B, clobbers C
#define NODE(B,C)               \
    B   <- C << BPERBITS      ; \
    B   <- B + TN             ; \
    C   <- C & (BPER - 1)     ; \
    B   <- B | C              ; \
    //

// IRANK gets SZ rank in C, returns SZ inverted-rank in B
#define IRANK(B,C)              \
    B   <- - C + (RANKS - 1)  ; \
    //

// DIFF gets NP a in C, NP b in D, returns SZ distance in B
#define DIFF(B,C,D)             \
    B   <- C - D              ; \
    //

// INDEX gets NP n in C, returns SZ distance from TN in B
#define INDEX(B,C)              \
    DIFF(B,C,TN)              ; \
    //

// LLINK gets NP n in C, returns NP left-child in B, clobbers C
#define LLINK(B,C)              \
    INDEX(B,C)                ; \
    C   <- B << 1             ; \
    C   <- C +  1             ; \
    NODE(B,C)                 ; \
    //

// RLINK gets NP n in C, returns NP right-child in B, clobbers C
#define RLINK(B,C)              \
    INDEX(B,C)                ; \
    C   <- B << 1             ; \
    C   <- C +  2             ; \
    NODE(B,C)                 ; \
    //

// ISLEFT gets NP n in C, returns SZ leftness in B
#define ISLEFT(B,C)             \
    INDEX(B,C)                ; \
    B   <- B & 1              ; \
    B   <- B == 1             ; \
    //

// ISRIGHT gets NP n in C, returns SZ rightness in B
#define ISRIGHT(B,C)            \
    INDEX(B,C)                ; \
    B   <- B & 1              ; \
    B   <- B == 0             ; \
    //

// PARENT gets NP n in C, returns NP parent in B, clobbers C
#define PARENT(B,C)             \
    INDEX(B,C)                ; \
    C   <- B -  1             ; \
    C   <- C >> 1             ; \
    NODE(B,C)                 ; \
    //

// SIBLING gets NP n in C, returns NP sibling in B, clobbers C
#define SIBLING(B,C)            \
    INDEX(B,C)                ; \
    ISLEFT(C,B)               ; \
    B   <- B - C              ; \
    C   <- ~ C                ; \
    B   <- B + C              ; \
    //

// NODE2RANK gets NP n in C, returns SZ rank in B
#define NODE2RANK(B,C)          \
    call(NODE2RANK_func)      ; \
    //

NODE2RANK_func:
    push(D)
    D   <- 0
L_NODE2RANK_top:
    INDEX(B,C)
    B   <- B == 0
    jnzrel(B, L_NODE2RANK_done)
    D   <- D + 1
    B   <- C
    PARENT(C,B)
    goto(L_NODE2RANK_top)
L_NODE2RANK_done:
    B   <- - D + (RANKS - 1)
    pop(D)
    ret

#define SIZE2RANK(B,C) \
    call(SIZE2RANK_func)
    //

SIZE2RANK_func:
    B   <- C <> 0
    jzrel(B, L_SIZE2RANK_zero)
    C   <- C - 1
    C   <- C >> RANK_0_LOG
    call(ilog2)
    B   <- B + 1
L_SIZE2RANK_zero:
    ret

// RANK2WORDS gets SZ rank in C, returns SZ word-count in B, clobbers C
#define RANK2WORDS(B,C)         \
    C   <- C + RANK_0_LOG     ; \
    B   <- 1                  ; \
    B   <- B << C             ; \
    //

// GET_COUNT gets SZ rank in C, returns SZ free-count in B
#define GET_COUNT(B,C)          \
    B   <- [C + rel(counts)]  ; \
    //

// SET_COUNT gets SZ rank in C, SZ free-count in D, returns nothing
#define SET_COUNT(C,D)          \
    D   -> [C + rel(counts)]  ; \
    //

// GET_NODE gets NP n in C, returns SZ shifted-word in B, clobbers C
#define GET_NODE(B,C)           \
    B   <- C >> BPERBITS      ; \
    C   <- C & (BPER - 1)     ; \
    C   <- C << BPERBITS      ; \
    B   <- [B + rel(nodes)]   ; \
    B   <- B >> C             ; \
    //

// SET_NODE gets NP n in C, SZ pre-shift-word in D, clobbers C and D
#define SET_NODE(C,D,T0,T1)                                                 \
    T0  <- C & (BPER - 1)       /* T0 is shift count in nodes        */   ; \
    T0  <- T0 << BPERBITS       /* T0 is shift count in bits         */   ; \
    D   <- D << T0              /* D  is now shifted over            */   ; \
    T1  <- (BPER - 1)           /* T1 is mask                        */   ; \
    T1  <- T1 << T0             /* T1 is shifted mask                */   ; \
    T0  <- C >> BPERBITS        /* T0 is index into node array       */   ; \
    C   <- [T0 + rel(nodes)]    /* C  is current word                */   ; \
    C   <- C &~ T1              /* C  is word with node masked out   */   ; \
    C   <- C | D                /* C  is word with new node included */   ; \
    C   -> [T0 + rel(nodes)]    /* C  gets written back to node      */   ; \
    //

// GET_LEAF gets NP n in C, returns SZ leafiness in B, clobbers C
#define GET_LEAF(B,C)           \
    GET_NODE(B,C)             ; \
    B   <- B &  1             ; \
    B   <- B == 0             ; \
    //

// GET_FULL gets NP n in C, returns SZ fullness in B, clobbers C
#define GET_FULL(B,C)           \
    GET_NODE(B,C)             ; \
    B   <- B &  2             ; \
    B   <- B == 2             ; \
    //

// GET_VALID gets NP n in C, returns SZ validity in B
#define GET_VALID(B,C,T0)       \
    T0  <- C                  ; \
    INDEX(B,C)                ; \
    PARENT(C,T0)              ; \
    GET_LEAF(T0,C)            ; \
    B   <- B &~ T0            ; \
    //

// TODO SET_LEAF
// SET_LEAF gets NP n in C, SZ truth in D, returns nothing, clobbers C
// We take a shortcut ; either we are setting the leaf true from false, and it
// is therefore not full, or we are setting it false from true, and fullness
// has no meaning. This allows us to avoid reading and rewriting the fullness
// value.
#define SET_LEAF(C,D,T0,T1)     \
    D   <- D & 1              ; \
    SET_NODE(C,D,T0,T1)       ; \
    //

// TODO SET_FULL
#define SET_FULL(C,D) \
    //
// TODO NODE2ADDR
// ADDR2NODE gets SZ addr in C, returns NP node in B
#define ADDR2NODE(B,C)          \
    call(ADDR2NODE_func)      ; \
    //

// C is key
ADDR2NODE_func:
    pushall(D,E,F,G,H)
    D   <- (RANKS - 1)  // D is current rank
    E   <- POOL         // E is base being built
    F   <- C            // F is a scratch register
    G   <- TN           // G is the node being checked
    H   <- G            // H is another scratch
L_ADDR2NODE_looptop:
    GET_LEAF(B,H)
    jnzrel(B,L_ADDR2NODE_loopdone)
    RANK2WORDS(B,F)
    H   <- C - E        // H is (key - base)
    F   <- H < B        // if true, then left-link
    jnzrel(F,L_ADDR2NODE_left)
    H   <- G
    RLINK(G,H)          // n = RLINK(n)
    E   <- E + B        // base += RANK2WORDS(rank)
    goto(L_ADDR2NODE_loopbottom)
L_ADDR2NODE_left:
    H   <- G
    LLINK(G,H)          // n = LLINK(n)
    // fallthrough

L_ADDR2NODE_loopbottom:
    D   <- D - 1
    goto(L_ADDR2NODE_looptop)

L_ADDR2NODE_loopdone:
    B   <- C == E
    jnzrel(B,L_ADDR2NODE_done)
    call(abort)

L_ADDR2NODE_done:
    popall(D,E,F,G,H)
    ret

.global buddy_malloc
buddy_malloc:
    goto(buddy_alloc)

.global buddy_calloc
buddy_calloc:
    C   <- C * D
    push(C)
    call(buddy_alloc)
    C   <- B
    D   <- 0
    pop(E)
    call(memset)
    ret

.global buddy_free
buddy_free:
    // TODO
    ret

.global buddy_realloc
buddy_realloc:
    // TODO
    ret

// -----------------------------------------------------------------------------

buddy_splitnode:
    // TODO
    ret

buddy_autosplit:
    // TODO
    ret

// TODO check for clobbering
// buddy_alloc gets SZ size in C, returns address or 0 in B
buddy_alloc:
    pushall(D,E,F,G)
    SIZE2RANK(B,C)
    D   <- B < RANKS
    jzrel(D,L_buddy_alloc_error)
    D   <- B
    GET_COUNT(E,D)
    D   <- E == 0
    jnzrel(D,L_buddy_alloc_do_split)
    // here is the main body, where we have a nonzero count of the needed
    // rank. B is rank, D is scratch, E is scratch and currently count
    D   <- B
    IRANK(E,D)
    F   <- 1
    F   <- F << E       // F is loop bound
    D   <- F - 1        // D is first index to check
    F   <- F + D        // F is loop bound adjust for `first'
L_buddy_alloc_loop_top:
    NODE(E,D)           // E is NODE(D)
    GET_VALID(C,E,G)    // C is validity
    jzrel(C,L_buddy_alloc_loop_bottom)
    GET_LEAF(C,E)       // C is leafiness
    jzrel(C,L_buddy_alloc_loop_bottom)
    GET_FULL(C,E)       // C is fullness
    jnzrel(C,L_buddy_alloc_loop_bottom)
    SET_FULL(E,-1)
    GET_COUNT(C,B)
    C   <- C - 1
    SET_COUNT(E,C)
    C   <- E
    ADDR2NODE(B,C)
    goto(L_buddy_alloc_done)

L_buddy_alloc_loop_bottom:
    D   <- D + 1    // D is loop index
    goto(L_buddy_alloc_loop_top)

L_buddy_alloc_do_split:
    D   <- E
    call(buddy_autosplit)
    goto(L_buddy_alloc_done)

L_buddy_alloc_error:
    D   <- ENOMEM
    D   -> errno
    B   <- 0
L_buddy_alloc_done:
    popall(D,E,F,G)
    ret

<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: viewcontactofunocontrol.hxx,v $
 * $Revision: 1.7 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef SVX_SDR_CONTACT_VIEWCONTACTOFUNOCONTROL_HXX
#define SVX_SDR_CONTACT_VIEWCONTACTOFUNOCONTROL_HXX

/** === begin UNO includes === **/
#include <com/sun/star/uno/Reference.hxx>
/** === end UNO includes === **/
#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
#include <svx/svxdllapi.h>

#include <memory>

class OutputDevice;
class Window;
class SdrUnoObj;
namespace com { namespace sun { namespace star {
    namespace awt {
        class XControl;
        class XControlContainer;
    }
} } }

//........................................................................
namespace sdr { namespace contact {
//........................................................................

    //====================================================================
    //= ViewContactOfUnoControl
    //====================================================================
    class ViewContactOfUnoControl_Impl;
    class SVX_DLLPRIVATE ViewContactOfUnoControl : public ViewContactOfSdrObj
    {
    private:
        ::std::auto_ptr< ViewContactOfUnoControl_Impl >   m_pImpl;

    public:
        ViewContactOfUnoControl( SdrUnoObj& _rUnoObject );
        virtual ~ViewContactOfUnoControl();

        /** access control to selected members
        */
        struct SdrUnoObjAccessControl { friend class ::SdrUnoObj; private: SdrUnoObjAccessControl() { } };

        /** retrieves a temporary XControl instance, whose parent is the given window
            @seealso SdrUnoObj::GetTemporaryControlForWindow
        */
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XControl >
            getTemporaryControlForWindow( const Window& _rWindow, ::com::sun::star::uno::Reference< ::com::sun::star::awt::XControlContainer >& _inout_ControlContainer ) const;

        /** invalidates all ViewObjectContacts

            This method is necessary when an SdrUnoObj changes completely, e.g. when
            some foreign instance set a new ->XControlModel.
        */
        void    invalidateAllContacts( const SdrUnoObjAccessControl&  );

    protected:
        virtual ViewObjectContact& CreateObjectSpecificViewObjectContact( ObjectContact& _rObjectContact );

        // ViewContactOfSdrObj overridables
        virtual sal_Bool ShouldPaintObject( DisplayInfo& rDisplayInfo, const ViewObjectContact& rAssociatedVOC );

    private:
        ViewContactOfUnoControl();                                            // never implemented
        ViewContactOfUnoControl( const ViewContactOfUnoControl& );              // never implemented
        ViewContactOfUnoControl& operator=( const ViewContactOfUnoControl& );   // never implemented
    };

//........................................................................
} } // namespace sdr::contact
//........................................................................

#endif // SVX_SDR_CONTACT_VIEWCONTACTOFUNOCONTROL_HXX

<commit_msg>INTEGRATION: CWS aw033 (1.1.4); FILE MERGED 2008/05/14 13:58:49 aw 1.1.4.10: RESYNC: (1.5-1.7); FILE MERGED 2008/01/22 12:29:06 aw 1.1.4.9: adaptions and 1st stripping 2007/08/09 17:14:27 aw 1.1.4.8: RESYNC: (1.4-1.5); FILE MERGED 2007/07/06 13:42:27 aw 1.1.4.7: #i39532# moved from Primitive2DReference to Primitive2DSequence where possible to avoid extra-group primitive creations and deeper hierarchies as necessary 2007/03/20 13:54:09 aw 1.1.4.6: RESYNC: (1.2-1.3); FILE MERGED 2006/12/13 10:20:20 aw 1.1.4.5: #i39532# use correct verion from previously added file 2006/12/12 16:28:30 hr 1.1.4.4: RESYNC; 2006/12/11 17:40:13 aw 1.1.4.3: #i39532# changes after resync 2006/10/19 10:53:46 aw 1.1.4.2: #i39532# primitive 2006/08/09 17:04:55 aw 1.1.4.1: #i39532#<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: viewcontactofunocontrol.hxx,v $
 * $Revision: 1.8 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef SVX_SDR_CONTACT_VIEWCONTACTOFUNOCONTROL_HXX
#define SVX_SDR_CONTACT_VIEWCONTACTOFUNOCONTROL_HXX

/** === begin UNO includes === **/
#include <com/sun/star/uno/Reference.hxx>
/** === end UNO includes === **/
#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
#include <svx/svxdllapi.h>

#include <memory>

class OutputDevice;
class Window;
class SdrUnoObj;
namespace com { namespace sun { namespace star {
    namespace awt {
        class XControl;
        class XControlContainer;
    }
} } }

//........................................................................
namespace sdr { namespace contact {
//........................................................................

    //====================================================================
    //= ViewContactOfUnoControl
    //====================================================================
    class ViewContactOfUnoControl_Impl;
    class SVX_DLLPRIVATE ViewContactOfUnoControl : public ViewContactOfSdrObj
    {
    private:
        ::std::auto_ptr< ViewContactOfUnoControl_Impl >   m_pImpl;

    public:
        // access to SdrObject
        SdrUnoObj& GetSdrUnoObj() const
        {
            return ((SdrUnoObj&)GetSdrObject());
        }

        ViewContactOfUnoControl( SdrUnoObj& _rUnoObject );
        virtual ~ViewContactOfUnoControl();

        /** access control to selected members
        */
        struct SdrUnoObjAccessControl { friend class ::SdrUnoObj; private: SdrUnoObjAccessControl() { } };

        /** retrieves a temporary XControl instance, whose parent is the given window
            @seealso SdrUnoObj::GetTemporaryControlForWindow
        */
        ::com::sun::star::uno::Reference< ::com::sun::star::awt::XControl >
            getTemporaryControlForWindow( const Window& _rWindow, ::com::sun::star::uno::Reference< ::com::sun::star::awt::XControlContainer >& _inout_ControlContainer ) const;

    protected:
        virtual ViewObjectContact& CreateObjectSpecificViewObjectContact( ObjectContact& _rObjectContact );

    private:
        ViewContactOfUnoControl();                                            // never implemented
        ViewContactOfUnoControl( const ViewContactOfUnoControl& );              // never implemented
        ViewContactOfUnoControl& operator=( const ViewContactOfUnoControl& );   // never implemented

    protected:
        // This method is responsible for creating the graphical visualisation data
        // ONLY based on model data
        virtual drawinglayer::primitive2d::Primitive2DSequence createViewIndependentPrimitive2DSequence() const;
    };

//........................................................................
} } // namespace sdr::contact
//........................................................................

#endif // SVX_SDR_CONTACT_VIEWCONTACTOFUNOCONTROL_HXX
// eof
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: viewcontactofsdrmediaobj.cxx,v $
 * $Revision: 1.11 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
#include <svx/svdomedia.hxx>
#include <svx/sdr/contact/viewobjectcontactofsdrmediaobj.hxx>

namespace sdr { namespace contact {

// ----------------------------
// - ViewContactOfSdrMediaObj -
// ----------------------------

ViewContactOfSdrMediaObj::ViewContactOfSdrMediaObj( SdrMediaObj& rMediaObj ) :
    ViewContactOfSdrObj( rMediaObj )
{
}

// ------------------------------------------------------------------------------

ViewContactOfSdrMediaObj::~ViewContactOfSdrMediaObj()
{
}

// ------------------------------------------------------------------------------

sal_Bool ViewContactOfSdrMediaObj::PaintObject(DisplayInfo& rDisplayInfo, Rectangle& rPaintRectangle, const ViewObjectContact& rAssociatedVOC)
{
    return ViewContactOfSdrObj::PaintObject( rDisplayInfo, rPaintRectangle, rAssociatedVOC );
}

// ------------------------------------------------------------------------------

ViewObjectContact& ViewContactOfSdrMediaObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
{
    return *( new ViewObjectContactOfSdrMediaObj( rObjectContact, *this, static_cast< SdrMediaObj& >( GetSdrObject() ).getMediaProperties() ) );
}

// ------------------------------------------------------------------------------

bool ViewContactOfSdrMediaObj::hasPreferredSize() const
{
    // #i71805# Since we may have a whole bunch of VOCs here, make a loop
    // return true if all have their preferred size
    bool bRetval(true);

    for(sal_uInt32 a(0L); bRetval && a < maVOCList.Count(); a++)
    {
        if(!static_cast< ViewObjectContactOfSdrMediaObj* >( maVOCList.GetObject( 0 ) )->hasPreferredSize())
        {
            bRetval = false;
        }
    }

    return bRetval;
}

// ------------------------------------------------------------------------------

Size ViewContactOfSdrMediaObj::getPreferredSize() const
{
    // #i71805# Since we may have a whole bunch of VOCs here, make a loop
    // return first useful size -> the size from the first which is visualized as a window
    for(sal_uInt32 a(0L); a < maVOCList.Count(); a++)
    {
        Size aSize(static_cast< ViewObjectContactOfSdrMediaObj* >( maVOCList.GetObject( 0 ) )->getPreferredSize());

        if(0 != aSize.getWidth() || 0 != aSize.getHeight())
        {
            return aSize;
        }
    }

    return Size();
}

// ------------------------------------------------------------------------------

void ViewContactOfSdrMediaObj::updateMediaItem( ::avmedia::MediaItem& rItem ) const
{
    // #i71805# Since we may have a whole bunch of VOCs here, make a loop
    for(sal_uInt32 a(0L); a < maVOCList.Count(); a++)
    {
        static_cast< ViewObjectContactOfSdrMediaObj* >(maVOCList.GetObject(a))->updateMediaItem(rItem);
    }
}

// ------------------------------------------------------------------------------

void ViewContactOfSdrMediaObj::executeMediaItem( const ::avmedia::MediaItem& rItem )
{
    for( sal_uInt32 n(0L); n < maVOCList.Count(); n++ )
    {
        static_cast< ViewObjectContactOfSdrMediaObj* >( maVOCList.GetObject( n ) )->executeMediaItem( rItem );
    }
}

// ------------------------------------------------------------------------------

void ViewContactOfSdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewState )
{
    static_cast< SdrMediaObj& >( GetSdrObject() ).mediaPropertiesChanged( rNewState );
}

// ------------------------------------------------------------------------------
// #i72701#
sal_Bool ViewContactOfSdrMediaObj::ShouldPaintObject(DisplayInfo& rDisplayInfo, const ViewObjectContact& rAssociatedVOC)
{
    // set pos/size of associated media window
    const ViewObjectContactOfSdrMediaObj& rVOC(dynamic_cast< const ViewObjectContactOfSdrMediaObj& >(rAssociatedVOC));
    rVOC.checkMediaWindowPosition(rDisplayInfo);

    // call parent
    return ViewContactOfSdrObj::ShouldPaintObject(rDisplayInfo, rAssociatedVOC);
}

// ------------------------------------------------------------------------------

}} // end of namespace sdr::contact

// eof
<commit_msg>INTEGRATION: CWS aw033 (1.4.14); FILE MERGED 2008/05/27 14:49:58 aw 1.4.14.16: #i39532# changes DEV300 m12 resync corrections 2008/05/14 14:00:15 aw 1.4.14.15: RESYNC: (1.10-1.11); FILE MERGED 2008/02/12 10:42:35 aw 1.4.14.14: CVS update from internal development GIT 2008/01/29 10:27:32 aw 1.4.14.13: updated refresh for ActionChanged(), diverse removals 2008/01/22 12:29:30 aw 1.4.14.12: adaptions and 1st stripping 2007/08/09 18:46:50 aw 1.4.14.11: RESYNC: (1.9-1.10); FILE MERGED 2007/07/06 13:43:07 aw 1.4.14.10: #i39532# moved from Primitive2DReference to Primitive2DSequence where possible to avoid extra-group primitive creations and deeper hierarchies as necessary 2007/03/20 14:15:51 aw 1.4.14.9: RESYNC: (1.8-1.9); FILE MERGED 2007/03/06 12:39:27 aw 1.4.14.8: #i39532# 2007/01/19 13:19:02 aw 1.4.14.7: RESYNC: (1.7-1.8); FILE MERGED 2006/12/12 10:14:26 aw 1.4.14.6: #i39532# changes after resync 2006/11/28 19:24:40 aw 1.4.14.5: RESYNC: (1.6-1.7); FILE MERGED 2006/10/19 10:59:14 aw 1.4.14.4: #i39532# primitive 2006/09/26 19:21:27 aw 1.4.14.3: RESYNC: (1.4-1.6); FILE MERGED 2006/08/09 17:12:24 aw 1.4.14.2: #i39532# 2006/05/12 12:46:23 aw 1.4.14.1: code changes for primitive support<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: viewcontactofsdrmediaobj.cxx,v $
 * $Revision: 1.12 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
#include <svx/svdomedia.hxx>
#include <svx/sdr/contact/viewobjectcontactofsdrmediaobj.hxx>
#include <drawinglayer/primitive2d/mediaprimitive2d.hxx>

namespace sdr { namespace contact {

// ----------------------------
// - ViewContactOfSdrMediaObj -
// ----------------------------

ViewContactOfSdrMediaObj::ViewContactOfSdrMediaObj( SdrMediaObj& rMediaObj ) :
    ViewContactOfSdrObj( rMediaObj )
{
}

// ------------------------------------------------------------------------------

ViewContactOfSdrMediaObj::~ViewContactOfSdrMediaObj()
{
}

// ------------------------------------------------------------------------------

ViewObjectContact& ViewContactOfSdrMediaObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
{
    return *( new ViewObjectContactOfSdrMediaObj( rObjectContact, *this, static_cast< SdrMediaObj& >( GetSdrObject() ).getMediaProperties() ) );
}

// ------------------------------------------------------------------------------

bool ViewContactOfSdrMediaObj::hasPreferredSize() const
{
    // #i71805# Since we may have a whole bunch of VOCs here, make a loop
    // return true if all have their preferred size
    const sal_uInt32 nCount(getViewObjectContactCount());
    bool bRetval(true);

    for(sal_uInt32 a(0); bRetval && a < nCount; a++)
    {
        ViewObjectContact* pCandidate = getViewObjectContact(a);

        if(pCandidate && !static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->hasPreferredSize())
        {
            bRetval = false;
        }
    }

    return bRetval;
}

// ------------------------------------------------------------------------------

Size ViewContactOfSdrMediaObj::getPreferredSize() const
{
    // #i71805# Since we may have a whole bunch of VOCs here, make a loop
    // return first useful size -> the size from the first which is visualized as a window
    const sal_uInt32 nCount(getViewObjectContactCount());

    for(sal_uInt32 a(0); a < nCount; a++)
    {
        ViewObjectContact* pCandidate = getViewObjectContact(a);
        Size aSize(pCandidate ? static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->getPreferredSize() : Size());

        if(0 != aSize.getWidth() || 0 != aSize.getHeight())
        {
            return aSize;
        }
    }

    return Size();
}

// ------------------------------------------------------------------------------

void ViewContactOfSdrMediaObj::updateMediaItem( ::avmedia::MediaItem& rItem ) const
{
    // #i71805# Since we may have a whole bunch of VOCs here, make a loop
    const sal_uInt32 nCount(getViewObjectContactCount());

    for(sal_uInt32 a(0); a < nCount; a++)
    {
        ViewObjectContact* pCandidate = getViewObjectContact(a);

        if(pCandidate)
        {
            static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->updateMediaItem(rItem);
        }
    }
}

// ------------------------------------------------------------------------------

void ViewContactOfSdrMediaObj::executeMediaItem( const ::avmedia::MediaItem& rItem )
{
    const sal_uInt32 nCount(getViewObjectContactCount());

    for(sal_uInt32 a(0); a < nCount; a++)
    {
        ViewObjectContact* pCandidate = getViewObjectContact(a);

        if(pCandidate)
        {
            static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->executeMediaItem(rItem);
        }
    }
}

// ------------------------------------------------------------------------------

void ViewContactOfSdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewState )
{
    static_cast< SdrMediaObj& >(GetSdrObject()).mediaPropertiesChanged(rNewState);
}

}} // end of namespace sdr::contact

namespace sdr
{
    namespace contact
    {
        drawinglayer::primitive2d::Primitive2DSequence ViewContactOfSdrMediaObj::createViewIndependentPrimitive2DSequence() const
        {
            // create range using the model data directly. This is in SdrTextObj::aRect which i will access using
            // GetGeoRect() to not trigger any calculations. It's the unrotated geometry which is okay for MediaObjects ATM.
            const Rectangle& rRectangle(GetSdrMediaObj().GetGeoRect());
            const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());

            // create object transform
            basegfx::B2DHomMatrix aTransform;
            aTransform.set(0, 0, aRange.getWidth());
            aTransform.set(1, 1, aRange.getHeight());
            aTransform.set(0, 2, aRange.getMinX());
            aTransform.set(1, 2, aRange.getMinY());

            // create media primitive
            const basegfx::BColor aBackgroundColor(67.0 / 255.0, 67.0 / 255.0, 67.0 / 255.0);
            const rtl::OUString& rURL(GetSdrMediaObj().getURL());
            const sal_uInt32 nPixelBorder(4L);
            const drawinglayer::primitive2d::Primitive2DReference xRetval(new drawinglayer::primitive2d::MediaPrimitive2D(
                aTransform, rURL, aBackgroundColor, nPixelBorder));

            return drawinglayer::primitive2d::Primitive2DSequence(&xRetval, 1);
        }
    } // end of namespace contact
} // end of namespace sdr

//////////////////////////////////////////////////////////////////////////////
// eof
<|endoftext|>


<commit_before>// source_merge.C -- Merge two timeseries.
//
// Copyright 2005 Per Abrahamsen and KVL.
//
// This file is part of Daisy.
//
// Daisy is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// Daisy is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser Public License for more details.
//
// You should have received a copy of the GNU Lesser Public License
// along with Daisy; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "source.h"
#include "gnuplot_utils.h"
#include "vcheck.h"
#include "memutils.h"

struct SourceMerge : public Source
{
  // Content.
  const std::vector<Source*> source;
  const std::string title_;
  std::string dimension_;
  std::string with_;
  const int style_;
  void add_entry (const Time& time, std::vector<double>& vals);
  std::vector<Time> times;
  std::vector<double> values;
  std::vector<double> ebars;

  // Interface.
public:
  const std::string& with () const
  { return with_; }
  int style () const
  { return style_; }
  const std::vector<Time>& time () const
  { return times; }
  const std::vector<double>& value () const
  { return values; }
  const std::vector<double>& ebar () const
  { return ebars; }
  const std::string& title () const
  { return title_; }
  const std::string& dimension () const
  { return dimension_; }

  // Read.
public:
 bool load (Treelog& msg);

  // Create and Destroy.
public:
  explicit SourceMerge (Block& al);
  ~SourceMerge ()
  { sequence_delete (source.begin (), source.end ()); }
};

void
SourceMerge::add_entry (const Time& time, const std::vector<double>& vals)
{
  if (vals.size () > 1 && !explicit_with)
    with_ = "errorbars";
  const double total = std::accumulate (vals.begin (), vals.end (), 0.0);
  const double N = vals.size ();
  const double mean = total / N;
  double variance = 0;
  for (size_t i = 0; i < vals.size (); i++)
    {
      const double diff = vals[i] - mean;
      variance += diff * diff;
    }
  variance /= N;
  const double std_deviation = sqrt (variance);
  times.push_back (time);
  values.push_back (mean);
  ebars.push_back (std_deviation);
  daisy_assert (times.size () == values.size ());
  daisy_assert (values.size () == ebars.size ());
  vals.clear ();
}

bool
SourceMerge::load (Treelog& msg)
{
  // Propagate.
  bool ok = true;
  for (size_t i = 0; i < source.size (); i++)
    {
      // Load.
      std::ostringstream tmp;
      tmp << "[" << i << "] " << source[i]->title ();
      Treelog::Open nest (msg, tmp.str ());
      if (!source[i]->load (msg))
        {
          ok = false;
          continue;
        }

      // Set or check dimension.
      if (dimension_ == Syntax::Unknown ())
        dimension_ = source[i]->dimension ();
      else if (!Units::can_convert (source[i]->dimension (), dimension_))
        {
          msg.error ("Cannot convert dimension from ["
                     + source[i]->dimension () + "] to ["
                     + dimension_ + "]");
          ok = false;
        }
    }
  if (!ok)
    return false;

  // Keep track of each source.
  std::vector<size_t> index (source.size (), 0);

  // Merge them.
  while (true)
    {

      // Find first time.
      Time first_time (9999, 12, 31, 23);
      for (size_t i = 0; i < source.size (); i++)
        {
          const size_t cur = index[i];
          const std::vector<Time> times = source[i].time ();

          if (times.size () == cur)
            // No more data.
            continue;

          const Time time = times[cur];
          if (time < first_time)
            // First time
            first_time = time;
        }

      // Find vals;
      std::vector<double> vals;
      for (size_t i = 0; i < source.size (); i++)
        {
          const size_t cur = index[i];
          const std::vector<Time> times = source[i].time ();

          if (times.size () == cur)
            // No more data.
            continue;

          const Time time = times[cur];
          if (time == first_time)
            {
              // Add this element.
              vals.push_back (source[i].value ()[cur]);
              index[i]++;
            }
          else
            daisy_assert (time > first_time);
        }

      // Done yet?
      if (vals.size () == 0)
        break;

      // Add it.
      add_entry (first_time, vals);
    }

  // Inherit style.
  if (with_ == "")
    with_ = source[0]->with ();

  // Done.
  return true;
}

SourceMerge::SourceMerge (Block& al)
  : Source (al),
    source (Librarian<Source>::build_vector (al, "source")),
    title_ (al.name ("title", expr->title ())),
    dimension_ (al.name ("dimension", Syntax::Unknown ())),
    with_ (al.name ("with", "")),
    style_ (al.integer ("style", -1))
{ }

static struct SourceMergeSyntax
{
  static Source& make (Block& al)
  { return *new SourceMerge (al); }

  SourceMergeSyntax ()
  {
    Syntax& syntax = *new Syntax ();
    AttributeList& alist = *new AttributeList ();
    Source::load_syntax (syntax, alist);
    GnuplotUtil::load_style (syntax, alist, "\
By default, let the first source decide.", "\
By default the name of the 'expr' object.");
    alist.add ("description",
	       "Merge multiple timeseries into one.\n\
Any errorbars on the original timeseries is ignored, but the merged\n\
timeseries may have errorbars if there are multiple values for the\n\
same time.");
    syntax.add ("source", Librarian<Source>::library (),
		Syntax::State, Syntax::Sequence, "\
List of timeseries to merge.");
    syntax.add ("dimension", Syntax::String, Syntax::OptionalConst, "\
Dimension of data to plot.\n\
By default use the first source with a known dimension.");

    Librarian<Source>::add_type ("merge", alist, syntax, &make);
  }
} SourceMerge_syntax;

// source_merge.C ends here.
<commit_msg>kvl-t<commit_after>// source_merge.C -- Merge two timeseries.
//
// Copyright 2005 Per Abrahamsen and KVL.
//
// This file is part of Daisy.
//
// Daisy is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// Daisy is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser Public License for more details.
//
// You should have received a copy of the GNU Lesser Public License
// along with Daisy; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "source.h"
#include "gnuplot_utils.h"
#include "units.h"
#include "vcheck.h"
#include "mathlib.h"
#include "memutils.h"
#include <numeric>
#include <sstream>

struct SourceMerge : public Source
{
  // Content.
  const std::vector<Source*> source;
  const std::string title_;
  std::string dimension_;
  std::string with_;
  const int style_;
  void add_entry (const Time& time, const std::vector<double>& vals);
  std::vector<Time> times;
  std::vector<double> values;
  std::vector<double> ebars;

  // Interface.
public:
  const std::string& with () const
  { return with_; }
  int style () const
  { return style_; }
  const std::vector<Time>& time () const
  { return times; }
  const std::vector<double>& value () const
  { return values; }
  const std::vector<double>& ebar () const
  { return ebars; }
  const std::string& title () const
  { return title_; }
  const std::string& dimension () const
  { return dimension_; }

  // Read.
public:
 bool load (Treelog& msg);

  // Create and Destroy.
public:
  explicit SourceMerge (Block& al);
  ~SourceMerge ()
  { sequence_delete (source.begin (), source.end ()); }
};

void
SourceMerge::add_entry (const Time& time, const std::vector<double>& vals)
{
  if (vals.size () > 1 && with_ == "")
    with_ = "errorbars";
  const double total = std::accumulate (vals.begin (), vals.end (), 0.0);
  const double N = vals.size ();
  const double mean = total / N;
  double variance = 0;
  for (size_t i = 0; i < vals.size (); i++)
    {
      const double diff = vals[i] - mean;
      variance += diff * diff;
    }
  variance /= N;
  const double std_deviation = sqrt (variance);
  times.push_back (time);
  values.push_back (mean);
  ebars.push_back (std_deviation);
  daisy_assert (times.size () == values.size ());
  daisy_assert (values.size () == ebars.size ());
}

bool
SourceMerge::load (Treelog& msg)
{
  // Propagate.
  bool ok = true;
  for (size_t i = 0; i < source.size (); i++)
    {
      // Load.
      std::ostringstream tmp;
      tmp << "[" << i << "] " << source[i]->title ();
      Treelog::Open nest (msg, tmp.str ());
      if (!source[i]->load (msg))
        {
          ok = false;
          continue;
        }

      // Set or check dimension.
      if (dimension_ == Syntax::Unknown ())
        dimension_ = source[i]->dimension ();
      else if (!Units::can_convert (source[i]->dimension (), dimension_))
        {
          msg.error ("Cannot convert dimension from ["
                     + source[i]->dimension () + "] to ["
                     + dimension_ + "]");
          ok = false;
        }
    }
  if (!ok)
    return false;

  // Keep track of each source.
  std::vector<size_t> index (source.size (), 0);

  // Merge them.
  while (true)
    {

      // Find first time.
      Time first_time (9999, 12, 31, 23);
      for (size_t i = 0; i < source.size (); i++)
        {
          const size_t cur = index[i];
          const std::vector<Time> times = source[i]->time ();

          if (times.size () == cur)
            // No more data.
            continue;

          const Time time = times[cur];
          if (time < first_time)
            // First time
            first_time = time;
        }

      // Find vals;
      std::vector<double> vals;
      for (size_t i = 0; i < source.size (); i++)
        {
          const size_t cur = index[i];
          const std::vector<Time> times = source[i]->time ();

          if (times.size () == cur)
            // No more data.
            continue;

          const Time time = times[cur];
          if (time == first_time)
            {
              // Add this element.
              vals.push_back (source[i]->value ()[cur]);
              index[i]++;
            }
          else
            daisy_assert (time > first_time);
        }

      // Done yet?
      if (vals.size () == 0)
        break;

      // Add it.
      add_entry (first_time, vals);
    }

  // Inherit style.
  if (with_ == "")
    with_ = source[0]->with ();

  // Done.
  return true;
}

SourceMerge::SourceMerge (Block& al)
  : Source (al),
    source (Librarian<Source>::build_vector (al, "source")),
    title_ (al.name ("title")),
    dimension_ (al.name ("dimension", Syntax::Unknown ())),
    with_ (al.name ("with", "")),
    style_ (al.integer ("style", -1))
{ }

static struct SourceMergeSyntax
{
  static Source& make (Block& al)
  { return *new SourceMerge (al); }

  SourceMergeSyntax ()
  {
    Syntax& syntax = *new Syntax ();
    AttributeList& alist = *new AttributeList ();
    Source::load_syntax (syntax, alist);
    GnuplotUtil::load_style (syntax, alist, "\
By default, let the first source decide.", "");
    alist.add ("description",
	       "Merge multiple timeseries into one.\n\
Any errorbars on the original timeseries is ignored, but the merged\n\
timeseries may have errorbars if there are multiple values for the\n\
same time.");
    syntax.add ("source", Librarian<Source>::library (),
		Syntax::State, Syntax::Sequence, "\
List of timeseries to merge.");
    syntax.add_check ("source", VCheck::min_size_1 ());
    syntax.add ("dimension", Syntax::String, Syntax::OptionalConst, "\
Dimension of data to plot.\n\
By default use the first source with a known dimension.");

    Librarian<Source>::add_type ("merge", alist, syntax, &make);
  }
} SourceMerge_syntax;

// source_merge.C ends here.
<|endoftext|>