<commit_before>#if _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define WIN_PAUSE 0
#include <objbase.h>
#endif

#include <clocale>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cstdarg>
#include <signal.h>
#include <regex>
#include <list>
#include "hecl/Database.hpp"
#include "hecl/Blender/Connection.hpp"
#include "logvisor/logvisor.hpp"

logvisor::Module LogModule("hecl::Driver");

#include "ToolBase.hpp"
#include "ToolInit.hpp"
#include "ToolSpec.hpp"
#include "ToolExtract.hpp"
#include "ToolCook.hpp"
#include "ToolPackage.hpp"
#include "ToolImage.hpp"
#include "ToolHelp.hpp"

/* Static reference to dataspec additions
 * (used by MSVC to definitively link DataSpecs) */
#include "DataSpecRegistry.hpp"

bool XTERM_COLOR = false;

/*
#define HECL_GIT 1234567
#define HECL_GIT_S "1234567"
#define HECL_BRANCH master
#define HECL_BRANCH_S "master"
*/

/* Main usage message */
static void printHelp(const hecl::SystemChar* pname) {
  if (XTERM_COLOR)
    fmt::print(fmt(_SYS_STR("" BOLD "HECL" NORMAL "")));
  else
    fmt::print(fmt(_SYS_STR("HECL")));
#if HECL_HAS_NOD
#define TOOL_LIST "extract|init|cook|package|image|help"
#else
#define TOOL_LIST "extract|init|cook|package|help"
#endif
#if HECL_GIT
  fmt::print(fmt(_SYS_STR(" Commit " HECL_GIT_S " " HECL_BRANCH_S "\nUsage: {} " TOOL_LIST "\n")), pname);
#elif HECL_VER
  fmt::print(fmt(_SYS_STR(" Version " HECL_VER_S "\nUsage: {} " TOOL_LIST "\n")), pname);
#else
  fmt::print(fmt(_SYS_STR("\nUsage: {} " TOOL_LIST "\n")), pname);
#endif
}

/* Regex patterns */
static const hecl::SystemRegex regOPEN(_SYS_STR("-o([^\"]*|\\S*)"), std::regex::ECMAScript | std::regex::optimize);

static ToolBase* ToolPtr = nullptr;

/* SIGINT will gracefully close blender connections and delete blends in progress */
static void SIGINTHandler(int sig) {
  if (ToolPtr)
    ToolPtr->cancel();
  hecl::blender::Connection::Shutdown();
  logvisor::KillProcessTree();
  exit(1);
}

static logvisor::Module AthenaLog("Athena");
static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, fmt::string_view fmt,
                      fmt::format_args args) {
  AthenaLog.vreport(logvisor::Level(level), fmt, args);
}

hecl::SystemString ExeDir;

#if _WIN32
static ToolPassInfo CreateInfo(int argc, const wchar_t** argv) {
#else
static ToolPassInfo CreateInfo(int argc, const char** argv) {
#endif
  hecl::SystemChar cwdbuf[1024];

  ToolPassInfo info;
  info.pname = argv[0];

  if (hecl::Getcwd(cwdbuf, static_cast<int>(std::size(cwdbuf)))) {
    info.cwd = cwdbuf;
    if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\')) {
#if _WIN32
      info.cwd += _SYS_STR('\\');
#else
      info.cwd += _SYS_STR('/');
#endif
    }

    if (hecl::PathRelative(argv[0])) {
      ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/');
    }
    hecl::SystemString Argv0(argv[0]);
    hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
    if (lastIdx != hecl::SystemString::npos) {
      ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
    }
  }

  /* Concatenate args */
  std::vector<hecl::SystemString> args;
  args.reserve(argc - 2);
  for (int i = 2; i < argc; ++i) {
    args.emplace_back(argv[i]);
  }

  if (!args.empty()) {
    /* Extract output argument */
    for (auto it = args.cbegin(); it != args.cend();) {
      const hecl::SystemString& arg = *it;
      hecl::SystemRegexMatch oMatch;

      if (std::regex_search(arg, oMatch, regOPEN)) {
        const hecl::SystemString& token = oMatch[1].str();

        if (token.size()) {
          if (info.output.empty()) {
            info.output = oMatch[1].str();
          }

          it = args.erase(it);
        } else {
          it = args.erase(it);

          if (it == args.end()) {
            break;
          }

          if (info.output.empty()) {
            info.output = *it;
          }

          it = args.erase(it);
        }

        continue;
      }

      ++it;
    }

    /* Iterate flags */
    bool threadArg = false;
    for (auto it = args.cbegin(); it != args.cend();) {
      const hecl::SystemString& arg = *it;
      if (threadArg) {
        threadArg = false;
        hecl::CpuCountOverride = int(hecl::StrToUl(arg.c_str(), nullptr, 0));
        it = args.erase(it);
        continue;
      }
      if (arg.size() < 2 || arg[0] != _SYS_STR('-') || arg[1] == _SYS_STR('-')) {
        ++it;
        continue;
      }

      for (auto chit = arg.cbegin() + 1; chit != arg.cend(); ++chit) {
        if (*chit == _SYS_STR('v'))
          ++info.verbosityLevel;
        else if (*chit == _SYS_STR('f'))
          info.force = true;
        else if (*chit == _SYS_STR('y'))
          info.yes = true;
        else if (*chit == _SYS_STR('g'))
          info.gui = true;
        else if (*chit == _SYS_STR('j')) {
          ++chit;
          if (*chit)
            hecl::CpuCountOverride = int(hecl::StrToUl(&*chit, nullptr, 0));
          else
            threadArg = true;
          break;
        } else
          info.flags.push_back(*chit);
      }

      it = args.erase(it);
    }

    /* Gather remaining args */
    info.args.reserve(args.size());
    for (const hecl::SystemString& arg : args)
      info.args.push_back(arg);
  }

  return info;
}

static std::unique_ptr<hecl::Database::Project> FindProject(hecl::SystemStringView cwd) {
  const hecl::ProjectRootPath rootPath = hecl::SearchForProject(cwd);
  if (!rootPath) {
    return nullptr;
  }

  const size_t ErrorRef = logvisor::ErrorCount;
  auto newProj = std::make_unique<hecl::Database::Project>(rootPath);
  if (logvisor::ErrorCount > ErrorRef) {
#if WIN_PAUSE
    system("PAUSE");
#endif
    return nullptr;
  }

  return newProj;
}

static std::unique_ptr<ToolBase> MakeSelectedTool(hecl::SystemString toolName, ToolPassInfo& info) {
  hecl::SystemString toolNameLower = toolName;
  hecl::ToLower(toolNameLower);

  if (toolNameLower == _SYS_STR("init")) {
    return std::make_unique<ToolInit>(info);
  }

  if (toolNameLower == _SYS_STR("spec")) {
    return std::make_unique<ToolSpec>(info);
  }

  if (toolNameLower == _SYS_STR("extract")) {
    return std::make_unique<ToolExtract>(info);
  }

  if (toolNameLower == _SYS_STR("cook")) {
    return std::make_unique<ToolCook>(info);
  }

  if (toolNameLower == _SYS_STR("package") || toolNameLower == _SYS_STR("pack")) {
    return std::make_unique<ToolPackage>(info);
  }

#if HECL_HAS_NOD
  if (toolNameLower == _SYS_STR("image")) {
    return std::make_unique<ToolImage>(info);
  }
#endif

  if (toolNameLower == _SYS_STR("help")) {
    return std::make_unique<ToolHelp>(info);
  }

  std::unique_ptr<FILE, decltype(&std::fclose)> fp{hecl::Fopen(toolName.c_str(), _SYS_STR("rb")), std::fclose};
  if (fp == nullptr) {
    LogModule.report(logvisor::Error, fmt(_SYS_STR("unrecognized tool '{}'")), toolNameLower);
    return nullptr;
  }
  fp.reset();

  /* Shortcut-case: implicit extract */
  info.args.insert(info.args.begin(), std::move(toolName));
  return std::make_unique<ToolExtract>(info);
}

#if _WIN32
int wmain(int argc, const wchar_t** argv)
#else
/* SIGWINCH should do nothing */
static void SIGWINCHHandler(int sig) {}
int main(int argc, const char** argv)
#endif
{
  if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
    fmt::print(fmt("{}\n"), HECL_DLPACKAGE);
    return 100;
  }

#if _WIN32
  CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
#else
  std::setlocale(LC_ALL, "en-US.UTF-8");
#endif

  /* Xterm check */
#if _WIN32
  const char* conemuANSI = getenv("ConEmuANSI");
  if (conemuANSI && !strcmp(conemuANSI, "ON"))
    XTERM_COLOR = true;
#else
  const char* term = getenv("TERM");
  if (term && !strncmp(term, "xterm", 5))
    XTERM_COLOR = true;
  signal(SIGWINCH, SIGWINCHHandler);
#endif
  signal(SIGINT, SIGINTHandler);

  logvisor::RegisterStandardExceptions();
  logvisor::RegisterConsoleLogger();
  atSetExceptionHandler(AthenaExc);

  /* Basic usage check */
  if (argc == 1) {
    printHelp(argv[0]);
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 0;
  } else if (argc == 0) {
    printHelp(_SYS_STR("hecl"));
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 0;
  }

  /* Prepare DataSpecs */
  HECLRegisterDataSpecs();

  /* Assemble common tool pass info */
  ToolPassInfo info = CreateInfo(argc, argv);

  /* Attempt to find hecl project */
  auto project = FindProject(info.cwd);
  if (project != nullptr) {
    info.project = project.get();
  }

  /* Construct selected tool */
  size_t ErrorRef = logvisor::ErrorCount;
  auto tool = MakeSelectedTool(argv[1], info);

  if (logvisor::ErrorCount > ErrorRef) {
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 1;
  }

  if (info.verbosityLevel) {
    LogModule.report(logvisor::Info, fmt(_SYS_STR("Constructed tool '{}' {}\n")), tool->toolName(),
                     info.verbosityLevel);
  }

  /* Run tool */
  ErrorRef = logvisor::ErrorCount;
  ToolPtr = tool.get();
  int retval = tool->run();
  ToolPtr = nullptr;
  if (logvisor::ErrorCount > ErrorRef) {
    hecl::blender::Connection::Shutdown();
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 1;
  }

  hecl::blender::Connection::Shutdown();
#if WIN_PAUSE
  system("PAUSE");
#endif
  return retval;
}
<commit_msg>driver/main: Use separate variables for error checking in main()<commit_after>#if _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define WIN_PAUSE 0
#include <objbase.h>
#endif

#include <clocale>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cstdarg>
#include <signal.h>
#include <regex>
#include <list>
#include "hecl/Database.hpp"
#include "hecl/Blender/Connection.hpp"
#include "logvisor/logvisor.hpp"

logvisor::Module LogModule("hecl::Driver");

#include "ToolBase.hpp"
#include "ToolInit.hpp"
#include "ToolSpec.hpp"
#include "ToolExtract.hpp"
#include "ToolCook.hpp"
#include "ToolPackage.hpp"
#include "ToolImage.hpp"
#include "ToolHelp.hpp"

/* Static reference to dataspec additions
 * (used by MSVC to definitively link DataSpecs) */
#include "DataSpecRegistry.hpp"

bool XTERM_COLOR = false;

/*
#define HECL_GIT 1234567
#define HECL_GIT_S "1234567"
#define HECL_BRANCH master
#define HECL_BRANCH_S "master"
*/

/* Main usage message */
static void printHelp(const hecl::SystemChar* pname) {
  if (XTERM_COLOR)
    fmt::print(fmt(_SYS_STR("" BOLD "HECL" NORMAL "")));
  else
    fmt::print(fmt(_SYS_STR("HECL")));
#if HECL_HAS_NOD
#define TOOL_LIST "extract|init|cook|package|image|help"
#else
#define TOOL_LIST "extract|init|cook|package|help"
#endif
#if HECL_GIT
  fmt::print(fmt(_SYS_STR(" Commit " HECL_GIT_S " " HECL_BRANCH_S "\nUsage: {} " TOOL_LIST "\n")), pname);
#elif HECL_VER
  fmt::print(fmt(_SYS_STR(" Version " HECL_VER_S "\nUsage: {} " TOOL_LIST "\n")), pname);
#else
  fmt::print(fmt(_SYS_STR("\nUsage: {} " TOOL_LIST "\n")), pname);
#endif
}

/* Regex patterns */
static const hecl::SystemRegex regOPEN(_SYS_STR("-o([^\"]*|\\S*)"), std::regex::ECMAScript | std::regex::optimize);

static ToolBase* ToolPtr = nullptr;

/* SIGINT will gracefully close blender connections and delete blends in progress */
static void SIGINTHandler(int sig) {
  if (ToolPtr)
    ToolPtr->cancel();
  hecl::blender::Connection::Shutdown();
  logvisor::KillProcessTree();
  exit(1);
}

static logvisor::Module AthenaLog("Athena");
static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, fmt::string_view fmt,
                      fmt::format_args args) {
  AthenaLog.vreport(logvisor::Level(level), fmt, args);
}

hecl::SystemString ExeDir;

#if _WIN32
static ToolPassInfo CreateInfo(int argc, const wchar_t** argv) {
#else
static ToolPassInfo CreateInfo(int argc, const char** argv) {
#endif
  hecl::SystemChar cwdbuf[1024];

  ToolPassInfo info;
  info.pname = argv[0];

  if (hecl::Getcwd(cwdbuf, static_cast<int>(std::size(cwdbuf)))) {
    info.cwd = cwdbuf;
    if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\')) {
#if _WIN32
      info.cwd += _SYS_STR('\\');
#else
      info.cwd += _SYS_STR('/');
#endif
    }

    if (hecl::PathRelative(argv[0])) {
      ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/');
    }
    hecl::SystemString Argv0(argv[0]);
    hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
    if (lastIdx != hecl::SystemString::npos) {
      ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
    }
  }

  /* Concatenate args */
  std::vector<hecl::SystemString> args;
  args.reserve(argc - 2);
  for (int i = 2; i < argc; ++i) {
    args.emplace_back(argv[i]);
  }

  if (!args.empty()) {
    /* Extract output argument */
    for (auto it = args.cbegin(); it != args.cend();) {
      const hecl::SystemString& arg = *it;
      hecl::SystemRegexMatch oMatch;

      if (std::regex_search(arg, oMatch, regOPEN)) {
        const hecl::SystemString& token = oMatch[1].str();

        if (token.size()) {
          if (info.output.empty()) {
            info.output = oMatch[1].str();
          }

          it = args.erase(it);
        } else {
          it = args.erase(it);

          if (it == args.end()) {
            break;
          }

          if (info.output.empty()) {
            info.output = *it;
          }

          it = args.erase(it);
        }

        continue;
      }

      ++it;
    }

    /* Iterate flags */
    bool threadArg = false;
    for (auto it = args.cbegin(); it != args.cend();) {
      const hecl::SystemString& arg = *it;
      if (threadArg) {
        threadArg = false;
        hecl::CpuCountOverride = int(hecl::StrToUl(arg.c_str(), nullptr, 0));
        it = args.erase(it);
        continue;
      }
      if (arg.size() < 2 || arg[0] != _SYS_STR('-') || arg[1] == _SYS_STR('-')) {
        ++it;
        continue;
      }

      for (auto chit = arg.cbegin() + 1; chit != arg.cend(); ++chit) {
        if (*chit == _SYS_STR('v'))
          ++info.verbosityLevel;
        else if (*chit == _SYS_STR('f'))
          info.force = true;
        else if (*chit == _SYS_STR('y'))
          info.yes = true;
        else if (*chit == _SYS_STR('g'))
          info.gui = true;
        else if (*chit == _SYS_STR('j')) {
          ++chit;
          if (*chit)
            hecl::CpuCountOverride = int(hecl::StrToUl(&*chit, nullptr, 0));
          else
            threadArg = true;
          break;
        } else
          info.flags.push_back(*chit);
      }

      it = args.erase(it);
    }

    /* Gather remaining args */
    info.args.reserve(args.size());
    for (const hecl::SystemString& arg : args)
      info.args.push_back(arg);
  }

  return info;
}

static std::unique_ptr<hecl::Database::Project> FindProject(hecl::SystemStringView cwd) {
  const hecl::ProjectRootPath rootPath = hecl::SearchForProject(cwd);
  if (!rootPath) {
    return nullptr;
  }

  const size_t ErrorRef = logvisor::ErrorCount;
  auto newProj = std::make_unique<hecl::Database::Project>(rootPath);
  if (logvisor::ErrorCount > ErrorRef) {
#if WIN_PAUSE
    system("PAUSE");
#endif
    return nullptr;
  }

  return newProj;
}

static std::unique_ptr<ToolBase> MakeSelectedTool(hecl::SystemString toolName, ToolPassInfo& info) {
  hecl::SystemString toolNameLower = toolName;
  hecl::ToLower(toolNameLower);

  if (toolNameLower == _SYS_STR("init")) {
    return std::make_unique<ToolInit>(info);
  }

  if (toolNameLower == _SYS_STR("spec")) {
    return std::make_unique<ToolSpec>(info);
  }

  if (toolNameLower == _SYS_STR("extract")) {
    return std::make_unique<ToolExtract>(info);
  }

  if (toolNameLower == _SYS_STR("cook")) {
    return std::make_unique<ToolCook>(info);
  }

  if (toolNameLower == _SYS_STR("package") || toolNameLower == _SYS_STR("pack")) {
    return std::make_unique<ToolPackage>(info);
  }

#if HECL_HAS_NOD
  if (toolNameLower == _SYS_STR("image")) {
    return std::make_unique<ToolImage>(info);
  }
#endif

  if (toolNameLower == _SYS_STR("help")) {
    return std::make_unique<ToolHelp>(info);
  }

  std::unique_ptr<FILE, decltype(&std::fclose)> fp{hecl::Fopen(toolName.c_str(), _SYS_STR("rb")), std::fclose};
  if (fp == nullptr) {
    LogModule.report(logvisor::Error, fmt(_SYS_STR("unrecognized tool '{}'")), toolNameLower);
    return nullptr;
  }
  fp.reset();

  /* Shortcut-case: implicit extract */
  info.args.insert(info.args.begin(), std::move(toolName));
  return std::make_unique<ToolExtract>(info);
}

#if _WIN32
int wmain(int argc, const wchar_t** argv)
#else
/* SIGWINCH should do nothing */
static void SIGWINCHHandler(int sig) {}
int main(int argc, const char** argv)
#endif
{
  if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
    fmt::print(fmt("{}\n"), HECL_DLPACKAGE);
    return 100;
  }

#if _WIN32
  CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
#else
  std::setlocale(LC_ALL, "en-US.UTF-8");
#endif

  /* Xterm check */
#if _WIN32
  const char* conemuANSI = getenv("ConEmuANSI");
  if (conemuANSI && !strcmp(conemuANSI, "ON"))
    XTERM_COLOR = true;
#else
  const char* term = getenv("TERM");
  if (term && !strncmp(term, "xterm", 5))
    XTERM_COLOR = true;
  signal(SIGWINCH, SIGWINCHHandler);
#endif
  signal(SIGINT, SIGINTHandler);

  logvisor::RegisterStandardExceptions();
  logvisor::RegisterConsoleLogger();
  atSetExceptionHandler(AthenaExc);

  /* Basic usage check */
  if (argc == 1) {
    printHelp(argv[0]);
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 0;
  } else if (argc == 0) {
    printHelp(_SYS_STR("hecl"));
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 0;
  }

  /* Prepare DataSpecs */
  HECLRegisterDataSpecs();

  /* Assemble common tool pass info */
  ToolPassInfo info = CreateInfo(argc, argv);

  /* Attempt to find hecl project */
  auto project = FindProject(info.cwd);
  if (project != nullptr) {
    info.project = project.get();
  }

  /* Construct selected tool */
  const size_t MakeToolErrorRef = logvisor::ErrorCount;
  auto tool = MakeSelectedTool(argv[1], info);
  if (logvisor::ErrorCount > MakeToolErrorRef) {
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 1;
  }
  if (info.verbosityLevel) {
    LogModule.report(logvisor::Info, fmt(_SYS_STR("Constructed tool '{}' {}\n")), tool->toolName(),
                     info.verbosityLevel);
  }

  /* Run tool */
  const size_t RunToolErrorRef = logvisor::ErrorCount;
  ToolPtr = tool.get();
  const int retval = tool->run();
  ToolPtr = nullptr;
  if (logvisor::ErrorCount > RunToolErrorRef) {
    hecl::blender::Connection::Shutdown();
#if WIN_PAUSE
    system("PAUSE");
#endif
    return 1;
  }

  hecl::blender::Connection::Shutdown();
#if WIN_PAUSE
  system("PAUSE");
#endif
  return retval;
}
<|endoftext|>


<commit_before>/**
 * @file namespace.cpp
 * @brief  Brief description of file.
 *
 */
#include "angort.h"

namespace angort {

void Namespace::list(){
    locations.listKeys();
}

int NamespaceManager::getFromNamespace(Namespace *sp, const char *name,bool allowPrivate){
    int iidx = sp->get(name);
    if(iidx>=0){
        NamespaceEnt *ent = sp->getEnt(iidx);
        if(allowPrivate || ent->isImported)
            return makeIndex(sp->idx,iidx);
    }
    return -1;
}

int NamespaceManager::get(const char *name, bool scanImports){
    // does this contain a $?
    const char *dollar;
    if((dollar=strchr(name,'$'))){
        char buf[32];
        if(dollar-name > 32){
            throw RUNT(EX_LONGNAME,"namespace name too long");
        }
        strncpy(buf,name,dollar-name);
        buf[dollar-name]=0;
        int spaceidx = spaces.get(buf);
        if(spaceidx<0)return -1; // no such namespace
        Namespace *sp = spaces.getEnt(spaceidx);
        int subidx = sp->get(dollar+1);
        if(subidx<0)return -1; // no such entry
        if(!sp->getEnt(subidx)->isPriv)
            return makeIndex(spaceidx,sp->get(dollar+1));
        else
            return -1;

    }

    // and this is for names in an unspecified space -
    // first, scan the current list.

    int idx = getFromNamespace(spaces.getEnt(currentIdx),name,true);
    if(idx>=0)
        return idx;

    // now we scan the "imported namespaces" list
    if(scanImports){
        // we count in reverse, so we look at the most
        // recent namespaces first.
        for(int i=importedNamespaces.count()-1;i>=0;i--){
            int nsidx = *importedNamespaces.get(i);
            Namespace *ns = spaces.getEnt(nsidx);
            // again, only returns public items.
            int idx = getFromNamespace(ns,name);
            if(idx>=0)
                return idx;
        }
        // identically scan the 'with' stack
        for(int i=0;i<withNamespaces.ct;i++){
            int nsidx = withNamespaces.peek(i);
            Namespace *ns = spaces.getEnt(nsidx);
            printf("scanning %d (%s)\n",nsidx,spaces.getName(nsidx));
            printf("found %d\n",ns->get(name));
            // getFromNamespace only returns items; here we haven't implicitly
            // imported so we have to do the check manually.
            int idx = ns->get(name);
            if(idx>=0){
                NamespaceEnt *ent = ns->getEnt(idx);
                if(!ent->isPriv) // only permit public items
                    return makeIndex(ns->idx,idx);
            }
        }
    }
    return -1; // not found
}

const char *NamespaceManager::getFQN(int idx,char *buf,int len){
    int nsidx = getNamespaceIndex(idx);
    int vidx = getItemIndex(idx);

    Namespace *ns = spaces.getEnt(nsidx);
    const char *nsname = spaces.getName(nsidx);
    const char *vname = ns->getName(vidx);

    snprintf(buf,len,"%s$%s",nsname,vname);
    buf[len-1]=0; // make sure terminated
    return buf;
}

// this works by adding the namespace to the "imported namespaces"
// list. If there is no list, all public entries are marked as imported;
// if such a list exists, only those which are named are so marked.

void NamespaceManager::import(int nsidx,ArrayList<Value> *lst){

    Namespace *ns = spaces.getEnt(nsidx);
    *importedNamespaces.append() = nsidx;

    if(lst){
        // go through the list, marking those which are considered
        // imported.
        ArrayListIterator<Value> iter(lst);
        for(iter.first();!iter.isDone();iter.next()){
            Value *v = iter.current();
            int nidx = ns->get(v->toString().get());
            if(nidx>=0){
                NamespaceEnt *e = ns->getEnt(nidx);
                e->isImported=true;
            } else {
                throw RUNT(EX_NOTFOUND,"").set("cannot find '%s' in namespace",
                                            v->toString().get());
            }
        }
    } else {
        // otherwise, mark all non-private entries as imported
        ns->markAllImported();
        // and also mark that subsequent public symbols should also
        // be imported
        ns->isImported=true;
    }
}

const char *NamespaceManager::getNameByValue(Value *v,char *out,int len){
    // first we need to iterate the namespaces

    for(int nsidx=0;nsidx<spaces.count();nsidx++){
        const char *nsName = spaces.getName(nsidx);
        Namespace *ns = spaces.getEnt(nsidx);

        // now we need to do a "find by value" in each space
        for(int i=0;i<ns->count();i++){
            const char *name = ns->getName(i);
            NamespaceEnt *ent = ns->getEnt(i);

            // we're going to use some special cases here.
            Value *c = &ent->v;
            bool cmp=false;
            if(v->t == c->t){
                if(v->t == Types::tNative)
                    cmp = v->v.native == c->v.native;
                else if(v->t == Types::tProp)
                    cmp = v->v.property == c->v.property;
                else if(v->t == Types::tList)
                    cmp = v->v.list == c->v.list;
                else if(v->t == Types::tHash)
                    cmp = v->v.hash == c->v.hash;
                else cmp = v->equalForHashTable(c);
            }
            if(cmp){
                snprintf(out,len,"%s$%s",nsName,name);
                return out;
            }
        }
    }
    strcpy(out,"??");
    return out;

}

}
<commit_msg>and another one<commit_after>/**
 * @file namespace.cpp
 * @brief  Brief description of file.
 *
 */
#include "angort.h"

namespace angort {

void Namespace::list(){
    locations.listKeys();
}

int NamespaceManager::getFromNamespace(Namespace *sp, const char *name,bool allowPrivate){
    int iidx = sp->get(name);
    if(iidx>=0){
        NamespaceEnt *ent = sp->getEnt(iidx);
        if(allowPrivate || ent->isImported)
            return makeIndex(sp->idx,iidx);
    }
    return -1;
}

int NamespaceManager::get(const char *name, bool scanImports){
    // does this contain a $?
    const char *dollar;
    if((dollar=strchr(name,'$'))){
        char buf[32];
        if(dollar-name > 32){
            throw RUNT(EX_LONGNAME,"namespace name too long");
        }
        strncpy(buf,name,dollar-name);
        buf[dollar-name]=0;
        int spaceidx = spaces.get(buf);
        if(spaceidx<0)return -1; // no such namespace
        Namespace *sp = spaces.getEnt(spaceidx);
        int subidx = sp->get(dollar+1);
        if(subidx<0)return -1; // no such entry
        if(!sp->getEnt(subidx)->isPriv)
            return makeIndex(spaceidx,sp->get(dollar+1));
        else
            return -1;

    }

    // and this is for names in an unspecified space -
    // first, scan the current list.

    int idx = getFromNamespace(spaces.getEnt(currentIdx),name,true);
    if(idx>=0)
        return idx;

    // now we scan the "imported namespaces" list
    if(scanImports){
        // we count in reverse, so we look at the most
        // recent namespaces first.
        for(int i=importedNamespaces.count()-1;i>=0;i--){
            int nsidx = *importedNamespaces.get(i);
            Namespace *ns = spaces.getEnt(nsidx);
            // again, only returns public items.
            int idx = getFromNamespace(ns,name);
            if(idx>=0)
                return idx;
        }
        // identically scan the 'with' stack
        for(int i=0;i<withNamespaces.ct;i++){
            int nsidx = withNamespaces.peek(i);
            Namespace *ns = spaces.getEnt(nsidx);
            // getFromNamespace only returns items; here we haven't implicitly
            // imported so we have to do the check manually.
            int idx = ns->get(name);
            if(idx>=0){
                NamespaceEnt *ent = ns->getEnt(idx);
                if(!ent->isPriv) // only permit public items
                    return makeIndex(ns->idx,idx);
            }
        }
    }
    return -1; // not found
}

const char *NamespaceManager::getFQN(int idx,char *buf,int len){
    int nsidx = getNamespaceIndex(idx);
    int vidx = getItemIndex(idx);

    Namespace *ns = spaces.getEnt(nsidx);
    const char *nsname = spaces.getName(nsidx);
    const char *vname = ns->getName(vidx);

    snprintf(buf,len,"%s$%s",nsname,vname);
    buf[len-1]=0; // make sure terminated
    return buf;
}

// this works by adding the namespace to the "imported namespaces"
// list. If there is no list, all public entries are marked as imported;
// if such a list exists, only those which are named are so marked.

void NamespaceManager::import(int nsidx,ArrayList<Value> *lst){

    Namespace *ns = spaces.getEnt(nsidx);
    *importedNamespaces.append() = nsidx;

    if(lst){
        // go through the list, marking those which are considered
        // imported.
        ArrayListIterator<Value> iter(lst);
        for(iter.first();!iter.isDone();iter.next()){
            Value *v = iter.current();
            int nidx = ns->get(v->toString().get());
            if(nidx>=0){
                NamespaceEnt *e = ns->getEnt(nidx);
                e->isImported=true;
            } else {
                throw RUNT(EX_NOTFOUND,"").set("cannot find '%s' in namespace",
                                            v->toString().get());
            }
        }
    } else {
        // otherwise, mark all non-private entries as imported
        ns->markAllImported();
        // and also mark that subsequent public symbols should also
        // be imported
        ns->isImported=true;
    }
}

const char *NamespaceManager::getNameByValue(Value *v,char *out,int len){
    // first we need to iterate the namespaces

    for(int nsidx=0;nsidx<spaces.count();nsidx++){
        const char *nsName = spaces.getName(nsidx);
        Namespace *ns = spaces.getEnt(nsidx);

        // now we need to do a "find by value" in each space
        for(int i=0;i<ns->count();i++){
            const char *name = ns->getName(i);
            NamespaceEnt *ent = ns->getEnt(i);

            // we're going to use some special cases here.
            Value *c = &ent->v;
            bool cmp=false;
            if(v->t == c->t){
                if(v->t == Types::tNative)
                    cmp = v->v.native == c->v.native;
                else if(v->t == Types::tProp)
                    cmp = v->v.property == c->v.property;
                else if(v->t == Types::tList)
                    cmp = v->v.list == c->v.list;
                else if(v->t == Types::tHash)
                    cmp = v->v.hash == c->v.hash;
                else cmp = v->equalForHashTable(c);
            }
            if(cmp){
                snprintf(out,len,"%s$%s",nsName,name);
                return out;
            }
        }
    }
    strcpy(out,"??");
    return out;

}

}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../../../StroikaPreComp.h"

#include    <unistd.h>

#if     qPlatform_AIX
#include    <dirent.h>
#include    <sys/sysmacros.h>
#include    <sys/types.h>
#include    <unistd.h>
#endif

#include    "../../../Cache/CallerStalenessCache.h"
#include    "../../../Cache/LRUCache.h"
#include    "../../../Characters/String_Constant.h"
#include    "../../../Debug/TimingTrace.h"
#include    "../../../Memory/SmallStackBuffer.h"
#include    "../../ErrNoException.h"
#include    "../../Finally.h"

#include    "GetEXEPathWithHint.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Execution;
using   namespace   Stroika::Foundation::Execution::Platform::AIX;

using   Characters::String;
using   Characters::String_Constant;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_TIMING_TRACE_IN_THIS_MODULE_       1

namespace {
    SDKString   myProcessRunnerFirstLine_ (const SDKString& cmdLine)
    {
        /*
         *      NOTE - we intentionally use popen here avoid helpful Stroika code like ProcessRunner (), ThrowIfNull, etc,
         *      so that this code remains low level, and doesnt invoke any tracelog code, which would result in a deadlock/loop....
         *
         */
        FILE*   p = ::popen (cmdLine.c_str (), "r");
        string  accum;
        if (p != nullptr) {
            char    buf[10 * 1024];
            while (::fgets (buf, NEltsOf (buf), p) != nullptr) {
                accum += buf;
                size_t i = accum.rfind ('\n');
                if (i != -1) {
                    accum.erase (i);
                }
                break;  // minor speed hack since we always use with just one line
            }
            int result  =   ::pclose (p);
            // cannot report to avoid TRACE ;-(
        }
        return accum;
    }
    // find %s -xdev -type f -inum %lld -print 2> /dev/null - but much faster.
    // @todo - xdev part. Not only possible speed hack, but also needed for correctness since
    // we could match the wrong inode if we crossed filesystems.
    SDKString   FindPath2Inode_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        DIR*       dirIt    { ::opendir (dir.c_str ()) };
        if (dirIt == nullptr) {
            return SDKString ();
        }
        auto&& cleanup  =   Finally ([dirIt] () noexcept { ::closedir (dirIt); });
        for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
            if (::strcmp (cur->d_name, ".") == 0 or ::strcmp (cur->d_name, "..") == 0) {
                continue;
            }
            struct  stat    s;
            SDKString   filePath = dir + cur->d_name;
            if (::stat (filePath.c_str (), &s) == 0) {
                if (s.st_dev != restrictToDev) {
                    // another mounted volume
                    continue;
                }
                if (s.st_mode & S_IFDIR) {
                    SDKString   tmp = FindPath2Inode_ (filePath + "/", restrictToDev, inodeNumber);
                    if (not tmp.empty ()) {
                        return tmp;
                    }
                }
                else if ((s.st_mode & S_IFREG) and s.st_ino == inodeNumber) {
                    return filePath;
                }
            }
        }
        return SDKString ();    // signal not found
    }
    SDKString   FindPath2Inode_Caching_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   60.0;   // no good way to parameterize
        using   KEY = tuple<SDKString, dev_t, ino_t>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (dir, restrictToDev, inodeNumber), noOlderThan, [noOlderThan, dir, restrictToDev, inodeNumber] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindPath2Inode_ (dir, restrictToDev, inodeNumber);
        });
    }
    SDKString   FindPath2Inode_ (const SDKString& dir, ino_t inodeNumber)
    {
        struct  stat    s;
        if (::stat (dir.c_str (), &s) == 0) {
            if (s.st_mode & S_IFDIR) {
                return FindPath2Inode_Caching_ (dir, s.st_dev, inodeNumber);
            }
        }
        return SDKString ();
    }
    SDKString   FindFSForMajorMinorDev_LowLevel_ (int majorDev, int minorDev)
    {
        // @todo - this isn't truely safe... Esp df|grep part...
        string  fsBlockName;
        if (majorDev != -1 and minorDev != -1) {
            // ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev
            char buf[1024];
            (void)::snprintf (buf, NEltsOf (buf), "ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev);
            SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
            int     beforeFSName     =  devfsline.rfind (' ');
            if (beforeFSName != string::npos) {
                fsBlockName = devfsline.substr (beforeFSName + 1);
            }
        }
        string  fsName;
        if (not fsBlockName.empty ()) {
            char buf[1024];
            (void)::snprintf (buf, NEltsOf (buf), "df | grep %s", fsBlockName.c_str ());    // not - unreliable - could match multiple...
            SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
            int     beforeFSName     =  devfsline.rfind (' ');
            if (beforeFSName != string::npos) {
                fsName = devfsline.substr (beforeFSName + 1);
            }
        }
        return fsName;
    }
    SDKString   FindFSForMajorMinorDev_WithCaching_ (int majorDev, int minorDev)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   60 * 60.0;   // no good way to parameterize
        using   KEY = pair<int, int>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (majorDev, minorDev), noOlderThan, [noOlderThan, majorDev, minorDev] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindFSForMajorMinorDev_LowLevel_ (majorDev, minorDev);
        });
    }
    SDKString   AIX_GET_EXE_PATH_ (pid_t pid, const String* hint)
    {
        /*
         *  What a PITA!
         *      http://unix.stackexchange.com/questions/109175/how-to-identify-executable-path-with-its-pid-on-aix-5-or-more
         *
         *  This is INSANELY slow and kludgey. If we need to support this, we will need to find a better way.
         *  We can code all this direclty in C++, without popen, but with a bit of work.
         *      --
         *
         *  NOTE - PART of this I think I can do better/faster with stat!!!
         */
        ino_t   inode {};
        {
            struct  stat    s;
            char aoutFileBuf[1024];
            snprintf (aoutFileBuf, NEltsOf (aoutFileBuf), "/proc/%d/object/a.out", pid);
            if (::stat (aoutFileBuf, &s) == 0) {
                inode = s.st_ino;
            }
        }
        if (inode == 0) {
            return SDKString ();
        }
        int majorDev { -1 };
        int minorDev { -1 };
        {
            // @todo - can we simplify this and look at st_dev of the file???
            // ls -li /proc/%d/object/ | egrep \"%lld$\""
            char procObjectDir[1024];
            (void)::snprintf (procObjectDir, NEltsOf (procObjectDir), "/proc/%d/object/", pid);
            DIR*       dirIt    { ::opendir (procObjectDir) };
            if (dirIt != nullptr) {
                auto&& cleanup  =   Finally ([dirIt] () noexcept { ::closedir (dirIt); });
                char    endsWithBuffer[1024];
                endsWithBuffer[0] = '\0';
                (void)::snprintf (endsWithBuffer, NEltsOf (endsWithBuffer), ".%lld", static_cast<long long> (inode));
                size_t  endsWithBufferLen = ::strlen (endsWithBuffer);
                for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
                    size_t  l   =   ::strlen (cur->d_name);
                    if (l > endsWithBufferLen) {
                        if (::strcmp (cur->d_name + l - endsWithBufferLen, endsWithBuffer) == 0) {
                            string  tmp { cur->d_name };
                            int     beforeMajorIdx   =  tmp.find ('.');
                            int     beforeMinorIdx   =  tmp.find ('.', beforeMajorIdx + 1);
                            if (beforeMajorIdx != string::npos and beforeMinorIdx != string::npos) {
                                majorDev   =   ::strtoll (tmp.c_str () + beforeMajorIdx + 1, nullptr, 10);
                                minorDev   =   ::strtoll (tmp.c_str () + beforeMinorIdx + 1, nullptr, 10);
                            }
                        }
                    }
                }
            }
        }
        static  Synchronized<Cache::LRUCache<String, SDKString, Cache::LRUCacheSupport::DefaultTraits<String, SDKString, 7>>>   sHintCache_ (256);
        if (hint != nullptr) {
            using   Memory::Optional;
            if (Optional<SDKString> o   =   sHintCache_->Lookup (*hint)) {
                struct  stat    hintStats;
                if (::stat (o->c_str (), &hintStats) == 0) {
                    if (hintStats.st_ino == inode and hintStats.st_dev == makedev (majorDev, minorDev)) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                        DbgTrace (L"CacheHit for hint '%s' (maps to '%s')", hint->c_str (), String::FromSDKString (*o).c_str ());
#endif
                        return *o;
                    }
                }
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                DbgTrace (L"CacheMiss for hint '%s'", hint->c_str ());
#endif
            }
        }
        SDKString   fsName  =   FindFSForMajorMinorDev_WithCaching_ (majorDev, minorDev);
        string      exeName;
        if (not fsName.empty ()) {
            exeName = FindPath2Inode_ (fsName + "/", inode);
        }
        if (hint != nullptr and not exeName.empty ()) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace ("Priming cache for hint '%s' (maps to '%s')", hint->c_str (), exeName.c_str ());
#endif
            sHintCache_->Add (*hint, exeName);
        }
        return exeName;
    }
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHintT ********************
 ********************************************************************************
 */
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/1", 0.1 };
#endif
    return AIX_GET_EXE_PATH_ (processID, nullptr);
}
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID, const SDKString& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/2", 0.1 };
#endif
    String tmp { String::FromSDKString (associationHint) };
    return AIX_GET_EXE_PATH_ (processID, &tmp);
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHint *********************
 ********************************************************************************
 */
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/1", 0.1 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, nullptr));
}
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID, const String& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/2", 0.1 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, &associationHint));
}
<commit_msg>Completed rewrite of https://analitiqa.atlassian.net/browse/AII-27 - Execution::Platform::AIX::GetEXEPathWithHintT - still sucks - but at least no popen<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../../../StroikaPreComp.h"

#include    <unistd.h>

#if     qPlatform_AIX
#include    <dirent.h>
#include    <sys/sysmacros.h>
#include    <sys/types.h>
#include    <unistd.h>
#include    <fstream>
#endif

#include    "../../../Cache/CallerStalenessCache.h"
#include    "../../../Cache/LRUCache.h"
#include    "../../../Characters/String_Constant.h"
#include    "../../../Debug/TimingTrace.h"
#include    "../../../Memory/SmallStackBuffer.h"
#include    "../../ErrNoException.h"
#include    "../../Finally.h"

#include    "GetEXEPathWithHint.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Execution;
using   namespace   Stroika::Foundation::Execution::Platform::AIX;

using   Characters::String;
using   Characters::String_Constant;

//
// Comment this in to turn on aggressive noisy DbgTrace in this module
// NB: CURRENTLY safe to turn on (2016-04-01) - but risky if triggered loading with TraceLog file getting its name from
// calling here...
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1
//

//
// Comment this in to turn on somewhat noisy timing DbgTrace in this module
// NB: This timing trace code contains HACK - to NOT report anyhting for processID==getpid () - since
// that is called early on trying to get the EXE name for this process - before main - for the tracelog
// and doing DbgTrace calls in response causes grave disorder
//
//#define   USE_TIMING_TRACE_IN_THIS_MODULE_       1
//

namespace {
    bool    IsProcessSafeToLogFor_ (pid_t p)
    {
        return p != getpid ();
    }
}

namespace {
    // find %s -xdev -type f -inum %lld -print 2> /dev/null - but much faster.
    // @todo - xdev part. Not only possible speed hack, but also needed for correctness since
    // we could match the wrong inode if we crossed filesystems.
    SDKString   FindPath2Inode_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        DIR*       dirIt    { ::opendir (dir.c_str ()) };
        if (dirIt == nullptr) {
            return SDKString ();
        }
        auto&& cleanup  =   Finally ([dirIt] () noexcept { ::closedir (dirIt); });
        for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
            if (::strcmp (cur->d_name, ".") == 0 or ::strcmp (cur->d_name, "..") == 0) {
                continue;
            }
            struct  stat    s;
            SDKString   filePath = dir + cur->d_name;
            if (::stat (filePath.c_str (), &s) == 0) {
                if (s.st_dev != restrictToDev) {
                    // another mounted volume
                    continue;
                }
                if (s.st_mode & S_IFDIR) {
                    SDKString   tmp = FindPath2Inode_ (filePath + "/", restrictToDev, inodeNumber);
                    if (not tmp.empty ()) {
                        return tmp;
                    }
                }
                else if ((s.st_mode & S_IFREG) and s.st_ino == inodeNumber) {
                    return filePath;
                }
            }
        }
        return SDKString ();    // signal not found
    }
    SDKString   FindPath2Inode_Caching_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   5 * 60.0;   // no good way to parameterize
        using   KEY = tuple<SDKString, dev_t, ino_t>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (dir, restrictToDev, inodeNumber), noOlderThan, [noOlderThan, dir, restrictToDev, inodeNumber] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindPath2Inode_ (dir, restrictToDev, inodeNumber);
        });
    }
    SDKString   FindPath2Inode_ (const SDKString& dir, ino_t inodeNumber)
    {
        struct  stat    s;
        if (::stat (dir.c_str (), &s) == 0) {
            if (s.st_mode & S_IFDIR) {
                return FindPath2Inode_Caching_ (dir, s.st_dev, inodeNumber);
            }
        }
        return SDKString ();
    }
    SDKString   GetBlockDeviceFileName2MajorMinorDev_ (int majorDev, int minorDev)
    {
        /*
         *
         *  if (majorDev != -1 and minorDev != -1) {
         *      // ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev
         *      char buf[1024];
         *      (void)::snprintf (buf, NEltsOf (buf), "ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev);
         *      SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
         *      int     beforeFSName     =  devfsline.rfind (' ');
         *      if (beforeFSName != string::npos) {
         *          fsBlockName = devfsline.substr (beforeFSName + 1);
         *      }
         *  }
         */
        // NOTE - this could probably be memoized/cached, as the list of block devices on a machine probably doesn't change much ;-)
        DIR*       dirIt    { ::opendir ("/dev") };
        if (dirIt != nullptr) {
            auto&& cleanup  =   Finally ([dirIt] () noexcept { ::closedir (dirIt); });
            for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
                SDKString   filePath = SDKString { "/dev/" } + cur->d_name;
                struct  stat    s;
                if (::stat (filePath.c_str (), &s) == 0) {
                    if (S_ISBLK(s.st_mode) and major (s.st_rdev) == majorDev and minor (s.st_rdev) == minorDev) {
                        return filePath;
                    }
                }
            }
        }
        return SDKString {};
    }
    SDKString   GetFSNameForBlockName_ (const SDKString& blockDeviceName)
    {
        /*
         *  What we used to do:
         *      if (not fsBlockName.empty ()) {
         *          char buf[1024];
         *          (void)::snprintf (buf, NEltsOf (buf), "df | grep %s", fsBlockName.c_str ());    // not - unreliable - could match multiple...
         *          SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
         *          int     beforeFSName     =  devfsline.rfind (' ');
         *          if (beforeFSName != string::npos) {
         *              return devfsline.substr (beforeFSName + 1);
         *          }
         *      }
         *
         *  Weak way - but probably better then what we used to do:
         *      read /etc/filesystem
         */
        ifstream    fsFile ("/etc/filesystems");
        char line[4 * 1024];
        string  readingFilesystem;
        string  forDev;
        while (fsFile.getline (line, NEltsOf (line))) {
            if (line[0] == '/') {
                char* p = ::strchr (line, ':');
                if (p != nullptr) {
                    *p = '\0';
                    readingFilesystem = line;
                    forDev = string {};
                }
            }
            if (not readingFilesystem.empty () and forDev.empty () and ::strstr (line, "dev") != nullptr) {
                char* p = ::strchr (line, '=');
                if (p != nullptr) {
                    p++;
                    while (*p and isspace (*p)) {
                        ++p;
                    }
                }
                if (p != nullptr and * p != '\0') {
                    forDev = p;
                }
            }
            if (blockDeviceName == forDev) {
                return readingFilesystem;
            }
        }
        return SDKString {};
    }
    SDKString   FindFSForMajorMinorDev_LowLevel_ (int majorDev, int minorDev)
    {
        SDKString fsBlockName = GetBlockDeviceFileName2MajorMinorDev_ (majorDev, minorDev);
        if (not fsBlockName.empty ()) {
            return GetFSNameForBlockName_ (fsBlockName);
        }
        return SDKString {};
    }
    SDKString   FindFSForMajorMinorDev_WithCaching_ (int majorDev, int minorDev)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   60 * 60.0;   // no good way to parameterize
        using   KEY = pair<int, int>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (majorDev, minorDev), noOlderThan, [noOlderThan, majorDev, minorDev] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindFSForMajorMinorDev_LowLevel_ (majorDev, minorDev);
        });
    }
    SDKString   AIX_GET_EXE_PATH_ (pid_t pid, const String* hint)
    {
        /*
         *  What a PITA!
         *      http://unix.stackexchange.com/questions/109175/how-to-identify-executable-path-with-its-pid-on-aix-5-or-more
         *
         *  This is INSANELY slow and kludgey. If we need to support this, we will need to find a better way.
         *
         *  In the meantime, I at least rewrote it all without popen, so a bit faster, but still aweful.
         *
         *  NOTE - PART of this I think I can do better/faster with stat!!!
         */
        ino_t   inode {};
        {
            struct  stat    s;
            char aoutFileBuf[1024];
            snprintf (aoutFileBuf, NEltsOf (aoutFileBuf), "/proc/%d/object/a.out", pid);
            if (::stat (aoutFileBuf, &s) == 0) {
                inode = s.st_ino;
            }
        }
        if (inode == 0) {
            return SDKString ();
        }
        int majorDev { -1 };
        int minorDev { -1 };
        {
            // @todo - can we simplify this and look at st_dev of the file???
            // ls -li /proc/%d/object/ | egrep \"%lld$\""
            char procObjectDir[1024];
            (void)::snprintf (procObjectDir, NEltsOf (procObjectDir), "/proc/%d/object/", pid);
            DIR*       dirIt    { ::opendir (procObjectDir) };
            if (dirIt != nullptr) {
                auto&& cleanup  =   Finally ([dirIt] () noexcept { ::closedir (dirIt); });
                char    endsWithBuffer[1024];
                endsWithBuffer[0] = '\0';
                (void)::snprintf (endsWithBuffer, NEltsOf (endsWithBuffer), ".%lld", static_cast<long long> (inode));
                size_t  endsWithBufferLen = ::strlen (endsWithBuffer);
                for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
                    size_t  l   =   ::strlen (cur->d_name);
                    if (l > endsWithBufferLen) {
                        if (::strcmp (cur->d_name + l - endsWithBufferLen, endsWithBuffer) == 0) {
                            string  tmp { cur->d_name };
                            int     beforeMajorIdx   =  tmp.find ('.');
                            int     beforeMinorIdx   =  tmp.find ('.', beforeMajorIdx + 1);
                            if (beforeMajorIdx != string::npos and beforeMinorIdx != string::npos) {
                                majorDev   =   ::strtoll (tmp.c_str () + beforeMajorIdx + 1, nullptr, 10);
                                minorDev   =   ::strtoll (tmp.c_str () + beforeMinorIdx + 1, nullptr, 10);
                            }
                        }
                    }
                }
            }
        }
        static  Synchronized<Cache::LRUCache<String, SDKString, Cache::LRUCacheSupport::DefaultTraits<String, SDKString, 7>>>   sHintCache_ (256);
        if (hint != nullptr) {
            using   Memory::Optional;
            if (Optional<SDKString> o   =   sHintCache_->Lookup (*hint)) {
                struct  stat    hintStats;
                if (::stat (o->c_str (), &hintStats) == 0) {
                    if (hintStats.st_ino == inode and hintStats.st_dev == makedev (majorDev, minorDev)) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                        if (IsProcessSafeToLogFor_ (pid)) {
                            DbgTrace (L"CacheHit for hint '%s' (maps to '%s')", hint->c_str (), String::FromSDKString (*o).c_str ());
                        }
#endif
                        return *o;
                    }
                }
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                if (IsProcessSafeToLogFor_ (pid)) {
                    DbgTrace (L"CacheMiss for hint '%s'", hint->c_str ());
                }
#endif
            }
        }
        SDKString   fsName  =   FindFSForMajorMinorDev_WithCaching_ (majorDev, minorDev);
        string      exeName;
        if (not fsName.empty ()) {
            exeName = FindPath2Inode_ (fsName + "/", inode);
        }
        if (hint != nullptr and not exeName.empty ()) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
            if (IsProcessSafeToLogFor_ (pid)) {
                DbgTrace ("Priming cache for hint '%s' (maps to '%s')", hint->c_str (), exeName.c_str ());
            }
#endif
            sHintCache_->Add (*hint, exeName);
        }
        return exeName;
    }
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHintT ********************
 ********************************************************************************
 */
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/1", IsProcessSafeToLogFor_ (processID) ? 0.001 : 1000.0 };
#endif
    return AIX_GET_EXE_PATH_ (processID, nullptr);
}
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID, const SDKString& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/2", IsProcessSafeToLogFor_ (processID) ? 0.001 : 1000.0 };
#endif
    String tmp { String::FromSDKString (associationHint) };
    return AIX_GET_EXE_PATH_ (processID, &tmp);
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHint *********************
 ********************************************************************************
 */
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/1", IsProcessSafeToLogFor_ (processID) ? 0.001 : 1000.0 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, nullptr));
}
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID, const String& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/2", IsProcessSafeToLogFor_ (processID) ? 0.001 : 1000.0 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, &associationHint));
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../../../StroikaPreComp.h"

#include    <unistd.h>

#if     qPlatform_AIX
#include    <dirent.h>
#include    <sys/sysmacros.h>
#include    <sys/types.h>
#include    <unistd.h>
#endif

#include    "../../../Cache/CallerStalenessCache.h"
#include    "../../../Cache/LRUCache.h"
#include    "../../../Characters/String_Constant.h"
#include    "../../../Debug/TimingTrace.h"
#include    "../../../Memory/SmallStackBuffer.h"
#include    "../../ErrNoException.h"

#include    "GetEXEPathWithHint.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Execution;
using   namespace   Stroika::Foundation::Execution::Platform::AIX;

using   Characters::String;
using   Characters::String_Constant;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_TIMING_TRACE_IN_THIS_MODULE_       1

namespace {
    SDKString   myProcessRunnerFirstLine_ (const SDKString& cmdLine)
    {
        /*
         *      NOTE - we intentionally use popen here avoid helpful Stroika code like ProcessRunner (), ThrowIfNull, etc,
         *      so that this code remains low level, and doesnt invoke any tracelog code, which would result in a deadlock/loop....
         *
         */
        FILE*   p = ::popen (cmdLine.c_str (), "r");
        string  accum;
        if (p != nullptr) {
            char    buf[10 * 1024];
            while (::fgets (buf, NEltsOf (buf), p) != nullptr) {
                accum += buf;
                size_t i = accum.rfind ('\n');
                if (i != -1) {
                    accum.erase (i);
                }
                break;  // minor speed hack since we always use with just one line
            }
            int result  =   ::pclose (p);
            // cannot report to avoid TRACE ;-(
        }
        return accum;
    }
    // find %s -xdev -type f -inum %lld -print 2> /dev/null - but much faster.
    // @todo - xdev part. Not only possible speed hack, but also needed for correctness since
    // we could match the wrong inode if we crossed filesystems.
    SDKString   FindPath2Inode_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        DIR*       dirIt    { ::opendir (dir.c_str ()) };
        if (dirIt == nullptr) {
            return SDKString ();
        }
        struct CLEANUP_ {
            DIR*       fDirIt_;
            CLEANUP_ (DIR* d) : fDirIt_ (d) {}
            ~CLEANUP_ ()
            {
                if (fDirIt_ != nullptr) {
                    ::closedir (fDirIt_);
                }
            }
        };
        CLEANUP_ c { dirIt };
        for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
            if (::strcmp (cur->d_name, ".") == 0 or ::strcmp (cur->d_name, "..") == 0) {
                continue;
            }
            struct  stat    s;
            SDKString   filePath = dir + cur->d_name;
            if (::stat (filePath.c_str (), &s) == 0) {
                if (s.st_dev != restrictToDev) {
                    // another mounted volume
                    continue;
                }
                if (s.st_mode & S_IFDIR) {
                    SDKString   tmp = FindPath2Inode_ (filePath + "/", restrictToDev, inodeNumber);
                    if (not tmp.empty ()) {
                        return tmp;
                    }
                }
                else if ((s.st_mode & S_IFREG) and s.st_ino == inodeNumber) {
                    return filePath;
                }
            }
        }
        return SDKString ();    // signal not found
    }
    SDKString   FindPath2Inode_Caching_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   60.0;   // no good way to parameterize
        using   KEY = tuple<SDKString, dev_t, ino_t>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (dir, restrictToDev, inodeNumber), noOlderThan, [noOlderThan, dir, restrictToDev, inodeNumber] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindPath2Inode_ (dir, restrictToDev, inodeNumber);
        });
    }
    SDKString   FindPath2Inode_ (const SDKString& dir, ino_t inodeNumber)
    {
        struct  stat    s;
        if (::stat (dir.c_str (), &s) == 0) {
            if (s.st_mode & S_IFDIR) {
                return FindPath2Inode_Caching_ (dir, s.st_dev, inodeNumber);
            }
        }
        return SDKString ();
    }
    SDKString   FindFSForMajorMinorDev_LowLevel_ (int majorDev, int minorDev)
    {
        // @todo - this isn't truely safe... Esp df|grep part...
        string  fsBlockName;
        if (majorDev != -1 and minorDev != -1) {
            // ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev
            char buf[1024];
            (void)::snprintf (buf, NEltsOf (buf), "ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev);
            SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
            int     beforeFSName     =  devfsline.rfind (' ');
            if (beforeFSName != string::npos) {
                fsBlockName = devfsline.substr (beforeFSName + 1);
            }
        }
        string  fsName;
        if (not fsBlockName.empty ()) {
            char buf[1024];
            (void)::snprintf (buf, NEltsOf (buf), "df | grep %s", fsBlockName.c_str ());    // not - unreliable - could match multiple...
            SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
            int     beforeFSName     =  devfsline.rfind (' ');
            if (beforeFSName != string::npos) {
                fsName = devfsline.substr (beforeFSName + 1);
            }
        }
        return fsName;
    }
    SDKString   FindFSForMajorMinorDev_WithCaching_ (int majorDev, int minorDev)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   60 * 60.0;   // no good way to parameterize
        using   KEY = pair<int, int>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (majorDev, minorDev), noOlderThan, [noOlderThan, majorDev, minorDev] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindFSForMajorMinorDev_LowLevel_ (majorDev, minorDev);
        });
    }
    SDKString   AIX_GET_EXE_PATH_ (pid_t pid, const String* hint)
    {
        /*
         *  What a PITA!
         *      http://unix.stackexchange.com/questions/109175/how-to-identify-executable-path-with-its-pid-on-aix-5-or-more
         *
         *  This is INSANELY slow and kludgey. If we need to support this, we will need to find a better way.
         *  We can code all this direclty in C++, without popen, but with a bit of work.
         *      --
         *
         *  NOTE - PART of this I think I can do better/faster with stat!!!
         */
        ino_t   inode {};
        {
            struct  stat    s;
            char aoutFileBuf[1024];
            snprintf (aoutFileBuf, NEltsOf (aoutFileBuf), "/proc/%d/object/a.out", pid);
            if (::stat (aoutFileBuf, &s) == 0) {
                inode = s.st_ino;
            }
        }
        if (inode == 0) {
            return SDKString ();
        }
        int majorDev { -1 };
        int minorDev { -1 };
        {
            // @todo - can we simplify this and look at st_dev of the file???
            // ls -li /proc/%d/object/ | egrep \"%lld$\""
            char procObjectDir[1024];
            (void)::snprintf (procObjectDir, NEltsOf (procObjectDir), "/proc/%d/object/", pid);
            DIR*       dirIt    { ::opendir (procObjectDir) };
            if (dirIt != nullptr) {
                struct CLEANUP_ {
                    DIR*       fDirIt_;
                    CLEANUP_ (DIR* d) : fDirIt_ (d) {}
                    ~CLEANUP_ ()
                    {
                        if (fDirIt_ != nullptr) {
                            ::closedir (fDirIt_);
                        }
                    }
                };
                CLEANUP_ c { dirIt };
                char    endsWithBuffer[1024];
                endsWithBuffer[0] = '\0';
                (void)::snprintf (endsWithBuffer, NEltsOf (endsWithBuffer), ".%lld", static_cast<long long> (inode));
                size_t  endsWithBufferLen = ::strlen (endsWithBuffer);
                for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
                    size_t  l   =   ::strlen (cur->d_name);
                    if (l > endsWithBufferLen) {
                        if (::strcmp (cur->d_name + l - endsWithBufferLen, endsWithBuffer) == 0) {
                            string  tmp { cur->d_name };
                            int     beforeMajorIdx   =  tmp.find ('.');
                            int     beforeMinorIdx   =  tmp.find ('.', beforeMajorIdx + 1);
                            if (beforeMajorIdx != string::npos and beforeMinorIdx != string::npos) {
                                majorDev   =   ::strtoll (tmp.c_str () + beforeMajorIdx + 1, nullptr, 10);
                                minorDev   =   ::strtoll (tmp.c_str () + beforeMinorIdx + 1, nullptr, 10);
                            }
                        }
                    }
                }
            }
        }
        static  Synchronized<Cache::LRUCache<String, SDKString, Cache::LRUCacheSupport::DefaultTraits<String, SDKString, 7>>>   sHintCache_ (256);
        if (hint != nullptr) {
            using   Memory::Optional;
            if (Optional<SDKString> o   =   sHintCache_->Lookup (*hint)) {
                struct  stat    hintStats;
                if (::stat (o->c_str (), &hintStats) == 0) {
                    if (hintStats.st_ino == inode and hintStats.st_dev == makedev (majorDev, minorDev)) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                        DbgTrace (L"CacheHit for hint '%s' (maps to '%s')", hint->c_str (), String::FromSDKString (*o).c_str ());
#endif
                        return *o;
                    }
                }
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                DbgTrace (L"CacheMiss for hint '%s'", hint->c_str ());
#endif
            }
        }
        SDKString   fsName  =   FindFSForMajorMinorDev_WithCaching_ (majorDev, minorDev);
        string      exeName;
        if (not fsName.empty ()) {
            exeName = FindPath2Inode_ (fsName + "/", inode);
        }
        if (hint != nullptr and not exeName.empty ()) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace ("Priming cache for hint '%s' (maps to '%s')", hint->c_str (), exeName.c_str ());
#endif
            sHintCache_->Add (*hint, exeName);
        }
        return exeName;
    }
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHintT ********************
 ********************************************************************************
 */
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/1", 0.1 };
#endif
    return AIX_GET_EXE_PATH_ (processID, nullptr);
}
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID, const SDKString& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/2", 0.1 };
#endif
    String tmp { String::FromSDKString (associationHint) };
    return AIX_GET_EXE_PATH_ (processID, &tmp);
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHint *********************
 ********************************************************************************
 */
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/1", 0.1 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, nullptr));
}
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID, const String& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/2", 0.1 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, &associationHint));
}
<commit_msg>cleanup Execution/Platform/AIX/GetEXEPathWithHint using Finally<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../../../StroikaPreComp.h"

#include    <unistd.h>

#if     qPlatform_AIX
#include    <dirent.h>
#include    <sys/sysmacros.h>
#include    <sys/types.h>
#include    <unistd.h>
#endif

#include    "../../../Cache/CallerStalenessCache.h"
#include    "../../../Cache/LRUCache.h"
#include    "../../../Characters/String_Constant.h"
#include    "../../../Debug/TimingTrace.h"
#include    "../../../Memory/SmallStackBuffer.h"
#include    "../../ErrNoException.h"
#include    "../../Finally.h"

#include    "GetEXEPathWithHint.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Execution;
using   namespace   Stroika::Foundation::Execution::Platform::AIX;

using   Characters::String;
using   Characters::String_Constant;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_TIMING_TRACE_IN_THIS_MODULE_       1

namespace {
    SDKString   myProcessRunnerFirstLine_ (const SDKString& cmdLine)
    {
        /*
         *      NOTE - we intentionally use popen here avoid helpful Stroika code like ProcessRunner (), ThrowIfNull, etc,
         *      so that this code remains low level, and doesnt invoke any tracelog code, which would result in a deadlock/loop....
         *
         */
        FILE*   p = ::popen (cmdLine.c_str (), "r");
        string  accum;
        if (p != nullptr) {
            char    buf[10 * 1024];
            while (::fgets (buf, NEltsOf (buf), p) != nullptr) {
                accum += buf;
                size_t i = accum.rfind ('\n');
                if (i != -1) {
                    accum.erase (i);
                }
                break;  // minor speed hack since we always use with just one line
            }
            int result  =   ::pclose (p);
            // cannot report to avoid TRACE ;-(
        }
        return accum;
    }
    // find %s -xdev -type f -inum %lld -print 2> /dev/null - but much faster.
    // @todo - xdev part. Not only possible speed hack, but also needed for correctness since
    // we could match the wrong inode if we crossed filesystems.
    SDKString   FindPath2Inode_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        DIR*       dirIt    { ::opendir (dir.c_str ()) };
        if (dirIt == nullptr) {
            return SDKString ();
        }
        auto&& cleanup  =   Finally ([dirIt] () noexcept { ::closedir (dirIt); });
        for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
            if (::strcmp (cur->d_name, ".") == 0 or ::strcmp (cur->d_name, "..") == 0) {
                continue;
            }
            struct  stat    s;
            SDKString   filePath = dir + cur->d_name;
            if (::stat (filePath.c_str (), &s) == 0) {
                if (s.st_dev != restrictToDev) {
                    // another mounted volume
                    continue;
                }
                if (s.st_mode & S_IFDIR) {
                    SDKString   tmp = FindPath2Inode_ (filePath + "/", restrictToDev, inodeNumber);
                    if (not tmp.empty ()) {
                        return tmp;
                    }
                }
                else if ((s.st_mode & S_IFREG) and s.st_ino == inodeNumber) {
                    return filePath;
                }
            }
        }
        return SDKString ();    // signal not found
    }
    SDKString   FindPath2Inode_Caching_ (const SDKString& dir, dev_t restrictToDev, ino_t inodeNumber)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   60.0;   // no good way to parameterize
        using   KEY = tuple<SDKString, dev_t, ino_t>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (dir, restrictToDev, inodeNumber), noOlderThan, [noOlderThan, dir, restrictToDev, inodeNumber] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindPath2Inode_ (dir, restrictToDev, inodeNumber);
        });
    }
    SDKString   FindPath2Inode_ (const SDKString& dir, ino_t inodeNumber)
    {
        struct  stat    s;
        if (::stat (dir.c_str (), &s) == 0) {
            if (s.st_mode & S_IFDIR) {
                return FindPath2Inode_Caching_ (dir, s.st_dev, inodeNumber);
            }
        }
        return SDKString ();
    }
    SDKString   FindFSForMajorMinorDev_LowLevel_ (int majorDev, int minorDev)
    {
        // @todo - this isn't truely safe... Esp df|grep part...
        string  fsBlockName;
        if (majorDev != -1 and minorDev != -1) {
            // ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev
            char buf[1024];
            (void)::snprintf (buf, NEltsOf (buf), "ls -l /dev/ | egrep \"^b.*%d, *%d.+$\"", majorDev, minorDev);
            SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
            int     beforeFSName     =  devfsline.rfind (' ');
            if (beforeFSName != string::npos) {
                fsBlockName = devfsline.substr (beforeFSName + 1);
            }
        }
        string  fsName;
        if (not fsBlockName.empty ()) {
            char buf[1024];
            (void)::snprintf (buf, NEltsOf (buf), "df | grep %s", fsBlockName.c_str ());    // not - unreliable - could match multiple...
            SDKString   devfsline = myProcessRunnerFirstLine_ (buf);
            int     beforeFSName     =  devfsline.rfind (' ');
            if (beforeFSName != string::npos) {
                fsName = devfsline.substr (beforeFSName + 1);
            }
        }
        return fsName;
    }
    SDKString   FindFSForMajorMinorDev_WithCaching_ (int majorDev, int minorDev)
    {
        const   Time::DurationSecondsType   kTimeValidFor_      =   60 * 60.0;   // no good way to parameterize
        using   KEY = pair<int, int>;
        static  Synchronized<Cache::CallerStalenessCache<KEY, SDKString>>   sCache_;
        Time::DurationSecondsType       noOlderThan = Time::GetTickCount () - kTimeValidFor_;
        return sCache_->Lookup (KEY (majorDev, minorDev), noOlderThan, [noOlderThan, majorDev, minorDev] () -> SDKString {
            // must find better time/way todo this
            sCache_->ClearOlderThan (noOlderThan);
            return FindFSForMajorMinorDev_LowLevel_ (majorDev, minorDev);
        });
    }
    SDKString   AIX_GET_EXE_PATH_ (pid_t pid, const String* hint)
    {
        /*
         *  What a PITA!
         *      http://unix.stackexchange.com/questions/109175/how-to-identify-executable-path-with-its-pid-on-aix-5-or-more
         *
         *  This is INSANELY slow and kludgey. If we need to support this, we will need to find a better way.
         *  We can code all this direclty in C++, without popen, but with a bit of work.
         *      --
         *
         *  NOTE - PART of this I think I can do better/faster with stat!!!
         */
        ino_t   inode {};
        {
            struct  stat    s;
            char aoutFileBuf[1024];
            snprintf (aoutFileBuf, NEltsOf (aoutFileBuf), "/proc/%d/object/a.out", pid);
            if (::stat (aoutFileBuf, &s) == 0) {
                inode = s.st_ino;
            }
        }
        if (inode == 0) {
            return SDKString ();
        }
        int majorDev { -1 };
        int minorDev { -1 };
        {
            // @todo - can we simplify this and look at st_dev of the file???
            // ls -li /proc/%d/object/ | egrep \"%lld$\""
            char procObjectDir[1024];
            (void)::snprintf (procObjectDir, NEltsOf (procObjectDir), "/proc/%d/object/", pid);
            DIR*       dirIt    { ::opendir (procObjectDir) };
            if (dirIt != nullptr) {
                struct CLEANUP_ {
                    DIR*       fDirIt_;
                    CLEANUP_ (DIR* d) : fDirIt_ (d) {}
                    ~CLEANUP_ ()
                    {
                        if (fDirIt_ != nullptr) {
                            ::closedir (fDirIt_);
                        }
                    }
                };
                CLEANUP_ c { dirIt };
                char    endsWithBuffer[1024];
                endsWithBuffer[0] = '\0';
                (void)::snprintf (endsWithBuffer, NEltsOf (endsWithBuffer), ".%lld", static_cast<long long> (inode));
                size_t  endsWithBufferLen = ::strlen (endsWithBuffer);
                for (dirent* cur = ::readdir (dirIt); cur != nullptr; cur = ::readdir (dirIt)) {
                    size_t  l   =   ::strlen (cur->d_name);
                    if (l > endsWithBufferLen) {
                        if (::strcmp (cur->d_name + l - endsWithBufferLen, endsWithBuffer) == 0) {
                            string  tmp { cur->d_name };
                            int     beforeMajorIdx   =  tmp.find ('.');
                            int     beforeMinorIdx   =  tmp.find ('.', beforeMajorIdx + 1);
                            if (beforeMajorIdx != string::npos and beforeMinorIdx != string::npos) {
                                majorDev   =   ::strtoll (tmp.c_str () + beforeMajorIdx + 1, nullptr, 10);
                                minorDev   =   ::strtoll (tmp.c_str () + beforeMinorIdx + 1, nullptr, 10);
                            }
                        }
                    }
                }
            }
        }
        static  Synchronized<Cache::LRUCache<String, SDKString, Cache::LRUCacheSupport::DefaultTraits<String, SDKString, 7>>>   sHintCache_ (256);
        if (hint != nullptr) {
            using   Memory::Optional;
            if (Optional<SDKString> o   =   sHintCache_->Lookup (*hint)) {
                struct  stat    hintStats;
                if (::stat (o->c_str (), &hintStats) == 0) {
                    if (hintStats.st_ino == inode and hintStats.st_dev == makedev (majorDev, minorDev)) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                        DbgTrace (L"CacheHit for hint '%s' (maps to '%s')", hint->c_str (), String::FromSDKString (*o).c_str ());
#endif
                        return *o;
                    }
                }
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
                DbgTrace (L"CacheMiss for hint '%s'", hint->c_str ());
#endif
            }
        }
        SDKString   fsName  =   FindFSForMajorMinorDev_WithCaching_ (majorDev, minorDev);
        string      exeName;
        if (not fsName.empty ()) {
            exeName = FindPath2Inode_ (fsName + "/", inode);
        }
        if (hint != nullptr and not exeName.empty ()) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace ("Priming cache for hint '%s' (maps to '%s')", hint->c_str (), exeName.c_str ());
#endif
            sHintCache_->Add (*hint, exeName);
        }
        return exeName;
    }
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHintT ********************
 ********************************************************************************
 */
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/1", 0.1 };
#endif
    return AIX_GET_EXE_PATH_ (processID, nullptr);
}
SDKString   Execution::Platform::AIX::GetEXEPathWithHintT (pid_t processID, const SDKString& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHintT/2", 0.1 };
#endif
    String tmp { String::FromSDKString (associationHint) };
    return AIX_GET_EXE_PATH_ (processID, &tmp);
}

/*
 ********************************************************************************
 ************************ Platform::AIX::GetEXEPathWithHint *********************
 ********************************************************************************
 */
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/1", 0.1 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, nullptr));
}
String   Execution::Platform::AIX::GetEXEPathWithHint (pid_t processID, const String& associationHint)
{
#if     USE_TIMING_TRACE_IN_THIS_MODULE_
    Debug::TimingTrace ctx { L"Platform::AIX::GetEXEPathWithHint/2", 0.1 };
#endif
    return String::FromSDKString (AIX_GET_EXE_PATH_ (processID, &associationHint));
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <iostream>
#include <signal.h>
#include <X11/Xlib.h>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/scoped_ptr.h"
#include "base/thread.h"
#include "base/waitable_event.h"
#include "media/base/media.h"
#include "media/base/media_switches.h"
#include "media/base/pipeline_impl.h"
#include "media/filters/audio_renderer_impl.h"
#include "media/filters/ffmpeg_audio_decoder.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"
#include "media/filters/file_data_source.h"
#include "media/filters/null_audio_renderer.h"
#include "media/filters/omx_video_decoder.h"

#if defined(RENDERER_GL)
#include "media/tools/player_x11/gl_video_renderer.h"
typedef GlVideoRenderer Renderer;
#elif defined(RENDERER_GLES)
#include "media/tools/player_x11/gles_video_renderer.h"
typedef GlesVideoRenderer Renderer;
#elif defined(RENDERER_X11)
#include "media/tools/player_x11/x11_video_renderer.h"
typedef X11VideoRenderer Renderer;
#else
#error No video renderer defined.
#endif

Display* g_display = NULL;
Window g_window = 0;
bool g_running = false;

// Initialize X11. Returns true if successful. This method creates the X11
// window. Further initialization is done in X11VideoRenderer.
bool InitX11() {
  g_display = XOpenDisplay(NULL);
  if (!g_display) {
    std::cout << "Error - cannot open display" << std::endl;
    return false;
  }

  // Get properties of the screen.
  int screen = DefaultScreen(g_display);
  int root_window = RootWindow(g_display, screen);

  // Creates the window.
  g_window = XCreateSimpleWindow(g_display, root_window, 1, 1, 100, 50, 0,
                                 BlackPixel(g_display, screen),
                                 BlackPixel(g_display, screen));
  XStoreName(g_display, g_window, "X11 Media Player");

  XSelectInput(g_display, g_window, ExposureMask | ButtonPressMask);
  XMapWindow(g_display, g_window);
  return true;
}

bool InitPipeline(MessageLoop* message_loop,
                  const char* filename, bool enable_audio,
                  scoped_refptr<media::PipelineImpl>* pipeline) {
  // Initialize OpenMAX.
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableOpenMax) &&
      !media::InitializeOpenMaxLibrary(FilePath())) {
    std::cout << "Unable to initialize OpenMAX library."<< std::endl;
    return false;
  }

  // Load media libraries.
  if (!media::InitializeMediaLibrary(FilePath())) {
    std::cout << "Unable to initialize the media library." << std::endl;
    return false;
  }

  // Create our filter factories.
  scoped_refptr<media::FilterFactoryCollection> factories =
      new media::FilterFactoryCollection();
  factories->AddFactory(media::FileDataSource::CreateFactory());
  factories->AddFactory(media::FFmpegAudioDecoder::CreateFactory());
  factories->AddFactory(media::FFmpegDemuxer::CreateFilterFactory());
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableOpenMax)) {
    factories->AddFactory(media::OmxVideoDecoder::CreateFactory());
  }
  factories->AddFactory(media::FFmpegVideoDecoder::CreateFactory());
  factories->AddFactory(Renderer::CreateFactory(g_display, g_window));

  if (enable_audio) {
    factories->AddFactory(media::AudioRendererImpl::CreateFilterFactory());
  } else {
    factories->AddFactory(media::NullAudioRenderer::CreateFilterFactory());
  }

  // Creates the pipeline and start it.
  *pipeline = new media::PipelineImpl(message_loop);
  (*pipeline)->Start(factories, filename, NULL);

  // Wait until the pipeline is fully initialized.
  while (true) {
    PlatformThread::Sleep(100);
    if ((*pipeline)->IsInitialized())
      break;
    if ((*pipeline)->GetError() != media::PIPELINE_OK) {
      (*pipeline)->Stop(NULL);
      return false;
    }
  }

  // And starts the playback.
  (*pipeline)->SetPlaybackRate(1.0f);
  return true;
}

void TerminateHandler(int signal) {
  g_running = false;
}

void PeriodicalUpdate(MessageLoop* message_loop, bool audio_only) {
  if (!g_running) {
    message_loop->Quit();
    return;
  }

  // Consume all the X events
  while (XPending(g_display)) {
    XEvent e;
    XNextEvent(g_display, &e);
    if (e.type == Expose) {
      if (!audio_only) {
        // Tell the renderer to paint.
        DCHECK(Renderer::instance());
        Renderer::instance()->Paint();
      }
    } else if (e.type == ButtonPress) {
      g_running = false;
      // QuitNow is more responsive than Quit since renderer_base is till
      // posting paint messages.
      message_loop->QuitNow();
      return;
    }
  }

  message_loop->PostDelayedTask(FROM_HERE,
      NewRunnableFunction(PeriodicalUpdate, message_loop, audio_only), 10);
}

int main(int argc, char** argv) {
  // Read arguments.
  if (argc == 1) {
    std::cout << "Usage: " << argv[0] << " --file=FILE" << std::endl
              << std::endl
              << "Optional arguments:" << std::endl
              << "  [--enable-openmax]"
              << "  [--audio]"
              << "  [--alsa-device=DEVICE]" << std::endl;
    return 1;
  }

  // Read command line.
  CommandLine::Init(argc, argv);
  std::string filename =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII("file");
  bool enable_audio = CommandLine::ForCurrentProcess()->HasSwitch("audio");
  bool audio_only = false;

  // Install the signal handler.
  signal(SIGTERM, &TerminateHandler);
  signal(SIGINT, &TerminateHandler);

  // Initialize X11.
  if (!InitX11())
    return 1;

  // Initialize the pipeline thread and the pipeline.
  base::AtExitManager at_exit;
  scoped_ptr<base::Thread> thread;
  scoped_refptr<media::PipelineImpl> pipeline;
  MessageLoop message_loop;
  thread.reset(new base::Thread("PipelineThread"));
  thread->Start();
  if (InitPipeline(thread->message_loop(), filename.c_str(),
                   enable_audio, &pipeline)) {
    // Main loop of the application.
    g_running = true;

    // Check if video is present.
    audio_only = !pipeline->IsRendered(media::mime_type::kMajorTypeVideo);

    if (!audio_only) {
      // Tell the renderer to paint.
      DCHECK(Renderer::instance());
      Renderer::instance()->set_glx_thread_message_loop(&message_loop);
    }

    message_loop.PostTask(FROM_HERE,
        NewRunnableFunction(PeriodicalUpdate, &message_loop, audio_only));
    message_loop.Run();

    // Need to wait for pipeline to be fully stopped before stopping the thread.
    base::WaitableEvent event(false, false);
    pipeline->Stop(NewCallback(&event, &base::WaitableEvent::Signal));
    event.Wait();
  } else{
    std::cout << "Pipeline initialization failed..." << std::endl;
  }

  // Cleanup tasks.
  thread->Stop();
  XDestroyWindow(g_display, g_window);
  XCloseDisplay(g_display);
  return 0;
}
<commit_msg>add play control<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <iostream>
#include <signal.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/scoped_ptr.h"
#include "base/thread.h"
#include "base/waitable_event.h"
#include "media/base/media.h"
#include "media/base/media_switches.h"
#include "media/base/pipeline_impl.h"
#include "media/filters/audio_renderer_impl.h"
#include "media/filters/ffmpeg_audio_decoder.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"
#include "media/filters/file_data_source.h"
#include "media/filters/null_audio_renderer.h"
#include "media/filters/omx_video_decoder.h"

#if defined(RENDERER_GL)
#include "media/tools/player_x11/gl_video_renderer.h"
typedef GlVideoRenderer Renderer;
#elif defined(RENDERER_GLES)
#include "media/tools/player_x11/gles_video_renderer.h"
typedef GlesVideoRenderer Renderer;
#elif defined(RENDERER_X11)
#include "media/tools/player_x11/x11_video_renderer.h"
typedef X11VideoRenderer Renderer;
#else
#error No video renderer defined.
#endif

Display* g_display = NULL;
Window g_window = 0;
bool g_running = false;

// Initialize X11. Returns true if successful. This method creates the X11
// window. Further initialization is done in X11VideoRenderer.
bool InitX11() {
  g_display = XOpenDisplay(NULL);
  if (!g_display) {
    std::cout << "Error - cannot open display" << std::endl;
    return false;
  }

  // Get properties of the screen.
  int screen = DefaultScreen(g_display);
  int root_window = RootWindow(g_display, screen);

  // Creates the window.
  g_window = XCreateSimpleWindow(g_display, root_window, 1, 1, 100, 50, 0,
                                 BlackPixel(g_display, screen),
                                 BlackPixel(g_display, screen));
  XStoreName(g_display, g_window, "X11 Media Player");

  XSelectInput(g_display, g_window,
               ExposureMask | ButtonPressMask | KeyPressMask);
  XMapWindow(g_display, g_window);
  return true;
}

bool InitPipeline(MessageLoop* message_loop,
                  const char* filename, bool enable_audio,
                  scoped_refptr<media::PipelineImpl>* pipeline) {
  // Initialize OpenMAX.
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableOpenMax) &&
      !media::InitializeOpenMaxLibrary(FilePath())) {
    std::cout << "Unable to initialize OpenMAX library."<< std::endl;
    return false;
  }

  // Load media libraries.
  if (!media::InitializeMediaLibrary(FilePath())) {
    std::cout << "Unable to initialize the media library." << std::endl;
    return false;
  }

  // Create our filter factories.
  scoped_refptr<media::FilterFactoryCollection> factories =
      new media::FilterFactoryCollection();
  factories->AddFactory(media::FileDataSource::CreateFactory());
  factories->AddFactory(media::FFmpegAudioDecoder::CreateFactory());
  factories->AddFactory(media::FFmpegDemuxer::CreateFilterFactory());
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableOpenMax)) {
    factories->AddFactory(media::OmxVideoDecoder::CreateFactory());
  }
  factories->AddFactory(media::FFmpegVideoDecoder::CreateFactory());
  factories->AddFactory(Renderer::CreateFactory(g_display, g_window));

  if (enable_audio) {
    factories->AddFactory(media::AudioRendererImpl::CreateFilterFactory());
  } else {
    factories->AddFactory(media::NullAudioRenderer::CreateFilterFactory());
  }

  // Creates the pipeline and start it.
  *pipeline = new media::PipelineImpl(message_loop);
  (*pipeline)->Start(factories, filename, NULL);

  // Wait until the pipeline is fully initialized.
  while (true) {
    PlatformThread::Sleep(100);
    if ((*pipeline)->IsInitialized())
      break;
    if ((*pipeline)->GetError() != media::PIPELINE_OK) {
      (*pipeline)->Stop(NULL);
      return false;
    }
  }

  // And starts the playback.
  (*pipeline)->SetPlaybackRate(1.0f);
  return true;
}

void TerminateHandler(int signal) {
  g_running = false;
}

void PeriodicalUpdate(
    media::PipelineImpl* pipeline,
    MessageLoop* message_loop,
    bool audio_only) {
  if (!g_running) {
    message_loop->Quit();
    return;
  }

  // Consume all the X events
  while (XPending(g_display)) {
    XEvent e;
    XNextEvent(g_display, &e);
    switch (e.type) {
      case Expose:
        if (!audio_only) {
          // Tell the renderer to paint.
          DCHECK(Renderer::instance());
          Renderer::instance()->Paint();
        }
        break;
      case ButtonPress:
        {
          Window window;
          int x, y;
          unsigned int width, height, border_width, depth;
          XGetGeometry(g_display,
                       g_window,
                       &window,
                       &x,
                       &y,
                       &width,
                       &height,
                       &border_width,
                       &depth);
          base::TimeDelta time = pipeline->GetMediaDuration();
          pipeline->Seek(time*e.xbutton.x/width, NULL);
        }
        break;
      case KeyPress:
        {
          KeySym key = XKeycodeToKeysym(g_display, e.xkey.keycode, 0);
          if (key == XK_Escape) {
            g_running = false;
            // QuitNow is more responsive than Quit since renderer_base is till
            // posting paint messages.
            message_loop->QuitNow();
            return;
          } else if (key == XK_space) {
            if (pipeline->GetPlaybackRate() < 0.01f) // paused
              pipeline->SetPlaybackRate(1.0f);
            else
              pipeline->SetPlaybackRate(0.0f);
          }
        }
        break;
      default:
        break;
    }
  }

  message_loop->PostDelayedTask(FROM_HERE,
      NewRunnableFunction(PeriodicalUpdate, pipeline,
                          message_loop, audio_only), 10);
}

int main(int argc, char** argv) {
  // Read arguments.
  if (argc == 1) {
    std::cout << "Usage: " << argv[0] << " --file=FILE" << std::endl
              << std::endl
              << "Optional arguments:" << std::endl
              << "  [--enable-openmax]"
              << "  [--audio]"
              << "  [--alsa-device=DEVICE]" << std::endl
              << " Press [ESC] to stop" << std::endl
              << " Press [SPACE] to toggle pause/play" << std::endl
              << " Press mouse left button to seek" << std::endl;
    return 1;
  }

  // Read command line.
  CommandLine::Init(argc, argv);
  std::string filename =
      CommandLine::ForCurrentProcess()->GetSwitchValueASCII("file");
  bool enable_audio = CommandLine::ForCurrentProcess()->HasSwitch("audio");
  bool audio_only = false;

  // Install the signal handler.
  signal(SIGTERM, &TerminateHandler);
  signal(SIGINT, &TerminateHandler);

  // Initialize X11.
  if (!InitX11())
    return 1;

  // Initialize the pipeline thread and the pipeline.
  base::AtExitManager at_exit;
  scoped_ptr<base::Thread> thread;
  scoped_refptr<media::PipelineImpl> pipeline;
  MessageLoop message_loop;
  thread.reset(new base::Thread("PipelineThread"));
  thread->Start();
  if (InitPipeline(thread->message_loop(), filename.c_str(),
                   enable_audio, &pipeline)) {
    // Main loop of the application.
    g_running = true;

    // Check if video is present.
    audio_only = !pipeline->IsRendered(media::mime_type::kMajorTypeVideo);

    if (!audio_only) {
      // Tell the renderer to paint.
      DCHECK(Renderer::instance());
      Renderer::instance()->set_glx_thread_message_loop(&message_loop);
    }

    message_loop.PostTask(FROM_HERE,
        NewRunnableFunction(PeriodicalUpdate, pipeline.get(),
                            &message_loop, audio_only));
    message_loop.Run();

    // Need to wait for pipeline to be fully stopped before stopping the thread.
    base::WaitableEvent event(false, false);
    pipeline->Stop(NewCallback(&event, &base::WaitableEvent::Signal));
    event.Wait();
  } else{
    std::cout << "Pipeline initialization failed..." << std::endl;
  }

  // Cleanup tasks.
  thread->Stop();
  XDestroyWindow(g_display, g_window);
  XCloseDisplay(g_display);
  return 0;
}
<|endoftext|>